| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | const UINT COST_FILEFORMATTING = 2000; |
| 6 | |
| 7 | |
| 8 | // |
| 9 | // WixSchedFormatFiles - immediate CA to schedule format files CAs |
| 10 | // |
| 11 | extern "C" UINT __stdcall WixSchedFormatFiles( |
| 12 | __in MSIHANDLE hInstall |
| 13 | ) |
| 14 | { |
| 15 | HRESULT hr = S_OK; |
| 16 | UINT er = ERROR_SUCCESS; |
| 17 | PSCZ sczBinaryKey; |
| 18 | PSCZ sczFileKey; |
| 19 | PSCZ sczComponentKey; |
| 20 | PSCZ sczFormattedFile; |
| 21 | PSCZ sczFilePath; |
| 22 | PMSIHANDLE hView; |
| 23 | PMSIHANDLE hRec; |
| 24 | PSCZ sczFileContent; |
| 25 | PSCZ sczFormattedContent; |
| 26 | PSCZ sczExecCustomActionData; |
| 27 | PSCZ sczRollbackCustomActionData; |
| 28 | |
| 29 | LPCWSTR wzQuery = |
| 30 | L"SELECT `Wix4FormatFile`.`Binary_`, `Wix4FormatFile`.`File_`, `File`.`Component_` " |
| 31 | L"FROM `Wix4FormatFile`, `File` " |
| 32 | L"WHERE `Wix4FormatFile`.`File_` = `File`.`File`"; |
| 33 | enum eQuery { eqBinaryKey = 1, eqFileKey, eqComponentKey }; |
| 34 | |
| 35 | // initialize |
| 36 | hr = WcaInitialize(hInstall, "WixSchedFormatFiles"); |
| 37 | ExitOnFailure(hr, "Failed to initialize for WixSchedFormatFiles."); |
| 38 | |
| 39 | // query and loop through all the files |
| 40 | hr = WcaOpenExecuteView(wzQuery, &hView); |
| 41 | ExitOnFailure(hr, "Failed to open view on Wix4FormatFile table"); |
| 42 | |
| 43 | DWORD cFiles = 0; |
| 44 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) |
| 45 | { |
| 46 | ++cFiles; |
| 47 | |
| 48 | hr = WcaGetRecordString(hRec, eqBinaryKey, &sczBinaryKey); |
| 49 | ExitOnFailure(hr, "Failed to get Binary table key."); |
| 50 | |
| 51 | hr = WcaGetRecordString(hRec, eqFileKey, &sczFileKey); |
| 52 | ExitOnFailure(hr, "Failed to get File table key."); |
| 53 | |
| 54 | hr = WcaGetRecordString(hRec, eqComponentKey, &sczComponentKey); |
| 55 | ExitOnFailure(hr, "Failed to get Component table key."); |
| 56 | |
| 57 | // we need to know if the component's being installed, uninstalled, or reinstalled |
| 58 | WCA_TODO todo = WcaGetComponentToDo(sczComponentKey); |
| 59 | if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo) |
| 60 | { |
| 61 | // turn the file key into the path to the target file |
| 62 | hr = StrAllocFormatted(&sczFormattedFile, L"[#%ls]", sczFileKey); |
| 63 | ExitOnFailure(hr, "Failed to format file string for file: %ls", sczFileKey); |
| 64 | hr = WcaGetFormattedString(sczFormattedFile, &sczFilePath); |
| 65 | ExitOnFailure(hr, "Failed to get path for file: %ls", sczFileKey); |
| 66 | |
| 67 | // extract binary to string |
| 68 | WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN; |
| 69 | hr = WcaExtractBinaryToString(sczBinaryKey, &sczFileContent, &encoding); |
| 70 | ExitOnFailure(hr, "Failed to extract binary: %ls", sczBinaryKey); |
| 71 | |
| 72 | // format string |
| 73 | hr = WcaGetFormattedString(sczFileContent, &sczFormattedContent); |
| 74 | ExitOnFailure(hr, "Failed to format file content: %ls", sczFileContent); |
| 75 | |
| 76 | // write to deferred custom action data |
| 77 | hr = WcaWriteStringToCaData(sczFilePath, &sczExecCustomActionData); |
| 78 | ExitOnFailure(hr, "Failed to write deferred custom action data for file: %ls", sczFilePath); |
| 79 | |
| 80 | hr = WcaWriteIntegerToCaData(encoding, &sczExecCustomActionData); |
| 81 | ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding); |
| 82 | |
| 83 | hr = WcaWriteStringToCaData(sczFormattedContent, &sczExecCustomActionData); |
| 84 | ExitOnFailure(hr, "Failed to write deferred custom action data for file content: %ls", sczFilePath); |
| 85 | |
| 86 | // write to rollback custom action data |
| 87 | hr = WcaWriteStringToCaData(sczFilePath, &sczRollbackCustomActionData); |
| 88 | ExitOnFailure(hr, "Failed to write rollback custom action data for file: %ls", sczFilePath); |
| 89 | |
| 90 | hr = WcaWriteIntegerToCaData(encoding, &sczRollbackCustomActionData); |
| 91 | ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding); |
| 92 | |
| 93 | hr = WcaWriteStringToCaData(sczFileContent, &sczRollbackCustomActionData); |
| 94 | ExitOnFailure(hr, "Failed to write rollback custom action data for file content: %ls", sczFilePath); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // reaching the end of the list is actually a good thing, not an error |
| 99 | if (E_NOMOREITEMS == hr) |
| 100 | { |
| 101 | hr = S_OK; |
| 102 | } |
| 103 | ExitOnFailure(hr, "Failure occurred while processing Wix4FormatFile table"); |
| 104 | |
| 105 | // schedule deferred CAs if there's anything to do |
| 106 | if (sczRollbackCustomActionData && *sczRollbackCustomActionData) |
| 107 | { |
| 108 | hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFormatFiles"), sczRollbackCustomActionData, cFiles * COST_FILEFORMATTING); |
| 109 | ExitOnFailure(hr, "Failed to schedule RollbackFormatFiles"); |
| 110 | } |
| 111 | |
| 112 | if (sczExecCustomActionData && *sczExecCustomActionData) |
| 113 | { |
| 114 | hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFormatFiles"), sczExecCustomActionData, cFiles * COST_FILEFORMATTING); |
| 115 | ExitOnFailure(hr, "Failed to schedule ExecFormatFiles"); |
| 116 | } |
| 117 | |
| 118 | LExit: |
| 119 | return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); |
| 120 | } |
| 121 | |
| 122 | |
| 123 | // |
| 124 | // WixExecFormatFiles - deferred and rollback CAs to write formatted files |
| 125 | // |
| 126 | extern "C" UINT __stdcall WixExecFormatFiles( |
| 127 | __in MSIHANDLE hInstall |
| 128 | ) |
| 129 | { |
| 130 | HRESULT hr = S_OK; |
| 131 | UINT er = ERROR_SUCCESS; |
| 132 | PSCZ sczCustomActionData; |
| 133 | LPWSTR pwz = NULL; |
| 134 | PSCZ sczFilePath; |
| 135 | PSCZ sczFileContent; |
| 136 | LPSTR psz = NULL; |
| 137 | |
| 138 | // initialize |
| 139 | hr = WcaInitialize(hInstall, "WixExecFormatFiles"); |
| 140 | ExitOnFailure(hr, "Failed to initialize for WixExecFormatFiles."); |
| 141 | |
| 142 | hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData); |
| 143 | ExitOnFailure(hr, "Failed to get CustomActionData."); |
| 144 | #ifdef _DEBUG |
| 145 | WcaLog(LOGMSG_STANDARD, "CustomActionData: %ls", sczCustomActionData); |
| 146 | #endif |
| 147 | |
| 148 | // loop through all the passed in data |
| 149 | pwz = sczCustomActionData; |
| 150 | while (pwz && *pwz) |
| 151 | { |
| 152 | // extract the custom action data |
| 153 | hr = WcaReadStringFromCaData(&pwz, &sczFilePath); |
| 154 | ExitOnFailure(hr, "Failed to read file path from custom action data"); |
| 155 | |
| 156 | WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN; |
| 157 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&encoding)); |
| 158 | ExitOnFailure(hr, "Failed to read encoding from custom action data"); |
| 159 | |
| 160 | hr = WcaReadStringFromCaData(&pwz, &sczFileContent); |
| 161 | ExitOnFailure(hr, "Failed to read file content from custom action data"); |
| 162 | |
| 163 | // re-encode content |
| 164 | LPCBYTE pbData = NULL; |
| 165 | size_t cbData = 0; |
| 166 | switch (encoding) |
| 167 | { |
| 168 | case WCA_ENCODING_UTF_16: |
| 169 | pbData = reinterpret_cast<LPCBYTE>(LPCWSTR(sczFileContent)); |
| 170 | cbData = lstrlenW(sczFileContent) * sizeof(WCHAR); |
| 171 | break; |
| 172 | |
| 173 | case WCA_ENCODING_UTF_8: |
| 174 | hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_UTF8); |
| 175 | ExitOnFailure(hr, "Failed to convert Unicode to UTF-8."); |
| 176 | pbData = reinterpret_cast<LPCBYTE>(psz); |
| 177 | |
| 178 | hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData); |
| 179 | ExitOnFailure(hr, "Failed to count UTF-8 bytes."); |
| 180 | break; |
| 181 | |
| 182 | case WCA_ENCODING_ANSI: |
| 183 | hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_ACP); |
| 184 | ExitOnFailure(hr, "Failed to convert Unicode to ANSI."); |
| 185 | pbData = reinterpret_cast<LPCBYTE>(psz); |
| 186 | |
| 187 | hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData); |
| 188 | ExitOnFailure(hr, "Failed to count UTF-8 bytes."); |
| 189 | break; |
| 190 | |
| 191 | default: |
| 192 | break; |
| 193 | } |
| 194 | |
| 195 | #ifdef _DEBUG |
| 196 | WcaLog(LOGMSG_STANDARD, "File: %ls", sczCustomActionData); |
| 197 | WcaLog(LOGMSG_STANDARD, "Content: %ls", sczFileContent); |
| 198 | #endif |
| 199 | |
| 200 | // write file and preserve modified time |
| 201 | FILETIME filetime; |
| 202 | |
| 203 | hr = FileGetTime(sczFilePath, NULL, NULL, &filetime); |
| 204 | ExitOnFailure(hr, "Failed to get modified time of file : %ls", sczFilePath); |
| 205 | |
| 206 | hr = FileWrite(sczFilePath, FILE_ATTRIBUTE_NORMAL, pbData, cbData, NULL); |
| 207 | ExitOnFailure(hr, "Failed to write file content: %ls", sczFilePath); |
| 208 | |
| 209 | hr = FileSetTime(sczFilePath, NULL, NULL, &filetime); |
| 210 | ExitOnFailure(hr, "Failed to set modified time of file : %ls", sczFilePath); |
| 211 | |
| 212 | // Tick the progress bar |
| 213 | hr = WcaProgressMessage(COST_FILEFORMATTING, FALSE); |
| 214 | ExitOnFailure(hr, "Failed to tick progress bar for file: %ls", sczFilePath); |
| 215 | } |
| 216 | |
| 217 | LExit: |
| 218 | ReleaseStr(psz); |
| 219 | |
| 220 | return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); |
| 221 | } |