【发布时间】:2015-08-04 05:54:23
【问题描述】:
我创建了一个安装程序,它将调用两个自定义操作。第一个自定义操作是WRITEFILETODISK,它将根据传递给安装程序的参数创建一个配置文件并将其存储在%ProgramData%\SomeFolder 中。第二个自定义操作是 ResidueRemove,它将清除 %ProgramData%\SomeFolder 上的任何残留物。
以下是我的 Wix 文件中自定义操作的 sn-p:
<Binary Id="SetupCA" SourceFile="..\..\ext_library\SetupCACPP\SetupCACPP\bin\Release\SetupCACPP.dll"/>
<CustomAction Id="WRITEFILETODISK" Execute="immediate" BinaryKey="SetupCA" DllEntry="WriteFileToDisk" />
<CustomAction Id="ResidueRemove" Execute="immediate" BinaryKey="SetupCA" DllEntry="DeleteResidue" />
<InstallExecuteSequence>
<Custom Action="WRITEFILETODISK" After="InstallFinalize">NOT Installed</Custom>
<Custom Action="ResidueRemove" After="InstallFinalize">REMOVE~="ALL"</Custom>
<!-- Rob Menshing Answer @ http://stackoverflow.com/a/321874/2634612 -->
</InstallExecuteSequence>
ResidueRemove 自定义操作已添加到此处:
UINT __stdcall DeleteResidue(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
SC_HANDLE sHandleLPA, sHandleLPAM;
SERVICE_STATUS servStatusLPA, servStatusLPAM;
SHFILEOPSTRUCT shFileCommFolder, shFileInstalledFolder;
LPWSTR lpaCommFolder, lpaInstalledFolder;
std::wstring wlpaCommFolder, wlpaInstalledFolder;
std::string errorCode;
hr = WcaInitialize(hInstall, "DeleteResidue");
ExitOnFailure(hr, "Failed to initialize");
//Stop the LPA and LPA Monitor Service. Then delete the residue.
WcaLog(LOGMSG_STANDARD, "Doing Delete Residue"); // Fails after printing this log
hr = WcaGetProperty(L"LPCOMMAPPFOLDER",&lpaCommFolder);
ExitOnFailure(hr, "Failure in Common Folder"); // Getting the %ProgramData%\SomeFolder here
hr = WcaGetProperty(L"INSTALLFOLDER",&lpaInstalledFolder);
ExitOnFailure(hr, "Failure in getting Installed Folder"); // Getting Installed Folder here
//Path should be double Terminated for SHFILEOPSTRUCT.pFrom
wlpaCommFolder = std::wstring(lpaCommFolder);
wlpaCommFolder.push_back(L'\0');
wlpaInstalledFolder = std::wstring(lpaInstalledFolder);
wlpaInstalledFolder.push_back(L'\0');
try
{
sHandleLPAM = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS);
if(sHandleLPAM == NULL)
{
WcaLog(LOGMSG_STANDARD, "OpenSCManager NULL Handle");
}
sHandleLPAM = OpenService(sHandleLPAM, L"SomeServiceOne",SERVICE_ALL_ACCESS);
if(sHandleLPAM == NULL)
{
WcaLog(LOGMSG_STANDARD, "OpenService NULL Handle");
}
bool res = ControlService(sHandleLPAM, SERVICE_CONTROL_STOP,(LPSERVICE_STATUS) &servStatusLPAM);
if(!res)
{
WcaLog(LOGMSG_STANDARD, "ControlService Cannot Stop the service");
}
// Free the service Handler
CloseServiceHandle(sHandleLPAM);
}
catch(std::exception& e)
{
WcaLog(LOGMSG_STANDARD, e.what());
}
try
{
sHandleLPA = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS);
if(sHandleLPA == NULL)
{
WcaLog(LOGMSG_STANDARD, "OpenSCManager NULL Handle");
}
sHandleLPA = OpenService(sHandleLPA, L"SomeServiceTwo",SERVICE_ALL_ACCESS);
if(sHandleLPA == NULL)
{
WcaLog(LOGMSG_STANDARD, "OpenService NULL Handle");
}
bool res = ControlService(sHandleLPA, SERVICE_CONTROL_STOP,(LPSERVICE_STATUS) &servStatusLPA);
if(!res)
{
WcaLog(LOGMSG_STANDARD, "ControlService Cannot Stop the service");
}
// Free the service Handler
CloseServiceHandle(sHandleLPA);
}
catch(std::exception& e)
{
WcaLog(LOGMSG_STANDARD, e.what());
}
ZeroMemory(&shFileCommFolder, sizeof(SHFILEOPSTRUCT));
shFileCommFolder.hwnd = NULL;
shFileCommFolder.wFunc = FO_DELETE;
shFileCommFolder.pFrom = wlpaCommFolder.c_str();
shFileCommFolder.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI;
BOOL res = DirectoryExists(lpaCommFolder);
if(res)
{
WcaLog(LOGMSG_STANDARD, "The directory exist");
int result = SHFileOperation(&shFileCommFolder);
if(!result)
WcaLog(LOGMSG_STANDARD, "The directory should have deleted by now");
else
{
errorCode = GetLastErrorStdStr();
WcaLog(LOGMSG_STANDARD, "The directory could not be deleted %s", errorCode);
}
}
else
{
WcaLog(LOGMSG_STANDARD, "It Seems the Installed Folder is No more there");
}
ZeroMemory(&shFileInstalledFolder, sizeof(SHFILEOPSTRUCT));
shFileInstalledFolder.hwnd = NULL;
shFileInstalledFolder.wFunc = FO_DELETE;
shFileInstalledFolder.pFrom = wlpaInstalledFolder.c_str();
shFileInstalledFolder.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI;
res = DirectoryExists(lpaInstalledFolder);
if(res)
{
WcaLog(LOGMSG_STANDARD, "The directory exist");
int result = SHFileOperation(&shFileInstalledFolder);
if(!result)
WcaLog(LOGMSG_STANDARD, "The directory should have deleted by now");
else
{
errorCode = GetLastErrorStdStr();
WcaLog(LOGMSG_STANDARD, "The directory could not be deleted %s", errorCode);
}
}
else
{
WcaLog(LOGMSG_STANDARD, "It Seems the Installed Folder is No more there");
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
日志Delete Residue 后出现错误,即:
DeleteResidue: Doing Delete Residue
MSI (s) (64:A4) [10:54:51:643]: Leaked MSIHANDLE (11) of type 790541 for thread 980
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2769 2: ResidueRemove 3: 1
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2205 2: 3: Error
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2769
DEBUG: Error 2769: Custom Action ResidueRemove did not close 1 MSIHANDLEs.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2769. The arguments are: ResidueRemove, 1,
Action ended 10:54:51: ResidueRemove. Return value 3.
Action ended 10:54:51: INSTALL. Return value 3.
在(非英语)德语 Windows 中完成安装程序升级时会复制此内容。经过一些研究,我发现The InstallExecuteSequence may have been authored incorrectly. Actions that change the system must be sequenced between the InstallInitialize and InstallFinalize actions. Perform package validation and check for ICE77. 这是否意味着我的InstallExecuteSequence 是错误的?第一个 CA WriteFileToDisk 应该在安装之后和服务启动之前运行(因为可执行文件依赖于文件)。第二个 CA 应该清除残留物,卸载完成后停止服务。我的代码有什么问题?任何帮助表示赞赏。
【问题讨论】:
标签: windows wix windows-installer wix3.8