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