作為開發者,將軟件打包為 MSI 安裝包不僅顯得專業,還能實現註冊表寫入、桌面快捷方式創建、組件註冊等標準安裝行為。本文記錄使用 Windows Installer API (MSI.lib) 從零創建一個基本 MSI 數據庫文件的完整過程,並提供可編譯運行的 VC++ 示例代碼。
使用 MsiOpenDatabase 配合 MSIDBOPEN_CREATEDIRECT 標誌創建新的 MSI 文件。若文件已存在則直接打開。錯誤處理通過 MsiGetLastErrorRecord 獲取錯誤記錄,再用 FormatMessage 轉換為可讀字符串。
MsiDatabaseCommit 將所有更改持久化到磁盤,MsiCloseHandle 釋放資源。每一步都需檢查返回值是否為 ERROR_SUCCESS,失敗時彈出錯誤對話框並退出。
#pragma once
#include <tchar.h>
#include <Windows.h>
#pragma comment(lib, "msi.lib")
#include <Msi.h>
#include <MsiQuery.h>
INT APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, INT) {
MSIHANDLE hDatabase = NULL;
UINT result = MsiOpenDatabase(_T("Setup.msi"), MSIDBOPEN_CREATEDIRECT, &hDatabase);
if (result != ERROR_SUCCESS) {
ShowMsiError(result);
return -1;
}
result = MsiDatabaseCommit(hDatabase);
if (result != ERROR_SUCCESS) {
ShowMsiError(result);
MsiCloseHandle(hDatabase);
return -1;
}
result = MsiCloseHandle(hDatabase);
if (result != ERROR_SUCCESS) {
ShowMsiError(result);
return -1;
}
return 0;
}
void ShowMsiError(UINT errorCode) {
MSIHANDLE hRec = MsiGetLastErrorRecord();
LPVOID pMsg = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
(LPTSTR)&pMsg, 0, NULL);
MessageBoxEx(NULL, (LPTSTR)pMsg, _T("MSI Error"), MB_OK,
MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT));
LocalFree(pMsg);
}
創建空數據庫只是第一步。真正的安裝包需要填充以下核心表:
可使用 MsiViewExecute 執行 SQL 插入語句填充表,或使用 ORCA 工具手動編輯驗證結構。建議從簡單的 "Hello World" 開始,逐步添加功能。
msival2 或 ORCA 的「驗證」功能可檢測 ICE 錯誤/l*v install.log 參數生成詳細日誌本文實現了 MSI 文件的創建、提交、關閉最小閉環。後續工作包括:表結構設計、文件壓縮嵌入(CAB)、用戶界面定制、升級補丁支持等。MSI 數據庫關系複雜,建議參考《Windows Installer SDK》文檔,結合 ORCA 逆向分析成熟安裝包(如 Chrome、VS Code)的結構,漸進式掌握。