+typedef struct {
+ const IUnknownVtbl *lpVtbl;
+ LONG refs;
+} Test_CallContext;
+
+static HRESULT WINAPI Test_CallContext_QueryInterface(
+ IUnknown *iface,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ if (ppvObj == NULL) return E_POINTER;
+
+ if (IsEqualGUID(riid, &IID_IUnknown))
+ {
+ *ppvObj = iface;
+ IUnknown_AddRef(iface);
+ return S_OK;
+ }
+
+ *ppvObj = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Test_CallContext_AddRef(IUnknown *iface)
+{
+ Test_CallContext *This = (Test_CallContext*)iface;
+ return InterlockedIncrement(&This->refs);
+}
+
+static ULONG WINAPI Test_CallContext_Release(IUnknown *iface)
+{
+ Test_CallContext *This = (Test_CallContext*)iface;
+ ULONG refs = InterlockedDecrement(&This->refs);
+ if (!refs)
+ HeapFree(GetProcessHeap(), 0, This);
+ return refs;
+}
+
+static const IUnknownVtbl TestCallContext_Vtbl =
+{
+ Test_CallContext_QueryInterface,
+ Test_CallContext_AddRef,
+ Test_CallContext_Release
+};
+
+static void test_CoGetCallContext(void)
+{
+ HRESULT hr;
+ ULONG refs;
+ IUnknown *pUnk;
+ IUnknown *test_object;
+
+ if (!pCoSwitchCallContext)
+ {
+ skip("CoSwitchCallContext not present\n");
+ return;
+ }
+
+ CoInitialize(NULL);
+
+ test_object = HeapAlloc(GetProcessHeap(), 0, sizeof(Test_CallContext));
+ ((Test_CallContext*)test_object)->lpVtbl = &TestCallContext_Vtbl;
+ ((Test_CallContext*)test_object)->refs = 1;
+
+ hr = CoGetCallContext(&IID_IUnknown, (void**)&pUnk);
+ ok(hr == RPC_E_CALL_COMPLETE, "Expected RPC_E_CALL_COMPLETE, got 0x%08x\n", hr);
+
+ pUnk = (IUnknown*)0xdeadbeef;
+ hr = pCoSwitchCallContext(test_object, &pUnk);
+ ok_ole_success(hr, "CoSwitchCallContext");
+ ok(pUnk == NULL, "expected NULL, got %p\n", pUnk);
+ refs = IUnknown_AddRef(test_object);
+ ok(refs == 2, "Expected refcount 2, got %d\n", refs);
+ IUnknown_Release(test_object);
+
+ pUnk = (IUnknown*)0xdeadbeef;
+ hr = CoGetCallContext(&IID_IUnknown, (void**)&pUnk);
+ ok_ole_success(hr, "CoGetCallContext");
+ ok(pUnk == test_object, "expected %p, got %p\n", test_object, pUnk);
+ refs = IUnknown_AddRef(test_object);
+ ok(refs == 3, "Expected refcount 3, got %d\n", refs);
+ IUnknown_Release(test_object);
+ IUnknown_Release(pUnk);
+
+ pUnk = (IUnknown*)0xdeadbeef;
+ hr = pCoSwitchCallContext(NULL, &pUnk);
+ ok_ole_success(hr, "CoSwitchCallContext");
+ ok(pUnk == test_object, "expected %p, got %p\n", test_object, pUnk);
+ refs = IUnknown_AddRef(test_object);
+ ok(refs == 2, "Expected refcount 2, got %d\n", refs);
+ IUnknown_Release(test_object);
+
+ hr = CoGetCallContext(&IID_IUnknown, (void**)&pUnk);
+ ok(hr == RPC_E_CALL_COMPLETE, "Expected RPC_E_CALL_COMPLETE, got 0x%08x\n", hr);
+
+ IUnknown_Release(test_object);
+
+ CoUninitialize();
+}
+
+static void test_CoGetContextToken(void)
+{
+ HRESULT hr;
+ ULONG refs;
+ ULONG_PTR token;
+ IObjContext *ctx;
+ struct info info;
+ HANDLE thread;
+ DWORD tid, exitcode;
+
+ if (!pCoGetContextToken)
+ {
+ win_skip("CoGetContextToken not present\n");
+ return;
+ }
+
+ token = 0xdeadbeef;
+ hr = pCoGetContextToken(&token);
+ ok(hr == CO_E_NOTINITIALIZED, "Expected CO_E_NOTINITIALIZED, got 0x%08x\n", hr);
+ ok(token == 0xdeadbeef, "Expected 0, got 0x%lx\n", token);
+
+ /* show that COM doesn't have to be initialized for multi-threaded apartments if another
+ thread has already done so */
+
+ info.wait = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+ info.stop = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
+
+ thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
+ ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
+
+ ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
+
+ token = 0;
+ hr = pCoGetContextToken(&token);
+ ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+
+ SetEvent(info.stop);
+ ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
+
+ GetExitCodeThread(thread, &exitcode);
+ hr = exitcode;
+ ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
+
+ CloseHandle(thread);
+ CloseHandle(info.wait);
+ CloseHandle(info.stop);
+
+ CoInitialize(NULL);
+
+ hr = pCoGetContextToken(NULL);
+ ok(hr == E_POINTER, "Expected E_POINTER, got 0x%08x\n", hr);
+
+ token = 0;
+ hr = pCoGetContextToken(&token);
+ ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+ ok(token, "Expected token != 0\n");
+
+ refs = IUnknown_AddRef((IUnknown *)token);
+ todo_wine ok(refs == 1, "Expected 1, got %u\n", refs);
+
+ hr = pCoGetObjectContext(&IID_IObjContext, (void **)&ctx);
+ ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+ todo_wine ok(ctx == (IObjContext *)token, "Expected interface pointers to be the same\n");
+
+ refs = IUnknown_AddRef((IUnknown *)ctx);
+ todo_wine ok(refs == 3, "Expected 3, got %u\n", refs);
+
+ refs = IUnknown_Release((IUnknown *)ctx);
+ todo_wine ok(refs == 2, "Expected 2, got %u\n", refs);
+
+ refs = IUnknown_Release((IUnknown *)token);
+ ok(refs == 1, "Expected 1, got %u\n", refs);
+
+ /* CoGetContextToken does not add a reference */
+ token = 0;
+ hr = pCoGetContextToken(&token);
+ ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+ ok(token, "Expected token != 0\n");
+ todo_wine ok(ctx == (IObjContext *)token, "Expected interface pointers to be the same\n");
+
+ refs = IUnknown_AddRef((IUnknown *)ctx);
+ ok(refs == 2, "Expected 1, got %u\n", refs);
+
+ refs = IUnknown_Release((IUnknown *)ctx);
+ ok(refs == 1, "Expected 0, got %u\n", refs);
+
+ refs = IUnknown_Release((IUnknown *)ctx);
+ ok(refs == 0, "Expected 0, got %u\n", refs);
+
+ CoUninitialize();
+}
+
+static void test_CoGetTreatAsClass(void)
+{
+ HRESULT hr;
+ CLSID out;
+ static GUID deadbeef = {0xdeadbeef,0xdead,0xbeef,{0xde,0xad,0xbe,0xef,0xde,0xad,0xbe,0xef}};
+
+ if (!pCoGetTreatAsClass)
+ {
+ win_skip("CoGetTreatAsClass not present\n");
+ return;
+ }
+ hr = pCoGetTreatAsClass(&deadbeef,&out);
+ ok (hr == S_FALSE, "expected S_FALSE got %x\n",hr);
+ ok (IsEqualGUID(&out,&deadbeef), "expected to get same clsid back\n");
+}
+
+static void test_CoInitializeEx(void)
+{
+ HRESULT hr;
+
+ hr = pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ ok(hr == S_OK, "CoInitializeEx failed with error 0x%08x\n", hr);
+
+ /* Calling OleInitialize for the first time should yield S_OK even with
+ * apartment already initialized by previous CoInitialize(Ex) calls. */
+ hr = OleInitialize(NULL);
+ ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr);
+
+ /* Subsequent calls to OleInitialize should return S_FALSE */
+ hr = OleInitialize(NULL);
+ ok(hr == S_FALSE, "Expected S_FALSE, hr = 0x%08x\n", hr);
+
+ /* Cleanup */
+ CoUninitialize();
+ OleUninitialize();
+}
+