gameux/tests: Add basic test for InstallGame and UninstallGame.
[wine] / dlls / gameux / tests / gameexplorer.c
1 /*
2  * IGameExplorer and IGameExplorer2 tests
3  *
4  * Copyright (C) 2010 Mariusz PluciƄski
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 #define COBJMACROS
22
23 #include "windows.h"
24 #include "ole2.h"
25 #include "objsafe.h"
26 #include "objbase.h"
27 #include "shlwapi.h"
28 #include "sddl.h"
29 #include "shobjidl.h"
30
31 #include "initguid.h"
32 #include "gameux.h"
33
34 #include "wine/test.h"
35
36 /*******************************************************************************
37  * Pointers used instead of direct calls. These procedures are not available on
38  * older system, which causes problem while loading test binary.
39  */
40 static BOOL WINAPI (*_ConvertSidToStringSidW)(PSID,LPWSTR*);
41 static LONG WINAPI (*_RegGetValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,LPDWORD,PVOID,LPDWORD);
42
43 /*******************************************************************************
44  *_loadDynamicRoutines
45  *
46  * Helper function, prepares pointers to system procedures which may be not
47  * available on older operating systems.
48  *
49  * Returns:
50  *  TRUE                        procedures were loaded successfunnly
51  *  FALSE                       procedures were not loaded successfunnly
52  */
53 static BOOL _loadDynamicRoutines(void)
54 {
55     HMODULE hAdvapiModule = GetModuleHandleA( "advapi32.dll" );
56
57     _ConvertSidToStringSidW = (LPVOID)GetProcAddress(hAdvapiModule, "ConvertSidToStringSidW");
58     if (!_ConvertSidToStringSidW) return FALSE;
59     _RegGetValueW = (LPVOID)GetProcAddress(hAdvapiModule, "RegGetValueW");
60     if (!_RegGetValueW) return FALSE;
61     return TRUE;
62 }
63
64 /*******************************************************************************
65  * _buildGameRegistryPath
66  *
67  * Helper function, builds registry path to key, where game's data are stored
68  *
69  * Parameters:
70  *  installScope                [I]     the scope which was used in AddGame/InstallGame call
71  *  gameInstanceId              [I]     game instance GUID
72  *  lpRegistryPath              [O]     pointer which will receive address to string
73  *                                      containing expected registry path. Path
74  *                                      is relative to HKLM registry key. It
75  *                                      must be freed by calling CoTaskMemFree
76  */
77 static HRESULT _buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
78         LPCGUID gameInstanceId,
79         LPWSTR* lpRegistryPath)
80 {
81     static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
82             'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
83             'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
84     static const WCHAR sGames[] = {'G','a','m','e','s',0};
85     static const WCHAR sBackslash[] = {'\\',0};
86
87     HRESULT hr = S_OK;
88     HANDLE hToken = NULL;
89     PTOKEN_USER pTokenUser = NULL;
90     DWORD dwLength;
91     LPWSTR lpSID = NULL;
92     WCHAR sInstanceId[40];
93     WCHAR sRegistryPath[8192];
94
95     lstrcpyW(sRegistryPath, sGameUxRegistryPath);
96     lstrcatW(sRegistryPath, sBackslash);
97
98     if(installScope == GIS_CURRENT_USER)
99     {
100         /* build registry path containing user's SID */
101         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
102             hr = HRESULT_FROM_WIN32(GetLastError());
103
104         if(SUCCEEDED(hr))
105         {
106             if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
107                     GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
108                 hr = HRESULT_FROM_WIN32(GetLastError());
109
110             if(SUCCEEDED(hr))
111             {
112                 pTokenUser = CoTaskMemAlloc(dwLength);
113                 if(!pTokenUser)
114                     hr = E_OUTOFMEMORY;
115             }
116
117             if(SUCCEEDED(hr))
118                 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
119                     hr = HRESULT_FROM_WIN32(GetLastError());
120
121             if(SUCCEEDED(hr))
122                 if(!_ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
123                     hr = HRESULT_FROM_WIN32(GetLastError());
124
125             if(SUCCEEDED(hr))
126             {
127                 lstrcatW(sRegistryPath, lpSID);
128                 LocalFree(lpSID);
129             }
130
131             CoTaskMemFree(pTokenUser);
132             CloseHandle(hToken);
133         }
134     }
135     else if(installScope == GIS_ALL_USERS)
136         /* build registry path without SID */
137         lstrcatW(sRegistryPath, sGames);
138     else
139         hr = E_INVALIDARG;
140
141     /* put game's instance id on the end of path */
142     if(SUCCEEDED(hr))
143         hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
144
145     if(SUCCEEDED(hr))
146     {
147         lstrcatW(sRegistryPath, sBackslash);
148         lstrcatW(sRegistryPath, sInstanceId);
149     }
150
151     if(SUCCEEDED(hr))
152     {
153         *lpRegistryPath = CoTaskMemAlloc((lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
154         if(!*lpRegistryPath)
155             hr = E_OUTOFMEMORY;
156     }
157
158     if(SUCCEEDED(hr))
159         lstrcpyW(*lpRegistryPath, sRegistryPath);
160
161     return hr;
162 }
163 /*******************************************************************************
164  *  _validateRegistryValue
165  *
166  * Helper function, verifies single registry value with expected
167  *
168  * Parameters:
169  *  hKey                        [I]     handle to game's key. Key must be opened
170  *  keyPath                     [I]     string with path to game's key. Used only
171  *                                      to display more useful message on test fail
172  *  valueName                   [I]     name of value to check
173  *  dwExpectedType              [I]     expected type of value. It should be
174  *                                      one of RRF_RT_* flags
175  *  lpExpectedContent           [I]     expected content of value. It should be
176  *                                      pointer to variable with same type as
177  *                                      passed in dwExpectedType
178  *
179  * Returns:
180  *  S_OK                                value exists and contains expected data
181  *  S_FALSE                             value exists, but contains other data
182  *                                      than expected
183  *  E_OUTOFMEMORY                       allocation problem
184  *  HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
185  *                                      value does not exist
186  *
187  * Note: this function returns error codes instead of failing test case, because
188  * it is sometimes expected that given value may not exist on some systems, or
189  * contain other data.
190  */
191 static HRESULT _validateRegistryValue(
192         HKEY hKey,
193         LPCWSTR keyPath,
194         LPCWSTR valueName,
195         DWORD dwExpectedType,
196         LPCVOID lpExpectedContent)
197 {
198     HRESULT hr;
199     DWORD dwType, dwSize;
200     LPVOID lpData = NULL;
201
202     hr = HRESULT_FROM_WIN32(_RegGetValueW(hKey, NULL, valueName, dwExpectedType, &dwType, NULL, &dwSize));
203     if(FAILED(hr)) trace("registry value cannot be opened\n"
204                 "   key: %s\n"
205                 "   value: %s\n"
206                 "   expected type: 0x%x\n"
207                 "   found type: 0x%x\n"
208                 "   result code: 0x%0x\n",
209                 wine_dbgstr_w(keyPath),
210                 wine_dbgstr_w(valueName),
211                 dwExpectedType,
212                 dwType,
213                 hr);
214
215     if(SUCCEEDED(hr))
216     {
217         lpData = CoTaskMemAlloc(dwSize);
218         if(!lpData)
219             hr = E_OUTOFMEMORY;
220     }
221
222     if(SUCCEEDED(hr))
223         hr = HRESULT_FROM_WIN32(_RegGetValueW(hKey, NULL, valueName, dwExpectedType, &dwType, lpData, &dwSize));
224
225     if(SUCCEEDED(hr))
226     {
227         if(memcmp(lpData, lpExpectedContent, dwSize)==0)
228             hr = S_OK;
229         else
230         {
231             if(dwExpectedType == RRF_RT_REG_SZ)
232                 /* if value type is REG_SZ, display expected and found values */
233                 trace("not expected content of registry value\n"
234                         "   key: %s\n"
235                         "   value: %s\n"
236                         "   expected REG_SZ content: %s\n"
237                         "   found REG_SZ content:    %s\n",
238                         wine_dbgstr_w(keyPath),
239                         wine_dbgstr_w(valueName),
240                         wine_dbgstr_w(lpExpectedContent),
241                         wine_dbgstr_w(lpData));
242             else
243                 /* in the other case, do not display content */
244                 trace("not expected content of registry value\n"
245                         "   key: %s\n"
246                         "   value: %s\n"
247                         "   value type: 0x%x\n",
248                         wine_dbgstr_w(keyPath),
249                         wine_dbgstr_w(valueName),
250                         dwType);
251
252             hr = S_FALSE;
253         }
254     }
255
256     CoTaskMemFree(lpData);
257     return hr;
258 }
259 /*******************************************************************************
260  *  _validateGameRegistryValues
261  *
262  * Helper function, verifies values in game's registry key
263  *
264  * Parameters:
265  *  line                        [I]     place of original call. Used only to display
266  *                                      more useful message on test fail
267  *  hKey                        [I]     handle to game's key. Key must be opened
268  *                                      with KEY_READ access permission
269  *  keyPath                     [I]     string with path to game's key. Used only
270  *                                      to display more useful message on test fail
271  *  gameApplicationId           [I]     game application identifier
272  *  gameExePath                 [I]     directory where game executable is stored
273  *  gameExeName                 [I]     full path to executable, including directory and file name
274  */
275 static void _validateGameRegistryValues(int line,
276         HKEY hKey,
277         LPCWSTR keyPath,
278         LPCGUID gameApplicationId,
279         LPCWSTR gameExePath,
280         LPCWSTR gameExeName)
281 {
282     static const WCHAR sApplicationId[] = {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
283     static const WCHAR sConfigApplicationPath[] = {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
284     static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
285     static const WCHAR sDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
286     static const WCHAR sExampleGame[] = {'E','x','a','m','p','l','e',' ','G','a','m','e',0};
287     static const WCHAR sGameDescription[] = {'G','a','m','e',' ','D','e','s','c','r','i','p','t','i','o','n',0};
288     static const WCHAR sTitle[] = {'T','i','t','l','e',0};
289
290     HRESULT hr;
291     WCHAR sGameApplicationId[40];
292
293     hr = (StringFromGUID2(gameApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
294     ok_(__FILE__, line)(hr == S_OK, "cannot convert game application id to string\n");
295
296     /* these values exist up from Vista */
297     hr = _validateRegistryValue(hKey, keyPath,   sApplicationId,            RRF_RT_REG_SZ,      sGameApplicationId);
298     ok_(__FILE__, line)(hr==S_OK, "failed while checking registry value (error 0x%x)\n", hr);
299     hr = _validateRegistryValue(hKey, keyPath,   sConfigApplicationPath,    RRF_RT_REG_SZ,      gameExePath);
300     ok_(__FILE__, line)(hr==S_OK, "failed while checking registry value (error 0x%x)\n", hr);
301     hr = _validateRegistryValue(hKey, keyPath,   sConfigGDFBinaryPath,      RRF_RT_REG_SZ,      gameExeName);
302     ok_(__FILE__, line)(hr==S_OK, "failed while checking registry value (error 0x%x)\n", hr);
303     hr = _validateRegistryValue(hKey, keyPath,   sTitle,                    RRF_RT_REG_SZ,      sExampleGame);
304     ok_(__FILE__, line)(hr==S_OK, "failed while checking registry value (error 0x%x)\n", hr);
305
306     /* this value exists up from Win7 */
307     hr = _validateRegistryValue(hKey, keyPath,   sDescription,              RRF_RT_REG_SZ,      sGameDescription);
308     ok_(__FILE__, line)(hr==S_OK || broken(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)), "failed while checking registry value (error 0x%x)\n", hr);
309 }
310 /*******************************************************************************
311  *  _validateGameKey
312  *
313  * Helper function, verifies current state of game's registry key with expected.
314  *
315  * Parameters:
316  *  line                        [I]     place of original call. Used only to display
317  *                                      more useful message on test fail
318  *  installScope                [I]     the scope which was used in AddGame/InstallGame call
319  *  gameInstanceId              [I]     game instance identifier
320  *  gameApplicationId           [I]     game application identifier
321  *  gameExePath                 [I]     directory where game executable is stored
322  *  gameExeName                 [I]     full path to executable, including directory and file name
323  *  presenceExpected            [I]     is it expected that game should be currently
324  *                                      registered or not. Should be TRUE if checking
325  *                                      after using AddGame/InstallGame, and FALSE
326  *                                      if checking after RemoveGame/UninstallGame
327  */
328 static void _validateGameRegistryKey(int line,
329         GAME_INSTALL_SCOPE installScope,
330         LPCGUID gameInstanceId,
331         LPCGUID gameApplicationId,
332         LPCWSTR gameExePath,
333         LPCWSTR gameExeName,
334         BOOL presenceExpected)
335 {
336     HRESULT hr;
337     LPWSTR lpRegistryPath = NULL;
338     HKEY hKey;
339
340     /* check key presence */
341     hr = _buildGameRegistryPath(installScope, gameInstanceId, &lpRegistryPath);
342
343     if(SUCCEEDED(hr))
344     {
345         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, 0,
346                 KEY_READ | KEY_WOW64_64KEY, &hKey));
347
348         if(presenceExpected)
349             ok_(__FILE__, line)(hr == S_OK,
350                     "problem while trying to open registry key (HKLM): %s, error: 0x%x\n", wine_dbgstr_w(lpRegistryPath), hr);
351         else
352             ok_(__FILE__, line)(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
353                     "other than expected (FILE_NOT_FOUND) error while trying to open registry key (HKLM): %s, error: 0x%x\n", wine_dbgstr_w(lpRegistryPath), hr);
354     }
355
356     if(SUCCEEDED(hr))
357     {
358         if(presenceExpected)
359             /* if the key exists and we expected it, let's verify it's content */
360             _validateGameRegistryValues(line, hKey, lpRegistryPath, gameApplicationId, gameExePath, gameExeName);
361
362         RegCloseKey(hKey);
363     }
364
365     CoTaskMemFree(lpRegistryPath);
366 }
367
368 /*******************************************************************************
369  * Test routines
370  */
371 static void test_create(BOOL* gameExplorerAvailable, BOOL* gameExplorer2Available)
372 {
373     HRESULT hr;
374
375     IGameExplorer* ge = NULL;
376     IGameExplorer2* ge2 = NULL;
377
378     /* interface available up from Vista */
379     hr = CoCreateInstance( &CLSID_GameExplorer, NULL, CLSCTX_INPROC_SERVER, &IID_IGameExplorer, (LPVOID*)&ge);
380     if(ge)
381     {
382         ok(hr == S_OK, "IGameExplorer creating failed (result false)\n");
383         *gameExplorerAvailable = TRUE;
384         IGameExplorer_Release(ge);
385     }
386     else
387         win_skip("IGameExplorer cannot be created\n");
388
389     /* interface available up from Win7 */
390     hr = CoCreateInstance( &CLSID_GameExplorer, NULL, CLSCTX_INPROC_SERVER, &IID_IGameExplorer2, (LPVOID*)&ge2);
391     if(ge2)
392     {
393         ok( hr == S_OK, "IGameExplorer2 creating failed (result false)\n");
394         *gameExplorer2Available = TRUE;
395         IGameExplorer2_Release(ge2);
396     }
397     else
398         win_skip("IGameExplorer2 cannot be created\n");
399 }
400
401 static void test_add_remove_game(void)
402 {
403     static const GUID defaultGUID = {0x01234567, 0x89AB, 0xCDEF,
404         { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}};
405     static const GUID applicationId = { 0x17A6558E, 0x60BE, 0x4078,
406         { 0xB6, 0x6F, 0x9C, 0x3A, 0xDA, 0x2A, 0x32, 0xE6 }};
407
408     HRESULT hr;
409
410     IGameExplorer* ge = NULL;
411     WCHAR sExeName[MAX_PATH];
412     WCHAR sExePath[MAX_PATH];
413     BSTR bstrExeName = NULL, bstrExePath = NULL;
414     DWORD dwExeNameLen;
415     GUID guid;
416
417     hr = CoCreateInstance(&CLSID_GameExplorer, NULL, CLSCTX_INPROC_SERVER, &IID_IGameExplorer, (LPVOID*) & ge);
418     ok(ge != NULL, "cannot create coclass IGameExplorer\n");
419     ok(hr == S_OK, "cannot create coclass IGameExplorer\n");
420
421     if(ge)
422     {
423         /* prepare path to binary */
424         dwExeNameLen = GetModuleFileNameW(NULL, sExeName, sizeof (sExeName) / sizeof (sExeName[0]));
425         ok(dwExeNameLen != 0, "GetModuleFileNameW returned invalid value\n");
426         lstrcpynW(sExePath, sExeName, StrRChrW(sExeName, NULL, '\\') - sExeName + 1);
427         bstrExeName = SysAllocString(sExeName);
428         ok(bstrExeName != NULL, "cannot allocate string for exe name\n");
429         bstrExePath = SysAllocString(sExePath);
430         ok(bstrExePath != NULL, "cannot allocate string for exe path\n");
431
432         if(bstrExeName && bstrExePath)
433         {
434             trace("prepared EXE name: %s\n", wine_dbgstr_w(bstrExeName));
435             trace("prepared EXE path: %s\n", wine_dbgstr_w(bstrExePath));
436
437
438             /* try to register game with provided guid */
439             memcpy(&guid, &defaultGUID, sizeof (guid));
440
441             hr = IGameExplorer_AddGame(ge, bstrExeName, bstrExePath, GIS_CURRENT_USER, &guid);
442             ok(SUCCEEDED(hr), "IGameExplorer::AddGame failed (error 0x%08x)\n", hr);
443             ok(memcmp(&guid, &defaultGUID, sizeof (guid)) == 0, "AddGame unexpectedly modified GUID\n");
444
445             if(SUCCEEDED(hr))
446             {
447                 _validateGameRegistryKey(__LINE__, GIS_CURRENT_USER, &guid, &applicationId, sExePath, sExeName, TRUE);
448
449                 hr = IGameExplorer_RemoveGame(ge, guid);
450                 ok(SUCCEEDED(hr), "IGameExplorer::RemoveGame failed (error 0x%08x)\n", hr);
451             }
452
453             _validateGameRegistryKey(__LINE__, GIS_CURRENT_USER, &guid, &applicationId, sExePath, sExeName, FALSE);
454
455
456             /* try to register game with empty guid */
457             memcpy(&guid, &GUID_NULL, sizeof (guid));
458
459             hr = IGameExplorer_AddGame(ge, bstrExeName, bstrExePath, GIS_CURRENT_USER, &guid);
460             ok(SUCCEEDED(hr), "IGameExplorer::AddGame failed (error 0x%08x)\n", hr);
461             ok(memcmp(&guid, &GUID_NULL, sizeof (guid)) != 0, "AddGame did not modify GUID\n");
462
463             if(SUCCEEDED(hr))
464             {
465                 _validateGameRegistryKey(__LINE__, GIS_CURRENT_USER, &guid, &applicationId, sExePath, sExeName, TRUE);
466
467                 hr = IGameExplorer_RemoveGame(ge, guid);
468                 ok(SUCCEEDED(hr), "IGameExplorer::RemoveGame failed (error 0x%08x)\n", hr);
469             }
470
471             _validateGameRegistryKey(__LINE__, GIS_CURRENT_USER, &guid, &applicationId, sExePath, sExeName, FALSE);
472         }
473
474         /* free allocated resources */
475         SysFreeString(bstrExePath);
476         SysFreeString(bstrExeName);
477
478         IGameExplorer_Release(ge);
479     }
480 }
481 void test_install_uninstall_game(void)
482 {
483     HRESULT hr;
484
485     IGameExplorer2* ge2 = NULL;
486     WCHAR sExeName[MAX_PATH];
487     WCHAR sExePath[MAX_PATH];
488     DWORD dwExeNameLen;
489
490     hr = CoCreateInstance(&CLSID_GameExplorer, NULL, CLSCTX_INPROC_SERVER, &IID_IGameExplorer2, (LPVOID*)&ge2);
491     ok(ge2 != NULL, "cannot create coclass IGameExplorer2\n");
492     ok(hr == S_OK, "cannot create coclass IGameExplorer2\n");
493
494     if(ge2)
495     {
496         /* prepare path to binary */
497         dwExeNameLen = GetModuleFileNameW(NULL, sExeName, sizeof (sExeName) / sizeof (sExeName[0]));
498         ok(dwExeNameLen != 0, "GetModuleFileNameW returned invalid value\n");
499         lstrcpynW(sExePath, sExeName, StrRChrW(sExeName, NULL, '\\') - sExeName + 1);
500
501         trace("prepared EXE name: %s\n", wine_dbgstr_w(sExeName));
502         trace("prepared EXE path: %s\n", wine_dbgstr_w(sExePath));
503
504
505         hr = IGameExplorer2_InstallGame(ge2, sExeName, sExePath, GIS_CURRENT_USER);
506         todo_wine ok(SUCCEEDED(hr), "IGameExplorer2::InstallGame failed (error 0x%08x)\n", hr);
507
508         if(SUCCEEDED(hr))
509         {
510             hr = IGameExplorer2_UninstallGame(ge2, sExeName);
511             todo_wine ok(SUCCEEDED(hr), "IGameExplorer2::UninstallGame failed (error 0x%08x)\n", hr);
512         }
513
514         IGameExplorer2_Release(ge2);
515     }
516 }
517
518 START_TEST(gameexplorer)
519 {
520     HRESULT r;
521     BOOL gameExplorerAvailable = FALSE;
522     BOOL gameExplorer2Available = FALSE;
523
524     if(_loadDynamicRoutines())
525     {
526         r = CoInitialize( NULL );
527         ok( r == S_OK, "failed to init COM\n");
528
529         test_create(&gameExplorerAvailable, &gameExplorer2Available);
530
531         if(gameExplorerAvailable)
532             test_add_remove_game();
533
534         if(gameExplorer2Available)
535             test_install_uninstall_game();
536     }
537     else
538         /* this is not a failure, because both procedures loaded by address
539          * are always available on systems which has gameux.dll */
540         win_skip("too old system, cannot load required dynamic procedures\n");
541
542
543     CoUninitialize();
544 }