diff options
Diffstat (limited to 'src/dtf/SfxCA/SfxUtil.cpp')
-rw-r--r-- | src/dtf/SfxCA/SfxUtil.cpp | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/dtf/SfxCA/SfxUtil.cpp b/src/dtf/SfxCA/SfxUtil.cpp new file mode 100644 index 00000000..1bf2c5b2 --- /dev/null +++ b/src/dtf/SfxCA/SfxUtil.cpp | |||
@@ -0,0 +1,209 @@ | |||
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 | #include "SfxUtil.h" | ||
5 | |||
6 | /// <summary> | ||
7 | /// Writes a formatted message to the MSI log. | ||
8 | /// Does out-of-proc MSI calls if necessary. | ||
9 | /// </summary> | ||
10 | void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...) | ||
11 | { | ||
12 | const int LOG_BUFSIZE = 4096; | ||
13 | wchar_t szBuf[LOG_BUFSIZE]; | ||
14 | va_list args; | ||
15 | va_start(args, szMessage); | ||
16 | StringCchVPrintf(szBuf, LOG_BUFSIZE, szMessage, args); | ||
17 | |||
18 | if (!g_fRunningOutOfProc || NULL == g_pRemote) | ||
19 | { | ||
20 | MSIHANDLE hRec = MsiCreateRecord(1); | ||
21 | MsiRecordSetString(hRec, 0, L"SFXCA: [1]"); | ||
22 | MsiRecordSetString(hRec, 1, szBuf); | ||
23 | MsiProcessMessage(hSession, INSTALLMESSAGE_INFO, hRec); | ||
24 | MsiCloseHandle(hRec); | ||
25 | } | ||
26 | else | ||
27 | { | ||
28 | // Logging is the only remote-MSI operation done from unmanaged code. | ||
29 | // It's not very convenient here because part of the infrastructure | ||
30 | // for remote MSI APIs is on the managed side. | ||
31 | |||
32 | RemoteMsiSession::RequestData req; | ||
33 | RemoteMsiSession::RequestData* pResp = NULL; | ||
34 | SecureZeroMemory(&req, sizeof(RemoteMsiSession::RequestData)); | ||
35 | |||
36 | req.fields[0].vt = VT_UI4; | ||
37 | req.fields[0].uiValue = 1; | ||
38 | g_pRemote->SendRequest(RemoteMsiSession::MsiCreateRecord, &req, &pResp); | ||
39 | MSIHANDLE hRec = (MSIHANDLE) pResp->fields[0].iValue; | ||
40 | |||
41 | req.fields[0].vt = VT_I4; | ||
42 | req.fields[0].iValue = (int) hRec; | ||
43 | req.fields[1].vt = VT_UI4; | ||
44 | req.fields[1].uiValue = 0; | ||
45 | req.fields[2].vt = VT_LPWSTR; | ||
46 | req.fields[2].szValue = L"SFXCA: [1]"; | ||
47 | g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp); | ||
48 | |||
49 | req.fields[0].vt = VT_I4; | ||
50 | req.fields[0].iValue = (int) hRec; | ||
51 | req.fields[1].vt = VT_UI4; | ||
52 | req.fields[1].uiValue = 1; | ||
53 | req.fields[2].vt = VT_LPWSTR; | ||
54 | req.fields[2].szValue = szBuf; | ||
55 | g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp); | ||
56 | |||
57 | req.fields[0].vt = VT_I4; | ||
58 | req.fields[0].iValue = (int) hSession; | ||
59 | req.fields[1].vt = VT_I4; | ||
60 | req.fields[1].iValue = (int) INSTALLMESSAGE_INFO; | ||
61 | req.fields[2].vt = VT_I4; | ||
62 | req.fields[2].iValue = (int) hRec; | ||
63 | g_pRemote->SendRequest(RemoteMsiSession::MsiProcessMessage, &req, &pResp); | ||
64 | |||
65 | req.fields[0].vt = VT_I4; | ||
66 | req.fields[0].iValue = (int) hRec; | ||
67 | req.fields[1].vt = VT_EMPTY; | ||
68 | req.fields[2].vt = VT_EMPTY; | ||
69 | g_pRemote->SendRequest(RemoteMsiSession::MsiCloseHandle, &req, &pResp); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | /// <summary> | ||
74 | /// Deletes a directory, including all files and subdirectories. | ||
75 | /// </summary> | ||
76 | /// <param name="szDir">Path to the directory to delete, | ||
77 | /// not including a trailing backslash.</param> | ||
78 | /// <returns>True if the directory was successfully deleted, or false | ||
79 | /// if the deletion failed (most likely because some files were locked). | ||
80 | /// </returns> | ||
81 | bool DeleteDirectory(const wchar_t* szDir) | ||
82 | { | ||
83 | size_t cchDir = wcslen(szDir); | ||
84 | size_t cchPathBuf = cchDir + 3 + MAX_PATH; | ||
85 | wchar_t* szPath = (wchar_t*) _alloca(cchPathBuf * sizeof(wchar_t)); | ||
86 | if (szPath == NULL) return false; | ||
87 | StringCchCopy(szPath, cchPathBuf, szDir); | ||
88 | StringCchCat(szPath, cchPathBuf, L"\\*"); | ||
89 | WIN32_FIND_DATA fd; | ||
90 | HANDLE hSearch = FindFirstFile(szPath, &fd); | ||
91 | while (hSearch != INVALID_HANDLE_VALUE) | ||
92 | { | ||
93 | StringCchCopy(szPath + cchDir + 1, cchPathBuf - (cchDir + 1), fd.cFileName); | ||
94 | if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) | ||
95 | { | ||
96 | if (wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0) | ||
97 | { | ||
98 | DeleteDirectory(szPath); | ||
99 | } | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | DeleteFile(szPath); | ||
104 | } | ||
105 | if (!FindNextFile(hSearch, &fd)) | ||
106 | { | ||
107 | FindClose(hSearch); | ||
108 | hSearch = INVALID_HANDLE_VALUE; | ||
109 | } | ||
110 | } | ||
111 | return RemoveDirectory(szDir) != 0; | ||
112 | } | ||
113 | |||
114 | bool DirectoryExists(const wchar_t* szDir) | ||
115 | { | ||
116 | if (szDir != NULL) | ||
117 | { | ||
118 | DWORD dwAttrs = GetFileAttributes(szDir); | ||
119 | if (dwAttrs != -1 && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0) | ||
120 | { | ||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | /// <summary> | ||
128 | /// Extracts a cabinet that is concatenated to a module | ||
129 | /// to a new temporary directory. | ||
130 | /// </summary> | ||
131 | /// <param name="hSession">Handle to the installer session, | ||
132 | /// used just for logging.</param> | ||
133 | /// <param name="hModule">Module that has the concatenated cabinet.</param> | ||
134 | /// <param name="szTempDir">Buffer for returning the path of the | ||
135 | /// created temp directory.</param> | ||
136 | /// <param name="cchTempDirBuf">Size in characters of the buffer. | ||
137 | /// <returns>True if the files were extracted, or false if the | ||
138 | /// buffer was too small or the directory could not be created | ||
139 | /// or the extraction failed for some other reason.</returns> | ||
140 | __success(return != false) | ||
141 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, | ||
142 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf) | ||
143 | { | ||
144 | wchar_t szModule[MAX_PATH]; | ||
145 | DWORD cchCopied = GetModuleFileName(hModule, szModule, MAX_PATH - 1); | ||
146 | if (cchCopied == 0) | ||
147 | { | ||
148 | Log(hSession, L"Failed to get module path. Error code %d.", GetLastError()); | ||
149 | return false; | ||
150 | } | ||
151 | else if (cchCopied == MAX_PATH - 1) | ||
152 | { | ||
153 | Log(hSession, L"Failed to get module path -- path is too long."); | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | if (szTempDir == NULL || cchTempDirBuf < wcslen(szModule) + 1) | ||
158 | { | ||
159 | Log(hSession, L"Temp directory buffer is NULL or too small."); | ||
160 | return false; | ||
161 | } | ||
162 | StringCchCopy(szTempDir, cchTempDirBuf, szModule); | ||
163 | StringCchCat(szTempDir, cchTempDirBuf, L"-"); | ||
164 | |||
165 | DWORD cchTempDir = (DWORD) wcslen(szTempDir); | ||
166 | for (int i = 0; DirectoryExists(szTempDir); i++) | ||
167 | { | ||
168 | swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i); | ||
169 | } | ||
170 | |||
171 | if (!CreateDirectory(szTempDir, NULL)) | ||
172 | { | ||
173 | cchCopied = GetTempPath(cchTempDirBuf, szTempDir); | ||
174 | if (cchCopied == 0 || cchCopied >= cchTempDirBuf) | ||
175 | { | ||
176 | Log(hSession, L"Failed to get temp directory. Error code %d", GetLastError()); | ||
177 | return false; | ||
178 | } | ||
179 | |||
180 | wchar_t* szModuleName = wcsrchr(szModule, L'\\'); | ||
181 | if (szModuleName == NULL) szModuleName = szModule; | ||
182 | else szModuleName = szModuleName + 1; | ||
183 | StringCchCat(szTempDir, cchTempDirBuf, szModuleName); | ||
184 | StringCchCat(szTempDir, cchTempDirBuf, L"-"); | ||
185 | |||
186 | cchTempDir = (DWORD) wcslen(szTempDir); | ||
187 | for (int i = 0; DirectoryExists(szTempDir); i++) | ||
188 | { | ||
189 | swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i); | ||
190 | } | ||
191 | |||
192 | if (!CreateDirectory(szTempDir, NULL)) | ||
193 | { | ||
194 | Log(hSession, L"Failed to create temp directory. Error code %d", GetLastError()); | ||
195 | return false; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir); | ||
200 | int err = ExtractCabinet(szModule, szTempDir); | ||
201 | if (err != 0) | ||
202 | { | ||
203 | Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); | ||
204 | DeleteDirectory(szTempDir); | ||
205 | return false; | ||
206 | } | ||
207 | return true; | ||
208 | } | ||
209 | |||