/******************************************************************** MessageQueuingRollbackUninstall - CUSTOM ACTION ENTRY POINT Input: deferred CustomActionData - MessageQueuingRollbackUninstall ********************************************************************/ extern "C" UINT __stdcall MessageQueuingRollbackUninstall(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; // initialize hr = WcaInitialize(hInstall, "MessageQueuingRollbackUninstall"); ExitOnFailure(hr, "Failed to initialize MessageQueuingRollbackUninstall"); hr = MqiInitialize(); ExitOnFailure(hr, "Failed to initialize"); // get custom action data hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "Failed to get CustomActionData"); // delete message queues hr = MqiRollbackDeleteMessageQueues(&pwzData); ExitOnFailure(hr, "Failed to delete message queues"); // remove message queue permissions hr = MqiRollbackRemoveMessageQueuePermissions(&pwzData); ExitOnFailure(hr, "Failed to remove message queue permissions"); hr = S_OK; LExit: // clean up ReleaseStr(pwzData); // uninitialize MqiUninitialize(); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
UINT __stdcall SanitizeDwordFromRegistry(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "SanitizeDword"); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); /// wchar_t cPropertyName[MAX_PATH]; wchar_t cPropertyValue[MAX_PATH]; DWORD dwMaxLen = MAX_PATH; // TODO: Support multiple properties (separated by comma) MsiGetProperty (hInstall, L"SANITIZE_DWORD", cPropertyName, &dwMaxLen); MsiGetProperty (hInstall, cPropertyName, cPropertyValue, &dwMaxLen); if (cPropertyValue[0] == '#') { WcaLog(LOGMSG_STANDARD, "Property %s needs sanitation...", cPropertyName); for (unsigned int i = 1; i < dwMaxLen; i++) { cPropertyValue[i-1] = cPropertyValue[i]; cPropertyValue[i] = NULL; } WcaLog(LOGMSG_STANDARD, "Sanitation done."); } MsiSetProperty (hInstall, cPropertyName, cPropertyValue); /// LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
UINT __stdcall DetectJuurSK(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "DetectJuurSK"); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); if (findJuurSK()) { WcaLog(LOGMSG_STANDARD, "Juur-SK certificate already installed."); hr = WcaSetIntProperty(L"JUURCERTIFICATEINSTALLED", 1); ExitOnFailure(hr, "Failed to set JUURCERTIFICATEINSTALLED property value"); } else { WcaLog(LOGMSG_STANDARD, "Juur-SK certificate is not installed."); } LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
UINT WINAPI RemoveSmartCardConfiguration(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "RemoveSmartCardConfiguration"); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); /* clean a smart card database. As today the x64 setup doesn't handle x86 installation on x64 machine */ RemoveKey(SC_DATABASE); /* when this happens, just uncomment the following line: #ifdef _M_X64 RemoveKey(X86onX64_SC_DATABASE); #endif */ /* never fails or only if the msi uninstall didn't work. If the uninstall custom action trigger a failure, the user is unable to uninstall the software */ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
UINT wrap(MSIHANDLE hInstall, char *name, int check_only) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, name); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); TCHAR INSTALLDIR[1024]; DWORD INSTALLDIR_size = sizeof(INSTALLDIR); if(MsiGetPropertyW(hInstall, TEXT("CustomActionData"), INSTALLDIR, &INSTALLDIR_size) == ERROR_SUCCESS) { int rc = remove_service(INSTALLDIR, check_only); if(rc < 0) { er = ERROR_CANCELLED; } } else { er = ERROR_CANT_ACCESS_FILE; } LExit: return WcaFinalize(er); }
/******************************************************************** WixQueryDirectXCaps - entry point for WixQueryDirectXCaps CA Called as Type 1 custom action (DLL from the Binary table) from Windows Installer to set properties that identify the DirectX capabilities ("caps") of the system. ********************************************************************/ extern "C" UINT __stdcall WixQueryDirectXCaps( __in MSIHANDLE hInstall ) { #if 0 ::MessageBoxA(0, "break into debugger now please", "---->> ATTACH TO ME!", MB_ICONEXCLAMATION); #endif HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPDIRECT3D9 pD3D = NULL; hr = WcaInitialize(hInstall, "WixQueryDirectXCaps"); ExitOnFailure(hr, "failed to initialize"); pD3D = Direct3DCreate9(D3D_SDK_VERSION); ExitOnNull(pD3D, hr, E_FAIL, "Direct3DCreate9 failed"); D3DCAPS9 d3dCaps; hr = pD3D->GetDeviceCaps( 0, // first adapter D3DDEVTYPE_HAL, // fail on non-HAL devices &d3dCaps ); ExitOnFailure(hr, "GetDeviceCaps call failed"); int iVertexShaderVersion = D3DSHADER_VERSION_MAJOR(d3dCaps.VertexShaderVersion) * 100 + D3DSHADER_VERSION_MINOR(d3dCaps.VertexShaderVersion); WcaSetIntProperty(L"WIX_DIRECTX_VERTEXSHADERVERSION", iVertexShaderVersion); int iPixelShaderVersion = D3DSHADER_VERSION_MAJOR(d3dCaps.PixelShaderVersion) * 100 + D3DSHADER_VERSION_MINOR(d3dCaps.PixelShaderVersion); WcaSetIntProperty(L"WIX_DIRECTX_PIXELSHADERVERSION", iPixelShaderVersion); LExit: ReleaseObject(pD3D); return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); }
/****************************************************************** CaExecServiceConfig - entry point for ServiceConfig Custom Action called as Type 1025 CustomAction (deferred binary DLL) NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t... *******************************************************************/ extern "C" UINT __stdcall ExecServiceConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecServiceConfig"); HRESULT hr = S_OK; UINT uiResult = ERROR_SUCCESS; DWORD dwError = 0; LPVOID lpMsgBuf = NULL; LPWSTR pwzData = NULL; LPWSTR pwz = NULL; LPWSTR pwzServiceName = NULL; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; DWORD dwOpenServiceAccess = SERVICE_CHANGE_CONFIG; // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2() SERVICE_FAILURE_ACTIONSW sfa; SC_ACTION actions[3]; BOOL fResult = FALSE; // initialize hr = WcaInitialize(hInstall, "ExecServiceConfig"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzData); pwz = pwzData; // loop through all the passed in data while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Configuring Service: %S", pwzServiceName); // build up SC_ACTION array // TODO: why is delay only respected when SC_ACTION_RESTART is requested? actions[0].Type = GetSCActionType(pwzFirstFailureActionType); actions[0].Delay = 0; if (SC_ACTION_RESTART == actions[0].Type) { actions[0].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } actions[1].Type = GetSCActionType(pwzSecondFailureActionType); actions[1].Delay = 0; if (SC_ACTION_RESTART == actions[1].Type) { actions[1].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } actions[2].Type = GetSCActionType(pwzThirdFailureActionType); actions[2].Delay = 0; if (SC_ACTION_RESTART == actions[2].Type) { actions[2].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } // build up the SERVICE_FAILURE_ACTIONSW struct sfa.dwResetPeriod = dwResetPeriodInDays * 24 * 60 * 60; // days to seconds sfa.lpRebootMsg = pwzRebootMessage; sfa.lpCommand = pwzProgramCommandLine; sfa.cActions = 3; // the UI always shows 3 actions, so we'll always do 3 sfa.lpsaActions = actions; // Get a handle to the service control manager (if we don't already have) if (NULL == hSCM) { hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (hSCM == NULL) { dwError = ::GetLastError(); ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); ExitOnFailure1(hr = HRESULT_FROM_WIN32(dwError), "failed to get handle to SCM. Error: %S", (LPWSTR)lpMsgBuf); } } hService = ::OpenServiceW(hSCM, pwzServiceName, dwOpenServiceAccess); if (hService == NULL) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (dwError == ERROR_SERVICE_DOES_NOT_EXIST) { ExitOnFailure1(hr, "Service \"%S\" does not exist on this system.", pwzServiceName); } else { ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); ExitOnFailure2(hr, "Failed to get handle to the service \"%S\". Error: %S", pwzServiceName, (LPWSTR)lpMsgBuf); } } // Call ChangeServiceConfig2 to actually set up the failure actions fResult = ChangeServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&sfa); if (fResult == FALSE) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); // check if this is a service that can't be modified if(dwError == ERROR_CANNOT_DETECT_PROCESS_ABORT) { WcaLog(LOGMSG_STANDARD, "WARNING: Service \"%S\" is not configurable on this server and will not be set.", pwzServiceName); } ExitOnFailure1(hr, "Cannot change service configuration. Error: %S", (LPWSTR)lpMsgBuf); } // Per-service cleanup dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); } LExit: // Clean up handles ReleaseStr(pwzServiceName); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzData); if (lpMsgBuf) // Allocated with FormatString ::LocalFree(lpMsgBuf); if (hService) ::CloseServiceHandle(hService); if (hSCM) ::CloseServiceHandle(hSCM); if (FAILED(hr)) uiResult = ERROR_INSTALL_FAILURE; return WcaFinalize(uiResult); }
/******************************************************************** WixRegisterRestartResources - Immediate CA to register resources with RM. Enumerates components before InstallValidate and registers resources to be restarted by Restart Manager if the component action is anything other than None. Do not disable file system redirection. ********************************************************************/ extern "C" UINT __stdcall WixRegisterRestartResources( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; LPWSTR wzSessionKey = NULL; size_t cchSessionKey = 0; PRMU_SESSION pSession = NULL; LPWSTR wzRestartResource = NULL; LPWSTR wzComponent = NULL; LPWSTR wzResource = NULL; int iAttributes = NULL; BOOL fIsComponentNull = FALSE; WCA_TODO todo = WCA_TODO_UNKNOWN; int iType = etInvalid; hr = WcaInitialize(hInstall, "WixRegisterRestartResources"); ExitOnFailure(hr, "Failed to initialize."); // Skip if the table doesn't exist. if (S_OK != WcaTableExists(L"WixRestartResource")) { WcaLog(LOGMSG_STANDARD, "The RestartResource table does not exist; there are no resources to register with Restart Manager."); ExitFunction(); } // Get the existing Restart Manager session if available. hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey); ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property."); hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey); ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length."); // Skip if the property doesn't exist. if (0 == cchSessionKey) { WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join."); ExitFunction(); } // Join the existing Restart Manager session if supported. hr = RmuJoinSession(&pSession, wzSessionKey); if (E_MODNOTFOUND == hr) { WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping."); ExitFunction1(hr = S_OK); } else if (FAILED(hr)) { WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey); ExitFunction1(hr = S_OK); } // Loop through each record in the table. hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView); ExitOnFailure(hr, "Failed to open a view on the RestartResource table."); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource); ExitOnFailure(hr, "Failed to get the RestartResource field value."); hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent); ExitOnFailure(hr, "Failed to get the Component_ field value."); hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource); ExitOnFailure(hr, "Failed to get the Resource formatted field value."); hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes); ExitOnFailure(hr, "Failed to get the Attributes field value."); fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent); todo = WcaGetComponentToDo(wzComponent); // Only register resources for components that are null, or being installed, reinstalled, or uninstalled. if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo) { WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource); continue; } // Get the type from Attributes and add to the Restart Manager. iType = iAttributes & etTypeMask; switch (iType) { case etFilename: WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource); hr = RmuAddFile(pSession, wzResource); ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session."); break; case etApplication: WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource); hr = RmuAddProcessesByName(pSession, wzResource); ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session."); break; case etServiceName: WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource); hr = RmuAddService(pSession, wzResource); ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session."); break; default: WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource); break; } } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed while looping through all rows to register resources."); // Register the resources and unjoin the session. hr = RmuEndSession(pSession); if (FAILED(hr)) { WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager."); ExitFunction1(hr = S_OK); } LExit: ReleaseStr(wzRestartResource); ReleaseStr(wzComponent); ReleaseStr(wzResource); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
/****************************************************************** CaSchedSecureObjects - entry point for CaReadSecureObjects Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaSecureObjects ******************************************************************/ extern "C" UINT __stdcall SchedSecureObjects( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedSecureObjects"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzTargetPath = NULL; LPWSTR pwzFormattedString = NULL; int iRoot = 0; int iAllUsers = 0; LPWSTR pwzKey = NULL; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; MSIHANDLE hViewObject = NULL; // Don't free this since it's always a copy of either hViewService or hViewCreateFolder PMSIHANDLE hViewService = NULL; PMSIHANDLE hViewCreateFolder = NULL; PMSIHANDLE hViewFile = NULL; PMSIHANDLE hViewRegistry = NULL; PMSIHANDLE hRecObject = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; LPWSTR pwzCustomActionData = NULL; DWORD cchCustomActionData = 0; DWORD cObjects = 0; eOBJECTTYPE eType = OT_UNKNOWN; // // initialize // hr = WcaInitialize(hInstall, "SchedSecureObjects"); ExitOnFailure(hr, "failed to initialize"); // // loop through all the objects to be secured // hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView); ExitOnFailure(hr, "failed to open view on SecureObjects table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hViewObject = NULL; eType = OT_UNKNOWN; hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable); ExitOnFailure(hr, "failed to get object table"); // ensure we're looking at a known table if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) { eType = OT_SERVICE; } else if (0 == lstrcmpW(L"CreateFolder", pwzTable)) { eType = OT_FOLDER; } else if (0 == lstrcmpW(L"File", pwzTable)) { eType = OT_FILE; } else if (0 == lstrcmpW(L"Registry", pwzTable)) { eType = OT_REGISTRY; } else { ExitOnFailure1(hr = E_INVALIDARG, "unknown SecureObject.Table: %S", pwzTable); } // if we haven't opened a view on the ServiceInstall/CreateFolder table, do that now if (OT_SERVICE == eType) { if (!hViewService) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_SERVICECOMPONENT, &hViewService); ExitOnFailure(hr, "failed to open view on ServiceInstall table"); } hViewObject = hViewService; } else if (OT_FOLDER == eType) { if (!hViewCreateFolder) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_CREATEFOLDERCOMPONENT, &hViewCreateFolder); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewCreateFolder; } else if (OT_FILE== eType) { if (!hViewFile) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_FILECOMPONENT, &hViewFile); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewFile; } else if (OT_REGISTRY== eType) { if (!hViewRegistry) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_REGISTRYCOMPONENT, &hViewRegistry); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewRegistry; } Assert(hViewObject); // execute a view looking for the object's Component_ hr = WcaExecuteView(hViewObject, hRec); ExitOnFailure1(hr, "failed to execute view on %S table", pwzData); hr = WcaFetchSingleRecord(hViewObject, &hRecObject); ExitOnFailure(hr, "failed to fetch Component for secure object"); hr = WcaGetRecordString(hRecObject, QSOC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get Component name for secure object"); // // if we are installing this Component // er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %S", pwzData); if (WcaIsInstalling(isInstalled, isAction)) { // add the data to the CustomActionData hr = WcaGetRecordString(hRecObject, QSOC_OBJECTNAME, &pwzData); ExitOnFailure(hr, "failed to get name of object"); if (OT_SERVICE == eType) { hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FOLDER == eType) { hr = WcaGetTargetPath(pwzData, &pwzTargetPath); ExitOnFailure1(hr, "failed to get target path for directory id: %S", pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FILE == eType) { hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzData); ExitOnFailure1(hr, "failed to create formatted string for securing file object: %S", pwzData); hr = WcaGetFormattedString(pwzFormattedString, &pwzTargetPath); ExitOnFailure2(hr, "failed to get file path from formatted string: %S for secure object: %S", pwzFormattedString, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_REGISTRY == eType) { hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot); ExitOnFailure1(hr, "Failed to get reg key root for secure object: %S", pwzData); hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey); ExitOnFailure1(hr, "Failed to get reg key for secure object: %S", pwzData); // Decode the root value if (-1 == iRoot) { // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers); ExitOnFailure(hr, "failed to get value of ALLUSERS property"); if (1 == iAllUsers) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } } else if (/*msidbRegistryRootClassesRoot*/ 0 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CLASSES_ROOT\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCR root"); } else if (/*msidbRegistryRootCurrentUser*/ 1 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } else if (/*msidbRegistryRootLocalMachine*/ 2 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else if (/*msidbRegistryRootUsers*/ 3 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"USERS\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKU root"); } else { ExitOnFailure2(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%S' root: %d", pwzData, iRoot); } hr = StrAllocConcat(&pwzTargetPath, pwzKey, 0); ExitOnFailure2(hr, "Failed to concat key: %S for secure object: %S", pwzKey, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else { AssertSz(FALSE, "How did you get here?"); } hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData); ExitOnFailure(hr, "failed to get user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cObjects++; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // // schedule the custom action and add to progress bar // if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cObjects); hr = WcaDoDeferredAction(L"ExecSecureObjects", pwzCustomActionData, cObjects * COST_SECUREOBJECT); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzTable); ReleaseStr(pwzTargetPath); ReleaseStr(pwzFormattedString); ReleaseStr(pwzKey); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** CaExecSecureObjects - entry point for SecureObjects Custom Action called as Type 1025 CustomAction (deferred binary DLL) NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwPermissions\twzObject\t... ******************************************************************/ extern "C" UINT __stdcall ExecSecureObjects( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecSecureObjects"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR pwz = NULL; LPWSTR pwzData = NULL; LPWSTR pwzObject = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzDomain = NULL; DWORD dwRevision = 0; LPWSTR pwzUser = NULL; DWORD dwPermissions = 0; LPWSTR pwzAccount = NULL; PSID psid = NULL; EXPLICIT_ACCESSW ea = {0}; SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; PSECURITY_DESCRIPTOR psd = NULL; SECURITY_DESCRIPTOR_CONTROL sdc = {0}; SECURITY_INFORMATION si = {0}; PACL pAclExisting = NULL; // doesn't get freed PACL pAclNew = NULL; PMSIHANDLE hActionRec = ::MsiCreateRecord(1); // // initialize // hr = WcaInitialize(hInstall, "ExecSecureObjects"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzData); pwz = pwzData; // // loop through all the passed in data // while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzObject); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzTable); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzDomain); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzUser); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions)); ExitOnFailure(hr, "failed to processCustomActionData"); WcaLog(LOGMSG_VERBOSE, "Securing Object: %S Type: %S User: %S", pwzObject, pwzTable, pwzUser); // // create the appropriate SID // // figure out the right user to put into the access block if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone")) { hr = AclGetWellKnownSid(WinWorldSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators")) { hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem")) { hr = AclGetWellKnownSid(WinLocalSystemSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService")) { hr = AclGetWellKnownSid(WinLocalServiceSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService")) { hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser")) { hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests")) { hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid); } else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER")) { hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE")) { hr = AclGetWellKnownSid(WinInteractiveSid, &psid); } else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users")) { hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid); } else { hr = StrAllocFormatted(&pwzAccount, L"%s\\%s", *pwzDomain ? pwzDomain : L".", pwzUser); ExitOnFailure(hr, "failed to build domain user name"); hr = AclGetAccountSid(NULL, pwzAccount, &psid); } ExitOnFailure3(hr, "failed to get sid for account: %S%S%S", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser); // // build up the explicit access // ea.grfAccessPermissions = dwPermissions; ea.grfAccessMode = SET_ACCESS; if (0 == lstrcmpW(L"CreateFolder", pwzTable)) { ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; } else { ea.grfInheritance = NO_INHERITANCE; } ::BuildTrusteeWithSidW(&ea.Trustee, psid); if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) { objectType = SE_SERVICE; // always add these permissions for services // these are basic permissions that are often forgotten dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE; } else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable)) { objectType = SE_FILE_OBJECT; } else if (0 == lstrcmpW(L"Registry", pwzTable)) { objectType = SE_REGISTRY_KEY; } if (SE_UNKNOWN_OBJECT_TYPE != objectType) { er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %S", pwzObject); //Need to see if DACL is protected so getting Descriptor information if(!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) { ExitOnLastError1(hr, "failed to get security descriptor control for object: %S", pwzObject); } er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %S", pwzObject); if (sdc & SE_DACL_PROTECTED) { si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; } else { si = DACL_SECURITY_INFORMATION; } er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL); MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %S", pwzObject); } else { MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %S", pwzTable); } hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE); ExitOnFailure(hr, "failed to send progress message"); objectType = SE_UNKNOWN_OBJECT_TYPE; } LExit: ReleaseStr(pwzUser); ReleaseStr(pwzDomain); ReleaseStr(pwzTable); ReleaseStr(pwzObject); ReleaseStr(pwzData); ReleaseStr(pwzAccount); if (pAclNew) ::LocalFree(pAclNew); if (psd) ::LocalFree(psd); if (psid) AclFreeSid(psid); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** RollbackServiceConfig - entry point for ServiceConfig rollback Custom Action. NOTE: CustomActionScript Data == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t... *******************************************************************/ extern "C" UINT __stdcall RollbackServiceConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug RollbackServiceConfig"); HRESULT hr = S_OK; DWORD er = 0; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzScriptKey = NULL; WCA_CASCRIPT_HANDLE hRollbackScript = NULL; LPWSTR pwzServiceName = NULL; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; LPVOID lpMsgBuf = NULL; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; // initialize hr = WcaInitialize(hInstall, "RollbackServiceConfig"); ExitOnFailure(hr, "Failed to initialize 'RollbackServiceConfig'."); // Open the Services Control Manager up front. hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (NULL == hSCM) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); #pragma prefast(push) #pragma prefast(disable:25028) ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); #pragma prefast(pop) ExitOnFailure1(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf); // Make sure we still abort, in case hSCM was NULL but no error was returned from GetLastError ExitOnNull(hSCM, hr, E_POINTER, "Getting handle to SCM reported success, but no handle was returned."); } // Get the script key from the CustomAction data and use it to open // the rollback log and read the data over the CustomActionData // because all of the information is in the script data not the // CustomActionData. hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); if (!pwzScriptKey) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed."); } ExitOnFailure(hr, "Failed to read encoding key from CustomActionData."); hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript); ExitOnFailure(hr, "Failed to open rollback CustomAction script."); hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzCustomActionData); ExitOnFailure(hr, "Failed to read rollback script into CustomAction data."); // Loop through the script's CustomActionData, processing each // service config in turn. pwz = pwzCustomActionData; while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Reconfiguring Service: %ls", pwzServiceName); // Open the handle with all the permissions we might need. // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2(). // SERVICE_START is required in order to handle SC_ACTION_RESTART action. hr = GetService(hSCM, pwzServiceName, SERVICE_CHANGE_CONFIG | SERVICE_START, &hService); ExitOnFailure1(hr, "Failed to get service: %ls", pwzServiceName); hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType, pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine); ExitOnFailure1(hr, "Failed to configure service: %ls", pwzServiceName); hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); // Per-service cleanup ::CloseServiceHandle(hService); hService = NULL; dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; } LExit: if (lpMsgBuf) // Allocated with FormatString. { ::LocalFree(lpMsgBuf); } if (hService) { ::CloseServiceHandle(hService); } if (hSCM) { ::CloseServiceHandle(hSCM); } WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzServiceName); ReleaseStr(pwzScriptKey); ReleaseStr(pwzCustomActionData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/******************************************************************** ComPlusInstallExecuteCommit - CUSTOM ACTION ENTRY POINT Input: deferred CustomActionData - ComPlusInstallExecuteCommit ********************************************************************/ extern "C" UINT __stdcall ComPlusInstallExecuteCommit(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; LPWSTR pwzRollbackFileName = NULL; HANDLE hRollbackFile = INVALID_HANDLE_VALUE; BOOL fInitializedCom = FALSE; // initialize hr = WcaInitialize(hInstall, "ComPlusInstallExecuteCommit"); ExitOnFailure(hr, "Failed to initialize ComPlusInstallExecuteCommit"); hr = ::CoInitialize(NULL); ExitOnFailure(hr, "Failed to initialize COM"); fInitializedCom = TRUE; CpiInitialize(); // get custom action data hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "Failed to get CustomActionData"); // open rollback file hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); ExitOnFailure(hr, "Failed to read rollback file name"); hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); if (INVALID_HANDLE_VALUE == hRollbackFile) ExitOnFailure1(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hRollbackFile, 0, NULL, FILE_END)) ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set file pointer"); // register assemblies hr = CpiConfigureAssemblies(&pwzData, hRollbackFile); ExitOnFailure(hr, "Failed to register assemblies"); if (S_FALSE == hr) ExitFunction(); // create role assignments hr = CpiConfigureRoleAssignments(&pwzData, hRollbackFile); ExitOnFailure(hr, "Failed to create role assignments"); if (S_FALSE == hr) ExitFunction(); // create subscriptions hr = CpiConfigureSubscriptions(&pwzData, hRollbackFile); ExitOnFailure(hr, "Failed to create subscriptions"); if (S_FALSE == hr) ExitFunction(); hr = S_OK; LExit: // clean up ReleaseStr(pwzData); if (INVALID_HANDLE_VALUE != hRollbackFile) ::CloseHandle(hRollbackFile); // unitialize CpiFinalize(); if (fInitializedCom) ::CoUninitialize(); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action *******************************************************************/ extern "C" UINT __stdcall ExecXmlConfigRollback( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecXmlConfigRollback"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; int iIs64Bit; BOOL fIs64Bit = FALSE; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzFileName = NULL; LPBYTE pbData = NULL; DWORD_PTR cbData = 0; DWORD cbDataWritten = 0; FILETIME ft; HANDLE hFile = INVALID_HANDLE_VALUE; // initialize hr = WcaInitialize(hInstall, "ExecXmlConfigRollback"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit); ExitOnFailure(hr, "failed to read component bitness from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzFileName); ExitOnFailure(hr, "failed to read file name from custom action data"); hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData); ExitOnFailure(hr, "failed to read file contents from custom action data"); fIs64Bit = (BOOL)iIs64Bit; if (fIs64Bit) { hr = WcaInitializeWow64(); if (S_FALSE == hr) { hr = TYPE_E_DLLFUNCTIONNOTFOUND; } ExitOnFailure(hr, "failed to initialize Wow64 API"); if (!WcaIsWow64Process()) { hr = E_NOTIMPL; ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable."); } hr = WcaDisableWow64FSRedirection(); ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API."); } hr = FileGetTime(pwzFileName, NULL, NULL, &ft); ExitOnFailure1(hr, "Failed to get modified date of file %ls.", pwzFileName); // Open the file hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL); ExitOnInvalidHandleWithLastError1(hFile, hr, "failed to open file: %ls", pwzFileName); // Write out the old data if (!::WriteFile(hFile, pbData, (DWORD)cbData, &cbDataWritten, NULL)) { ExitOnLastError1(hr, "failed to write to file: %ls", pwzFileName); } Assert(cbData == cbDataWritten); ReleaseFile(hFile); hr = FileSetTime(pwzFileName, NULL, NULL, &ft); ExitOnFailure1(hr, "Failed to set modified date of file %ls.", pwzFileName); LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzFileName); ReleaseFile(hFile); if (fIs64Bit) { WcaRevertWow64FSRedirection(); WcaFinalizeWow64(); } ReleaseMem(pbData); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
/****************************************************************** ExecXmlConfig - entry point for XmlConfig Custom Action *******************************************************************/ extern "C" UINT __stdcall ExecXmlConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug ExecXmlConfig"); HRESULT hr = S_OK; HRESULT hrOpenFailure = S_OK; UINT er = ERROR_SUCCESS; BOOL fIsWow64Process = FALSE; BOOL fIsFSRedirectDisabled = FALSE; BOOL fPreserveDate = FALSE; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzData = NULL; LPWSTR pwzFile = NULL; LPWSTR pwzElementPath = NULL; LPWSTR pwzVerifyPath = NULL; LPWSTR pwzName = NULL; LPWSTR pwzValue = NULL; LPWSTR pwz = NULL; int cAdditionalChanges = 0; IXMLDOMDocument* pixd = NULL; IXMLDOMNode* pixn = NULL; IXMLDOMNode* pixnVerify = NULL; IXMLDOMNode* pixnNewNode = NULL; IXMLDOMNode* pixnRemovedChild = NULL; IXMLDOMDocument* pixdNew = NULL; IXMLDOMElement* pixeNew = NULL; FILETIME ft; int id = IDRETRY; eXmlAction xa; eXmlPreserveDate xd; // initialize hr = WcaInitialize(hInstall, "ExecXmlConfig"); ExitOnFailure(hr, "failed to initialize"); hr = XmlInitialize(); ExitOnFailure(hr, "failed to initialize xml utilities"); hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa); ExitOnFailure(hr, "failed to process CustomActionData"); // Initialize the Wow64 API - store the result in fWow64APIPresent // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases WcaInitializeWow64(); fIsWow64Process = WcaIsWow64Process(); if (xaOpenFile != xa && xaOpenFilex64 != xa) { ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data"); } // loop through all the passed in data while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzFile); ExitOnFailure(hr, "failed to read file name from custom action data"); // Default to not preserve date, preserve it if any modifications require us to fPreserveDate = FALSE; // Open the file ReleaseNullObject(pixd); if (xaOpenFilex64 == xa) { if (!fIsWow64Process) { hr = E_NOTIMPL; ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW."); } hr = WcaDisableWow64FSRedirection(); ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API."); fIsFSRedirectDisabled = TRUE; } hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd); if (FAILED(hr)) { // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter. hrOpenFailure = hr; hr = S_OK; } else { hrOpenFailure = S_OK; } WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile); while (pwz && *pwz) { // If we skip past an element that has additional changes we need to strip them off the stream before // moving on to the next element. Do that now and then restart the outer loop. if (cAdditionalChanges > 0) { while (cAdditionalChanges > 0) { hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzValue); ExitOnFailure(hr, "failed to process CustomActionData"); cAdditionalChanges--; } continue; } hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa); ExitOnFailure(hr, "failed to process CustomActionData"); // Break if we need to move on to a different file if (xaOpenFile == xa || xaOpenFilex64 == xa) { break; } hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd); ExitOnFailure(hr, "failed to process CustomActionData"); if (xdPreserve == xd) { fPreserveDate = TRUE; } // Get path, name, and value to be written hr = WcaReadStringFromCaData(&pwz, &pwzElementPath); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzValue); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges); ExitOnFailure(hr, "failed to process CustomActionData"); // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone. if (FAILED(hrOpenFailure)) { if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa) { MessageExitOnFailure1(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile); } else { continue; } } // Select the node we're about to modify ReleaseNullObject(pixn); hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn); // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone. if (S_FALSE == hr) { if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa) { hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); } else { hr = S_OK; continue; } } MessageExitOnFailure2(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile); // Make the modification switch (xa) { case xaWriteValue: if (pwzName && *pwzName) { // We're setting an attribute hr = XmlSetAttribute(pixn, pwzName, pwzValue); ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue); } else { // We're setting the text of the node hr = XmlSetText(pixn, pwzValue); ExitOnFailure2(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath); } break; case xaWriteDocument: if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0]) { hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify); if (S_OK == hr) { // We found the verify path which means we have no further work to do continue; } ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath); } hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew); ExitOnFailure(hr, "Failed to load value as document."); hr = pixdNew->get_documentElement(&pixeNew); ExitOnFailure(hr, "Failed to get document element."); hr = pixn->appendChild(pixeNew, NULL); ExitOnFailure(hr, "Failed to append document element on to parent element."); ReleaseNullObject(pixeNew); ReleaseNullObject(pixdNew); break; case xaCreateElement: if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0]) { hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify); if (S_OK == hr) { // We found the verify path which means we have no further work to do continue; } ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath); } hr = XmlCreateChild(pixn, pwzName, &pixnNewNode); ExitOnFailure1(hr, "failed to create child element: %ls", pwzName); if (pwzValue && *pwzValue) { hr = XmlSetText(pixnNewNode, pwzValue); ExitOnFailure2(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName); } while (cAdditionalChanges > 0) { hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzValue); ExitOnFailure(hr, "failed to process CustomActionData"); // Set the additional attribute hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue); ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue); cAdditionalChanges--; } ReleaseNullObject(pixnNewNode); break; case xaDeleteValue: if (pwzName && *pwzName) { // Delete the attribute hr = XmlRemoveAttribute(pixn, pwzName); ExitOnFailure1(hr, "failed to remove attribute: %ls", pwzName); } else { // Clear the text value for the node hr = XmlSetText(pixn, L""); ExitOnFailure(hr, "failed to clear text value"); } break; case xaDeleteElement: if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0]) { hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify); if (S_OK == hr) { hr = pixn->removeChild(pixnVerify, &pixnRemovedChild); ExitOnFailure(hr, "failed to remove created child element"); ReleaseNullObject(pixnRemovedChild); } else { WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath); hr = S_OK; } } else { // TODO: This requires a VerifyPath to delete an element. Should we support not having one? WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath); } break; default: ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data"); break; } } // Now that we've made all of the changes to this file, save it and move on to the next if (S_OK == hrOpenFailure) { if (fPreserveDate) { hr = FileGetTime(pwzFile, NULL, NULL, &ft); ExitOnFailure1(hr, "failed to get modified time of file : %ls", pwzFile); } int iSaveAttempt = 0; do { hr = XmlSaveDocument(pixd, pwzFile); if (FAILED(hr)) { id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile); switch (id) { case IDABORT: ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile); case IDRETRY: hr = S_FALSE; // hit me, baby, one more time break; case IDIGNORE: hr = S_OK; // pretend everything is okay and bail break; case 0: // No UI case, MsiProcessMessage returns 0 if (STIERR_SHARING_VIOLATION == hr) { // Only in case of sharing violation do we retry 30 times, once a second. if (iSaveAttempt < 30) { hr = S_FALSE; ++iSaveAttempt; WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt); Sleep(1000); } else { ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile); } } break; default: // Unknown error ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile); } } } while (S_FALSE == hr); if (fPreserveDate) { hr = FileSetTime(pwzFile, NULL, NULL, &ft); ExitOnFailure1(hr, "failed to set modified time of file : %ls", pwzFile); } if (fIsFSRedirectDisabled) { fIsFSRedirectDisabled = FALSE; WcaRevertWow64FSRedirection(); } } } LExit: // Make sure we revert FS Redirection if necessary before exiting if (fIsFSRedirectDisabled) { fIsFSRedirectDisabled = FALSE; WcaRevertWow64FSRedirection(); } WcaFinalizeWow64(); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzFile); ReleaseStr(pwzElementPath); ReleaseStr(pwzVerifyPath); ReleaseStr(pwzName); ReleaseStr(pwzValue); ReleaseObject(pixeNew); ReleaseObject(pixdNew); ReleaseObject(pixn); ReleaseObject(pixd); ReleaseObject(pixnNewNode); ReleaseObject(pixnRemovedChild); XmlUninitialize(); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
/****************************************************************** SchedXmlConfig - entry point for XmlConfig Custom Action ********************************************************************/ extern "C" UINT __stdcall SchedXmlConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedXmlConfig"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzCurrentFile = NULL; BOOL fCurrentFileChanged = FALSE; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; XML_CONFIG_CHANGE* pxfcHead = NULL; XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more? XML_CONFIG_CHANGE* pxfc = NULL; eXmlAction xa = xaUnknown; eXmlPreserveDate xd; LPWSTR pwzCustomActionData = NULL; DWORD cFiles = 0; // initialize hr = WcaInitialize(hInstall, "SchedXmlConfig"); ExitOnFailure(hr, "failed to initialize"); hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail); MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read XmlConfig table"); hr = ProcessChanges(&pxfcHead); ExitOnFailure(hr, "failed to process XmlConfig changes"); // loop through all the xml configurations for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext) { // If this is a different file, or the first file... if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile)) { // Remember the file we're currently working on hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0); ExitOnFailure(hr, "failed to copy file name"); fCurrentFileChanged = TRUE; } // // Figure out what action to take // xa = xaUnknown; // If it's being installed or reinstalled or uninstalled and that matches // what we are doing then calculate the right action. if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) || (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction))) { if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags) { xa = xaCreateElement; } else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags) { xa = xaDeleteElement; } else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags) { xa = xaDeleteValue; } else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags) { xa = xaWriteValue; } else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags) { xa = xaWriteDocument; } else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags) { hr = E_INVALIDARG; ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node."); } } if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags) { xd = xdPreserve; } else { xd= xdDontPreserve; } if (xaUnknown != xa) { if (fCurrentFileChanged) { hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData); ExitOnFailure1(hr, "failed to begin file change for file: %ls", pwzCurrentFile); fCurrentFileChanged = FALSE; ++cFiles; } hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData); ExitOnFailure(hr, "failed to write action indicator custom action data"); hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData); ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data"); hr = WriteChangeData(pxfc, xa, &pwzCustomActionData); ExitOnFailure(hr, "failed to write change data"); } } // If we looped through all records all is well if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "failed while looping through all objects to secure"); // Schedule the custom action and add to progress bar if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cFiles); hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE); ExitOnFailure(hr, "failed to schedule ExecXmlConfig action"); } LExit: ReleaseStr(pwzCurrentFile); ReleaseStr(pwzCustomActionData); FreeXmlConfigChangeList(pxfcHead); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; wchar_t* service= 0; wchar_t* dir= 0; wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE]; char installDir[MAX_PATH]; DWORD size =MAX_VERSION_PROPERTY_SIZE; int installerMajorVersion, installerMinorVersion, installerPatchVersion; bool upgradableServiceFound=false; hr = WcaInitialize(hInstall, __FUNCTION__); WcaLog(LOGMSG_STANDARD, "Initialized."); if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size) != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(GetLastError()); ExitOnFailure(hr, "MsiGetPropertyW failed"); } if (swscanf(installerVersion,L"%d.%d.%d", &installerMajorVersion, &installerMinorVersion, &installerPatchVersion) !=3) { assert(FALSE); } size= MAX_PATH; if (MsiGetPropertyA(hInstall,"INSTALLDIR", installDir, &size) != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(GetLastError()); ExitOnFailure(hr, "MsiGetPropertyW failed"); } SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); if (scm == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); ExitOnFailure(hr,"OpenSCManager failed"); } static BYTE buf[64*1024]; static BYTE config_buffer[8*1024]; DWORD bufsize= sizeof(buf); DWORD bufneed; DWORD num_services; BOOL ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL); if(!ok) { hr = HRESULT_FROM_WIN32(GetLastError()); ExitOnFailure(hr,"EnumServicesStatusEx failed"); } LPENUM_SERVICE_STATUS_PROCESSW info = (LPENUM_SERVICE_STATUS_PROCESSW)buf; int index=-1; for (ULONG i=0; i < num_services; i++) { SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, SERVICE_QUERY_CONFIG); if (!service) continue; QUERY_SERVICE_CONFIGW *config= (QUERY_SERVICE_CONFIGW*)(void *)config_buffer; DWORD needed; BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); CloseServiceHandle(service); if (ok) { mysqld_service_properties props; if (get_mysql_service_properties(config->lpBinaryPathName, &props)) continue; /* Only look for services that have mysqld.exe outside of the current installation directory. */ if(installDir[0] == 0 || strstr(props.mysqld_exe,installDir) == 0) { WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d", info[i].lpServiceName, props.version_major, props.version_minor); if(props.version_major < installerMajorVersion || (props.version_major == installerMajorVersion && props.version_minor <= installerMinorVersion)) { upgradableServiceFound= true; break; } } } } if(!upgradableServiceFound) { /* Disable optional checkbox at the end of installation */ MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT", L""); MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L""); } else { MsiSetPropertyW(hInstall, L"UpgradableServiceFound", L"1"); MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"1"); } LExit: if(scm) CloseServiceHandle(scm); return WcaFinalize(er); }
/* Checks SERVICENAME, PORT and BUFFERSIZE parameters */ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) { wchar_t ServiceName[MAX_PATH]={0}; wchar_t SkipNetworking[MAX_PATH]={0}; wchar_t QuickConfig[MAX_PATH]={0}; wchar_t Password[MAX_PATH]={0}; wchar_t EscapedPassword[2*MAX_PATH+2]; wchar_t Port[6]; wchar_t BufferPoolSize[16]; DWORD PortLen=6; bool haveInvalidPort=false; const wchar_t *ErrorMsg=0; HRESULT hr= S_OK; UINT er= ERROR_SUCCESS; hr = WcaInitialize(hInstall, __FUNCTION__); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); DWORD ServiceNameLen = MAX_PATH; MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen); if(ServiceName[0]) { if(ServiceNameLen > 256) { ErrorMsg= L"Invalid service name. The maximum length is 256 characters."; goto LExit; } for(DWORD i=0; i< ServiceNameLen;i++) { if(ServiceName[i] == L'\\' || ServiceName[i] == L'/' || ServiceName[i]=='\'' || ServiceName[i] ==L'"') { ErrorMsg = L"Invalid service name. Forward slash and back slash are forbidden." L"Single and double quotes are also not permitted."; goto LExit; } } if(CheckServiceExists(ServiceName)) { ErrorMsg= L"A service with the same name already exists. " L"Please use a different name."; goto LExit; } } DWORD PasswordLen= MAX_PATH; MsiGetPropertyW (hInstall, L"PASSWORD", Password, &PasswordLen); EscapeCommandLine(Password, EscapedPassword, sizeof(EscapedPassword)/sizeof(EscapedPassword[0])); MsiSetPropertyW(hInstall,L"ESCAPEDPASSWORD",EscapedPassword); DWORD SkipNetworkingLen= MAX_PATH; MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, &SkipNetworkingLen); MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen); if(SkipNetworking[0]==0 && Port[0] != 0) { /* Strip spaces */ for(DWORD i=PortLen-1; i > 0; i--) { if(Port[i]== ' ') Port[i] = 0; } if(PortLen > 5 || PortLen <= 3) haveInvalidPort = true; else { for (DWORD i=0; i< PortLen && Port[i] != 0;i++) { if(Port[i] < '0' || Port[i] >'9') { haveInvalidPort=true; break; } } } if (haveInvalidPort) { ErrorMsg = L"Invalid port number. Please use a number between 1025 and 65535."; goto LExit; } short port = (short)_wtoi(Port); if (!IsPortFree(port)) { ErrorMsg = L"The TCP Port you selected is already in use. " L"Please choose a different port."; goto LExit; } } DWORD QuickConfigLen = MAX_PATH; MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen); if(QuickConfig[0] !=0) { MEMORYSTATUSEX memstatus; memstatus.dwLength =sizeof(memstatus); wchar_t invalidValueMsg[256]; if (!GlobalMemoryStatusEx(&memstatus)) { WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx", GetLastError()); er= ERROR_INSTALL_FAILURE; goto LExit; } DWORD BufferPoolSizeLen= 16; MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize, &BufferPoolSizeLen); /* Strip spaces */ for(DWORD i=BufferPoolSizeLen-1; i > 0; i--) { if(BufferPoolSize[i]== ' ') BufferPoolSize[i] = 0; } unsigned long long availableMemory= GetMaxBufferSize(memstatus.ullTotalPhys)/ONE_MB; swprintf_s(invalidValueMsg, L"Invalid buffer pool size. Please use a number between 1 and %llu", availableMemory); if(BufferPoolSizeLen == 0 || BufferPoolSizeLen > 15) { ErrorMsg= invalidValueMsg; goto LExit; } for (DWORD i=0; i < BufferPoolSizeLen && BufferPoolSize[BufferPoolSizeLen]; i++) { if(BufferPoolSize[i]< '0' || BufferPoolSize[i] > '9') { ErrorMsg= invalidValueMsg; goto LExit; } } BufferPoolSize[BufferPoolSizeLen]=0; MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize); long long sz = _wtoi64(BufferPoolSize); if(sz <= 0 || sz > (long long)availableMemory) { if(sz > 0) { swprintf_s(invalidValueMsg, L"Value for buffer pool size is too large." L"Only approximately %llu MB is available for allocation." L"Please use a number between 1 and %llu.", availableMemory, availableMemory); } ErrorMsg= invalidValueMsg; goto LExit; } } LExit: MsiSetPropertyW (hInstall, L"WarningText", ErrorMsg); return WcaFinalize(er); }
/* Checks if database directory or service are modified by user For example, service may point to different mysqld.exe that it was originally installed, or some different service might use this database directory. This would normally mean user has done an upgrade of the database and in this case uninstall should neither delete service nor database directory. If this function find that service is modified by user (mysqld.exe used by service does not point to the installation bin directory), MSI public variable SERVICENAME is removed, if DATADIR is used by some other service, variables DATADIR and CLEANUPDATA are removed. The effect of variable removal is that service does not get uninstalled and datadir is not touched by uninstallation. Note that this function is running without elevation and does not use anything that would require special privileges. */ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) { static BYTE buf[256*1024]; /* largest possible buffer for EnumServices */ static char config_buffer[8*1024]; /*largest buffer for QueryServiceConfig */ HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; wchar_t *servicename= NULL; wchar_t *datadir= NULL; wchar_t *bindir=NULL; SC_HANDLE scm = NULL; ULONG bufsize = sizeof(buf); ULONG bufneed = 0x00; ULONG num_services = 0x00; LPENUM_SERVICE_STATUS_PROCESS info = NULL; hr = WcaInitialize(hInstall, __FUNCTION__); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); WcaGetProperty(L"SERVICENAME", &servicename); WcaGetProperty(L"DATADIR", &datadir); WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir); WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S", servicename, datadir, bindir); scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); if (scm == NULL) { ExitOnFailure(E_FAIL, "OpenSCManager failed"); } BOOL ok= EnumServicesStatusExW( scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL); if(!ok) { WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError()); ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed"); } info = (LPENUM_SERVICE_STATUS_PROCESS)buf; for (ULONG i=0; i < num_services; i++) { SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, SERVICE_QUERY_CONFIG); if (!service) continue; WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName); QUERY_SERVICE_CONFIGW *config= (QUERY_SERVICE_CONFIGW *)(void *)config_buffer; DWORD needed; BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); CloseServiceHandle(service); if (ok) { CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName, config); } } LExit: if(scm) CloseServiceHandle(scm); ReleaseStr(servicename); ReleaseStr(datadir); ReleaseStr(bindir); return WcaFinalize(er); }
/****************************************************************** SchedServiceConfig - entry point for SchedServiceConfig Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaExecServiceConfig ********************************************************************/ extern "C" UINT __stdcall SchedServiceConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug SchedServiceConfig"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzScriptKey = NULL; LPWSTR pwzCustomActionData = NULL; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; LPWSTR pwzData = NULL; int iData = 0; DWORD cServices = 0; // initialize hr = WcaInitialize(hInstall, "SchedServiceConfig"); ExitOnFailure(hr, "Failed to initialize."); // Get the script key for this CustomAction and put it on the front of the // CustomActionData of the install action. hr = WcaCaScriptCreateKey(&pwzScriptKey); ExitOnFailure(hr, "Failed to get encoding key."); hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add encoding key to CustomActionData."); // Loop through all the services to be configured. hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView); ExitOnFailure(hr, "Failed to open view on ServiceConfig table."); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; // Get component name to check if we are installing it. If so // then add the table data to the CustomActionData, otherwise // skip it. hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData); ExitOnFailure(hr, "Failed to get component name"); hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(hr), "Failed to get install state for Component: %ls", pwzData); if (WcaIsInstalling(isInstalled, isAction)) { // Add the data to the CustomActionData (for install). hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "Failed to get name of service."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add name to CustomActionData."); hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData); ExitOnFailure(hr, "Failed to get ServiceConfig.NewService."); hr = WcaWriteIntegerToCaData(0 != iData, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add NewService data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get first failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get second failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get third failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData); if (S_FALSE == hr) // deal w/ possible null value { iData = 0; } ExitOnFailure(hr, "failed to get reset period in days between service restart attempts."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData); if (S_FALSE == hr) // deal w/ possible null value { iData = 0; } ExitOnFailure(hr, "failed to get server restart delay value."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get command line to run on service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); ++cServices; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "failed while looping through all objects to secure"); // setup CustomActionData and add to progress bar for download if (0 < cServices) { hr = WcaDoDeferredAction(L"RollbackServiceConfig", pwzScriptKey, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule RollbackServiceConfig action"); hr = WcaDoDeferredAction(L"ExecServiceConfig", pwzCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecServiceConfig action"); } LExit: ReleaseStr(pwzData); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzScriptKey); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** CaSchedServiceConfig - entry point for CaSchedServiceConfig Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaExecServiceConfig ********************************************************************/ extern "C" UINT __stdcall SchedServiceConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedServiceConfig"); HRESULT hr = S_OK; UINT uiResult = ERROR_SUCCESS; DWORD dwError = 0; LPWSTR pwzData = NULL; int iData = 0; BOOL fExistingService = FALSE; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; LPSERVICE_FAILURE_ACTIONSW psfa; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzRollbackCustomActionData = NULL; DWORD cServices = 0; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD dwSizeNeeded = 0; // initialize hr = WcaInitialize(hInstall, "SchedServiceConfig"); ExitOnFailure(hr, "failed to initialize"); //Get a handle to the service control manager hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (hSCM == NULL) ExitOnLastError(hr, "failed to get handle to SCM"); // loop through all the services to be configured hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView); ExitOnFailure(hr, "failed to open view on ServiceConfig table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData); ExitOnFailure(hr, "failed to get object NewService"); fExistingService = 1 != iData; // Get component name hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get component name"); // check if we are installing this Component hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(hr), "failed to get install state for Component: %S", pwzData); // We want to configure either a service we're installing or one already on the box if (WcaIsInstalling(isInstalled, isAction)) { // Check if we're configuring an existing service if (fExistingService) { // Confirm the service is actually on the box hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get object NewService"); //Get a handle to the service hService = ::OpenServiceW(hSCM, pwzData, SERVICE_QUERY_CONFIG); if (hService == NULL) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (hr == ERROR_SERVICE_DOES_NOT_EXIST) { ExitOnFailure1(hr, "Service \"%s\" does not exist on this system.", pwzData); } else { ExitOnFailure1(hr, "Failed to get handle to the service \"%S\".", pwzData); } } // Get Current Service Config info if(!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwSizeNeeded) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitOnLastError(hr, "Failed to get current service config info."); } // Alloc space we were told we needed psfa = (LPSERVICE_FAILURE_ACTIONSW) MemAlloc(dwSizeNeeded, TRUE); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); // Now do the real query if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, dwSizeNeeded, &dwSizeNeeded)) ExitOnLastError(hr, "failed to Query Service."); // Build up rollback CA data so we can restore service state if necessary hr = WcaWriteStringToCaData(pwzData, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // If this service struct is empty, fill in defualt values if(psfa->cActions < 3) { hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use them // action 1 hr = GetSCActionTypeString(psfa->lpsaActions[0].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[0].Type) dwRestartDelay = psfa->lpsaActions[0].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 2 hr = GetSCActionTypeString(psfa->lpsaActions[1].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[1].Type) dwRestartDelay = psfa->lpsaActions[1].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 3 hr = GetSCActionTypeString(psfa->lpsaActions[2].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[2].Type) dwRestartDelay = psfa->lpsaActions[2].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } hr = WcaWriteIntegerToCaData(psfa->dwResetPeriod / (24 * 60 * 60), &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaWriteIntegerToCaData(dwRestartDelay, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); // check for value being null if(!psfa->lpCommand) psfa->lpCommand = L""; hr = WcaWriteStringToCaData(psfa->lpCommand, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // check for value being null if(!psfa->lpRebootMsg) psfa->lpRebootMsg = L""; hr = WcaWriteStringToCaData(psfa->lpRebootMsg, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Clear up per-service values if(psfa) MemFree(psfa); } // add the data to the CustomActionData (for install) hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get name of service"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get first failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get second failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get third failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get reset period in days between service restart attempts."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get server restart delay value."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get command line to run on service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cServices++; ::CloseServiceHandle(hService); hService = NULL; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // setup CustomActionData and add to progress bar for download if (pwzRollbackCustomActionData && *pwzRollbackCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfigRollback", pwzRollbackCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } // schedule the custom action and add to progress bar if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfig", pwzCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: // Clean up handles if (hService != NULL) ::CloseServiceHandle(hService); if (hSCM != NULL) ::CloseServiceHandle(hSCM); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzRollbackCustomActionData); ReleaseStr(pwzData); if (FAILED(hr)) uiResult = ERROR_INSTALL_FAILURE; return WcaFinalize(uiResult); }
UINT __stdcall ExecAddinRegistration(MSIHANDLE hInstall) { // AssertSz(FALSE, "debug ExecAddinRegistration"); LPWSTR pwzCustomActionData = NULL; LPWSTR pwzData = NULL; LPWSTR pwz = NULL; int iOperation = 0; LPWSTR pwzId = NULL; LPWSTR pwzFile = NULL; LPWSTR pwzName = NULL; LPWSTR pwzDescription = NULL; int iBitness = REG_KEY_DEFAULT; int iCommandLineSafe = 1; int iLoadBehavior = 3; LPWSTR pwzAllUsers = NULL; HRESULT hr = WcaInitialize(hInstall, "ExecAddinRegistration"); ExitOnFailure(hr, "Failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = RegInitialize(); ExitOnFailure(hr, "Failed to initialize the registry functions."); // loop through all the passed in data while (pwz && *pwz) { // extract the custom action data hr = WcaReadIntegerFromCaData(&pwz, &iOperation); ExitOnFailure(hr, "failed to read operation from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzId); ExitOnFailure(hr, "failed to read id from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzFile); ExitOnFailure(hr, "failed to read path from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to read name from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzDescription); ExitOnFailure(hr, "failed to read description from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iBitness); ExitOnFailure(hr, "failed to read bitness from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iCommandLineSafe); ExitOnFailure(hr, "failed to read CommandLineSafe from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iLoadBehavior); ExitOnFailure(hr, "failed to read LoadBehavior from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzAllUsers); ExitOnFailure(hr, "failed to read ALLUSERS from custom action data"); BOOL fPerUserInstall = (!pwzAllUsers || !*pwzAllUsers); // if rolling back, swap INSTALL and UNINSTALL if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)) { if (WCA_TODO_INSTALL == iOperation) { iOperation = WCA_TODO_UNINSTALL; } else if (WCA_TODO_UNINSTALL == iOperation) { iOperation = WCA_TODO_INSTALL; } } switch (iOperation) { case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: hr = CreateOfficeRegistryKey(pwzId, pwzFile, pwzName, pwzDescription, iCommandLineSafe, iLoadBehavior, fPerUserInstall, iBitness); ExitOnFailure1(hr, "failed to register addin %ls", pwzId); break; case WCA_TODO_UNINSTALL: hr = DeleteOfficeRegistryKey(pwzId, fPerUserInstall, iBitness); ExitOnFailure1(hr, "failed to unregister addin %ls", pwzId); break; } // Tick the progress bar along for this addin hr = WcaProgressMessage(COST_REGISTER_ADDIN, FALSE); ExitOnFailure1(hr, "failed to tick progress bar for addin registration: %ls", pwzId); } LExit: RegUninitialize(); ReleaseStr(pwzAllUsers); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzId); ReleaseStr(pwzFile); ReleaseStr(pwzName); ReleaseStr(pwzDescription); return WcaFinalize(SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE); }
extern "C" UINT __stdcall ExecSecureObjectsRollback( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecSecureObjectsRollback"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR pwz = NULL; LPWSTR pwzData = NULL; LPWSTR pwzObject = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzSecurityInfo = NULL; SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; PSECURITY_DESCRIPTOR psd = NULL; ULONG psdSize; SECURITY_DESCRIPTOR_CONTROL sdc = {0}; SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; PACL pDacl = NULL; BOOL bDaclPresent = false; BOOL bDaclDefaulted = false; DWORD dwRevision = 0; int iProtected; // initialize hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); pwz = pwzData; hr = WcaReadStringFromCaData(&pwz, &pwzObject); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzTable); ExitOnFailure(hr, "failed to process CustomActionData"); objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable)); if (SE_UNKNOWN_OBJECT_TYPE != objectType) { hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, &iProtected); ExitOnFailure(hr, "failed to process CustomActionData"); if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize)) { ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor"); } if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted)) { hr = E_UNEXPECTED; ExitOnFailure2(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError()); } // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it. if (!bDaclPresent) { hr = E_UNEXPECTED; ExitOnFailure(hr, "security descriptor does not contain a DACL"); } //Need to see if DACL is protected so getting Descriptor information if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) { ExitOnLastError1(hr, "failed to get security descriptor control for object: %ls", pwzObject); } // Write a 1 if DACL is protected, 0 otherwise switch (iProtected) { case 0: // Unnecessary to do anything - leave si to the default flags break; case 1: si = si | PROTECTED_DACL_SECURITY_INFORMATION; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "unrecognized value in CustomActionData"); break; } er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL); ExitOnFailure2(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError()); } else { MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable); } LExit: ReleaseStr(pwzData); ReleaseStr(pwzObject); ReleaseStr(pwzTable); ReleaseStr(pwzSecurityInfo); if (psd) { ::LocalFree(psd); } if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
extern "C" UINT WINAPI WixRemoveFoldersEx( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug WixRemoveFoldersEx"); HRESULT hr = S_OK; PMSIHANDLE hView; PMSIHANDLE hRec; LPWSTR sczId = NULL; LPWSTR sczComponent = NULL; LPWSTR sczProperty = NULL; LPWSTR sczPath = NULL; LPWSTR sczExpandedPath = NULL; int iMode = 0; DWORD dwCounter = 0; DWORD_PTR cchLen = 0; MSIHANDLE hTable = NULL; MSIHANDLE hColumns = NULL; hr = WcaInitialize(hInstall, "WixRemoveFoldersEx"); ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx."); // anything to do? if (S_OK != WcaTableExists(L"WixRemoveFolderEx")) { WcaLog(LOGMSG_STANDARD, "WixRemoveFolderEx table doesn't exist, so there are no folders to remove."); ExitFunction(); } // query and loop through all the remove folders exceptions hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView); ExitOnFailure(hr, "Failed to open view on WixRemoveFolderEx table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordString(hRec, rfqId, &sczId); ExitOnFailure(hr, "Failed to get remove folder identity."); hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent); ExitOnFailure(hr, "Failed to get remove folder component."); hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty); ExitOnFailure(hr, "Failed to get remove folder property."); hr = WcaGetRecordInteger(hRec, feqMode, &iMode); ExitOnFailure(hr, "Failed to get remove folder mode"); hr = WcaGetProperty(sczProperty, &sczPath); ExitOnFailure2(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId); // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen)); if (SUCCEEDED(hr)) { ExitOnFailure2(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId); } hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT); ExitOnFailure2(hr, "Failed to expand path: %S for row: %S", sczPath, sczId); hr = PathBackslashTerminate(&sczExpandedPath); ExitOnFailure1(hr, "Failed to backslash-terminate path: %S", sczExpandedPath); WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId); hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, &dwCounter, &hTable, &hColumns); ExitOnFailure2(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId); } // reaching the end of the list is actually a good thing, not an error if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure occured while processing WixRemoveFolderEx table"); LExit: if (hColumns) { ::MsiCloseHandle(hColumns); } if (hTable) { ::MsiCloseHandle(hTable); } ReleaseStr(sczExpandedPath); ReleaseStr(sczPath); ReleaseStr(sczProperty); ReleaseStr(sczComponent); ReleaseStr(sczId); DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** WixSchedInternetShortcuts - entry point ********************************************************************/ extern "C" UINT __stdcall WixSchedInternetShortcuts( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; UINT uiCost = 0; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; MSIHANDLE hCreateFolderTable = NULL; MSIHANDLE hCreateFolderColumns = NULL; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzComponent = NULL; LPWSTR pwzDirectory = NULL; LPWSTR pwzFilename = NULL; LPWSTR pwzTarget = NULL; LPWSTR pwzShortcutPath = NULL; int iAttr = 0; LPWSTR pwzIconFile = NULL; int iIconIndex = 0; IUniformResourceLocatorW* piURL = NULL; IShellLinkW* piShellLink = NULL; BOOL fInitializedCom = FALSE; hr = WcaInitialize(hInstall, "WixSchedInternetShortcuts"); ExitOnFailure(hr, "failed to initialize WixSchedInternetShortcuts."); // anything to do? if (S_OK != WcaTableExists(L"WixInternetShortcut")) { WcaLog(LOGMSG_STANDARD, "WixInternetShortcut table doesn't exist, so there are no Internet shortcuts to process"); goto LExit; } // check to see if we can create a shortcut - Server Core and others may not have a shell registered. hr = ::CoInitialize(NULL); ExitOnFailure(hr, "failed to initialize COM"); fInitializedCom = TRUE; hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL); if (S_OK != hr) { WcaLog(LOGMSG_STANDARD, "failed to create an instance of IUniformResourceLocatorW, skipping shortcut creation"); ExitFunction1(hr = S_OK); } hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink); if (S_OK != hr) { WcaLog(LOGMSG_STANDARD, "failed to create an instance of IShellLinkW, skipping shortcut creation"); ExitFunction1(hr = S_OK); } // query and loop through all the shortcuts hr = WcaOpenExecuteView(vcsShortcutsQuery, &hView); ExitOnFailure(hr, "failed to open view on WixInternetShortcut table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { // read column values hr = WcaGetRecordString(hRec, esqComponent, &pwzComponent); ExitOnFailure(hr, "failed to get shortcut component"); hr = WcaGetRecordString(hRec, esqDirectory, &pwzDirectory); ExitOnFailure(hr, "failed to get shortcut directory"); hr = WcaGetRecordString(hRec, esqFilename, &pwzFilename); ExitOnFailure(hr, "failed to get shortcut filename"); hr = WcaGetRecordFormattedString(hRec, esqTarget, &pwzTarget); ExitOnFailure(hr, "failed to get shortcut target"); hr = WcaGetRecordInteger(hRec, esqAttributes, &iAttr); ExitOnFailure(hr, "failed to get shortcut attributes"); hr = WcaGetRecordFormattedString(hRec, esqIconFile, &pwzIconFile); ExitOnFailure(hr, "failed to get shortcut icon file"); hr = WcaGetRecordInteger(hRec, esqIconIndex, &iIconIndex); ExitOnFailure(hr, "failed to get shortcut icon index"); // skip processing this WixInternetShortcut row if the component isn't being configured WCA_TODO todo = WcaGetComponentToDo(pwzComponent); if (WCA_TODO_UNKNOWN == todo) { WcaLog(LOGMSG_VERBOSE, "Skipping shortcut for null-action component '%ls'", pwzComponent); continue; } // we need to create the directory where the shortcut is supposed to live; rather // than doing so in our deferred custom action, use the CreateFolder table to have MSI // make (and remove) them on our behalf (including the correct cleanup of parent directories). MSIDBERROR dbError = MSIDBERROR_NOERROR; WcaLog(LOGMSG_STANDARD, "Adding folder '%ls', component '%ls' to the CreateFolder table", pwzDirectory, pwzComponent); hr = WcaAddTempRecord(&hCreateFolderTable, &hCreateFolderColumns, L"CreateFolder", &dbError, 0, 2, pwzDirectory, pwzComponent); if (MSIDBERROR_DUPLICATEKEY == dbError) { WcaLog(LOGMSG_STANDARD, "Folder '%ls' already exists in the CreateFolder table; the above error is harmless", pwzDirectory); hr = S_OK; } ExitOnFailure(hr, "Couldn't add temporary CreateFolder row"); // only if we're installing/reinstalling do we need to schedule the deferred CA // (uninstallation is handled via permanent RemoveFile rows and temporary CreateFolder rows) if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo) { // turn the Directory_ id into a path hr = WcaGetTargetPath(pwzDirectory, &pwzShortcutPath); ExitOnFailure(hr, "failed to allocate string for shortcut directory"); // append the shortcut filename hr = StrAllocConcat(&pwzShortcutPath, pwzFilename, 0); ExitOnFailure(hr, "failed to allocate string for shortcut filename"); // write the shortcut path and target to custom action data for deferred CAs hr = WcaWriteStringToCaData(pwzShortcutPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut path to custom action data"); hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut target to custom action data"); hr = WcaWriteIntegerToCaData(iAttr, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut attributes to custom action data"); hr = WcaWriteStringToCaData(pwzIconFile, &pwzCustomActionData); ExitOnFailure(hr, "failed to write icon file to custom action data"); hr = WcaWriteIntegerToCaData(iIconIndex, &pwzCustomActionData); ExitOnFailure(hr, "failed to write icon index to custom action data"); uiCost += COST_INTERNETSHORTCUT; } } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure occured while processing WixInternetShortcut table"); // if we have any shortcuts to install if (pwzCustomActionData && *pwzCustomActionData) { // add cost to progress bar hr = WcaProgressMessage(uiCost, TRUE); ExitOnFailure(hr, "failed to extend progress bar for InternetShortcuts"); // provide custom action data to deferred and rollback CAs hr = WcaSetProperty(PLATFORM_DECORATION(L"WixRollbackInternetShortcuts"), pwzCustomActionData); ExitOnFailure(hr, "failed to set WixRollbackInternetShortcuts rollback custom action data"); hr = WcaSetProperty(PLATFORM_DECORATION(L"WixCreateInternetShortcuts"), pwzCustomActionData); ExitOnFailure(hr, "failed to set WixCreateInternetShortcuts custom action data"); } LExit: if (hCreateFolderTable) { ::MsiCloseHandle(hCreateFolderTable); } if (hCreateFolderColumns) { ::MsiCloseHandle(hCreateFolderColumns); } ReleaseStr(pwzCustomActionData); ReleaseStr(pwzComponent); ReleaseStr(pwzDirectory); ReleaseStr(pwzFilename); ReleaseStr(pwzTarget); ReleaseStr(pwzShortcutPath); ReleaseObject(piShellLink); ReleaseObject(piURL); if (fInitializedCom) { ::CoUninitialize(); } er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/******************************************************************** ComPlusRollbackUninstallExecute - CUSTOM ACTION ENTRY POINT Input: deferred CustomActionData - ComPlusRollbackUninstallExecute ********************************************************************/ extern "C" UINT __stdcall ComPlusRollbackUninstallExecute(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; LPWSTR pwzRollbackFileName = NULL; HANDLE hRollbackFile = INVALID_HANDLE_VALUE; CPI_ROLLBACK_DATA* prdPartitions = NULL; CPI_ROLLBACK_DATA* prdUsersInPartitionRoles = NULL; CPI_ROLLBACK_DATA* prdPartitionUsers = NULL; CPI_ROLLBACK_DATA* prdApplications = NULL; CPI_ROLLBACK_DATA* prdApplicationRoles = NULL; CPI_ROLLBACK_DATA* prdUsersApplicationRoles = NULL; CPI_ROLLBACK_DATA* prdAssemblies = NULL; CPI_ROLLBACK_DATA* prdRoleAssignments = NULL; CPI_ROLLBACK_DATA* prdSubscriptions = NULL; BOOL fInitializedCom = FALSE; // initialize hr = WcaInitialize(hInstall, "ComPlusRollbackUninstallExecute"); ExitOnFailure(hr, "Failed to initialize ComPlusRollbackUninstallExecute"); hr = ::CoInitialize(NULL); ExitOnFailure(hr, "Failed to initialize COM"); fInitializedCom = TRUE; CpiInitialize(); // get custom action data hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "Failed to get CustomActionData"); // open rollback file hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); ExitOnFailure(hr, "Failed to read rollback file name"); hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); if (INVALID_HANDLE_VALUE == hRollbackFile) ExitOnFailure1(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); // read rollback data hr = CpiReadRollbackDataList(hRollbackFile, &prdSubscriptions); ExitOnFailure(hr, "Failed to read subscription rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdRoleAssignments); ExitOnFailure(hr, "Failed to read role assignments rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdAssemblies); ExitOnFailure(hr, "Failed to read assemblies rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersApplicationRoles); ExitOnFailure(hr, "Failed to read users in application roles rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdApplicationRoles); ExitOnFailure(hr, "Failed to read application roles rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdApplications); ExitOnFailure(hr, "Failed to read applications rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitionUsers); ExitOnFailure(hr, "Failed to read partition users rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersInPartitionRoles); ExitOnFailure(hr, "Failed to read users in partition roles rollback data"); hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitions); ExitOnFailure(hr, "Failed to read partitions rollback data"); ::CloseHandle(hRollbackFile); hRollbackFile = INVALID_HANDLE_VALUE; // rollback remove partitions hr = CpiRollbackConfigurePartitions(&pwzData, prdPartitions); ExitOnFailure(hr, "Failed to rollback delete partitions"); // rollback remove users in partition roles hr = CpiRollbackConfigureUsersInPartitionRoles(&pwzData, prdUsersInPartitionRoles); ExitOnFailure(hr, "Failed to rollback delete users in partition roles"); // rollback remove partition users hr = CpiRollbackConfigurePartitionUsers(&pwzData, prdPartitionUsers); ExitOnFailure(hr, "Failed to rollback delete partition users"); // rollback remove applications hr = CpiRollbackConfigureApplications(&pwzData, prdApplications); ExitOnFailure(hr, "Failed to rollback delete applications"); // rollback remove application roles hr = CpiRollbackConfigureApplicationRoles(&pwzData, prdApplicationRoles); ExitOnFailure(hr, "Failed to rollback delete application roles"); // rollback remove users in application roles hr = CpiRollbackConfigureUsersInApplicationRoles(&pwzData, prdUsersApplicationRoles); ExitOnFailure(hr, "Failed to rollback delete users in application roles"); // rollback unregister assemblies hr = CpiRollbackConfigureAssemblies(&pwzData, prdAssemblies); ExitOnFailure(hr, "Failed to rollback unregister assemblies"); // rollback delete role assignments hr = CpiRollbackConfigureRoleAssignments(&pwzData, prdAssemblies); ExitOnFailure(hr, "Failed to rollback delete role assignments"); // rollback delete subscriptions hr = CpiRollbackConfigureSubscriptions(&pwzData, prdSubscriptions); ExitOnFailure(hr, "Failed to rollback delete subscriptions"); hr = S_OK; LExit: // clean up ReleaseStr(pwzData); ReleaseStr(pwzRollbackFileName); if (INVALID_HANDLE_VALUE != hRollbackFile) ::CloseHandle(hRollbackFile); if (prdPartitions) CpiFreeRollbackDataList(prdPartitions); if (prdUsersInPartitionRoles) CpiFreeRollbackDataList(prdUsersInPartitionRoles); if (prdPartitionUsers) CpiFreeRollbackDataList(prdPartitionUsers); if (prdApplications) CpiFreeRollbackDataList(prdApplications); if (prdApplicationRoles) CpiFreeRollbackDataList(prdApplicationRoles); if (prdUsersApplicationRoles) CpiFreeRollbackDataList(prdUsersApplicationRoles); if (prdAssemblies) CpiFreeRollbackDataList(prdAssemblies); if (prdRoleAssignments) CpiFreeRollbackDataList(prdRoleAssignments); if (prdSubscriptions) CpiFreeRollbackDataList(prdSubscriptions); // unitialize CpiFinalize(); if (fInitializedCom) ::CoUninitialize(); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/****************************************************************** CaExecServiceConfig - entry point for ServiceConfig Custom Action. NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t... *******************************************************************/ extern "C" UINT __stdcall ExecServiceConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug ExecServiceConfig"); HRESULT hr = S_OK; DWORD er = 0; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzScriptKey = NULL; WCA_CASCRIPT_HANDLE hRollbackScript = NULL; LPWSTR pwzServiceName = NULL; BOOL fNewService = FALSE; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; LPVOID lpMsgBuf = NULL; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD cbExistingServiceConfig = 0; SERVICE_FAILURE_ACTIONSW* psfa = NULL; // initialize hr = WcaInitialize(hInstall, "ExecServiceConfig"); ExitOnFailure(hr, "failed to initialize"); // Open the Services Control Manager up front. hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (NULL == hSCM) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); #pragma prefast(push) #pragma prefast(disable:25028) ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); #pragma prefast(pop) ExitOnFailure1(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf); } // First, get the script key out of the CustomActionData and // use that to create the rollback script for this action. hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); if (!pwzScriptKey) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed."); } ExitOnFailure(hr, "Failed to read encoding key from CustomActionData."); hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript); ExitOnFailure(hr, "Failed to open rollback CustomAction script."); // Next, loop through the rest of the CustomActionData, processing // each service config row in turn. while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName); // Open the handle with all the permissions we might need: // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2(). // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2(). // SERVICE_START is required in order to handle SC_ACTION_RESTART action. hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService); ExitOnFailure1(hr, "Failed to get service: %ls", pwzServiceName); // If we are configuring a service that existed on the machine, we need to // read the existing service configuration and write it out to the rollback // log so rollback can put it back if anything goes wrong. if (!fNewService) { // First, read the existing service config. if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitWithLastError(hr, "Failed to get current service config info."); } psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE)); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig)) { ExitOnLastError(hr, "failed to Query Service."); } // Build up rollback log so we can restore service state if necessary hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName); ExitOnFailure(hr, "Failed to add service name to Rollback Log"); // If this service struct is empty, fill in default values if (3 > psfa->cActions) { hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use the first three. for (int i = 0; i < 3; ++i) { hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName)); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type) { dwRestartDelay = psfa->lpsaActions[i].Delay / 1000; } hr = WcaCaScriptWriteString(hRollbackScript, wzActionName); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } } hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60)); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay); ExitOnFailure(hr, "failed to add data to CustomActionData"); // Handle the null cases. if (!psfa->lpCommand) { psfa->lpCommand = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Handle the null cases. if (!psfa->lpRebootMsg) { psfa->lpRebootMsg = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Nudge the system to get all our rollback data written to disk. WcaCaScriptFlush(hRollbackScript); ReleaseNullMem(psfa); } hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType, pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine); ExitOnFailure1(hr, "Failed to configure service: %ls", pwzServiceName); hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); // Per-service cleanup ::CloseServiceHandle(hService); hService = NULL; dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; } LExit: WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE); if (lpMsgBuf) { ::LocalFree(lpMsgBuf); } if (hService) { ::CloseServiceHandle(hService); } if (hSCM) { ::CloseServiceHandle(hSCM); } ReleaseMem(psfa); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzServiceName); ReleaseStr(pwzScriptKey); ReleaseStr(pwzCustomActionData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
/******************************************************************** PrintEula - Custom Action entry point ********************************************************************/ extern "C" UINT __stdcall PrintEula(MSIHANDLE hInstall) { //AssertSz(FALSE, "Debug PrintEula"); HRESULT hr = S_OK; HWND hWndMain = NULL; HMODULE hRichEdit = NULL; BOOL fRegisteredClass = FALSE; hr = WcaInitialize(hInstall, "PrintEula"); ExitOnFailure(hr, "failed to initialize"); // Initialize then display print dialog. vpPrintDlg = (PRINTDLGEXW*)GlobalAlloc(GPTR, sizeof(PRINTDLGEXW)); // MSDN says to allocate on heap. ExitOnNullWithLastError(vpPrintDlg, hr, "Failed to allocate memory for print dialog struct."); vpPrintDlg->lStructSize = sizeof(PRINTDLGEX); vpPrintDlg->hwndOwner = ::FindWindowW(L"MsiDialogCloseClass", NULL); vpPrintDlg->Flags = PD_RETURNDC | PD_COLLATE | PD_NOCURRENTPAGE | PD_ALLPAGES | PD_NOPAGENUMS | PD_NOSELECTION; vpPrintDlg->nCopies = NO_OF_COPIES; vpPrintDlg->nStartPage = START_PAGE_GENERAL; hr = ::PrintDlgExW(vpPrintDlg); ExitOnFailure(hr, "Failed to show print dialog"); // If user said they want to print. if (PD_RESULT_PRINT == vpPrintDlg->dwResultAction) { // Get the stream for Eula hr = ReadEulaText(hInstall, &vpszEulaText); ExitOnFailure(hr, "failed to read Eula text from MSI database"); // Have to load Rich Edit since we'll be creating a Rich Edit control in the window hr = LoadSystemLibrary(L"Riched20.dll", &hRichEdit); ExitOnFailure(hr, "failed to load rich edit 2.0 library"); hr = CreateRichTextWindow(&hWndMain, &fRegisteredClass); ExitOnFailure(hr, "failed to create rich text window for printing"); hr = PrintRichText(hWndMain); if (FAILED(hr)) // Since we've already shown the print dialog, we better show them a dialog explaining why it didn't print { ShowErrorMessage(hr); } } LExit: ReleaseNullStr(vpszEulaText); if (vpPrintDlg) { if (vpPrintDlg->hDevMode) { ::GlobalFree(vpPrintDlg->hDevMode); } if (vpPrintDlg->hDevNames) { ::GlobalFree(vpPrintDlg->hDevNames); } if (vpPrintDlg->hDC) { ::DeleteDC(vpPrintDlg->hDC); } ::GlobalFree(vpPrintDlg); vpPrintDlg = NULL; } if (fRegisteredClass) { ::UnregisterClassW(WINDOW_CLASS, NULL); } if (NULL != hRichEdit) { ::FreeLibrary(hRichEdit); } // Always return success since we dont want to stop the // installation even if the Eula printing fails. return WcaFinalize(ERROR_SUCCESS); }
/****************************************************************** SchedFirewallExceptions - immediate custom action worker to register and remove firewall exceptions. ********************************************************************/ static UINT SchedFirewallExceptions( __in MSIHANDLE hInstall, WCA_TODO todoSched ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; int cFirewallExceptions = 0; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzName = NULL; LPWSTR pwzRemoteAddresses = NULL; LPWSTR pwzPort = NULL; int iProtocol = 0; int iAttributes = 0; int iProfile = 0; LPWSTR pwzProgram = NULL; LPWSTR pwzComponent = NULL; LPWSTR pwzFormattedFile = NULL; LPWSTR pwzDescription = NULL; // initialize hr = WcaInitialize(hInstall, "SchedFirewallExceptions"); ExitOnFailure(hr, "failed to initialize"); // anything to do? if (S_OK != WcaTableExists(L"WixFirewallException")) { WcaLog(LOGMSG_STANDARD, "WixFirewallException table doesn't exist, so there are no firewall exceptions to configure."); ExitFunction(); } // query and loop through all the firewall exceptions hr = WcaOpenExecuteView(vcsFirewallExceptionQuery, &hView); ExitOnFailure(hr, "failed to open view on WixFirewallException table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordFormattedString(hRec, feqName, &pwzName); ExitOnFailure(hr, "failed to get firewall exception name"); hr = WcaGetRecordFormattedString(hRec, feqRemoteAddresses, &pwzRemoteAddresses); ExitOnFailure(hr, "failed to get firewall exception remote addresses"); hr = WcaGetRecordFormattedString(hRec, feqPort, &pwzPort); ExitOnFailure(hr, "failed to get firewall exception port"); hr = WcaGetRecordInteger(hRec, feqProtocol, &iProtocol); ExitOnFailure(hr, "failed to get firewall exception protocol"); hr = WcaGetRecordFormattedString(hRec, feqProgram, &pwzProgram); ExitOnFailure(hr, "failed to get firewall exception program"); hr = WcaGetRecordInteger(hRec, feqAttributes, &iAttributes); ExitOnFailure(hr, "failed to get firewall exception attributes"); hr = WcaGetRecordInteger(hRec, feqProfile, &iProfile); ExitOnFailure(hr, "failed to get firewall exception profile"); hr = WcaGetRecordString(hRec, feqComponent, &pwzComponent); ExitOnFailure(hr, "failed to get firewall exception component"); hr = WcaGetRecordString(hRec, feqDescription, &pwzDescription); ExitOnFailure(hr, "failed to get firewall description"); // figure out what we're doing for this exception, treating reinstall the same as install WCA_TODO todoComponent = WcaGetComponentToDo(pwzComponent); if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched) { WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d)", pwzComponent, todoComponent, todoSched); continue; } // action :: name :: profile :: remoteaddresses :: attributes :: target :: {port::protocol | path} ++cFirewallExceptions; hr = WcaWriteIntegerToCaData(todoComponent, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception action to custom action data"); hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception name to custom action data"); hr = WcaWriteIntegerToCaData(iProfile, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception profile to custom action data"); hr = WcaWriteStringToCaData(pwzRemoteAddresses, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception remote addresses to custom action data"); hr = WcaWriteIntegerToCaData(iAttributes, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception attributes to custom action data"); if (*pwzProgram) { // If program is defined, we have an application exception. hr = WcaWriteIntegerToCaData(fetApplication, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception target (application) to custom action data"); hr = WcaWriteStringToCaData(pwzProgram, &pwzCustomActionData); ExitOnFailure(hr, "failed to write application path to custom action data"); } else { // we have a port-only exception hr = WcaWriteIntegerToCaData(fetPort, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception target (port) to custom action data"); } hr = WcaWriteStringToCaData(pwzPort, &pwzCustomActionData); ExitOnFailure(hr, "failed to write application path to custom action data"); hr = WcaWriteIntegerToCaData(iProtocol, &pwzCustomActionData); ExitOnFailure(hr, "failed to write exception protocol to custom action data"); hr = WcaWriteStringToCaData(pwzDescription, &pwzCustomActionData); ExitOnFailure(hr, "failed to write firewall rule description to custom action data"); } // reaching the end of the list is actually a good thing, not an error if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "failure occured while processing WixFirewallException table"); // schedule ExecFirewallExceptions if there's anything to do if (pwzCustomActionData && *pwzCustomActionData) { WcaLog(LOGMSG_STANDARD, "Scheduling firewall exception (%ls)", pwzCustomActionData); if (WCA_TODO_INSTALL == todoSched) { hr = WcaDoDeferredAction(L"WixRollbackFirewallExceptionsInstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); ExitOnFailure(hr, "failed to schedule firewall install exceptions rollback"); hr = WcaDoDeferredAction(L"WixExecFirewallExceptionsInstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); ExitOnFailure(hr, "failed to schedule firewall install exceptions execution"); } else { hr = WcaDoDeferredAction(L"WixRollbackFirewallExceptionsUninstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions rollback"); hr = WcaDoDeferredAction(L"WixExecFirewallExceptionsUninstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions execution"); } } else { WcaLog(LOGMSG_STANDARD, "No firewall exceptions scheduled"); } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzName); ReleaseStr(pwzRemoteAddresses); ReleaseStr(pwzPort); ReleaseStr(pwzProgram); ReleaseStr(pwzComponent); ReleaseStr(pwzDescription); ReleaseStr(pwzFormattedFile); return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); }
/****************************************************************** WixCreateInternetShortcuts - entry point for Internet shortcuts custom action *******************************************************************/ extern "C" UINT __stdcall WixCreateInternetShortcuts( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwz = NULL; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzTarget = NULL; LPWSTR pwzShortcutPath = NULL; LPWSTR pwzIconPath = NULL; BOOL fInitializedCom = FALSE; int iAttr = 0; int iIconIndex = 0; // initialize hr = WcaInitialize(hInstall, "WixCreateInternetShortcuts"); ExitOnFailure(hr, "failed to initialize WixCreateInternetShortcuts"); hr = ::CoInitialize(NULL); ExitOnFailure(hr, "failed to initialize COM"); fInitializedCom = TRUE; // extract the custom action data hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); // loop through all the custom action data pwz = pwzCustomActionData; while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath); ExitOnFailure(hr, "failed to read shortcut path from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzTarget); ExitOnFailure(hr, "failed to read shortcut target from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iAttr); ExitOnFailure(hr, "failed to read shortcut attributes from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzIconPath); ExitOnFailure(hr, "failed to read shortcut icon path from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iIconIndex); ExitOnFailure(hr, "failed to read shortcut icon index from custom action data"); if ((iAttr & esaURL) == esaURL) { hr = CreateUrl(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex); } else { hr = CreateLink(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex); } ExitOnFailure(hr, "failed to create Internet shortcut"); // tick the progress bar hr = WcaProgressMessage(COST_INTERNETSHORTCUT, FALSE); ExitOnFailure1(hr, "failed to tick progress bar for shortcut: %ls", pwzShortcutPath); } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzTarget); ReleaseStr(pwzShortcutPath); if (fInitializedCom) { ::CoUninitialize(); } er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er; return WcaFinalize(er); }
/****************************************************************** ExecFirewallExceptions - deferred custom action entry point to register and remove firewall exceptions. ********************************************************************/ extern "C" UINT __stdcall ExecFirewallExceptions( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; BOOL fSupportProfiles = FALSE; LPWSTR pwz = NULL; LPWSTR pwzCustomActionData = NULL; int iTodo = WCA_TODO_UNKNOWN; LPWSTR pwzName = NULL; LPWSTR pwzRemoteAddresses = NULL; int iAttributes = 0; int iTarget = fetUnknown; LPWSTR pwzFile = NULL; LPWSTR pwzPort = NULL; LPWSTR pwzDescription = NULL; int iProtocol = 0; int iProfile = 0; // initialize hr = WcaInitialize(hInstall, "ExecFirewallExceptions"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); hr = ::CoInitialize(NULL); ExitOnFailure(hr, "failed to initialize COM"); // Find out if we support profiles (only on Vista or later) fSupportProfiles = FSupportProfiles(); // loop through all the passed in data pwz = pwzCustomActionData; while (pwz && *pwz) { // extract the custom action data and if rolling back, swap INSTALL and UNINSTALL hr = WcaReadIntegerFromCaData(&pwz, &iTodo); ExitOnFailure(hr, "failed to read todo from custom action data"); if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)) { if (WCA_TODO_INSTALL == iTodo) { iTodo = WCA_TODO_UNINSTALL; } else if (WCA_TODO_UNINSTALL == iTodo) { iTodo = WCA_TODO_INSTALL; } } hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to read name from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iProfile); ExitOnFailure(hr, "failed to read profile from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzRemoteAddresses); ExitOnFailure(hr, "failed to read remote addresses from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); ExitOnFailure(hr, "failed to read attributes from custom action data"); BOOL fIgnoreFailures = feaIgnoreFailures == (iAttributes & feaIgnoreFailures); hr = WcaReadIntegerFromCaData(&pwz, &iTarget); ExitOnFailure(hr, "failed to read target from custom action data"); if (iTarget == fetApplication) { hr = WcaReadStringFromCaData(&pwz, &pwzFile); ExitOnFailure(hr, "failed to read file path from custom action data"); } hr = WcaReadStringFromCaData(&pwz, &pwzPort); ExitOnFailure(hr, "failed to read port from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iProtocol); ExitOnFailure(hr, "failed to read protocol from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzDescription); ExitOnFailure(hr, "failed to read protocol from custom action data"); switch (iTarget) { case fetPort: switch (iTodo) { case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol); hr = AddPortException(fSupportProfiles, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); ExitOnFailure3(hr, "failed to add/update port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol); break; case WCA_TODO_UNINSTALL: WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol); hr = RemovePortException(fSupportProfiles, pwzName, pwzPort, iProtocol, fIgnoreFailures); ExitOnFailure3(hr, "failed to remove port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol); break; } break; case fetApplication: switch (iTodo) { case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls (%ls)", pwzName, pwzFile); hr = AddApplicationException(fSupportProfiles, pwzFile, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); ExitOnFailure2(hr, "failed to add/update application exception for name '%ls', file '%ls'", pwzName, pwzFile); break; case WCA_TODO_UNINSTALL: WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls (%ls)", pwzName, pwzFile); hr = RemoveApplicationException(fSupportProfiles, pwzName, pwzFile, fIgnoreFailures, pwzPort, iProtocol); ExitOnFailure2(hr, "failed to remove application exception for name '%ls', file '%ls'", pwzName, pwzFile); break; } break; } } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzName); ReleaseStr(pwzRemoteAddresses); ReleaseStr(pwzFile); ReleaseStr(pwzPort); ReleaseStr(pwzDescription); ::CoUninitialize(); return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); }