1 /*
2  * Unit test suite for virtual substituted drive functions.
3  *
4  * Copyright 2017 Giannis Adamopoulos
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 #define STRSECTION_MAGIC   0x64487353 /* dHsS */
24 
25 struct strsection_header
26 {
27     DWORD magic;
28     ULONG size;
29     DWORD unk1[3];
30     ULONG count;
31     ULONG index_offset;
32     DWORD unk2[2];
33     ULONG global_offset;
34     ULONG global_len;
35 };
36 
37 struct wndclass_redirect_data
38 {
39     ULONG size;
40     DWORD res;
41     ULONG name_len;
42     ULONG name_offset;   /* versioned name offset */
43     ULONG module_len;
44     ULONG module_offset; /* container name offset to the section base */
45 };
46 
47 struct dllredirect_data
48 {
49     ULONG size;
50     ULONG unk;
51     DWORD res[3];
52 };
53 
54 #include <pshpack1.h>
55 
56 struct assemply_data
57 {
58     ULONG size;
59     DWORD ulFlags;
60     DWORD ulEncodedAssemblyIdentityLength;
61     DWORD ulEncodedAssemblyIdentityOffset; /* offset to the section base */
62     DWORD ulManifestPathType;
63     DWORD ulManifestPathLength;
64     DWORD ulManifestPathOffset;            /* offset to the section base */
65     LARGE_INTEGER liManifestLastWriteTime;
66     DWORD unk3[11];
67     DWORD ulAssemblyDirectoryNameLength;
68     DWORD ulAssemblyDirectoryNameOffset;   /* offset to the section base */
69     DWORD unk4[3];  /* In win10 there are two more fields */
70 };
71 
72 #include <poppack.h>
73 
74 HANDLE _CreateActCtxFromFile(LPCWSTR FileName, int line)
75 {
76     ACTCTXW ActCtx = {sizeof(ACTCTX)};
77     HANDLE h;
78     WCHAR buffer[MAX_PATH] , *separator;
79 
80     ok (GetModuleFileNameW(NULL, buffer, MAX_PATH), "GetModuleFileName failed\n");
81     separator = wcsrchr(buffer, L'\\');
82     if (separator)
83         wcscpy(separator + 1, FileName);
84 
85     ActCtx.lpSource = buffer;
86 
87     SetLastError(0xdeaddead);
88     h = CreateActCtxW(&ActCtx);
89     ok_(__FILE__, line)(h != INVALID_HANDLE_VALUE, "CreateActCtx failed for %S\n", FileName);
90     // In win10 last error is unchanged and in win2k3 it is ERROR_BAD_EXE_FORMAT
91     ok_(__FILE__, line)(GetLastError() == ERROR_BAD_EXE_FORMAT || GetLastError() == 0xdeaddead, "Wrong last error %lu\n", GetLastError());
92 
93     return h;
94 }
95 
96 VOID _ActivateCtx(HANDLE h, ULONG_PTR *cookie, int line)
97 {
98     BOOL res;
99 
100     SetLastError(0xdeaddead);
101     res = ActivateActCtx(h, cookie);
102     ok_(__FILE__, line)(res == TRUE, "ActivateActCtx failed\n");
103     ok_(__FILE__, line)(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError());
104 }
105 
106 VOID _DeactivateCtx(ULONG_PTR cookie, int line)
107 {
108     BOOL res;
109 
110     SetLastError(0xdeaddead);
111     res = DeactivateActCtx(0, cookie);
112     ok_(__FILE__, line)(res == TRUE, "DeactivateActCtx failed\n");
113     ok_(__FILE__, line)(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError());
114 }
115 
116 void TestClassRedirection(HANDLE h, LPCWSTR ClassToTest, LPCWSTR ExpectedClassPart, LPCWSTR ExpectedModule, ULONG ExpectedClassCount)
117 {
118     ACTCTX_SECTION_KEYED_DATA KeyedData = { 0 };
119     BOOL res;
120     struct strsection_header *header;
121     struct wndclass_redirect_data *classData;
122     LPCWSTR VersionedClass, ClassLib;
123     int data_lenght;
124 
125     SetLastError(0xdeaddead);
126     KeyedData.cbSize = sizeof(KeyedData);
127     res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
128                                    NULL,
129                                    ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
130                                    ClassToTest,
131                                    &KeyedData);
132     ok(res == TRUE, "FindActCtxSectionString failed\n");
133     ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError());
134 
135     ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu\n", KeyedData.ulDataFormatVersion);
136     ok(KeyedData.hActCtx == h, "Wrong handle\n");
137     ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n");
138     ok(KeyedData.lpData != NULL, "Expected non null lpData\n");
139     header = (struct strsection_header*)KeyedData.lpSectionBase;
140     classData = (struct wndclass_redirect_data*)KeyedData.lpData;
141 
142     if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || header == NULL || classData == NULL)
143     {
144         skip("Can't read data for class. Skipping\n");
145     }
146     else
147     {
148         ok(header->magic == STRSECTION_MAGIC, "%lu\n", header->magic );
149         ok(header->size == sizeof(*header), "Got %lu instead of %d\n", header->size, sizeof(*header));
150         ok(header->count == ExpectedClassCount, "Expected %lu classes, got %lu\n", ExpectedClassCount, header->count );
151 
152         VersionedClass = (WCHAR*)((BYTE*)classData + classData->name_offset);
153         ClassLib = (WCHAR*)((BYTE*)header + classData->module_offset);
154         data_lenght = classData->size + classData->name_len + classData->module_len + 2*sizeof(WCHAR);
155         ok(KeyedData.ulLength == data_lenght, "Got lenght %lu instead of %d\n", KeyedData.ulLength, data_lenght);
156         ok(classData->size == sizeof(*classData), "Got %lu instead of %d\n", classData->size, sizeof(*classData));
157         ok(classData->res == 0, "Got res %lu\n", classData->res);
158         ok(classData->module_len == wcslen(ExpectedModule) * 2, "Got name len %lu, expected %d\n", classData->module_len, wcslen(ExpectedModule) *2);
159         ok(wcscmp(ClassLib, ExpectedModule) == 0, "Got %S, expected %S\n", ClassLib, ExpectedModule);
160         /* compare only if VersionedClass starts with ExpectedClassPart */
161         ok(memcmp(VersionedClass, ExpectedClassPart, sizeof(WCHAR) * wcslen(ExpectedClassPart)) == 0, "Expected %S to start with %S\n", VersionedClass, ExpectedClassPart);
162     }
163 }
164 
165 VOID TestLibDependency(HANDLE h)
166 {
167     ACTCTX_SECTION_KEYED_DATA KeyedData = { 0 };
168     BOOL res;
169     struct strsection_header *SectionHeader;
170     struct dllredirect_data *redirData;
171     struct assemply_data *assemplyData;
172 
173     SetLastError(0xdeaddead);
174     KeyedData.cbSize = sizeof(KeyedData);
175     res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
176                                    NULL,
177                                    ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
178                                    L"dep1.dll",
179                                    &KeyedData);
180     ok(res == TRUE, "FindActCtxSectionString failed\n");
181     ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError());
182 
183     ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu", KeyedData.ulDataFormatVersion);
184     ok(KeyedData.hActCtx == h, "Wrong handle\n");
185     ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n");
186     ok(KeyedData.lpData != NULL, "Expected non null lpData\n");
187     SectionHeader = (struct strsection_header*)KeyedData.lpSectionBase;
188     redirData = (struct dllredirect_data *)KeyedData.lpData;
189 
190     if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || SectionHeader == NULL || redirData == NULL)
191     {
192         skip("Can't read data for dep1.dll. Skipping\n");
193     }
194     else
195     {
196         ok(SectionHeader->magic == STRSECTION_MAGIC, "%lu\n", SectionHeader->magic );
197         ok(SectionHeader->size == sizeof(*SectionHeader), "Got %lu instead of %d\n", SectionHeader->size, sizeof(*SectionHeader));
198         ok(SectionHeader->count == 2, "%lu\n", SectionHeader->count ); /* 2 dlls? */
199         ok(redirData->size == sizeof(*redirData), "Got %lu instead of %d\n", redirData->size, sizeof(*redirData));
200     }
201 
202     SetLastError(0xdeaddead);
203     KeyedData.cbSize = sizeof(KeyedData);
204     res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
205                                    NULL,
206                                    ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION,
207                                    L"dep1",
208                                    &KeyedData);
209     ok(res == TRUE, "FindActCtxSectionString failed\n");
210     ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError());
211     ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu", KeyedData.ulDataFormatVersion);
212     ok(KeyedData.hActCtx == h, "Wrong handle\n");
213     ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n");
214     ok(KeyedData.lpData != NULL, "Expected non null lpData\n");
215     SectionHeader = (struct strsection_header*)KeyedData.lpSectionBase;
216     assemplyData = (struct assemply_data*)KeyedData.lpData;;
217 
218     if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || SectionHeader == NULL || assemplyData == NULL)
219     {
220         skip("Can't read data for dep1. Skipping\n");
221     }
222     else
223     {
224         LPCWSTR AssemblyIdentity, ManifestPath, AssemblyDirectory;
225         int data_lenght;
226         DWORD buffer[256];
227         PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION details = (PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION)buffer;
228 
229         ok(SectionHeader->magic == STRSECTION_MAGIC, "%lu\n", SectionHeader->magic );
230         ok(SectionHeader->size == sizeof(*SectionHeader), "Got %lu instead of %d\n", SectionHeader->size, sizeof(*SectionHeader));
231         ok(SectionHeader->count == 2, "%lu\n", SectionHeader->count ); /* 2 dlls? */
232 
233         data_lenght = assemplyData->size +
234                       assemplyData->ulEncodedAssemblyIdentityLength +
235                       assemplyData->ulManifestPathLength +
236                       assemplyData->ulAssemblyDirectoryNameLength + 2 * sizeof(WCHAR);
237         ok(assemplyData->size == sizeof(*assemplyData), "Got %lu instead of %d\n", assemplyData->size, sizeof(*assemplyData));
238         ok(KeyedData.ulLength == data_lenght, "Got lenght %lu instead of %d\n", KeyedData.ulLength, data_lenght);
239 
240         AssemblyIdentity = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulEncodedAssemblyIdentityOffset);
241         ManifestPath = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulManifestPathOffset);
242         AssemblyDirectory = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulAssemblyDirectoryNameOffset);
243 
244         /* Use AssemblyDetailedInformationInActivationContext so as to infer the contents of assemplyData */
245         res = QueryActCtxW(0, h, &KeyedData.ulAssemblyRosterIndex,
246                            AssemblyDetailedInformationInActivationContext,
247                            &buffer, sizeof(buffer), NULL);
248         ok(res == TRUE, "QueryActCtxW failed\n");
249         ok(assemplyData->ulFlags == details->ulFlags, "\n");
250         ok(assemplyData->ulEncodedAssemblyIdentityLength == details->ulEncodedAssemblyIdentityLength, "\n");
251         ok(assemplyData->ulManifestPathType == details->ulManifestPathType, "\n");
252         ok(assemplyData->ulManifestPathLength == details->ulManifestPathLength, "\n");
253         ok(assemplyData->ulAssemblyDirectoryNameLength == details->ulAssemblyDirectoryNameLength, "\n");
254         ok(assemplyData->liManifestLastWriteTime.QuadPart == details->liManifestLastWriteTime.QuadPart, "\n");
255 
256         ok(wcscmp(ManifestPath, details->lpAssemblyManifestPath) == 0, "Expected path %S, got %S\n", details->lpAssemblyManifestPath, ManifestPath);
257         ok(wcscmp(AssemblyDirectory, details->lpAssemblyDirectoryName) == 0, "Expected path %S, got %S\n", details->lpAssemblyManifestPath, ManifestPath);
258 
259         /* It looks like that AssemblyIdentity isn't null terminated */
260         ok(memcmp(AssemblyIdentity, details->lpAssemblyEncodedAssemblyIdentity, assemplyData->ulEncodedAssemblyIdentityLength) == 0, "Got wrong AssemblyIdentity\n");
261     }
262 }
263 
264 START_TEST(FindActCtxSectionStringW)
265 {
266     HANDLE h, h2;
267     ULONG_PTR cookie, cookie2;
268 
269     /*First run the redirection tests without using our own actctx */
270     TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27);
271     /* Something activates an activation context that mentions comctl32 but comctl32 is not loaded */
272     ok( GetModuleHandleW(L"comctl32.dll") == NULL, "Expected comctl32 not to be loaded\n");
273     ok( GetModuleHandleW(L"user32.dll") == NULL, "Expected user32 not to be loaded\n");
274 
275     /* Class redirection tests */
276     h = _CreateActCtxFromFile(L"classtest.manifest", __LINE__);
277     if (h != INVALID_HANDLE_VALUE)
278     {
279         _ActivateCtx(h, &cookie, __LINE__);
280         TestClassRedirection(h, L"Button", L"2.2.2.2!Button", L"testlib.dll", 5);
281         _ActivateCtx(NULL, &cookie2, __LINE__);
282         TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27);
283         _DeactivateCtx(cookie2, __LINE__);
284         _DeactivateCtx(cookie, __LINE__);
285     }
286     else
287     {
288         skip("Failed to create context for classtest.manifest\n");
289     }
290 
291     /* Class redirection tests with multiple contexts in the activation stack */
292     h2 = _CreateActCtxFromFile(L"classtest2.manifest", __LINE__);
293     if (h != INVALID_HANDLE_VALUE && h2 != INVALID_HANDLE_VALUE)
294     {
295         _ActivateCtx(h, &cookie, __LINE__);
296         _ActivateCtx(h2, &cookie2, __LINE__);
297         TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27);
298         TestClassRedirection(h2, L"MyClass", L"1.1.1.1!MyClass", L"testlib.dll", 5);
299         _DeactivateCtx(cookie2, __LINE__);
300         TestClassRedirection(h, L"Button", L"2.2.2.2!Button", L"testlib.dll", 5);
301         _DeactivateCtx(cookie, __LINE__);
302     }
303     else
304     {
305         skip("Failed to create context for classtest.manifest\n");
306     }
307 
308     /* Dependency tests */
309     h = _CreateActCtxFromFile(L"deptest.manifest", __LINE__);
310     if (h != INVALID_HANDLE_VALUE)
311     {
312         _ActivateCtx(h, &cookie, __LINE__);
313         TestLibDependency(h);
314         _DeactivateCtx(cookie, __LINE__);
315     }
316     else
317     {
318         skip("Failed to create context for deptest.manifest\n");
319     }
320 
321     /* Activate a context that depends on comctl32 v6 and run class tests again */
322     h = _CreateActCtxFromFile(L"comctl32dep.manifest", __LINE__);
323     if (h != INVALID_HANDLE_VALUE)
324     {
325         _ActivateCtx(h, &cookie, __LINE__);
326         TestClassRedirection(h, L"Button", L"6.0.", L"comctl32.dll", 29);
327         ok( GetModuleHandleW(L"comctl32.dll") == NULL, "Expected comctl32 not to be loaded\n");
328         ok( GetModuleHandleW(L"user32.dll") == NULL, "Expected user32 not to be loaded\n");
329         _DeactivateCtx(cookie, __LINE__);
330     }
331     else
332     {
333         skip("Failed to create context for comctl32dep.manifest\n");
334     }
335 }