extern "C" HRESULT CatalogElevatedUpdateCatalogFile( __in BURN_CATALOGS* pCatalogs, __in_z LPCWSTR wzId, __in_z LPCWSTR wzPath ) { HRESULT hr = S_OK; BURN_CATALOG* pCatalog = NULL; // Find the catalog hr = CatalogFindById(pCatalogs, wzId, &pCatalog); ExitOnFailure(hr, "Failed to locate catalog information."); if (NULL == pCatalog->sczLocalFilePath) { hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0); ExitOnFailure(hr, "Failed to allocated catalog path."); // Get a handle to the file pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == pCatalog->hFile) { ExitWithLastError1(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); } } LExit: return hr; }
static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) { HRESULT hr = S_OK; DWORD dwMoveMethod; LONG lMove = 0; switch (seektype) { case 0: // SEEK_SET dwMoveMethod = FILE_BEGIN; dist += static_cast<long>(vdw64EmbeddedOffset); break; case 1: /// SEEK_CUR dwMoveMethod = FILE_CURRENT; break; case 2: // SEEK_END dwMoveMethod = FILE_END; break; default : dwMoveMethod = 0; hr = E_UNEXPECTED; ExitOnFailure1(hr, "unexpected seektype in FDISeek(): %d", seektype); } // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod); if (0xFFFFFFFF == lMove) { ExitWithLastError1(hr, "failed to move file pointer %d bytes", dist); } LExit: return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset); }
HRESULT LaunchTarget( __in LPWSTR wzCommandline, __in LPWSTR wzCurrentDirectory, __out HANDLE * phProcess ) { HRESULT hr = S_OK; STARTUPINFOW startupInfo = {0}; PROCESS_INFORMATION procInfo = {0}; if (!::CreateProcessW(NULL, wzCommandline, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, wzCurrentDirectory, &startupInfo, &procInfo)) { ExitWithLastError1(hr, "Failed to execute %S.", wzCommandline); } LExit: if (procInfo.hThread) { ::CloseHandle(procInfo.hThread); } if (procInfo.hProcess) { *phProcess = procInfo.hProcess; } return hr; }
static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) { HRESULT hr = S_OK; INT_PTR pFile = -1; LPWSTR sczCabFile = NULL; // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) { hr = E_OUTOFMEMORY; ExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); } hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); ExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); pFile = reinterpret_cast<INT_PTR>(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile)) { ExitWithLastError1(hr, "failed to open file: %ls", sczCabFile); } if (vdw64EmbeddedOffset) { hr = CabExtractSeek(pFile, 0, 0); ExitOnFailure1(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); } LExit: ReleaseStr(sczCabFile); return FAILED(hr) ? -1 : pFile; }
HRESULT SendQwordString( __in DWORD dwThreadId, __in DWORD dwMessageId, __in DWORD dwDatabaseIndex, __in DWORD64 qwQword1, __in_z LPCWSTR wzString1 ) { HRESULT hr = S_OK; QWORD_STRING *pQwordString = NULL; pQwordString = static_cast<QWORD_STRING *>(MemAlloc(sizeof(QWORD_STRING), TRUE)); pQwordString->qwQword1 = qwQword1; if (NULL != wzString1) { hr = StrAllocString(&pQwordString->sczString1, wzString1, 0); ExitOnFailure1(hr, "Failed to allocate copy of string1: %ls", wzString1); } if (!::PostThreadMessageW(dwThreadId, dwMessageId, dwDatabaseIndex, reinterpret_cast<LPARAM>(pQwordString))) { ExitWithLastError1(hr, "Failed to send message %u to worker thread", dwMessageId); } pQwordString = NULL; LExit: ReleaseQwordString(pQwordString); return hr; }
static HRESULT InitializeResume( __in LPCWSTR wzDestinationPath, __out LPWSTR* psczResumePath, __out HANDLE* phResumeFile, __out DWORD64* pdw64ResumeOffset ) { HRESULT hr = S_OK; HANDLE hResumeFile = INVALID_HANDLE_VALUE; DWORD cbTotalReadResumeData = 0; DWORD cbReadData = 0; *pdw64ResumeOffset = 0; hr = CacheGetResumePath(wzDestinationPath, psczResumePath); ExitOnFailure1(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hResumeFile) { ExitWithLastError1(hr, "Failed to create resume file: %ls", *psczResumePath); } do { if (!::ReadFile(hResumeFile, reinterpret_cast<BYTE*>(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) { ExitWithLastError1(hr, "Failed to read resume file: %ls", *psczResumePath); } cbTotalReadResumeData += cbReadData; } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); // Start over if we couldn't get a resume offset. if (cbTotalReadResumeData != sizeof(DWORD64)) { *pdw64ResumeOffset = 0; } *phResumeFile = hResumeFile; hResumeFile = INVALID_HANDLE_VALUE; LExit: ReleaseFileHandle(hResumeFile); return hr; }
/******************************************************************** SetPropertyWellKnownSID Set a property with the localized name of a well known windows SID ********************************************************************/ static HRESULT SetPropertyWellKnownSID( __in WELL_KNOWN_SID_TYPE sidType, __in LPCWSTR wzPropertyName, __in BOOL fIncludeDomainName ) { HRESULT hr = S_OK; PSID psid = NULL; WCHAR wzRefDomain[MAX_PATH] = {0}; SID_NAME_USE nameUse; DWORD refSize = MAX_PATH; WCHAR wzName[MAX_PATH] = {0}; LPWSTR pwzPropertyValue = NULL; DWORD size = MAX_PATH; hr = AclGetWellKnownSid(sidType, &psid); ExitOnFailure1(hr, "Failed to get SID; skipping account %ls", wzPropertyName); if (!::LookupAccountSidW(NULL, psid, wzName, &size, wzRefDomain, &refSize, &nameUse)) { ExitWithLastError1(hr, "Failed to look up account for SID; skipping account %ls.", wzPropertyName); } if (fIncludeDomainName) { hr = StrAllocFormatted(&pwzPropertyValue, L"%s\\%s", wzRefDomain, wzName); ExitOnFailure(hr, "Failed to format property value"); hr = WcaSetProperty(wzPropertyName, pwzPropertyValue); ExitOnFailure(hr, "Failed write domain\\name property"); } else { hr = WcaSetProperty(wzPropertyName, wzName); ExitOnFailure(hr, "Failed write name-only property"); } LExit: if (NULL != psid) { ::LocalFree(psid); } ReleaseStr(pwzPropertyValue); return hr; }
HRESULT SendStringTriplet( __in DWORD dwThreadId, __in DWORD dwMessageId, __in DWORD dwDatabaseIndex, __in_z LPCWSTR wzString1, __in_z LPCWSTR wzString2, __in_z LPCWSTR wzString3 ) { HRESULT hr = S_OK; STRING_TRIPLET *pStringTriplet = NULL; pStringTriplet = static_cast<STRING_TRIPLET *>(MemAlloc(sizeof(STRING_TRIPLET), TRUE)); if (NULL != wzString1) { hr = StrAllocString(&pStringTriplet->sczString1, wzString1, 0); ExitOnFailure1(hr, "Failed to allocate copy of string1: %ls", wzString1); } if (NULL != wzString2) { hr = StrAllocString(&pStringTriplet->sczString2, wzString2, 0); ExitOnFailure1(hr, "Failed to allocate copy of string2: %ls", wzString2); } if (NULL != wzString3) { hr = StrAllocString(&pStringTriplet->sczString3, wzString3, 0); ExitOnFailure1(hr, "Failed to allocate copy of string3: %ls", wzString3); } if (!::PostThreadMessageW(dwThreadId, dwMessageId, dwDatabaseIndex, reinterpret_cast<LPARAM>(pStringTriplet))) { ExitWithLastError1(hr, "Failed to send message %u to worker thread", dwMessageId); } pStringTriplet = NULL; LExit: ReleaseStringTriplet(pStringTriplet); return hr; }
/******************************************************************** WcaCaScriptCreate() - creates the appropriate script for this CustomAction Script Key. ********************************************************************/ extern "C" HRESULT WIXAPI WcaCaScriptCreate( __in WCA_ACTION action, __in WCA_CASCRIPT script, __in BOOL fImpersonated, __in LPCWSTR wzScriptKey, __in BOOL fAppend, __in WCA_CASCRIPT_HANDLE* phScript ) { HRESULT hr = S_OK; LPWSTR pwzScriptPath = NULL; HANDLE hScriptFile = INVALID_HANDLE_VALUE; hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); ExitOnFailure(hr, "Failed to calculate script file name."); hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hScriptFile) { ExitWithLastError1(hr, "Failed to open CaScript: %S", pwzScriptPath); } if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END)) { ExitWithLastError(hr, "Failed to seek to end of file."); } *phScript = reinterpret_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); (*phScript)->pwzScriptPath = pwzScriptPath; pwzScriptPath = NULL; (*phScript)->hScriptFile = hScriptFile; hScriptFile = INVALID_HANDLE_VALUE; LExit: if (INVALID_HANDLE_VALUE != hScriptFile) { ::CloseHandle(hScriptFile); } ReleaseStr(pwzScriptPath); return hr; }
/******************************************************************** RmuAddProcessById - Adds the process ID to the Restart Manager sesion. You should call this multiple times as necessary before calling RmuRegisterResources. ********************************************************************/ extern "C" HRESULT DAPI RmuAddProcessById( __in PRMU_SESSION pSession, __in DWORD dwProcessId ) { HRESULT hr = S_OK; HANDLE hProcess = NULL; FILETIME CreationTime = {}; FILETIME ExitTime = {}; FILETIME KernelTime = {}; FILETIME UserTime = {}; BOOL fLocked = FALSE; hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); ExitOnNullWithLastError1(hProcess, hr, "Failed to open the process ID %d.", dwProcessId); if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) { ExitWithLastError1(hr, "Failed to get the process times for process ID %d.", dwProcessId); } ::EnterCriticalSection(&pSession->cs); fLocked = TRUE; hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); ExitOnFailure(hr, "Failed to add the application to the array."); LExit: if (hProcess) { ::CloseHandle(hProcess); } if (fLocked) { ::LeaveCriticalSection(&pSession->cs); } return hr; }
/******************************************************************** ResReadStringAnsi NOTE: ppszString should be freed with StrFree() ********************************************************************/ extern "C" HRESULT DAPI ResReadStringAnsi( __in HINSTANCE hinst, __in UINT uID, __deref_out_z LPSTR* ppszString ) { Assert(hinst && ppszString); HRESULT hr = S_OK; DWORD cch = 64; // first guess DWORD cchReturned = 0; do { hr = StrAnsiAlloc(ppszString, cch); ExitOnFailureDebugTrace1(hr, "Failed to allocate string for resource id: %d", uID); #pragma prefast(push) #pragma prefast(disable:25068) cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); #pragma prefast(pop) if (0 == cchReturned) { ExitWithLastError1(hr, "Failed to load string resource id: %d", uID); } // if the returned string count is one character too small, it's likely we have // more data to read if (cchReturned + 1 == cch) { cch *= 2; hr = S_FALSE; } } while (S_FALSE == hr); ExitOnFailure1(hr, "failed to load string resource id: %d", uID); LExit: return hr; }
/******************************************************************** ShelExec() - executes a target. *******************************************************************/ extern "C" HRESULT DAPI ShelExec( __in_z LPCWSTR wzTargetPath, __in_z_opt LPCWSTR wzParameters, __in_z_opt LPCWSTR wzVerb, __in_z_opt LPCWSTR wzWorkingDirectory, __in int nShowCmd, __in_opt HWND hwndParent, __out_opt HANDLE* phProcess ) { HRESULT hr = S_OK; SHELLEXECUTEINFOW shExecInfo = {}; shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; shExecInfo.hwnd = hwndParent; shExecInfo.lpVerb = wzVerb; shExecInfo.lpFile = wzTargetPath; shExecInfo.lpParameters = wzParameters; shExecInfo.lpDirectory = wzWorkingDirectory; shExecInfo.nShow = nShowCmd; if (!vpfnShellExecuteExW(&shExecInfo)) { ExitWithLastError1(hr, "ShellExecEx failed with return code: %d", Dutil_er); } if (phProcess) { *phProcess = shExecInfo.hProcess; shExecInfo.hProcess = NULL; } LExit: ReleaseHandle(shExecInfo.hProcess); return hr; }
extern "C" HRESULT CatalogLoadFromPayload( __in BURN_CATALOGS* pCatalogs, __in BURN_PAYLOADS* pPayloads ) { HRESULT hr = S_OK; BURN_CATALOG* pCatalog = NULL; BURN_PAYLOAD* pPayload = NULL; // go through each catalog file for (DWORD i = 0; i < pCatalogs->cCatalogs; i++) { pCatalog = &pCatalogs->rgCatalogs[i]; // get the payload for this catalog file hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload); ExitOnFailure(hr, "Failed to find payload for catalog file."); // Get the local file name hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0); ExitOnFailure(hr, "Failed to get catalog local file path"); // Get a handle to the file pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == pCatalog->hFile) { ExitWithLastError1(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); } // Verify the catalog file hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile); ExitOnFailure1(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath); } LExit: return hr; }
static HRESULT DownloadResource( __in BURN_USER_EXPERIENCE* pUX, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in HINTERNET hSession, __inout_z LPWSTR* psczUrl, __in_z_opt LPCWSTR wzUser, __in_z_opt LPCWSTR wzPassword, __in_z LPCWSTR wzDestinationPath, __in DWORD64 dw64AuthoredResourceLength, __in DWORD64 dw64ResourceLength, __in DWORD64 dw64ResumeOffset, __in HANDLE hResumeFile, __in_opt BURN_CACHE_CALLBACK* pCallback ) { HRESULT hr = S_OK; HANDLE hPayloadFile = INVALID_HANDLE_VALUE; DWORD cbMaxData = 64 * 1024; // 64 KB BYTE* pbData = NULL; BOOL fRangeRequestsAccepted = TRUE; LPWSTR sczRangeRequestHeader = NULL; HINTERNET hConnect = NULL; HINTERNET hUrl = NULL; LONGLONG llLength = 0; hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hPayloadFile) { ExitWithLastError1(hr, "Failed to create download destination file: %ls", wzDestinationPath); } // Allocate a memory block on a page boundary in case we want to do optimal writing. pbData = static_cast<BYTE*>(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); // Let's try downloading the file assuming that range requests are accepted. If range requests // are not supported we'll have to start over and accept the fact that we only get one shot // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't // like files that big. while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) { hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); ExitOnFailure(hr, "Failed to allocate range request header."); ReleaseNullInternet(hConnect); ReleaseNullInternet(hUrl); hr = MakeRequest(pUX, wzPackageOrContainerId, wzPayloadId, hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, &hConnect, &hUrl, &fRangeRequestsAccepted); ExitOnFailure1(hr, "Failed to request URL for download: %ls", *psczUrl); // If we didn't get the size of the resource from the initial "HEAD" request // then let's try to get the size from this "GET" request. if (0 == dw64ResourceLength) { hr = InternetGetSizeByHandle(hUrl, &llLength); if (SUCCEEDED(hr)) { dw64ResourceLength = llLength; } else // server didn't tell us the resource length. { // Fallback to the authored size of the resource. However, since we // don't really know the size on the server, don't try to use // range requests either. dw64ResourceLength = dw64AuthoredResourceLength; fRangeRequestsAccepted = FALSE; } } // If we just tried to do a range request and found out that it isn't supported, start over. if (!fRangeRequestsAccepted) { // TODO: log a message that the server did not accept range requests. dw64ResumeOffset = 0; } hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCallback); ExitOnFailure1(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); } LExit: ReleaseInternet(hUrl); ReleaseInternet(hConnect); ReleaseStr(sczRangeRequestHeader); if (pbData) { ::VirtualFree(pbData, 0, MEM_RELEASE); } ReleaseFileHandle(hPayloadFile); return hr; }
static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) { Assert(pFDINotify->pv); HRESULT hr = S_OK; INT_PTR ipResult = 0; // result to return on success CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv); LPCSTR sz; WCHAR wz[MAX_PATH]; FILETIME ft; switch (iNotification) { case fdintCOPY_FILE: // begin extracting a resource from cabinet ExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); if (pccs->fStopExtracting) { ExitFunction1(hr = S_FALSE); // no more extracting } // convert params to useful variables sz = static_cast<LPCSTR>(pFDINotify->psz1); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { ExitWithLastError1(hr, "failed to convert cabinet file id to unicode: %s", sz); } if (pccs->pfnProgress) { hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); if (S_OK != hr) { ExitFunction(); } } if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) { // get the created date for the resource in the cabinet FILETIME ftLocal; if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) { ExitWithLastError1(hr, "failed to get time for resource: %ls", wz); } ::LocalFileTimeToFileTime(&ftLocal, &ft); WCHAR wzPath[MAX_PATH]; hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); ExitOnFailure2(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); hr = ::StringCchCatW(wzPath, countof(wzPath), wz); ExitOnFailure2(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); ipResult = reinterpret_cast<INT_PTR>(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(ipResult)) { ExitWithLastError1(hr, "failed to create file: %s", wzPath); } ::SetFileTime(reinterpret_cast<HANDLE>(ipResult), &ft, &ft, &ft); // try to set the file time (who cares if it fails) if (::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) { if (::SetEndOfFile(reinterpret_cast<HANDLE>(ipResult))) { ::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), 0, NULL, FILE_BEGIN); // reset the file pointer } } } else // resource wasn't requested, skip it { hr = S_OK; ipResult = 0; } break; case fdintCLOSE_FILE_INFO: // resource extraction complete Assert(pFDINotify->hf && pFDINotify->psz1); ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); // convert params to useful variables sz = static_cast<LPCSTR>(pFDINotify->psz1); ExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) { ExitWithLastError1(hr, "failed to convert cabinet file id to unicode: %s", sz); } if (NULL != pFDINotify->hf) // just close the file { ::CloseHandle(reinterpret_cast<HANDLE>(pFDINotify->hf)); } if (pccs->pfnProgress) { hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); } if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going { ipResult = TRUE; } else // something went wrong or we only needed to extract one file { hr = S_OK; ipResult = FALSE; pccs->fStopExtracting = TRUE; } break; case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through case fdintNEXT_CABINET: __fallthrough; case fdintENUMERATE: __fallthrough; case fdintCABINET_INFO: break; default: AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); }; LExit: return (S_OK == hr) ? ipResult : -1; }
DAPI_(HRESULT) PathExpand( __out LPWSTR *psczFullPath, __in_z LPCWSTR wzRelativePath, __in DWORD dwResolveFlags ) { Assert(wzRelativePath && *wzRelativePath); HRESULT hr = S_OK; DWORD cch = 0; LPWSTR sczExpandedPath = NULL; DWORD cchExpandedPath = 0; LPWSTR sczFullPath = NULL; // // First, expand any environment variables. // if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) { cchExpandedPath = PATH_GOOD_ENOUGH; hr = StrAlloc(&sczExpandedPath, cchExpandedPath); ExitOnFailure(hr, "Failed to allocate space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); if (0 == cch) { ExitWithLastError1(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { cchExpandedPath = cch; hr = StrAlloc(&sczExpandedPath, cchExpandedPath); ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); if (0 == cch) { ExitWithLastError1(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); } else if (cchExpandedPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ExitOnFailure(hr, "Failed to allocate buffer for expanded path."); } } if (MAX_PATH < cch) { hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet if (E_INVALIDARG == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); hr = StrMaxLength(sczExpandedPath, reinterpret_cast<DWORD_PTR *>(&cchExpandedPath)); ExitOnFailure(hr, "Failed to get max length of expanded path."); } } // // Second, get the full path. // if (dwResolveFlags & PATH_EXPAND_FULLPATH) { LPWSTR wzFileName = NULL; LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; DWORD cchFullPath = PATH_GOOD_ENOUGH < cchExpandedPath ? cchExpandedPath : PATH_GOOD_ENOUGH; hr = StrAlloc(&sczFullPath, cchFullPath); ExitOnFailure(hr, "Failed to allocate space for full path."); cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); if (0 == cch) { ExitWithLastError1(hr, "Failed to get full path for string: %ls", wzPath); } else if (cchFullPath < cch) { cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed hr = StrAlloc(&sczFullPath, cchFullPath); ExitOnFailure(hr, "Failed to re-allocate more space for full path."); cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); if (0 == cch) { ExitWithLastError1(hr, "Failed to get full path for string: %ls", wzPath); } else if (cchFullPath < cch) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ExitOnFailure(hr, "Failed to allocate buffer for full path."); } } if (MAX_PATH < cch) { hr = PathPrefix(&sczFullPath); ExitOnFailure(hr, "Failed to prefix long path after expanding."); } } else { sczFullPath = sczExpandedPath; sczExpandedPath = NULL; } hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); ExitOnFailure(hr, "Failed to copy relative path into full path."); LExit: ReleaseStr(sczFullPath); ReleaseStr(sczExpandedPath); return hr; }
// Searches subdirectories of the given path for the highest version of ngen.exe available static HRESULT GetNgenVersion( __in LPWSTR pwzParentPath, __out LPWSTR* ppwzVersion ) { Assert(pwzParentPath); HRESULT hr = S_OK; DWORD dwError = 0; DWORD dwNgenFileFlags = 0; LPWSTR pwzVersionSearch = NULL; LPWSTR pwzNgen = NULL; LPWSTR pwzTemp = NULL; LPWSTR pwzTempVersion = NULL; DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering BOOL fFound = TRUE; WIN32_FIND_DATAW wfdVersionDirectories; HANDLE hFind = INVALID_HANDLE_VALUE; hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath); ExitOnFailure1(hr, "failed to create outer directory search string from string %ls", pwzParentPath); hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories); if (hFind == INVALID_HANDLE_VALUE) { ExitWithLastError1(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch); } while (fFound) { pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName); // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion)) { // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion); ExitOnFailure2(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion); // If Ngen.exe does exist as a file here, then let's check the file version if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY))) { hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion); if (FAILED(hr)) { WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen); } else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion)) { // If the version we found is the highest we've seen so far in this search, it will be our new best-so-far candidate hr = StrAllocString(ppwzVersion, pwzTempVersion, 0); ExitOnFailure1(hr, "failed to copy temp version string %ls to version string", pwzTempVersion); // Add one for the backslash after the directory name WcaLog(LOGMSG_VERBOSE, "Found highest-so-far version of ngen.exe (in directory %ls, version %u.%u.%u.%u)", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); dwMaxMajorVersion = dwMajorVersion; dwMaxMinorVersion = dwMinorVersion; } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it is from .NET Framework v1.1, which is not backwards compatible with other versions of the Framework and thus is not supported by this custom action.", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion); } fFound = FindNextFileW(hFind, &wfdVersionDirectories); if (!fFound) { dwError = ::GetLastError(); hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError); ExitOnFailure1(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch); } } if (NULL == *ppwzVersion) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); ExitOnRootFailure1(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath); } else { WcaLog(LOGMSG_VERBOSE, "Using highest version of ngen found, located in this subdirectory: %ls, version %u.%u.%u.%u", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); } LExit: if (hFind != INVALID_HANDLE_VALUE) { if (0 == FindClose(hFind)) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr); } hFind = INVALID_HANDLE_VALUE; } ReleaseStr(pwzVersionSearch); ReleaseStr(pwzNgen); ReleaseStr(pwzTemp); // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories return hr; }
static HRESULT InstallCertificate( __in HCERTSTORE hStore, __in BOOL fUserCertificateStore, __in LPCWSTR wzName, __in_opt BYTE* rgbData, __in DWORD cbData, __in_opt LPCWSTR wzPFXPassword ) { HRESULT hr = S_OK; HCERTSTORE hPfxCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CONTEXT pCertContextDelete = NULL; CERT_BLOB blob = { 0 }; DWORD dwEncodingType; DWORD dwContentType; DWORD dwFormatType; // Figure out what type of blob (certificate or PFX) we're dealing with here. blob.pbData = rgbData; blob.cbData = cbData; if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) { ExitWithLastError1(hr, "Failed to parse the certificate blob: %S", wzName); } if (!pCertContext) { // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. if (dwContentType & CERT_QUERY_CONTENT_PFX) { hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET); ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); // There should be at least one certificate in the PFX. pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, NULL); ExitOnNullWithLastError(pCertContext, hr, "Failed to read first certificate out of PFX file."); } else { hr = E_UNEXPECTED; ExitOnFailure(hr, "Unexpected certificate type processed."); } } // Update the friendly name of the certificate to be configured. blob.pbData = (BYTE*)wzName; blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) { ExitWithLastError1(hr, "Failed to set the friendly name of the certificate: %S", wzName); } WcaLog(LOGMSG_STANDARD, "Adding certificate: %S", wzName); if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) { MessageExitOnLastError(hr, msierrCERTFailedAdd, "Failed to add certificate to the store."); } hr = WcaProgressMessage(COST_CERT_ADD, FALSE); ExitOnFailure(hr, "Failed to send install progress message."); LExit: if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } // Close the stores after the context's are released. if (hPfxCertStore) { ::CertCloseStore(hPfxCertStore, 0); } return hr; }
/******************************************************************* PipeCreatePipes - create the pipes and event to signal child process. *******************************************************************/ extern "C" HRESULT PipeCreatePipes( __in BURN_PIPE_CONNECTION* pConnection, __in BOOL fCreateCachePipe, __out HANDLE* phEvent ) { Assert(pConnection->sczName); Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); HRESULT hr = S_OK; PSECURITY_DESCRIPTOR psd = NULL; SECURITY_ATTRIBUTES sa = { }; LPWSTR sczFullPipeName = NULL; HANDLE hPipe = INVALID_HANDLE_VALUE; HANDLE hCachePipe = INVALID_HANDLE_VALUE; // Only the grant special rights when the pipe is being used for "embedded" // scenarios (aka: there is no cache pipe). if (!fCreateCachePipe) { // Create the security descriptor that grants read/write/sync access to Everyone. // TODO: consider locking down "WD" to LogonIds (logon session) LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) { ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); } sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = psd; sa.bInheritHandle = FALSE; } // Create the pipe. hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure1(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); if (INVALID_HANDLE_VALUE == hPipe) { ExitWithLastError1(hr, "Failed to create pipe: %ls", sczFullPipeName); } if (fCreateCachePipe) { // Create the cache pipe. hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure1(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); if (INVALID_HANDLE_VALUE == hCachePipe) { ExitWithLastError1(hr, "Failed to create pipe: %ls", sczFullPipeName); } } pConnection->hCachePipe = hCachePipe; hCachePipe = INVALID_HANDLE_VALUE; pConnection->hPipe = hPipe; hPipe = INVALID_HANDLE_VALUE; // TODO: remove the following *phEvent = NULL; LExit: ReleaseFileHandle(hCachePipe); ReleaseFileHandle(hPipe); ReleaseStr(sczFullPipeName); if (psd) { ::LocalFree(psd); } return hr; }
extern "C" UINT __stdcall WixShellExecBinary( __in MSIHANDLE hInstall ) { Assert(hInstall); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzBinary = NULL; LPWSTR pwzFilename = NULL; BYTE* pbData = NULL; DWORD cbData = 0; HANDLE hFile = INVALID_HANDLE_VALUE; #if 0 ::MessageBoxA(0, "WixShellExecBinary", "-->> ATTACH HERE", MB_OK); #endif hr = WcaInitialize(hInstall, "WixShellExecBinary"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetFormattedProperty(L"WixShellExecBinaryId", &pwzBinary); ExitOnFailure(hr, "failed to get WixShellExecBinaryId"); WcaLog(LOGMSG_VERBOSE, "WixShellExecBinaryId is %ls", pwzBinary); if (!pwzBinary || !*pwzBinary) { hr = E_INVALIDARG; ExitOnFailure(hr, "failed to get WixShellExecBinaryId"); } // get temporary path for extracted file StrAlloc(&pwzFilename, MAX_PATH); ExitOnFailure(hr, "Failed to allocate temporary path"); ::GetTempPathW(MAX_PATH, pwzFilename); hr = ::StringCchCatW(pwzFilename, MAX_PATH, pwzBinary); ExitOnFailure(hr, "Failed to append filename."); // grab the bits hr = ExtractBinary(pwzBinary, &pbData, &cbData); ExitOnFailure(hr, "failed to extract binary data"); // write 'em to the temp file hFile = ::CreateFileW(pwzFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { ExitWithLastError1(hr, "Failed to open new temp file: %ls", pwzFilename); } DWORD cbWritten = 0; if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL)) { ExitWithLastError1(hr, "Failed to write data to new temp file: %ls", pwzFilename); } // close it ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; // and run it hr = ShellExec(pwzFilename); ExitOnFailure1(hr, "failed to launch target: %ls", pwzFilename); LExit: ReleaseStr(pwzBinary); ReleaseStr(pwzFilename); ReleaseMem(pbData); if (INVALID_HANDLE_VALUE != hFile) { ::CloseHandle(hFile); } if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
HRESULT RssUpdateTryLaunchUpdate( __in LPCWSTR wzAppId, __in DWORD64 dw64AppVersion, __out HANDLE* phUpdateProcess, __out_opt DWORD64* pdw64NextUpdateTime ) { HRESULT hr = S_OK; DWORD64 dw64NextUpdateTime = 0; BOOL fUpdateReady = FALSE; DWORD64 dw64UpdateVersion = 0; LPWSTR pwzLocalFeedPath = NULL; LPWSTR pwzLocalSetupPath = NULL; STARTUPINFOW startupInfo = {0}; PROCESS_INFORMATION procInfo = {0}; // If an update is available and higher version that the application currently on the local // machine, launch the install. hr = RssUpdateGetUpdateInfo(wzAppId, &dw64NextUpdateTime, &fUpdateReady, &dw64UpdateVersion, &pwzLocalFeedPath, &pwzLocalSetupPath); if (SUCCEEDED(hr) && fUpdateReady) { if (dw64AppVersion < dw64UpdateVersion) { Trace1(REPORT_DEBUG, "Launching a previously downloaded update at %ls.", pwzLocalSetupPath); if (!::CreateProcessW(NULL, pwzLocalSetupPath, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &procInfo)) { ExitWithLastError1(hr, "Failed to execute %S.", pwzLocalSetupPath); } RssUpdateDeleteUpdateInfo(wzAppId); ExitFunction(); } else // update is not newer, ignore it and continue normally { RssUpdateSetUpdateInfo(wzAppId, dw64NextUpdateTime, 0, NULL, NULL); } } if (pdw64NextUpdateTime) { *pdw64NextUpdateTime = dw64NextUpdateTime; } *phUpdateProcess = procInfo.hProcess; procInfo.hProcess = NULL; LExit: if (procInfo.hThread) { ::CloseHandle(procInfo.hThread); } if (procInfo.hProcess) { ::CloseHandle(procInfo.hProcess); } ReleaseStr(pwzLocalSetupPath); ReleaseStr(pwzLocalFeedPath); return hr; }
/******************************************************************* EmbeddedLaunchChildProcess - *******************************************************************/ extern "C" HRESULT EmbeddedRunBundle( __in LPCWSTR wzExecutablePath, __in LPCWSTR wzArguments, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out DWORD* pdwExitCode ) { HRESULT hr = S_OK; DWORD dwCurrentProcessId = ::GetCurrentProcessId(); HANDLE hCreatedPipesEvent = NULL; LPWSTR sczCommand = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; BURN_PIPE_RESULT result = { }; BURN_PIPE_CONNECTION connection = { }; PipeConnectionInitialize(&connection); BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; context.pfnGenericMessageHandler = pfnGenericMessageHandler; context.pvContext = pvContext; hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent); ExitOnFailure(hr, "Failed to create embedded pipe."); hr = StrAllocFormatted(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId); ExitOnFailure(hr, "Failed to allocate embedded command."); if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to create embedded process atpath: %ls", wzExecutablePath); } connection.dwProcessId = ::GetProcessId(pi.hProcess); connection.hProcess = pi.hProcess; pi.hProcess = NULL; hr = PipeWaitForChildConnect(&connection); ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result); ExitOnFailure(hr, "Failed to process messages from embedded message."); // Get the return code from the embedded process. hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode); ExitOnFailure1(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); LExit: ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); ReleaseStr(sczCommand); ReleaseHandle(hCreatedPipesEvent); PipeConnectionUninitialize(&connection); return hr; }
extern "C" HRESULT ApprovedExesLaunch( __in BURN_VARIABLES* pVariables, __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, __out DWORD* pdwProcessId ) { HRESULT hr = S_OK; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; LPWSTR sczExecutableDirectory = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; // build command if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) { hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); // Try to get the directory of the executable so we can set the current directory of the process to help those executables // that expect stuff to be relative to them. Best effort only. hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); if (FAILED(hr)) { ReleaseNullStr(sczExecutableDirectory); } LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); si.cb = sizeof(si); if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); } *pdwProcessId = pi.dwProcessId; if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) { ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); } LExit: StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseStr(sczExecutableDirectory); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); return hr; }
extern "C" HRESULT MsuEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; int nResult = IDNOACTION; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMsuPath = NULL; LPWSTR sczSystemPath = NULL; LPWSTR sczWusaPath = NULL; LPWSTR sczCommand = NULL; SC_HANDLE schWu = NULL; BOOL fWuWasDisabled = FALSE; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; GENERIC_EXECUTE_MESSAGE message = { }; DWORD dwExitCode = 0; BOOL fDoDependency = FALSE; // get wusa.exe path hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); ExitOnFailure(hr, "Failed to find System32 directory."); hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); // build command switch (pExecuteAction->msuPackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: // get cached executable path hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build executable path."); // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); ExitOnFailure(hr, "Failed to format MSU install command."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); ExitOnFailure(hr, "Failed to format MSU uninstall command."); break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for MSU package."); } if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) { hr = StrAllocConcat(&sczCommand, L" /log:", 0); ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); ExitOnFailure(hr, "Failed to append log path to MSU command-line."); } LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); hr = EnsureWUServiceEnabled(&schWu, &fWuWasDisabled); ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); // create process si.cb = sizeof(si); if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); // wait for process to terminate hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); // get process exit code if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) { ExitWithLastError(hr, "Failed to get process exit code."); } // We'll normalize the restart required error code from wusa.exe just in case. Most likely // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) { dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; } // handle exit code switch (dwExitCode) { case S_OK: __fallthrough; case WU_S_ALREADY_INSTALLED: fDoDependency = TRUE; __fallthrough; case S_FALSE: __fallthrough; case WU_E_NOT_APPLICABLE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; hr = S_OK; break; case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; case WU_S_REBOOT_REQUIRED: fDoDependency = TRUE; *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; hr = S_OK; break; default: hr = E_UNEXPECTED; break; } if (fDoDependency) { if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msuPackage.action) { hr = DependencyRegisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to register the package dependency providers."); } else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msuPackage.action) { hr = DependencyUnregisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to unregister the package dependency providers."); } } LExit: ReleaseStr(sczCachedDirectory); ReleaseStr(sczMsuPath); ReleaseStr(sczSystemPath); ReleaseStr(sczWusaPath); ReleaseStr(sczCommand); ReleaseHandle(pi.hProcess); ReleaseHandle(pi.hThread); if (fWuWasDisabled) { SetServiceStartType(schWu, SERVICE_DISABLED); } return hr; }
extern "C" HRESULT ExeEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WCHAR wzCurrentDirectory[MAX_PATH] = { }; BOOL fChangedCurrentDirectory = FALSE; int nResult = IDNOACTION; LPCWSTR wzArguments = NULL; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCachedDirectory = NULL; LPWSTR sczExecutablePath = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; // get cached executable path hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); ExitOnFailure(hr, "Failed to build executable path."); // pick arguments switch (pExecuteAction->exePackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for executable package."); } // build command if (wzArguments && *wzArguments) { hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { // Add the list of dependencies to ignore, if any, to the burn command line. if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); } // Add the list of ancestors, if any, to the burn command line. if (pExecuteAction->exePackage.sczAncestors) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); } } // Log before we add the secret pipe name and client token for embedded processes. LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); } else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); } else // create and wait for the executable process while sending fake progress to allow cancel. { // Make the cache location of the executable the current directory to help those executables // that expect stuff to be relative to them. if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) { fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); } si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); } if (pExecuteAction->exePackage.fFireAndForget) { ::WaitForInputIdle(pi.hProcess, 5000); ExitFunction(); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); } hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode); LExit: if (fChangedCurrentDirectory) { ::SetCurrentDirectoryW(wzCurrentDirectory); } StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); ReleaseStr(sczCachedDirectory); ReleaseStr(sczExecutablePath); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); // Best effort to clear the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); return hr; }
static DWORD WINAPI LoadThreadProc( __in LPVOID pvContext ) { HRESULT hr = S_OK; LOAD_THREAD_CONTEXT* pContext = static_cast<LOAD_THREAD_CONTEXT*>(pvContext); LPWSTR sczThemePath = pContext->sczThemePath; HWND hWnd = pContext->hWnd; // We can signal the initialization event as soon as we have copied the context // values into local variables. ::SetEvent(pContext->hInit); BOOL fComInitialized = FALSE; HANDLE hDirectory = INVALID_HANDLE_VALUE; LPWSTR sczDirectory = NULL; LPWSTR wzFileName = NULL; THEME* pTheme = NULL; HANDLE_THEME* pHandle = NULL; hr = ::CoInitialize(NULL); ExitOnFailure(hr, "Failed to initialize COM on load thread."); fComInitialized = TRUE; // Open a handle to the directory so we can put a notification on it. hr = PathGetDirectory(sczThemePath, &sczDirectory); ExitOnFailure(hr, "Failed to get path directory."); wzFileName = PathFile(sczThemePath); hDirectory = ::CreateFileW(sczDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hDirectory) { ExitWithLastError1(hr, "Failed to open directory: %ls", sczDirectory); } BOOL fUpdated = FALSE; do { // Get the last modified time on the file we're loading for verification that the // file actually gets changed down below. FILETIME ftModified = { }; FileGetTime(sczThemePath, NULL, NULL, &ftModified); // Try to load the theme file. hr = ThemeLoadFromFile(sczThemePath, &pTheme); if (FAILED(hr)) { ::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_ERROR, 0, hr); } else { hr = AllocHandleTheme(pTheme, &pHandle); ExitOnFailure(hr, "Failed to allocate handle to theme"); ::SendMessageW(hWnd, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle)); pHandle = NULL; } fUpdated = FALSE; do { DWORD rgbNotifications[1024]; DWORD cbRead = 0; if (!::ReadDirectoryChangesW(hDirectory, rgbNotifications, sizeof(rgbNotifications), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &cbRead, NULL, NULL)) { ExitWithLastError1(hr, "Failed while watching directory: %ls", sczDirectory); } // Wait for half a second to let all the file handles get closed to minimize access // denied errors. ::Sleep(500); FILE_NOTIFY_INFORMATION* pNotification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(rgbNotifications); while (pNotification) { // If our file was updated, check to see if the modified time really changed. The notifications // are often trigger happy thinking the file changed two or three times in a row. Maybe it's AV // software creating the problems but actually checking the modified date works well. if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pNotification->FileName, pNotification->FileNameLength / sizeof(WCHAR), wzFileName, -1)) { FILETIME ft = { }; FileGetTime(sczThemePath, NULL, NULL, &ft); fUpdated = (ftModified.dwHighDateTime < ft.dwHighDateTime) || (ftModified.dwHighDateTime == ft.dwHighDateTime && ftModified.dwLowDateTime < ft.dwLowDateTime); break; } pNotification = pNotification->NextEntryOffset ? reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(pNotification) + pNotification->NextEntryOffset) : NULL; } } while (!fUpdated); } while(fUpdated); LExit: if (fComInitialized) { ::CoUninitialize(); } ReleaseFileHandle(hDirectory); ReleaseStr(sczDirectory); ReleaseStr(sczThemePath); return hr; }
/******************************************************************* PipeChildConnect - Called from the child process to connect back to the pipe provided by the parent process. *******************************************************************/ extern "C" HRESULT PipeChildConnect( __in BURN_PIPE_CONNECTION* pConnection, __in BOOL fConnectCachePipe ) { Assert(pConnection->sczName); Assert(pConnection->sczSecret); Assert(!pConnection->hProcess); Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); HRESULT hr = S_OK; LPWSTR sczPipeName = NULL; // Try to connect to the parent. hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent pipe."); hr = E_UNEXPECTED; for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) { pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hPipe) { hr = HRESULT_FROM_WIN32(::GetLastError()); if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); } ::Sleep(PIPE_WAIT_FOR_CONNECTION); } else // we have a connection, go with it. { hr = S_OK; } } ExitOnRootFailure1(hr, "Failed to open parent pipe: %ls", sczPipeName) // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure1(hr, "Failed to verify parent pipe: %ls", sczPipeName); if (fConnectCachePipe) { // Connect to the parent for the cache pipe. hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) { ExitWithLastError1(hr, "Failed to open parent pipe: %ls", sczPipeName) } // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure1(hr, "Failed to verify parent pipe: %ls", sczPipeName); } pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); ExitOnNullWithLastError1(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); LExit: ReleaseStr(sczPipeName); return hr; }
static HRESULT InstallCertificatePackage( __in HCERTSTORE hStore, __in BOOL fUserCertificateStore, __in LPCWSTR wzName, __in_opt BYTE* rgbData, __in DWORD cbData, __in_opt LPCWSTR wzPFXPassword ) { HRESULT hr = S_OK; HCERTSTORE hPfxCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; CERT_BLOB blob = { 0 }; DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; DWORD dwEncodingType; DWORD dwContentType; DWORD dwFormatType; LPWSTR pwzUniqueName = NULL; int iUniqueId = 0; // Figure out what type of blob (certificate or PFX) we're dealing with here. blob.pbData = rgbData; blob.cbData = cbData; if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) { ExitWithLastError1(hr, "Failed to parse the certificate blob: %ls", wzName); } hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId); ExitOnFailure(hr, "Failed to format unique name"); if (!pCertContext) { // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. if (dwContentType & CERT_QUERY_CONTENT_PFX) { ExitOnNull(wzPFXPassword, hr, E_INVALIDARG, "Failed to import PFX blob because no password was provided"); // If we fail and our password is blank, also try passing in NULL for the password (according to the docs) hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset); if (NULL == hPfxCertStore && !*wzPFXPassword) { hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset); } ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); // Install all certificates in the PFX for (pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext); pCertContext; pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext)) { WcaLog(LOGMSG_STANDARD, "Adding certificate: %ls", pwzUniqueName); hr = CertInstallSingleCertificate(hStore, pCertContext, pwzUniqueName); MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store."); hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId); ExitOnFailure(hr, "Failed to format unique name"); } } else { hr = E_UNEXPECTED; ExitOnFailure(hr, "Unexpected certificate type processed."); } } else { WcaLog(LOGMSG_STANDARD, "Adding certificate: %ls", pwzUniqueName); hr = CertInstallSingleCertificate(hStore, pCertContext, pwzUniqueName); MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store."); } hr = WcaProgressMessage(COST_CERT_ADD, FALSE); ExitOnFailure(hr, "Failed to send install progress message."); LExit: ReleaseStr(pwzUniqueName); if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } // Close the stores after the context's are released. if (hPfxCertStore) { if (!::CertCloseStore(hPfxCertStore, CERT_CLOSE_STORE_CHECK_FLAG)) { WcaLog(LOGMSG_VERBOSE, "PFX cert store was closed but not all resources were freed. Error 0x%x", GetLastError()); } } return hr; }
extern "C" HRESULT NetFxRunChainer( __in LPCWSTR wzExecutablePath, __in LPCWSTR wzArguments, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out DWORD* pdwExitCode ) { HRESULT hr = S_OK; DWORD er = 0; UUID guid = { }; WCHAR wzGuid[39]; RPC_STATUS rs = RPC_S_OK; LPWSTR sczEventName = NULL; LPWSTR sczSectionName = NULL; LPWSTR sczCommand = NULL; NetFxChainer* pNetfxChainer = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; HRESULT hrInternalError = 0; // Create the unique name suffix. rs = ::UuidCreate(&guid); hr = HRESULT_FROM_RPC(rs); ExitOnFailure(hr, "Failed to create netfx chainer guid."); if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) { hr = E_OUTOFMEMORY; ExitOnRootFailure(hr, "Failed to convert netfx chainer guid into string."); } hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); ExitOnFailure(hr, "Failed to allocate section name."); hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); ExitOnFailure(hr, "Failed to allocate event name."); hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); ExitOnFailure(hr, "Failed to create netfx chainer."); hr = StrAllocFormatted(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); si.cb = sizeof(si); if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); } HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; for (;;) { er = ::WaitForMultipleObjects(2, handles, FALSE, 100); if (WAIT_OBJECT_0 == er) { // Process has exited *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); if (E_PENDING == *pdwExitCode) { if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) { ExitWithLastError(hr, "Failed to get netfx return code."); } } else if (FAILED(hrInternalError)) { // push internal error message OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); } break; } else if (WAIT_OBJECT_0 + 1 == er) { // Chainee has notified us of a change. hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); ExitOnFailure(hr, "Failed to process netfx chainer message."); } else if (WAIT_FAILED == er) { ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); } } LExit: ReleaseStr(sczSectionName); ReleaseStr(sczEventName); ReleaseStr(sczCommand); DestroyNetFxChainer(pNetfxChainer); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); return hr; }
/******************************************************************** StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string NOTE: caller is responsible for freeing ppwz even if function fails NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) NOTE: if cchSource == 0, length of wzSource is used instead ********************************************************************/ extern "C" HRESULT DAPI StrAllocStringAnsi( __inout LPWSTR* ppwz, __in LPCSTR szSource, __in DWORD_PTR cchSource, __in UINT uiCodepage ) { Assert(ppwz && szSource); HRESULT hr = S_OK; LPWSTR pwz = NULL; DWORD_PTR cch = 0; DWORD_PTR cchDest = cchSource; // at least enough if (*ppwz) { cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) if (-1 == cch) { ExitOnFailure(hr = E_INVALIDARG, "failed to get size of destination string"); } cch /= sizeof(WCHAR); //convert the count in bytes to count in characters } if (0 == cchSource) { cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); if (0 == cchDest) { ExitWithLastError1(hr, "failed to get required size for conversion to unicode: %s", szSource); } --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below } else if (L'\0' == szSource[cchSource]) // if the source already had a null terminator, don't count that in the character count because we track it below { cchDest = cchSource - 1; } if (cch < cchDest + 1) { cch = cchDest + 1; if (cch >= MAXDWORD / sizeof(WCHAR)) { ExitOnFailure1(hr = E_OUTOFMEMORY, "Not enough memory to allocate string of size: %d", cch); } if (*ppwz) { pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); } else { pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE)); } ExitOnNull1(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %d", cch); *ppwz = pwz; } if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) { ExitWithLastError1(hr, "failed to convert to unicode: %s", szSource); } (*ppwz)[cchDest] = L'\0'; LExit: return hr; }