VS 安装程序项目 - 安装后执行一些代码

OZ_*_*_CM 3 c# installation windows-services visual-studio topshelf

我有 Visual Studio 2017,并且刚刚添加了Microsoft Visual Studio 安装程序项目扩展。我想要添加到安装程序的项目是由Topshelf构建的 Windows 服务应用程序。

要手动安装 Windows 服务,我以管理员身份打开 CMD 窗口,并在应用程序文件夹(例如 bin/debug)中运行命令来安装 Windows 服务应用程序。

问题

现在,我确信安装程序没有方便的功能来安装服务,因为它唯一做的事情(简单来说)就是将一些文件放入目标目录中。

所以我的问题是

安装程序是否可以选择在安装完成后运行一些代码?如果没有,还有哪些替代方案?

Adr*_*ica 6

您可以将“自定义操作”添加到安装程序项目的各个阶段。在解决方案资源管理器中右键单击该项目,然后选择:“查看 -> 自定义操作”。然后,您可以添加一个 DLL(您已构建 - 见下文 - 并安装在目标 PC 上)以在该过程中的各个点“执行”。

添加 DLL 后,您需要指定“入口点”过程(例如MSR202030202在下面的示例中)和(可选)要传递给该过程的“自定义操作数据”([TARGETDIR]- 安装目录 - 在示例代码中)。

当安装程序到达您添加 DLL 的阶段时,它将使用给定的可用数据调用指定的入口点。

下面是一个示例 DLL 过程,它将在安装的“提交”阶段运行。此示例将目标目录中 VC-Redistributable 的版本与当前安装的任何版本进行比较,如果我们的版本较新,则“生成”执行。这是相当大的代码块,但希望能让您深入了解所需内容。

extern "C" uint32_t __declspec(dllexport) __stdcall MSR202030202(MSIHANDLE hInst)
{
    // First (default) action: Get the location of the installation (the destination folder) into "dstPath" variable...
    DWORD dstSize = MAX_PATH; wchar_t dstPath[MAX_PATH]; MsiGetProperty(hInst, L"CustomActionData", dstPath, &dstSize);
    CString regPath = L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\RunTimes\\";
    #if defined(_M_X64)
    wchar_t vcrFile[] = L"vc_redist.x64.exe";   regPath += L"x64";
    #elif defined(_M_IX86)
    wchar_t vcrFile[] = L"vc_redist.x86.exe";   regPath += L"x86";
    #endif
    CString vcrPath = CString(dstPath) + vcrFile;
    // Get the version numbers of the VC Runtime Redistributable currently installed (from the registry) ...
    WORD rVer[4] = { 0, 0, 0, 0 };
    HKEY hKey;  DWORD gotSize, gotType, gotData;
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        gotSize = sizeof(DWORD);
        if (RegQueryValueEx(hKey, L"Major", nullptr, &gotType, reinterpret_cast<BYTE *>(&gotData), &gotSize) == 0) {
            if ((gotType == REG_DWORD) && (gotSize == sizeof(DWORD))) rVer[0] = LOWORD(gotData);
        }
        gotSize = sizeof(DWORD);
        if (RegQueryValueEx(hKey, L"Minor", nullptr, &gotType, reinterpret_cast<BYTE *>(&gotData), &gotSize) == 0) {
            if ((gotType == REG_DWORD) && (gotSize == sizeof(DWORD))) rVer[1] = LOWORD(gotData);
        }
        gotSize = sizeof(DWORD);
        if (RegQueryValueEx(hKey, L"Bld", nullptr, &gotType, reinterpret_cast<BYTE *>(&gotData), &gotSize) == 0) {
            if ((gotType == REG_DWORD) && (gotSize == sizeof(DWORD))) rVer[2] = LOWORD(gotData);
        }
        gotSize = sizeof(DWORD);
        if (RegQueryValueEx(hKey, L"Rbld", nullptr, &gotType, reinterpret_cast<BYTE *>(&gotData), &gotSize) == 0) {
            if ((gotType == REG_DWORD) && (gotSize == sizeof(DWORD))) rVer[3] = LOWORD(gotData);
        }
        RegCloseKey(hKey);
    }
    // Get the version numbers of the VC Redistributable provided with this installation (from the file) ...
    WORD fVer[4] = { 0, 0, 0, 0 };
    if (_waccess(vcrPath, 0) == 0) {
        DWORD vHand, vSize = GetFileVersionInfoSize(vcrPath.GetString(), &vHand);
        VS_FIXEDFILEINFO *vInfo = nullptr;  unsigned vSiz2 = 0u;
        if (vSize > 0) {
            BYTE* vBuff = new BYTE[vSize]; vHand = 0;
            BOOL vStat = GetFileVersionInfo(vcrPath.GetString(), vHand, vSize, vBuff);
            if (vStat) vStat = VerQueryValue(vBuff, L"\\", reinterpret_cast<void **>(&vInfo), &vSiz2);
            if (vStat && (vInfo != nullptr)) {
                DWORD msdw = vInfo->dwFileVersionMS, lsdw = vInfo->dwFileVersionLS;
                fVer[0] = HIWORD(msdw);
                fVer[1] = LOWORD(msdw);
                fVer[2] = HIWORD(lsdw);
                fVer[3] = LOWORD(lsdw);
            }
            delete[] vBuff;
        }
    }
    // Compare the two sets of version numbers to see if we need to run the installer ...
    bool hasVCR = false;
    if (rVer[0] > fVer[0]) hasVCR = true;
    else if (rVer[0] == fVer[0]) {
        if (rVer[1] > fVer[1]) hasVCR = true;
        else if (rVer[1] == fVer[1]) {
            if (rVer[2] >= fVer[2]) hasVCR = true;
            // Don't bother checking the last ('rebuild') version!
        }
    }
    if (!hasVCR) { // Set up a ShellExecute command to run the installer when this program exits ...
        CString params = L"/q /norestart";
        SHELLEXECUTEINFO shexInfo;  memset(&shexInfo, 0, sizeof(SHELLEXECUTEINFO));
        shexInfo.cbSize = sizeof(SHELLEXECUTEINFO);
        shexInfo.fMask = SEE_MASK_DEFAULT;
        shexInfo.lpFile = vcrPath.GetString();
        shexInfo.lpParameters = params.GetString();
        ShellExecuteEx(&shexInfo);
    }
    return ERROR_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

ShellExecuteEx如果您已经知道要运行什么程序,您可能可以简化为仅部分。

习惯添加和定义此类自定义操作可能是一个陡峭的学习曲线,但大多数事情最终都可以通过坚持不懈(以及大量止痛药)来实现。