📜  c++ 编译到 msi - C++ (1)

📅  最后修改于: 2023-12-03 15:14:01.589000             🧑  作者: Mango

编译C++程序到MSI文件

在Windows下,MSI(Microsoft Installer)是一种常见的程序安装方式。因此,如果你想将你的C++程序分发给其他人,并让他们能够轻松安装,那么将你的程序编译成MSI文件是一个不错的选择。

本文将介绍如何使用WiX工具链将C++程序编译为MSI文件,并在过程中介绍一些相关概念和技巧。

WiX

WiX是一组用于创建Windows安装程序的工具。它使用XML描述安装程序,可以比较方便地定制和扩展。在本文中,我们将使用WiX Toolset v3.11。

安装WiX

WiX可以从其官网下载,也可以使用Chocolatey包管理器安装:

choco install wixtoolset
创建WiX项目

首先,让我们创建一个WiX项目。打开Visual Studio并选择File->New->Project。在弹出的对话框中,找到WiX类别并选择WiX Toolset Empty Project。根据需要更改项目名称和位置,并点击“创建”。

create wix project

接下来,让我们将我们的C++程序添加到WiX项目中。右键单击项目文件夹,并选择Add->Existing Item。选择你的C++项目中的可执行文件,并点击“添加”。

add executable

为了能够在安装期间启动我们的程序,我们还需要将程序的快捷方式添加到开始菜单。右键单击项目文件夹,并选择Add->New Item。在弹出的对话框中,选择WiX Toolset->Shortcut,并将新文件命名为StartMenuShortcut.wxs(或任何你喜欢的名称)。然后,将以下代码复制到新文件中:

<!-- StartMenuShortcut.wxs -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <DirectoryRef Id="ProgramMenuFolder">
      <Component Id="StartMenuShortcutComponent" Guid="{YOUR_GUID}">
        <Shortcut Id="StartMenuShortcut" Name="My Program" Target="[INSTALLFOLDER]MyProgram.exe" />
        <RemoveFolder Id="ProgramMenuFolder" On="uninstall" />
        <RegistryValue Root="HKCU" Key="Software\My Company\My Program" Name="installed" Type="integer" Value="1" KeyPath="yes" />
      </Component>
    </DirectoryRef>
  </Fragment>
</Wix>

注意将{YOUR_GUID}替换为自己的GUID。

生成WiX项目

现在我们已经创建了WiX项目,并将我们的可执行文件和快捷方式添加到了其中。为了将其编译为MSI文件,我们需要执行以下步骤:

  1. 在项目文件夹中添加Product.wxs文件,通过其中的<Directory>元素指定安装位置和菜单条目等细节
  2. 在项目文件夹中添加CustomActions.cpp文件,其中包含自定义操作的定义
  3. 更改main.cpp文件以在安装期间启动自定义操作

我们逐一完成这些步骤。

添加Product.wxs文件

右键单击项目文件夹,并选择Add->New Item。选择WiX Toolset->Product,并将新文件命名为Product.wxs。然后,将以下代码添加到新文件中:

<!-- Product.wxs -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?define ProductName="My Program"?>
  <?define ProductVersion="1.0.0"?>
  <?define Manufacturer="My Company"?>
  <?define UpgradeCode="{YOUR_UPGRADECODE_HERE}"?>

  <Product Id="*" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
    <!-- 定义组件 -->
    <Feature Id="ProductFeature" Title="$(var.ProductName)" Level="1">
      <ComponentRef Id="StartMenuShortcutComponent" />
      <ComponentGroupRef Id="MyProgramComponents" />
    </Feature>

    <!-- 定义安装文件夹 -->
    <InstallExecuteSequence>
      <Custom Action="SetFolderProperties" After="CostFinalize" />
      <RemoveExistingProducts Before="InstallInitialize" />
      <InstallInitialize />
      <InstallFinalize />
    </InstallExecuteSequence>

    <!-- 设置安装文件夹 -->
    <Property Id="APPLICATIONFOLDER" Value="My Program" />
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
          <ComponentGroupRef Id="MyProgramComponents" />
        </Directory>
      </Directory>
      <!-- 添加开始菜单条目 -->
      <Directory Id="ProgramMenuFolder" Name="Programs">
        <Directory Id="MyProgramMenuFolder" Name="$(var.ProductName)">
          <ComponentRef Id="StartMenuShortcutComponent" />
        </Directory>
      </Directory>
    </Directory>

    <!-- 设置升级代码 -->
    <Upgrade Id="$(var.UpgradeCode)">
      <UpgradeVersion Minimum="$(var.ProductVersion)" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADEFOUND" />
    </Upgrade>
    <CustomAction Id="SetFolderProperties" Property="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER" />

    <!-- 设置UI -->
    <UIRef Id="WixUI_InstallDir" />
  </Product>
</Wix>

其中,<Feature>元素定义安装包中的组件,<InstallExecuteSequence>元素定义执行安装时需要执行的操作,<Property><Directory>元素定义安装文件夹和开始菜单条目,<Upgrade>元素定义升级行为。更多细节,请参考WiX文档

添加CustomActions.cpp文件

将以下代码复制并粘贴到CustomActions.cpp文件中:

#include <windows.h>
#include <msi.h>
#pragma comment(lib, "msi.lib")

UINT __stdcall MyCustomAction(MSIHANDLE hInstall)
{
    MessageBox(NULL, L"Hello from custom action!", L"My Program", MB_OK);
    return ERROR_SUCCESS;
}

extern "C" __declspec(dllexport) UINT __stdcall LaunchMyProgram(MSIHANDLE hInstall)
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    WCHAR szPath[MAX_PATH];
    DWORD dwPathSize = MAX_PATH;
    MsiGetTargetPathW(hInstall, L"MyProgramFolder", szPath, &dwPathSize);

    wcscat_s(szPath, L"\\MyProgram.exe");
    if (CreateProcessW(NULL, szPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        return ERROR_SUCCESS;
    }
    return GetLastError();
}

这些代码定义了两个自定义操作,一个弹出气泡提示框,另一个启动我们的C++程序。

修改main.cpp文件

我们需要更改我们的C++程序以便在安装期间启动。在int main()方法的第一行添加以下代码:

#include <Windows.h>

int main()
{
    MessageBox(NULL, L"Hello from My Program!", L"My Program", MB_OK);
    return 0;
}

修改完成后,将我们的程序添加到WiX项目中(与之前的方法类似)。同时,添加以下代码以在安装期间启动程序:

<!-- Product.wxs -->
<CustomAction Id="LaunchMyProgram" Directory="MyProgramFolder" ExeCommand="LaunchMyProgram" />
<InstallExecuteSequence>
  <Custom Action="MyCustomAction" Before="InstallFinalize" />
  <Custom Action="LaunchMyProgram" After="MyCustomAction" />
  <RemoveExistingProducts Before="InstallInitialize" />
  <InstallInitialize />
  <InstallFinalize />
</InstallExecuteSequence>

其中,<CustomAction>元素定义自定义操作,<InstallExecuteSequence>元素定义执行顺序。

生成MSI文件

一旦我们完成了所有步骤,就可以生成我们的MSI文件了。在Visual Studio中,选择Build->Build MyProgram.msi(或你的项目名称)。

build msi

结论

通过使用WiX工具集,我们可以比较容易地将C++程序编译为MSI文件,从而实现简便的分发和安装方式。在本文中,我们了解了WiX的基本概念和使用方法,并介绍了一些常见的技巧和细节。希望本文能对你有所帮助!