msi: Any value of WindowsInstaller besides 0 means the product is installed.
[wine] / dlls / msi / tests / msi.c
1 /*
2  * tests for Microsoft Installer functionality
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
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 <stdio.h>
22 #include <windows.h>
23 #include <msi.h>
24 #include <msiquery.h>
25 #include <sddl.h>
26
27 #include "wine/test.h"
28
29 typedef struct test_MSIFILEHASHINFO {
30     ULONG dwFileHashInfoSize;
31     ULONG dwData[4];
32 } test_MSIFILEHASHINFO, *test_PMSIFILEHASHINFO;
33
34 typedef INSTALLSTATE (WINAPI *fnMsiUseFeatureExA)(LPCSTR, LPCSTR ,DWORD, DWORD );
35 fnMsiUseFeatureExA pMsiUseFeatureExA;
36 typedef UINT (WINAPI *fnMsiOpenPackageExA)(LPCSTR, DWORD, MSIHANDLE*);
37 fnMsiOpenPackageExA pMsiOpenPackageExA;
38 typedef UINT (WINAPI *fnMsiOpenPackageExW)(LPCWSTR, DWORD, MSIHANDLE*);
39 fnMsiOpenPackageExW pMsiOpenPackageExW;
40 typedef INSTALLSTATE (WINAPI *fnMsiGetComponentPathA)(LPCSTR, LPCSTR, LPSTR, DWORD*);
41 fnMsiGetComponentPathA pMsiGetComponentPathA;
42 typedef UINT (WINAPI *fnMsiGetFileHashA)(LPCSTR, DWORD, test_PMSIFILEHASHINFO);
43 fnMsiGetFileHashA pMsiGetFileHashA;
44
45 static void test_usefeature(void)
46 {
47     UINT r;
48
49     if (!pMsiUseFeatureExA)
50         return;
51
52     r = MsiQueryFeatureState(NULL,NULL);
53     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
54
55     r = MsiQueryFeatureState("{9085040-6000-11d3-8cfe-0150048383c9}" ,NULL);
56     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
57
58     r = pMsiUseFeatureExA(NULL,NULL,0,0);
59     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
60
61     r = pMsiUseFeatureExA(NULL, "WORDVIEWFiles", -2, 1 );
62     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
63
64     r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", 
65                          NULL, -2, 0 );
66     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
67
68     r = pMsiUseFeatureExA("{9085040-6000-11d3-8cfe-0150048383c9}", 
69                          "WORDVIEWFiles", -2, 0 );
70     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
71
72     r = pMsiUseFeatureExA("{0085040-6000-11d3-8cfe-0150048383c9}", 
73                          "WORDVIEWFiles", -2, 0 );
74     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
75
76     r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", 
77                          "WORDVIEWFiles", -2, 1 );
78     ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
79 }
80
81 static void test_null(void)
82 {
83     MSIHANDLE hpkg;
84     UINT r;
85     HKEY hkey;
86     DWORD dwType, cbData;
87     LPBYTE lpData = NULL;
88
89     r = pMsiOpenPackageExW(NULL, 0, &hpkg);
90     ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
91
92     r = MsiQueryProductStateW(NULL);
93     ok( r == INSTALLSTATE_INVALIDARG, "wrong return\n");
94
95     r = MsiEnumFeaturesW(NULL,0,NULL,NULL);
96     ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
97
98     r = MsiConfigureFeatureW(NULL, NULL, 0);
99     ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
100
101     r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", NULL, 0);
102     ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
103
104     r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", 0);
105     ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
106
107     r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", INSTALLSTATE_DEFAULT);
108     ok( r == ERROR_UNKNOWN_PRODUCT, "wrong error %d\n", r);
109
110     /* make sure empty string to MsiGetProductInfo is not a handle to default registry value, saving and restoring the
111      * necessary registry values */
112
113     /* empty product string */
114     r = RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", &hkey);
115     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
116
117     r = RegQueryValueExA(hkey, NULL, 0, &dwType, lpData, &cbData);
118     ok ( r == ERROR_SUCCESS || r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r);
119     if ( r == ERROR_SUCCESS )
120     {
121         lpData = HeapAlloc(GetProcessHeap(), 0, cbData);
122         if (!lpData)
123             skip("Out of memory\n");
124         else
125         {
126             r = RegQueryValueExA(hkey, NULL, 0, &dwType, lpData, &cbData);
127             ok ( r == ERROR_SUCCESS, "wrong error %d\n", r);
128         }
129     }
130
131     r = RegSetValueA(hkey, NULL, REG_SZ, "test", strlen("test"));
132     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
133
134     r = MsiGetProductInfoA("", "", NULL, NULL);
135     ok ( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
136
137     if (lpData)
138     {
139         r = RegSetValueExA(hkey, NULL, 0, dwType, lpData, cbData);
140         ok ( r == ERROR_SUCCESS, "wrong error %d\n", r);
141
142         HeapFree(GetProcessHeap(), 0, lpData);
143     }
144     else
145     {
146         r = RegDeleteValueA(hkey, NULL);
147         ok ( r == ERROR_SUCCESS, "wrong error %d\n", r);
148     }
149
150     r = RegCloseKey(hkey);
151     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
152
153     /* empty attribute */
154     r = RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}", &hkey);
155     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
156
157     r = RegSetValueA(hkey, NULL, REG_SZ, "test", strlen("test"));
158     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
159
160     r = MsiGetProductInfoA("{F1C3AF50-8B56-4A69-A00C-00773FE42F30}", "", NULL, NULL);
161     ok ( r == ERROR_UNKNOWN_PROPERTY, "wrong error %d\n", r);
162
163     r = RegCloseKey(hkey);
164     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
165
166     r = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}");
167     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
168 }
169
170 static void test_getcomponentpath(void)
171 {
172     INSTALLSTATE r;
173     char buffer[0x100];
174     DWORD sz;
175
176     if(!pMsiGetComponentPathA)
177         return;
178
179     r = pMsiGetComponentPathA( NULL, NULL, NULL, NULL );
180     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
181
182     r = pMsiGetComponentPathA( "bogus", "bogus", NULL, NULL );
183     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
184
185     r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", NULL, NULL );
186     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
187
188     sz = sizeof buffer;
189     buffer[0]=0;
190     r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", buffer, &sz );
191     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
192
193     r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C998E7}",
194         "{00000000-0000-0000-0000-000000000000}", buffer, &sz );
195     ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
196
197     r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
198         "{00000000-0000-0000-0000-00000000}", buffer, &sz );
199     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
200
201     r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
202         "{029E403D-A86A-1D11-5B5B0006799C897E}", buffer, &sz );
203     ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
204
205     r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C9987e}",
206                             "{00000000-A68A-11d1-5B5B-0006799C897E}", buffer, &sz );
207     ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
208 }
209
210 static void test_filehash(void)
211 {
212     const char name[] = "msitest.bin";
213     const char data[] = {'a','b','c'};
214     HANDLE handle;
215     UINT r;
216     test_MSIFILEHASHINFO hash;
217     DWORD count = 0;
218
219     if (!pMsiGetFileHashA)
220         return;
221
222     DeleteFile(name);
223
224     memset(&hash, 0, sizeof hash);
225     r = pMsiGetFileHashA(name, 0, &hash);
226     ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
227
228     r = pMsiGetFileHashA(name, 0, NULL);
229     ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
230
231     memset(&hash, 0, sizeof hash);
232     hash.dwFileHashInfoSize = sizeof hash;
233     r = pMsiGetFileHashA(name, 0, &hash);
234     ok( r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r);
235
236     handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, 
237                 CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
238     ok(handle != INVALID_HANDLE_VALUE, "failed to create file\n");
239
240     WriteFile(handle, data, sizeof data, &count, NULL);
241     CloseHandle(handle);
242
243     memset(&hash, 0, sizeof hash);
244     r = pMsiGetFileHashA(name, 0, &hash);
245     ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
246
247     memset(&hash, 0, sizeof hash);
248     hash.dwFileHashInfoSize = sizeof hash;
249     r = pMsiGetFileHashA(name, 1, &hash);
250     ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
251
252     r = pMsiGetFileHashA(name, 0, &hash);
253     ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
254
255     ok(hash.dwFileHashInfoSize == sizeof hash, "hash size changed\n");
256     ok(hash.dwData[0] == 0x98500190 &&
257        hash.dwData[1] == 0xb04fd23c &&
258        hash.dwData[2] == 0x7d3f96d6 &&
259        hash.dwData[3] == 0x727fe128, "hash of abc incorrect\n");
260
261     DeleteFile(name);
262 }
263
264 /* copied from dlls/msi/registry.c */
265 BOOL squash_guid(LPCWSTR in, LPWSTR out)
266 {
267     DWORD i,n=1;
268     GUID guid;
269
270     if (FAILED(CLSIDFromString((LPOLESTR)in, &guid)))
271         return FALSE;
272
273     for(i=0; i<8; i++)
274         out[7-i] = in[n++];
275     n++;
276     for(i=0; i<4; i++)
277         out[11-i] = in[n++];
278     n++;
279     for(i=0; i<4; i++)
280         out[15-i] = in[n++];
281     n++;
282     for(i=0; i<2; i++)
283     {
284         out[17+i*2] = in[n++];
285         out[16+i*2] = in[n++];
286     }
287     n++;
288     for( ; i<8; i++)
289     {
290         out[17+i*2] = in[n++];
291         out[16+i*2] = in[n++];
292     }
293     out[32]=0;
294     return TRUE;
295 }
296
297 static void create_test_guid(LPSTR prodcode, LPSTR squashed)
298 {
299     WCHAR guidW[MAX_PATH];
300     WCHAR squashedW[MAX_PATH];
301     GUID guid;
302     HRESULT hr;
303     int size;
304
305     hr = CoCreateGuid(&guid);
306     ok(hr == S_OK, "Expected S_OK, got %d\n", hr);
307
308     size = StringFromGUID2(&guid, (LPOLESTR)guidW, MAX_PATH);
309     ok(size == 39, "Expected 39, got %d\n", hr);
310
311     WideCharToMultiByte(CP_ACP, 0, guidW, size, prodcode, MAX_PATH, NULL, NULL);
312     squash_guid(guidW, squashedW);
313     WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL);
314 }
315
316 static void get_user_sid(LPSTR *usersid)
317 {
318     HANDLE token;
319     BYTE buf[1024];
320     DWORD size;
321     PTOKEN_USER user;
322
323     OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token);
324     size = sizeof(buf);
325     GetTokenInformation(token, TokenUser, (void *)buf, size, &size);
326     user = (PTOKEN_USER)buf;
327     ConvertSidToStringSid(user->User.Sid, usersid);
328 }
329
330 static void test_MsiQueryProductState(void)
331 {
332     CHAR prodcode[MAX_PATH];
333     CHAR prod_squashed[MAX_PATH];
334     CHAR keypath[MAX_PATH*2];
335     LPSTR usersid;
336     INSTALLSTATE state;
337     LONG res;
338     HKEY userkey, localkey, props;
339     DWORD data;
340
341     create_test_guid(prodcode, prod_squashed);
342     get_user_sid(&usersid);
343
344     /* NULL prodcode */
345     state = MsiQueryProductStateA(NULL);
346     ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state);
347
348     /* empty prodcode */
349     state = MsiQueryProductStateA("");
350     ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state);
351
352     /* garbage prodcode */
353     state = MsiQueryProductStateA("garbage");
354     ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state);
355
356     /* guid without brackets */
357     state = MsiQueryProductStateA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D");
358     ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state);
359
360     /* guid with brackets */
361     state = MsiQueryProductStateA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}");
362     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
363
364     /* same length as guid, but random */
365     state = MsiQueryProductStateA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93");
366     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
367
368     /* created guid cannot possibly be an installed product code */
369     state = MsiQueryProductStateA(prodcode);
370     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
371
372     lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\");
373     lstrcatA(keypath, prod_squashed);
374
375     res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey);
376     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
377
378     /* user product key exists */
379     state = MsiQueryProductStateA(prodcode);
380     ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
381
382     lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\");
383     lstrcatA(keypath, prodcode);
384
385     res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
386     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
387
388     /* local uninstall key exists */
389     state = MsiQueryProductStateA(prodcode);
390     ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
391
392     data = 1;
393     res = RegSetValueExA(localkey, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD));
394     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
395
396     /* WindowsInstaller value exists */
397     state = MsiQueryProductStateA(prodcode);
398     ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
399
400     RegDeleteValueA(localkey, "WindowsInstaller");
401     RegDeleteKeyA(localkey, "");
402
403     lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
404     lstrcatA(keypath, usersid);
405     lstrcatA(keypath, "\\Products\\");
406     lstrcatA(keypath, prod_squashed);
407
408     res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
409     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
410
411     /* local product key exists */
412     state = MsiQueryProductStateA(prodcode);
413     ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
414
415     res = RegCreateKeyA(localkey, "InstallProperties", &props);
416     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
417
418     /* install properties key exists */
419     state = MsiQueryProductStateA(prodcode);
420     ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
421
422     data = 1;
423     res = RegSetValueExA(props, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD));
424     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
425
426     /* WindowsInstaller value exists */
427     state = MsiQueryProductStateA(prodcode);
428     ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
429
430     data = 2;
431     res = RegSetValueExA(props, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD));
432     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
433
434     /* WindowsInstaller value is not 1 */
435     state = MsiQueryProductStateA(prodcode);
436     ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
437
438     RegDeleteKeyA(userkey, "");
439
440     /* user product key does not exist */
441     state = MsiQueryProductStateA(prodcode);
442     todo_wine
443     {
444         ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
445     }
446
447     LocalFree(usersid);
448     RegDeleteValueA(props, "WindowsInstaller");
449     RegDeleteKeyA(props, "");
450     RegDeleteKeyA(localkey, "");
451     RegCloseKey(userkey);
452     RegCloseKey(localkey);
453     RegCloseKey(props);
454 }
455
456 START_TEST(msi)
457 {
458     HMODULE hmod = GetModuleHandle("msi.dll");
459     pMsiUseFeatureExA = (fnMsiUseFeatureExA) 
460         GetProcAddress(hmod, "MsiUseFeatureExA");
461     pMsiOpenPackageExA = (fnMsiOpenPackageExA) 
462         GetProcAddress(hmod, "MsiOpenPackageExA");
463     pMsiOpenPackageExW = (fnMsiOpenPackageExW) 
464         GetProcAddress(hmod, "MsiOpenPackageExW");
465     pMsiGetComponentPathA = (fnMsiGetComponentPathA)
466         GetProcAddress(hmod, "MsiGetComponentPathA" );
467     pMsiGetFileHashA = (fnMsiGetFileHashA)
468         GetProcAddress(hmod, "MsiGetFileHashA" );
469
470     test_usefeature();
471     test_null();
472     test_getcomponentpath();
473     test_filehash();
474     test_MsiQueryProductState();
475 }