1 /*
2  * OLE client/server test suite
3  *
4  * Copyright 2013 Dmitry Timoshkov
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 #define CONST_VTABLE
23 
24 #include <windows.h>
25 #include <exdisp.h>
26 #include <tlhelp32.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include "wine/test.h"
30 
31 #include <initguid.h>
32 DEFINE_GUID(CLSID_WineTestObject, 0xdeadbeef,0xdead,0xbeef,0xde,0xad,0xbe,0xef,0xde,0xad,0xbe,0xef);
33 DEFINE_GUID(CLSID_UnknownUnmarshal,0x4c1e39e1,0xe3e3,0x4296,0xaa,0x86,0xec,0x93,0x8d,0x89,0x6e,0x92);
34 
35 struct winetest_info
36 {
37     LONG child_failures;
38 };
39 
40 static const struct
41 {
42     const GUID *guid;
43     const char *name;
44 } guid_name[] =
45 {
46 #define GUID_NAME(guid) \
47     { &IID_##guid, #guid }
48     GUID_NAME(IUnknown),
49     GUID_NAME(IClassFactory),
50     GUID_NAME(IOleObject),
51     GUID_NAME(IMarshal),
52     GUID_NAME(IStdMarshalInfo),
53     GUID_NAME(IExternalConnection),
54     GUID_NAME(IRunnableObject),
55     GUID_NAME(ICallFactory),
56     { &CLSID_IdentityUnmarshal, "CLSID_IdentityUnmarshal" },
57     { &CLSID_UnknownUnmarshal, "CLSID_UnknownUnmarshal" },
58 #undef GUID_NAME
59 };
60 
61 static LONG obj_ref, class_ref, server_locks;
62 
63 static const char *debugstr_guid(const GUID *guid)
64 {
65     int i;
66 
67     if (!guid) return "(null)";
68 
69     for (i = 0; i < ARRAY_SIZE(guid_name); i++)
70     {
71         if (IsEqualIID(guid, guid_name[i].guid))
72             return guid_name[i].name;
73     }
74 
75     return wine_dbgstr_guid(guid);
76 }
77 
78 /******************************* OLE server *******************************/
79 typedef struct
80 {
81     IUnknown IUnknown_iface;
82     LONG ref;
83 } UnknownImpl;
84 
85 static inline UnknownImpl *impl_from_IUnknown(IUnknown *iface)
86 {
87     return CONTAINING_RECORD(iface, UnknownImpl, IUnknown_iface);
88 }
89 
90 static HRESULT WINAPI UnknownImpl_QueryInterface(IUnknown *iface,
91     REFIID iid, void **ppv)
92 {
93     UnknownImpl *This = impl_from_IUnknown(iface);
94 
95     trace("server: unknown_QueryInterface: %p,%s,%p\n", iface, debugstr_guid(iid), ppv);
96 
97     if (!ppv) return E_INVALIDARG;
98 
99     if (IsEqualIID(&IID_IUnknown, iid))
100     {
101         *ppv = &This->IUnknown_iface;
102         IUnknown_AddRef(&This->IUnknown_iface);
103         return S_OK;
104     }
105 
106     *ppv = NULL;
107     return E_NOINTERFACE;
108 }
109 
110 static ULONG WINAPI UnknownImpl_AddRef(IUnknown *iface)
111 {
112     UnknownImpl *This = impl_from_IUnknown(iface);
113     ULONG ref = InterlockedIncrement(&This->ref);
114 
115     InterlockedIncrement(&obj_ref);
116 
117     trace("server: unknown_AddRef: %p, ref %u\n", iface, ref);
118     return ref;
119 }
120 
121 static ULONG WINAPI UnknownImpl_Release(IUnknown *iface)
122 {
123     UnknownImpl *This = impl_from_IUnknown(iface);
124     ULONG ref = InterlockedDecrement(&This->ref);
125 
126     InterlockedDecrement(&obj_ref);
127 
128     trace("server: unknown_Release: %p, ref %u\n", iface, ref);
129     if (ref == 0) HeapFree(GetProcessHeap(), 0, This);
130     return ref;
131 }
132 
133 static const IUnknownVtbl UnknownImpl_Vtbl =
134 {
135     UnknownImpl_QueryInterface,
136     UnknownImpl_AddRef,
137     UnknownImpl_Release,
138 };
139 
140 typedef struct
141 {
142     IClassFactory IClassFactory_iface;
143     LONG ref;
144 } ClassFactoryImpl;
145 
146 static inline ClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
147 {
148     return CONTAINING_RECORD(iface, ClassFactoryImpl, IClassFactory_iface);
149 }
150 
151 static HRESULT WINAPI ClassFactoryImpl_QueryInterface(IClassFactory *iface,
152     REFIID iid, void **ppv)
153 {
154     ClassFactoryImpl *This = impl_from_IClassFactory(iface);
155 
156     trace("server: factory_QueryInterface: %p,%s,%p\n", iface, debugstr_guid(iid), ppv);
157 
158     if (!ppv) return E_INVALIDARG;
159 
160     if (IsEqualIID(&IID_IUnknown, iid) ||
161         IsEqualIID(&IID_IClassFactory, iid))
162     {
163         IClassFactory_AddRef(&This->IClassFactory_iface);
164         *ppv = &This->IClassFactory_iface;
165         return S_OK;
166     }
167 
168     *ppv = NULL;
169     return E_NOINTERFACE;
170 }
171 
172 static ULONG WINAPI ClassFactoryImpl_AddRef(IClassFactory *iface)
173 {
174     ClassFactoryImpl *This = impl_from_IClassFactory(iface);
175     ULONG ref = InterlockedIncrement(&This->ref);
176 
177     InterlockedIncrement(&class_ref);
178 
179     trace("server: factory_AddRef: %p, ref %u\n", iface, ref);
180     return ref;
181 }
182 
183 static ULONG WINAPI ClassFactoryImpl_Release(IClassFactory *iface)
184 {
185     ClassFactoryImpl *This = impl_from_IClassFactory(iface);
186     ULONG ref = InterlockedDecrement(&This->ref);
187 
188     InterlockedDecrement(&class_ref);
189 
190     trace("server: factory_Release: %p, ref %u\n", iface, ref);
191     return ref;
192 }
193 
194 static HRESULT WINAPI ClassFactoryImpl_CreateInstance(IClassFactory *iface,
195     IUnknown *punkouter, REFIID iid, void **ppv)
196 {
197     UnknownImpl *unknown;
198     HRESULT hr;
199 
200     trace("server: factory_CreateInstance: %p,%s,%p\n", iface, debugstr_guid(iid), ppv);
201 
202     if (punkouter) return CLASS_E_NOAGGREGATION;
203 
204     unknown = HeapAlloc(GetProcessHeap(), 0, sizeof(*unknown));
205     if (!unknown) return E_OUTOFMEMORY;
206 
207     unknown->IUnknown_iface.lpVtbl = &UnknownImpl_Vtbl;
208     unknown->ref = 0;
209     IUnknown_AddRef(&unknown->IUnknown_iface);
210 
211     hr = IUnknown_QueryInterface(&unknown->IUnknown_iface, iid, ppv);
212     IUnknown_Release(&unknown->IUnknown_iface);
213 
214     return hr;
215 }
216 
217 static HRESULT WINAPI ClassFactoryImpl_LockServer(IClassFactory *iface, BOOL lock)
218 {
219     ULONG ref = lock ? InterlockedIncrement(&server_locks) : InterlockedDecrement(&server_locks);
220 
221     trace("server: factory_LockServer: %p,%d, ref %u\n", iface, lock, ref);
222     return S_OK;
223 }
224 
225 static const IClassFactoryVtbl ClassFactoryImpl_Vtbl =
226 {
227     ClassFactoryImpl_QueryInterface,
228     ClassFactoryImpl_AddRef,
229     ClassFactoryImpl_Release,
230     ClassFactoryImpl_CreateInstance,
231     ClassFactoryImpl_LockServer
232 };
233 
234 static ClassFactoryImpl factory = { { &ClassFactoryImpl_Vtbl }, 0 };
235 
236 static void ole_server(void)
237 {
238     HRESULT hr;
239     DWORD key;
240 
241     trace("server: starting %u\n", GetCurrentProcessId());
242 
243     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
244     if (hr == S_OK)
245     {
246         trace("server: registering class object\n");
247         hr = CoRegisterClassObject(&CLSID_WineTestObject, (IUnknown *)&factory,
248                                    CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &key);
249         if (hr == S_OK)
250         {
251             HANDLE done_event, init_done_event;
252 
253             done_event = OpenEventA(SYNCHRONIZE, FALSE, "ole_server_done_event");
254             ok(done_event != 0, "server: OpenEvent error %d\n", GetLastError());
255             init_done_event = OpenEventA(EVENT_MODIFY_STATE, FALSE, "ole_server_init_done_event");
256             ok(init_done_event != 0, "server: OpenEvent error %d\n", GetLastError());
257 
258             SetEvent(init_done_event);
259 
260             trace("server: waiting for requests\n");
261             WaitForSingleObject(done_event, INFINITE);
262 
263             /* 1 remainining class ref is supposed to be cleared by CoRevokeClassObject */
264             ok(class_ref == 1, "expected 1 class refs, got %d\n", class_ref);
265             ok(!obj_ref, "expected 0 object refs, got %d\n", obj_ref);
266             ok(!server_locks, "expected 0 server locks, got %d\n", server_locks);
267 
268             CloseHandle(done_event);
269             CloseHandle(init_done_event);
270             if (0)
271             {
272                 /* calling CoRevokeClassObject terminates process under Win7 */
273                 trace("call CoRevokeClassObject\n");
274                 CoRevokeClassObject(key);
275                 trace("ret CoRevokeClassObject\n");
276             }
277         }
278         trace("server: call CoUninitialize\n");
279         CoUninitialize();
280         trace("server: ret CoUninitialize\n");
281     }
282 
283     trace("server: exiting %u\n", GetCurrentProcessId());
284 }
285 
286 /******************************* OLE client *******************************/
287 static BOOL register_server(const char *server, BOOL inproc_handler)
288 {
289     static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0};
290     DWORD ret;
291     HKEY root;
292     WCHAR buf[39 + 6];
293     char server_path[MAX_PATH];
294 
295     lstrcpyA(server_path, server);
296     lstrcatA(server_path, " ole_server");
297 
298     lstrcpyW(buf, clsidW);
299     StringFromGUID2(&CLSID_WineTestObject, buf + 6, 39);
300 
301     ret = RegCreateKeyExW(HKEY_CLASSES_ROOT, buf, 0, NULL, 0,
302                           KEY_READ | KEY_WRITE | KEY_CREATE_SUB_KEY, NULL, &root, NULL);
303     if (ret == ERROR_SUCCESS)
304     {
305         ret = RegSetValueA(root, "LocalServer32", REG_SZ, server_path, strlen(server_path));
306         ok(ret == ERROR_SUCCESS, "RegSetValue error %u\n", ret);
307 
308         if (inproc_handler)
309         {
310             ret = RegSetValueA(root, "InprocHandler32", REG_SZ, "ole32.dll", 9);
311             ok(ret == ERROR_SUCCESS, "RegSetValue error %u\n", ret);
312         }
313 
314         RegCloseKey(root);
315     }
316 
317     return ret == ERROR_SUCCESS;
318 }
319 
320 static void unregister_server(void)
321 {
322     static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0};
323     DWORD ret;
324     HKEY root;
325     WCHAR buf[39 + 6];
326 
327     lstrcpyW(buf, clsidW);
328     StringFromGUID2(&CLSID_WineTestObject, buf + 6, 39);
329 
330     ret = RegCreateKeyExW(HKEY_CLASSES_ROOT, buf, 0, NULL, 0,
331                           DELETE, NULL, &root, NULL);
332     if (ret == ERROR_SUCCESS)
333     {
334         ret = RegDeleteKeyA(root, "InprocHandler32");
335         ok(ret == ERROR_SUCCESS, "RegDeleteKey error %u\n", ret);
336         ret = RegDeleteKeyA(root, "LocalServer32");
337         ok(ret == ERROR_SUCCESS, "RegDeleteKey error %u\n", ret);
338         ret = RegDeleteKeyA(root, "");
339         ok(ret == ERROR_SUCCESS, "RegDeleteKey error %u\n", ret);
340         RegCloseKey(root);
341     }
342 }
343 
344 static HANDLE start_server(const char *argv0)
345 {
346     PROCESS_INFORMATION pi;
347     STARTUPINFOA si;
348     SECURITY_ATTRIBUTES sa;
349     char cmdline[MAX_PATH * 2];
350     BOOL ret;
351 
352     memset(&si, 0, sizeof(si));
353     si.cb = sizeof(si);
354     si.dwFlags = STARTF_USESTDHANDLES;
355     si.hStdInput =  GetStdHandle(STD_INPUT_HANDLE);
356     si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
357     si.hStdError = si.hStdOutput;
358 
359     sa.nLength = sizeof(sa);
360     sa.lpSecurityDescriptor = NULL;
361     sa.bInheritHandle = TRUE;
362 
363     sprintf(cmdline, "\"%s\" ole_server -server", argv0);
364     ret = CreateProcessA(argv0, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
365     ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
366     if (!ret) return 0;
367 
368     CloseHandle(pi.hThread);
369     return pi.hProcess;
370 }
371 
372 START_TEST(ole_server)
373 {
374     CLSID clsid = CLSID_WineTestObject;
375     HRESULT hr;
376     IClassFactory *factory;
377     IUnknown *unknown;
378     IOleObject *oleobj;
379     IRunnableObject *runobj;
380     DWORD ret;
381     HANDLE mapping, done_event, init_done_event, process;
382     struct winetest_info *info;
383     int argc;
384     char **argv;
385 
386     mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "winetest_ole_server");
387     ok(mapping != 0, "CreateFileMapping failed\n");
388     info = MapViewOfFile(mapping, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 4096);
389 
390     argc = winetest_get_mainargs(&argv);
391 
392     done_event = CreateEventA(NULL, TRUE, FALSE, "ole_server_done_event");
393     ok(done_event != 0, "CreateEvent error %d\n", GetLastError());
394     init_done_event = CreateEventA(NULL, TRUE, FALSE, "ole_server_init_done_event");
395     ok(init_done_event != 0, "CreateEvent error %d\n", GetLastError());
396 
397     if (argc > 2)
398     {
399         if (!lstrcmpiA(argv[2], "-Embedding"))
400         {
401             trace("server: Refusing to be run by ole32\n");
402             return;
403         }
404 
405         if (!lstrcmpiA(argv[2], "-server"))
406         {
407             info->child_failures = 0;
408             ole_server();
409             info->child_failures = winetest_get_failures();
410             return;
411         }
412 
413         trace("server: Unknown parameter: %s\n", argv[2]);
414         return;
415     }
416 
417     if (!register_server(argv[0], FALSE))
418     {
419         win_skip("not enough permissions to create a server CLSID key\n");
420         return;
421     }
422 
423     if (!(process = start_server(argv[0])))
424     {
425         unregister_server();
426         return;
427     }
428     WaitForSingleObject(init_done_event, 5000);
429 
430     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
431     ok(hr == S_OK, "OleInitialize error %#x\n", hr);
432 
433     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&unknown);
434     ok(hr == REGDB_E_CLASSNOTREG, "expected REGDB_E_CLASSNOTREG, got %#x\n", hr);
435 
436     if (!register_server(argv[0], TRUE))
437     {
438         win_skip("not enough permissions to create a server CLSID key\n");
439         unregister_server();
440         return;
441     }
442 
443     trace("call CoCreateInstance(&IID_NULL)\n");
444     hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_NULL, (void **)&unknown);
445     trace("ret CoCreateInstance(&IID_NULL)\n");
446     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
447 
448     /* in-process handler supports IID_IUnknown starting from Vista */
449     trace("call CoCreateInstance(&IID_IUnknown)\n");
450     hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&unknown);
451     trace("ret CoCreateInstance(&IID_IUnknown)\n");
452     ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG) /* XP,win2000 and earlier */, "CoCreateInstance(IID_IUnknown) error %#x\n", hr);
453     if (hr != S_OK)
454     {
455         win_skip("In-process handler doesn't support IID_IUnknown on this platform\n");
456         goto test_local_server;
457     }
458 
459     trace("call CoCreateInstance(&IID_IOleObject)\n");
460     hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IOleObject, (void **)&oleobj);
461     trace("ret CoCreateInstance(&IID_IOleObject)\n");
462     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
463 
464     trace("call IUnknown_QueryInterface(&IID_IRunnableObject)\n");
465     hr = IUnknown_QueryInterface(unknown, &IID_IRunnableObject, (void **)&runobj);
466     trace("ret IUnknown_QueryInterface(&IID_IRunnableObject)\n");
467     ok(hr == S_OK, "QueryInterface(&IID_IRunnableObject) error %#x\n", hr);
468 
469     ret = IRunnableObject_IsRunning(runobj);
470     ok(!ret, "expected 0, got %d\n", ret);
471 
472     trace("call OleRun\n");
473     hr = OleRun(unknown);
474     trace("ret OleRun\n");
475 todo_wine
476     ok(hr == S_OK, "OleRun error %#x\n", hr);
477 
478     ret = IRunnableObject_IsRunning(runobj);
479 todo_wine
480     ok(ret == 1, "expected 1, got %d\n", ret);
481 
482     trace("call IRunnableObject_Release\n");
483     ret = IRunnableObject_Release(runobj);
484     trace("ret IRunnableObject_Release\n");
485     ok(ret == 1, "expected ref 1, got %u\n", ret);
486 
487     trace("call IUnknown_QueryInterface(&IID_IOleObject)\n");
488     hr = IUnknown_QueryInterface(unknown, &IID_IOleObject, (void **)&oleobj);
489     trace("ret IUnknown_QueryInterface(&IID_IOleObject)\n");
490     ok(hr == S_OK, "QueryInterface(&IID_IOleObject) error %#x\n", hr);
491 
492     trace("call IOleObject_Release\n");
493     ret = IOleObject_Release(oleobj);
494     trace("ret IOleObject_Release\n");
495     ok(ret == 1, "expected ref 1, got %u\n", ret);
496 
497     trace("call IUnknown_Release\n");
498     ret = IUnknown_Release(unknown);
499     trace("ret IUnknown_Release\n");
500     ok(!ret, "expected ref 0, got %u\n", ret);
501 
502 test_local_server:
503     /* local server supports IID_IUnknown */
504     trace("call CoCreateInstance(&IID_IUnknown)\n");
505     hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IUnknown, (void **)&unknown);
506     trace("ret CoCreateInstance(&IID_IUnknown)\n");
507     ok(hr == S_OK, "CoCreateInstance(IID_IUnknown) error %#x\n", hr);
508 
509     trace("call IUnknown_QueryInterface(&IID_IRunnableObject)\n");
510     hr = IUnknown_QueryInterface(unknown, &IID_IRunnableObject, (void **)&runobj);
511     trace("ret IUnknown_QueryInterface(&IID_IRunnableObject)\n");
512     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
513 
514     trace("call OleRun\n");
515     hr = OleRun(unknown);
516     trace("ret OleRun\n");
517     ok(hr == S_OK, "OleRun error %#x\n", hr);
518 
519     trace("call IUnknown_QueryInterface(&IID_IOleObject)\n");
520     hr = IUnknown_QueryInterface(unknown, &IID_IOleObject, (void **)&oleobj);
521     trace("ret IUnknown_QueryInterface(&IID_IOleObject)\n");
522     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
523 
524     trace("call IUnknown_Release\n");
525     ret = IUnknown_Release(unknown);
526     trace("ret IUnknown_Release\n");
527     ok(!ret, "expected ref 0, got %u\n", ret);
528 
529     trace("call CoGetClassObject(&IID_IClassFactory)\n");
530     hr = CoGetClassObject(&clsid, CLSCTX_LOCAL_SERVER, NULL, &IID_IClassFactory, (void **)&factory);
531     trace("ret CoGetClassObject(&IID_IClassFactory)\n");
532     ok(hr == S_OK, "CoGetClassObject error %#x\n", hr);
533 
534     trace("call IClassFactory_CreateInstance(&IID_NULL)\n");
535     hr = IClassFactory_CreateInstance(factory, NULL, &IID_NULL, (void **)&oleobj);
536     trace("ret IClassFactory_CreateInstance(&IID_NULL)\n");
537     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
538 
539     trace("call IClassFactory_CreateInstance(&IID_IOleObject)\n");
540     hr = IClassFactory_CreateInstance(factory, NULL, &IID_IOleObject, (void **)&oleobj);
541     trace("ret IClassFactory_CreateInstance(&IID_IOleObject)\n");
542     ok(hr == E_NOINTERFACE, "expected E_NOINTERFACE, got %#x\n", hr);
543 
544     trace("call IClassFactory_Release\n");
545     ret = IClassFactory_Release(factory);
546     trace("ret IClassFactory_Release\n");
547     ok(!ret, "expected ref 0, got %u\n", ret);
548 
549     trace("signalling termination\n");
550     SetEvent(done_event);
551     ret = WaitForSingleObject(process, 10000);
552     ok(ret == WAIT_OBJECT_0, "server failed to terminate\n");
553 
554     OleUninitialize();
555 
556     unregister_server();
557 
558     if (info->child_failures)
559     {
560         trace("%d failures in child process\n", info->child_failures);
561         winetest_add_failures(info->child_failures);
562     }
563 }
564