问题描述
我有Visual Studio 2017,只是添加了 Microsoft Visual Studio安装程序项目扩展名.我要添加到安装程序中的项目是由 Topshelf 构建的Windows Service应用程序.
I have Visual Studio 2017 and just added the Microsoft Visual Studio Installer Projects extension.The project I want to add to the installer is a Windows Service application build by Topshelf.
要手动安装Windows Service,我以管理员身份打开CMD窗口,并在应用程序文件夹中运行命令(例如bin/debug)以安装Windows Service应用程序.
To manually install the Windows Service, I open a CMD window as admin and run a command in the application folder (eg. bin/debug) to install the Windows Service app.
现在,我确定安装程序没有安装服务的便捷功能,因为它所做的唯一事情(简单而言)是将一些文件放在目标目录中.
Now, I know for sure that the installer has no convenient feature to install the Service, as the only thing it does (in simple terms), is to put some files in the target directory.
安装程序是否可以选择在安装完成后运行某些代码?如果没有,那还有什么选择?
Does the installer have an option to run some code after the installation is complete?If not, what are the alternatives?
推荐答案
您可以将自定义操作"添加到Installer项目的各个阶段.在解决方案资源管理器中右键单击该项目,然后选择:查看->自定义操作".然后,您可以添加一个DLL(已构建-参见下文-并安装在目标PC上)以在过程中的各个位置执行".
You can add "Custom Actions" to various stages of your Installer Project. Right-click on the project in Solution Explorer and select: "View -> Custom Actions." You can then add a DLL (which you have built - see below - and installed on the target PC) to 'execute' at various points in the process.
添加DLL后,您需要指定入口点"过程(例如,下例中的MSR202030202
)和(可选)自定义操作数据"以传递给该过程([TARGETDIR]
-安装目录-在示例代码中).
When you have added a DLL, you then need to specify the "Entry Point" procedure (e.g. MSR202030202
in the example below) and (optionally) the "Custom Action Data" to pass to that procedure ([TARGETDIR]
- the installation directory - in the sample code).
当安装程序到达添加DLL的阶段时,它将使用提供的给定数据调用指定的入口点.
When the installer reaches the stage in which you added the DLL, it will call the stated entry point with the given data available.
这是一个示例DLL过程,该过程将在安装的提交"阶段运行.此示例将比较目标目录中VC-Redistributable的版本与当前安装的所有版本,如果我们的版本较新,则将它们生成"执行.这是相当大的代码块,但希望能为您提供一些所需的见解.
Here is a sample DLL procedure that will run at the "Commit" stage of an installation. This example will compare the version of a VC-Redistributable in the target directory against any currently installed and 'spawn' execution if ours is newer. It's a fairly big chunk of code, but will hopefully give you some insights into what is required.
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;
}
如果您已经知道您要运行的程序,则可能仅可以简化为ShellExecuteEx
部分.
You can probably simplify down to only the ShellExecuteEx
part, if you already know what program(s) you want to run.
习惯于添加和定义此类自定义动作可能是一个艰难的学习曲线,但是大多数事情最终都可以通过毅力(以及足够的止痛药)来实现.
Getting used to adding and defining such Custom Actions can be a steep learning-curve, but most things can ultimately be achieved with perseverance (and a good supply of pain-killers).
这篇关于VS Installer项目-安装后执行一些代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!