diff --git a/rostests/winetests/ole32/clipboard.c b/rostests/winetests/ole32/clipboard.c index f06a4823ac0..626d244c073 100644 --- a/rostests/winetests/ole32/clipboard.c +++ b/rostests/winetests/ole32/clipboard.c @@ -477,8 +477,6 @@ static void test_get_clipboard(void) hr = IDataObject_QueryGetData(data_obj, &fmtetc); ok(hr == DV_E_FORMATETC || broken(hr == S_OK), "IDataObject_QueryGetData should have failed with DV_E_FORMATETC instead of 0x%08x\n", hr); - if (hr == S_OK) - ReleaseStgMedium(&stgmedium); InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); fmtetc.cfFormat = CF_RIFF; @@ -499,19 +497,19 @@ static void test_get_clipboard(void) InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium); ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr); - ReleaseStgMedium(&stgmedium); + if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium); InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); fmtetc.dwAspect = 0xdeadbeef; hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium); ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr); - ReleaseStgMedium(&stgmedium); + if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium); InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); fmtetc.dwAspect = DVASPECT_THUMBNAIL; hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium); ok(hr == S_OK, "IDataObject_GetData failed with error 0x%08x\n", hr); - ReleaseStgMedium(&stgmedium); + if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium); InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); fmtetc.lindex = 256; @@ -528,11 +526,13 @@ static void test_get_clipboard(void) fmtetc.cfFormat = CF_RIFF; hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium); ok(hr == DV_E_FORMATETC, "IDataObject_GetData should have failed with DV_E_FORMATETC instead of 0x%08x\n", hr); + if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium); InitFormatEtc(fmtetc, CF_TEXT, TYMED_HGLOBAL); fmtetc.tymed = TYMED_FILE; hr = IDataObject_GetData(data_obj, &fmtetc, &stgmedium); ok(hr == DV_E_TYMED, "IDataObject_GetData should have failed with DV_E_TYMED instead of 0x%08x\n", hr); + if(SUCCEEDED(hr)) ReleaseStgMedium(&stgmedium); ok(DataObjectImpl_GetData_calls == 6, "DataObjectImpl_GetData should have been called 6 times instead of %d times\n", DataObjectImpl_GetData_calls); @@ -735,7 +735,7 @@ static void test_cf_dataobject(IDataObject *data) DVTARGETDEVICE *target; ok(fmt_ptr->fmt.ptd != NULL, "target device offset zero\n"); - target = (DVTARGETDEVICE*)((char*)priv + (DWORD)fmt_ptr->fmt.ptd); + target = (DVTARGETDEVICE*)((char*)priv + (DWORD_PTR)fmt_ptr->fmt.ptd); ok(!memcmp(target, fmt.ptd, fmt.ptd->tdSize), "target devices differ\n"); CoTaskMemFree(fmt.ptd); } @@ -967,7 +967,7 @@ static void test_consumer_refs(void) hr = IDataObject_GetData(get1, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(DataObjectImpl_GetData_calls == 0, "GetData called\n"); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); hr = OleGetClipboard(&get2); ok(hr == S_OK, "got %08x\n", hr); @@ -1004,7 +1004,7 @@ static void test_consumer_refs(void) hr = IDataObject_GetData(get1, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n"); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); refs = count_refs(src); ok(refs == old_refs + 1, "%d %d\n", refs, old_refs); @@ -1014,7 +1014,7 @@ static void test_consumer_refs(void) hr = IDataObject_GetData(get1, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n"); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); refs = count_refs(src); ok(refs == 2, "%d\n", refs); @@ -1047,7 +1047,7 @@ static void test_consumer_refs(void) hr = IDataObject_GetData(get1, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n"); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); refs = count_refs(src); ok(refs == 1, "%d\n", refs); @@ -1096,23 +1096,24 @@ static void test_flushed_getdata(void) hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, CF_TEXT, TYMED_ISTREAM); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, CF_TEXT, TYMED_ISTORAGE); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == E_FAIL, "got %08x\n", hr); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, CF_TEXT, 0xffff); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); /* stream format -> global & stream */ @@ -1120,23 +1121,24 @@ static void test_flushed_getdata(void) hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_stream, TYMED_ISTORAGE); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == E_FAIL, "got %08x\n", hr); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_stream, TYMED_HGLOBAL); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_stream, 0xffff); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); /* storage format -> global, stream & storage */ @@ -1144,34 +1146,36 @@ static void test_flushed_getdata(void) hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); - hr = IStorage_Stat(med.u.pstg, &stat, STATFLAG_NONAME); - ok(hr == S_OK, "got %08x\n", hr); - ok(stat.grfMode == (STGM_SHARE_EXCLUSIVE | STGM_READWRITE), "got %08x\n", stat.grfMode); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) { + hr = IStorage_Stat(med.u.pstg, &stat, STATFLAG_NONAME); + ok(hr == S_OK, "got %08x\n", hr); + ok(stat.grfMode == (STGM_SHARE_EXCLUSIVE | STGM_READWRITE), "got %08x\n", stat.grfMode); + ReleaseStgMedium(&med); + } InitFormatEtc(fmt, cf_storage, TYMED_ISTREAM); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_storage, TYMED_HGLOBAL); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_storage, TYMED_HGLOBAL | TYMED_ISTREAM); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_storage, 0xffff); hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); /* complex format with target device */ @@ -1180,7 +1184,7 @@ static void test_flushed_getdata(void) ok(hr == DV_E_FORMATETC || broken(hr == S_OK), /* win9x, winme & nt4 */ "got %08x\n", hr); - if(hr == S_OK) ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); InitFormatEtc(fmt, cf_another, 0xffff); memset(&dm, 0, sizeof(dm)); @@ -1199,7 +1203,7 @@ static void test_flushed_getdata(void) hr = IDataObject_GetData(get, &fmt, &med); ok(hr == S_OK, "got %08x\n", hr); ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); - ReleaseStgMedium(&med); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); HeapFree(GetProcessHeap(), 0, fmt.ptd); @@ -1235,6 +1239,8 @@ static void test_nonole_clipboard(void) FORMATETC fmt; HGLOBAL h, hblob, htext; HENHMETAFILE emf; + STGMEDIUM med; + DWORD obj_type; r = OpenClipboard(NULL); ok(r, "gle %d\n", GetLastError()); @@ -1349,6 +1355,13 @@ static void test_nonole_clipboard(void) ok(hr == S_FALSE, "got %08x\n", hr); IEnumFORMATETC_Release(enum_fmt); + InitFormatEtc(fmt, CF_ENHMETAFILE, TYMED_ENHMF); + hr = IDataObject_GetData(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + obj_type = GetObjectType(U(med).hEnhMetaFile); + ok(obj_type == OBJ_ENHMETAFILE, "got %d\n", obj_type); + if(SUCCEEDED(hr)) ReleaseStgMedium(&med); + IDataObject_Release(get); r = OpenClipboard(NULL); diff --git a/rostests/winetests/ole32/compobj.c b/rostests/winetests/ole32/compobj.c index 7363f30f735..9d469b4b174 100644 --- a/rostests/winetests/ole32/compobj.c +++ b/rostests/winetests/ole32/compobj.c @@ -25,11 +25,12 @@ #include "windef.h" #include "winbase.h" +#define USE_COM_CONTEXT_DEF +#include "initguid.h" #include "objbase.h" #include "shlguid.h" #include "urlmon.h" /* for CLSID_FileProtocol */ -#include "initguid.h" #include "ctxtcall.h" #include "wine/test.h" @@ -39,6 +40,7 @@ HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit); HRESULT (WINAPI * pCoGetObjectContext)(REFIID riid, LPVOID *ppv); HRESULT (WINAPI * pCoSwitchCallContext)(IUnknown *pObject, IUnknown **ppOldObject); HRESULT (WINAPI * pCoGetTreatAsClass)(REFCLSID clsidOld, LPCLSID pClsidNew); +HRESULT (WINAPI * pCoGetContextToken)(ULONG_PTR *token); #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) #define ok_more_than_one_lock() ok(cLocks > 0, "Number of locks should be > 0, but actually is %d\n", cLocks) @@ -201,6 +203,13 @@ static void test_StringFromGUID2(void) { WCHAR str[50]; int len; + + /* invalid pointer */ + SetLastError(0xdeadbeef); + len = StringFromGUID2(NULL,str,50); + ok(len == 0, "len: %d (expected 0)\n", len); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %x\n", GetLastError()); + /* Test corner cases for buffer size */ len = StringFromGUID2(&CLSID_StdFont,str,50); ok(len == 39, "len: %d (expected 39)\n", len); @@ -231,7 +240,7 @@ static DWORD CALLBACK ole_initialize_thread(LPVOID pv) hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED); SetEvent(info->wait); - WaitForSingleObject(info->stop, INFINITE); + WaitForSingleObject(info->stop, 10000); CoUninitialize(); return hr; @@ -252,7 +261,25 @@ static void test_CoCreateInstance(void) ok(pUnk == NULL, "CoCreateInstance should have changed the passed in pointer to NULL, instead of %p\n", pUnk); OleInitialize(NULL); + + /* test errors returned for non-registered clsids */ + hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered inproc server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&pUnk); + ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered inproc handler should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_LOCAL_SERVER, &IID_IUnknown, (void **)&pUnk); + ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered local server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + hr = CoCreateInstance(&CLSID_non_existent, NULL, CLSCTX_REMOTE_SERVER, &IID_IUnknown, (void **)&pUnk); + ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance for non-registered remote server should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + if(hr == REGDB_E_CLASSNOTREG) + { + skip("IE not installed so can't test CoCreateInstance\n"); + OleUninitialize(); + return; + } + ok_ole_success(hr, "CoCreateInstance"); if(pUnk) IUnknown_Release(pUnk); OleUninitialize(); @@ -272,7 +299,7 @@ static void test_CoCreateInstance(void) thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(info.wait, INFINITE); + ok( !WaitForSingleObject(info.wait, 10000 ), "wait timed out\n" ); pUnk = (IUnknown *)0xdeadbeef; hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); @@ -280,7 +307,7 @@ static void test_CoCreateInstance(void) if (pUnk) IUnknown_Release(pUnk); SetEvent(info.stop); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; @@ -321,15 +348,20 @@ static void test_CoGetClassObject(void) thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(info.wait, INFINITE); + ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" ); pUnk = (IUnknown *)0xdeadbeef; hr = CoGetClassObject(rclsid, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk); - ok(hr == S_OK, "CoGetClassObject should have returned S_OK instead of 0x%08x\n", hr); - if (pUnk) IUnknown_Release(pUnk); + if(hr == REGDB_E_CLASSNOTREG) + skip("IE not installed so can't test CoGetClassObject\n"); + else + { + ok(hr == S_OK, "CoGetClassObject should have returned S_OK instead of 0x%08x\n", hr); + if (pUnk) IUnknown_Release(pUnk); + } SetEvent(info.stop); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; @@ -816,6 +848,14 @@ static void test_CoRegisterClassObject(void) hr = CoRevokeClassObject(cookie); ok_ole_success(hr, "CoRevokeClassObject"); + /* test whether an object that doesn't support IClassFactory can be + * registered for CLSCTX_LOCAL_SERVER */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, &Test_Unknown, + CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + /* test whether registered class becomes invalid when apartment is destroyed */ hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); @@ -935,7 +975,7 @@ static void test_registered_object_thread_affinity(void) thread = CreateThread(NULL, 0, get_class_object_thread, (LPVOID)CLSCTX_INPROC_SERVER, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; ok(hr == REGDB_E_CLASSNOTREG, "CoGetClassObject on inproc object " @@ -948,7 +988,7 @@ static void test_registered_object_thread_affinity(void) thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(thread, INFINITE); + ok ( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different thread should return S_OK instead of 0x%08x\n", hr); @@ -964,7 +1004,7 @@ static void test_registered_object_thread_affinity(void) thread = CreateThread(NULL, 0, get_class_object_proxy_thread, (LPVOID)CLSCTX_LOCAL_SERVER, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1) + while (MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT) == WAIT_OBJECT_0 + 1) { MSG msg; while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) @@ -985,7 +1025,7 @@ static void test_registered_object_thread_affinity(void) thread = CreateThread(NULL, 0, revoke_class_object_thread, (LPVOID)(DWORD_PTR)cookie, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; ok(hr == RPC_E_WRONG_THREAD, "CoRevokeClassObject called from different " @@ -993,7 +1033,7 @@ static void test_registered_object_thread_affinity(void) thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid); ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); GetExitCodeThread(thread, &exitcode); hr = exitcode; ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different " @@ -1030,7 +1070,7 @@ static void test_CoFreeUnusedLibraries(void) hr = CoCreateInstance(&CLSID_FileProtocol, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void **)&pUnk); if (hr == REGDB_E_CLASSNOTREG) { - trace("IE not installed so can't run CoFreeUnusedLibraries test\n"); + skip("IE not installed so can't run CoFreeUnusedLibraries test\n"); CoUninitialize(); return; } @@ -1047,7 +1087,7 @@ static void test_CoFreeUnusedLibraries(void) ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n"); thread = CreateThread(NULL, 0, free_libraries_thread, NULL, 0, &tid); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); CloseHandle(thread); ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n"); @@ -1065,8 +1105,12 @@ static void test_CoGetObjectContext(void) ULONG refs; IComThreadingInfo *pComThreadingInfo; IContextCallback *pContextCallback; + IObjContext *pObjContext; APTTYPE apttype; THDTYPE thdtype; + struct info info; + HANDLE thread; + DWORD tid, exitcode; if (!pCoGetObjectContext) { @@ -1078,6 +1122,36 @@ static void test_CoGetObjectContext(void) ok(hr == CO_E_NOTINITIALIZED, "CoGetObjectContext should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); ok(pComThreadingInfo == NULL, "pComThreadingInfo should have been set to NULL\n"); + /* 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" ); + + pComThreadingInfo = NULL; + hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo); + ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr); + IComThreadingInfo_Release(pComThreadingInfo); + + 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); + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo); @@ -1130,6 +1204,12 @@ static void test_CoGetObjectContext(void) ok(refs == 0, "pContextCallback should have 0 refs instead of %d refs\n", refs); } + hr = pCoGetObjectContext(&IID_IObjContext, (void **)&pObjContext); + ok_ole_success(hr, "CoGetObjectContext"); + + refs = IObjContext_Release(pObjContext); + ok(refs == 0, "pObjContext should have 0 refs instead of %d refs\n", refs); + CoUninitialize(); } @@ -1233,6 +1313,101 @@ static void test_CoGetCallContext(void) 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; @@ -1259,7 +1434,7 @@ static void test_CoInitializeEx(void) /* Calling OleInitialize for the first time should yield S_OK even with * apartment already initialized by previous CoInitialize(Ex) calls. */ hr = OleInitialize(NULL); - todo_wine ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr); + ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr); /* Subsequent calls to OleInitialize should return S_FALSE */ hr = OleInitialize(NULL); @@ -1276,6 +1451,7 @@ START_TEST(compobj) pCoGetObjectContext = (void*)GetProcAddress(hOle32, "CoGetObjectContext"); pCoSwitchCallContext = (void*)GetProcAddress(hOle32, "CoSwitchCallContext"); pCoGetTreatAsClass = (void*)GetProcAddress(hOle32,"CoGetTreatAsClass"); + pCoGetContextToken = (void*)GetProcAddress(hOle32, "CoGetContextToken"); if (!(pCoInitializeEx = (void*)GetProcAddress(hOle32, "CoInitializeEx"))) { trace("You need DCOM95 installed to run this test\n"); @@ -1301,6 +1477,7 @@ START_TEST(compobj) test_CoFreeUnusedLibraries(); test_CoGetObjectContext(); test_CoGetCallContext(); + test_CoGetContextToken(); test_CoGetTreatAsClass(); test_CoInitializeEx(); } diff --git a/rostests/winetests/ole32/hglobalstream.c b/rostests/winetests/ole32/hglobalstream.c index cae13ed2ea9..5632b24ae44 100644 --- a/rostests/winetests/ole32/hglobalstream.c +++ b/rostests/winetests/ole32/hglobalstream.c @@ -75,6 +75,157 @@ static void test_streamonhglobal(IStream *pStream) hr = IStream_SetSize(pStream, ull); ok_ole_success(hr, "IStream_SetSize"); + /* IStream_Seek -- NULL position argument */ + ll.u.HighPart = 0; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, NULL); + ok_ole_success(hr, "IStream_Seek"); + + /* IStream_Seek -- valid position argument (seek from current position) */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- invalid seek argument */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 123; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_END+1, &ull); + ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr); + ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should not have changed HighPart, got %d\n", ull.u.HighPart); + + /* IStream_Seek -- valid position argument (seek to beginning) */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- valid position argument (seek to end) */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_END, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- ignore HighPart in the move value (seek from current position) */ + ll.u.HighPart = 0; + ll.u.LowPart = sizeof(data); + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = -1; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- ignore HighPart in the move value (seek to beginning) */ + ll.u.HighPart = 0; + ll.u.LowPart = sizeof(data); + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = -1; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0, "should have set LowPart to 0 instead of %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- invalid LowPart value (seek from current position) */ + ll.u.HighPart = 0; + ll.u.LowPart = sizeof(data); + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0x80000000; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr); + ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- invalid LowPart value (seek to beginning) */ + ll.u.HighPart = 0; + ll.u.LowPart = sizeof(data); + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0x80000000; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok(hr == STG_E_SEEKERROR, "IStream_Seek should have returned STG_E_SEEKERROR instead of 0x%08x\n", hr); + ok(ull.u.LowPart == sizeof(data), "LowPart set to %d\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- valid LowPart value (seek to beginning) */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0x7FFFFFFF; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0x7FFFFFFF, "should have set LowPart to 0x7FFFFFFF instead of %08x\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- valid LowPart value (seek from current position) */ + ll.u.HighPart = 0; + ll.u.LowPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, &ull); + ok_ole_success(hr, "IStream_Seek"); + + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0x7FFFFFFF; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0x7FFFFFFF, "should have set LowPart to 0x7FFFFFFF instead of %08x\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- second seek allows you to go past 0x7FFFFFFF size */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 9; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0x80000008, "should have set LowPart to 0x80000008 instead of %08x\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + + /* IStream_Seek -- seek wraps position/size on integer overflow */ + ull.u.HighPart = 0xCAFECAFE; + ull.u.LowPart = 0xCAFECAFE; + ll.u.HighPart = 0; + ll.u.LowPart = 0x7FFFFFFF; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_CUR, &ull); + ok_ole_success(hr, "IStream_Seek"); + ok(ull.u.LowPart == 0x00000007, "should have set LowPart to 0x00000007 instead of %08x\n", ull.u.LowPart); + ok(ull.u.HighPart == 0, "should have set HighPart to 0 instead of %d\n", ull.u.HighPart); + hr = IStream_Commit(pStream, STGC_DEFAULT); ok_ole_success(hr, "IStream_Commit"); diff --git a/rostests/winetests/ole32/marshal.c b/rostests/winetests/ole32/marshal.c index 40824881b01..20b0d8ba8ef 100644 --- a/rostests/winetests/ole32/marshal.c +++ b/rostests/winetests/ole32/marshal.c @@ -27,11 +27,11 @@ #include "windef.h" #include "winbase.h" -#include "initguid.h" #include "objbase.h" #include "olectl.h" #include "shlguid.h" #include "shobjidl.h" +#include "initguid.h" #include "wine/test.h" @@ -276,7 +276,7 @@ static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid); /* wait for marshaling to complete before returning */ - WaitForSingleObject(marshal_event, INFINITE); + ok( !WaitForSingleObject(marshal_event, 10000), "wait timed out\n" ); CloseHandle(marshal_event); return tid; @@ -293,7 +293,7 @@ static void release_host_object(DWORD tid) { HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); PostThreadMessage(tid, RELEASEMARSHALDATA, 0, (LPARAM)event); - WaitForSingleObject(event, INFINITE); + ok( !WaitForSingleObject(event, 10000), "wait timed out\n" ); CloseHandle(event); } @@ -302,7 +302,7 @@ static void end_host_object(DWORD tid, HANDLE thread) BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0); ok(ret, "PostThreadMessage failed with error %d\n", GetLastError()); /* be careful of races - don't return until hosting thread has terminated */ - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); CloseHandle(thread); } @@ -843,7 +843,7 @@ static DWORD CALLBACK no_couninitialize_server_proc(LPVOID p) SetEvent(ncu_params->marshal_event); - WaitForSingleObject(ncu_params->unmarshal_event, INFINITE); + ok( !WaitForSingleObject(ncu_params->unmarshal_event, 10000), "wait timed out\n" ); /* die without calling CoUninitialize */ @@ -872,7 +872,7 @@ static void test_no_couninitialize_server(void) thread = CreateThread(NULL, 0, no_couninitialize_server_proc, &ncu_params, 0, &tid); - WaitForSingleObject(ncu_params.marshal_event, INFINITE); + ok( !WaitForSingleObject(ncu_params.marshal_event, 10000), "wait timed out\n" ); ok_more_than_one_lock(); IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); @@ -883,7 +883,7 @@ static void test_no_couninitialize_server(void) ok_more_than_one_lock(); SetEvent(ncu_params.unmarshal_event); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); ok_no_locks(); @@ -942,7 +942,7 @@ static void test_no_couninitialize_client(void) thread = CreateThread(NULL, 0, no_couninitialize_client_proc, &ncu_params, 0, &tid); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); CloseHandle(thread); ok_no_locks(); @@ -1109,7 +1109,7 @@ static DWORD CALLBACK weak_and_normal_marshal_thread_proc(void *p) SetEvent(data->hReadyEvent); - while (WAIT_OBJECT_0 + 1 == MsgWaitForMultipleObjects(1, &hQuitEvent, FALSE, INFINITE, QS_ALLINPUT)) + while (WAIT_OBJECT_0 + 1 == MsgWaitForMultipleObjects(1, &hQuitEvent, FALSE, 10000, QS_ALLINPUT)) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); @@ -1141,7 +1141,7 @@ static void test_tableweak_and_normal_marshal_and_unmarshal(void) ok_ole_success(hr, CreateStreamOnHGlobal); thread = CreateThread(NULL, 0, weak_and_normal_marshal_thread_proc, &data, 0, &tid); - WaitForSingleObject(data.hReadyEvent, INFINITE); + ok( !WaitForSingleObject(data.hReadyEvent, 10000), "wait timed out\n" ); CloseHandle(data.hReadyEvent); ok_more_than_one_lock(); @@ -1170,7 +1170,7 @@ static void test_tableweak_and_normal_marshal_and_unmarshal(void) IStream_Release(data.pStreamNormal); SetEvent(data.hQuitEvent); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); CloseHandle(thread); } @@ -1257,7 +1257,7 @@ static void test_lock_object_external(void) IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); hr = CoReleaseMarshalData(pStream); ok_ole_success(hr, CoReleaseMarshalData); - IStream_Release(pStream); + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); ok_more_than_one_lock(); @@ -1268,6 +1268,39 @@ static void test_lock_object_external(void) CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE); ok_no_locks(); + + /* test CoLockObjectExternal releases reference to object with + * fLastUnlockReleases as TRUE and there are only strong references on + * the object */ + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, FALSE); + + ok_more_than_one_lock(); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, FALSE); + + ok_no_locks(); + + /* test CoLockObjectExternal doesn't release the last reference to an + * object with fLastUnlockReleases as TRUE and there is a weak reference + * on the object */ + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLEWEAK); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, FALSE); + + ok_more_than_one_lock(); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, FALSE); + + ok_more_than_one_lock(); + + CoDisconnectObject((IUnknown*)&Test_ClassFactory, 0); + + ok_no_locks(); + + IStream_Release(pStream); } /* tests disconnecting stubs */ @@ -1449,7 +1482,7 @@ static void test_proxy_used_in_wrong_thread(void) /* create a thread that we can misbehave in */ thread = CreateThread(NULL, 0, bad_thread_proc, pProxy, 0, &tid2); - WaitForSingleObject(thread, INFINITE); + ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" ); CloseHandle(thread); /* do release statement on Win9x that we should have done above */ @@ -1799,6 +1832,7 @@ static HRESULT WINAPI TestRE_IClassFactory_CreateInstance( BOOL ret = SendMessageTimeout(hwnd_app, WM_NULL, 0, 0, SMTO_BLOCK, 5000, &res); ok(ret, "Timed out sending a message to originating window during RPC call\n"); } + *ppvObj = NULL; return S_FALSE; } @@ -2071,7 +2105,7 @@ static void test_freethreadedmarshaldata(IStream *pStream, MSHCTX mshctx, void * { DWORD expected_size = round_global_size(3*sizeof(DWORD) + sizeof(GUID)); ok(size == expected_size || - broken(size == round_global_size(2*sizeof(DWORD))) /* Win9x & NT4 */, + broken(size == (2*sizeof(DWORD))) /* Win9x & NT4 */, "size should have been %d instead of %d\n", expected_size, size); ok(*(DWORD *)marshal_data == mshlflags, "expected 0x%x, but got 0x%x for mshctx\n", mshlflags, *(DWORD *)marshal_data); @@ -2259,7 +2293,7 @@ static void reg_unreg_wine_test_class(BOOL Register) { error = RegCreateKeyEx(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0, KEY_SET_VALUE, NULL, &hkey, &dwDisposition); ok(error == ERROR_SUCCESS, "RegCreateKeyEx failed with error %d\n", error); - error = RegSetValueEx(hkey, NULL, 0, REG_SZ, (const unsigned char *)"ole32.dll", strlen("ole32.dll") + 1); + error = RegSetValueEx(hkey, NULL, 0, REG_SZ, (const unsigned char *)"\"ole32.dll\"", strlen("\"ole32.dll\"") + 1); ok(error == ERROR_SUCCESS, "RegSetValueEx failed with error %d\n", error); RegCloseKey(hkey); } @@ -2280,7 +2314,6 @@ static void test_inproc_handler(void) reg_unreg_wine_test_class(TRUE); hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&pObject); - todo_wine ok_ole_success(hr, "CoCreateInstance"); if (SUCCEEDED(hr)) @@ -2701,7 +2734,7 @@ static void test_local_server(void) ok(process != NULL, "couldn't start local server process, error was %d\n", GetLastError()); ready_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Ready Event"); - WaitForSingleObject(ready_event, INFINITE); + ok( !WaitForSingleObject(ready_event, 10000), "wait timed out\n" ); CloseHandle(ready_event); hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf); @@ -2781,13 +2814,13 @@ static void test_globalinterfacetable(void) * to exit before we can return */ thread = CreateThread(NULL, 0, get_global_interface_proc, ¶ms, 0, &tid); - ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT); + ret = MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT); while (ret == WAIT_OBJECT_0 + 1) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); - ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT); + ret = MsgWaitForMultipleObjects(1, &thread, FALSE, 10000, QS_ALLINPUT); } CloseHandle(thread); diff --git a/rostests/winetests/ole32/moniker.c b/rostests/winetests/ole32/moniker.c index 413045a06b3..b4e3e2f7db4 100644 --- a/rostests/winetests/ole32/moniker.c +++ b/rostests/winetests/ole32/moniker.c @@ -28,6 +28,7 @@ #include "windef.h" #include "winbase.h" #include "objbase.h" +#include "initguid.h" #include "comcat.h" #include "olectl.h" @@ -1142,7 +1143,7 @@ static void test_moniker( LPBYTE moniker_data; DWORD moniker_size; DWORD i; - BOOL same = TRUE; + BOOL same; BYTE buffer[128]; IMoniker * moniker_proxy; LPOLESTR display_name; @@ -1182,6 +1183,7 @@ static void test_moniker( testname, sizeof_expected_moniker_comparison_data, moniker_size); /* then do a byte-by-byte comparison */ + same = TRUE; for (i = 0; i < min(moniker_size, sizeof_expected_moniker_comparison_data); i++) { if (expected_moniker_comparison_data[i] != buffer[i]) @@ -1225,6 +1227,7 @@ static void test_moniker( testname, (DWORD)round_global_size(sizeof_expected_moniker_saved_data), moniker_size); /* then do a byte-by-byte comparison */ + same = TRUE; for (i = 0; i < min(moniker_size, round_global_size(sizeof_expected_moniker_saved_data)); i++) { if (expected_moniker_saved_data[i] != moniker_data[i]) @@ -1271,6 +1274,7 @@ static void test_moniker( testname, (DWORD)round_global_size(sizeof_expected_moniker_marshal_data), moniker_size); /* then do a byte-by-byte comparison */ + same = TRUE; if (expected_moniker_marshal_data) { for (i = 0; i < min(moniker_size, round_global_size(sizeof_expected_moniker_marshal_data)); i++) @@ -1428,6 +1432,16 @@ static void test_file_monikers(void) for (i = 0; i < COUNTOF(wszFile); ++i) { int j ; + if (i == 2) + { + BOOL used; + WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, wszFile[i], -1, NULL, 0, NULL, &used ); + if (used) + { + skip("string 2 doesn't round trip in codepage %u\n", GetACP() ); + continue; + } + } for (j = lstrlenW(wszFile[i]); j > 0; --j) { wszFile[i][j] = 0; @@ -1481,7 +1495,6 @@ static void test_item_moniker(void) /* IsRunning test */ hr = IMoniker_IsRunning(moniker, NULL, NULL, NULL); - todo_wine ok(hr == E_INVALIDARG, "IMoniker_IsRunning should return E_INVALIDARG, not 0x%08x\n", hr); hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); @@ -1617,7 +1630,6 @@ static void test_generic_composite_moniker(void) /* IsRunning test */ hr = IMoniker_IsRunning(moniker, NULL, NULL, NULL); - todo_wine ok(hr == E_INVALIDARG, "IMoniker_IsRunning should return E_INVALIDARG, not 0x%08x\n", hr); hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); @@ -1631,7 +1643,6 @@ static void test_generic_composite_moniker(void) todo_wine ok(hr == E_INVALIDARG, "IMoniker_BindToObject should return E_INVALIDARG, not 0x%08x\n", hr); - todo_wine hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); ok(hr == E_INVALIDARG, "IMoniker_BindToStorage should return E_INVALIDARG, not 0x%08x\n", hr); @@ -1853,6 +1864,75 @@ static void test_bind_context(void) ok(!refs, "bound object should have been destroyed, instead of having %d refs\n", refs); } +static void test_save_load_filemoniker(void) +{ + IMoniker* pMk; + IStream* pStm; + HRESULT hr; + ULARGE_INTEGER size; + LARGE_INTEGER zero_pos, dead_pos, nulls_pos; + DWORD some_val = 0xFEDCBA98; + int i; + + /* see FileMonikerImpl_Save docs */ + zero_pos.QuadPart = 0; + dead_pos.QuadPart = sizeof(WORD) + sizeof(DWORD) + (lstrlenW(wszFileName1) + 1) + sizeof(WORD); + nulls_pos.QuadPart = dead_pos.QuadPart + sizeof(WORD); + + /* create the stream we're going to write to */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStm); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + size.u.LowPart = 128; + hr = IStream_SetSize(pStm, size); + ok_ole_success(hr, "IStream_SetSize"); + + /* create and save a moniker */ + hr = CreateFileMoniker(wszFileName1, &pMk); + ok_ole_success(hr, "CreateFileMoniker"); + + hr = IMoniker_Save(pMk, pStm, TRUE); + ok_ole_success(hr, "IMoniker_Save"); + + hr = IMoniker_Release(pMk); + ok_ole_success(hr, "IMoniker_Release"); + + /* overwrite the constants with various values */ + hr = IStream_Seek(pStm, zero_pos, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + hr = IStream_Write(pStm, &some_val, sizeof(WORD), NULL); + ok_ole_success(hr, "IStream_Write"); + + hr = IStream_Seek(pStm, dead_pos, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + hr = IStream_Write(pStm, &some_val, sizeof(WORD), NULL); + ok_ole_success(hr, "IStream_Write"); + + hr = IStream_Seek(pStm, nulls_pos, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + for(i = 0; i < 5; ++i){ + hr = IStream_Write(pStm, &some_val, sizeof(DWORD), NULL); + ok_ole_success(hr, "IStream_Write"); + } + + /* go back to the start of the stream */ + hr = IStream_Seek(pStm, zero_pos, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + + /* create a new moniker and load into it */ + hr = CreateFileMoniker(wszFileName1, &pMk); + ok_ole_success(hr, "CreateFileMoniker"); + + hr = IMoniker_Load(pMk, pStm); + ok_ole_success(hr, "IMoniker_Load"); + + hr = IMoniker_Release(pMk); + ok_ole_success(hr, "IMoniker_Release"); + + hr = IStream_Release(pStm); + ok_ole_success(hr, "IStream_Release"); +} + START_TEST(moniker) { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); @@ -1866,6 +1946,7 @@ START_TEST(moniker) test_anti_moniker(); test_generic_composite_moniker(); test_pointer_moniker(); + test_save_load_filemoniker(); /* FIXME: test moniker creation funcs and parsing other moniker formats */ diff --git a/rostests/winetests/ole32/ole2.c b/rostests/winetests/ole32/ole2.c index 214c17e95a5..f796cd70249 100644 --- a/rostests/winetests/ole32/ole2.c +++ b/rostests/winetests/ole32/ole2.c @@ -1556,6 +1556,45 @@ static void test_runnable(void) g_showRunnable = TRUE; } +static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) *ppv = iface; + if (*ppv) + { + IUnknown_AddRef((IUnknown *)*ppv); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI Unknown_AddRef(IUnknown *iface) +{ + return 2; +} + +static ULONG WINAPI Unknown_Release(IUnknown *iface) +{ + return 1; +} + +static const IUnknownVtbl UnknownVtbl = +{ + Unknown_QueryInterface, + Unknown_AddRef, + Unknown_Release +}; + +static IUnknown Unknown2 = { &UnknownVtbl }; + +static void test_OleLockRunning(void) +{ + HRESULT hr; + + hr = OleLockRunning((LPUNKNOWN)&Unknown2, TRUE, FALSE); + ok(hr == S_OK, "OleLockRunning failed 0x%08x\n", hr); +} + START_TEST(ole2) { DWORD dwRegister; @@ -1587,6 +1626,7 @@ START_TEST(ole2) test_data_cache(); test_default_handler(); test_runnable(); + test_OleLockRunning(); CoUninitialize(); } diff --git a/rostests/winetests/ole32/stg_prop.c b/rostests/winetests/ole32/stg_prop.c index 82609462dc1..37e8e1031e2 100644 --- a/rostests/winetests/ole32/stg_prop.c +++ b/rostests/winetests/ole32/stg_prop.c @@ -178,6 +178,7 @@ static void testProps(void) ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val), "Didn't get expected type or value for property (got type %d, value %s)\n", var.vt, U(var).pszVal); + PropVariantClear(&var); /* read clipboard format */ spec.ulKind = PRSPEC_PROPID; @@ -282,6 +283,7 @@ static void testProps(void) ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val), "Didn't get expected type or value for property (got type %d, value %s)\n", var.vt, U(var).pszVal); + PropVariantClear(&var); IPropertyStorage_Release(propertyStorage); IPropertySetStorage_Release(propSetStorage); @@ -364,11 +366,13 @@ static void testCodepage(void) ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "hi"), "Didn't get expected type or value for property\n"); + PropVariantClear(&var); /* This seemingly non-sensical test is to show that the string is indeed * interpreted according to the current system code page, not according to * the property set's code page. (If the latter were true, the whole * string would be maintained. As it is, only the first character is.) */ + var.vt = VT_LPSTR; U(var).pszVal = (LPSTR)wval; hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); @@ -376,6 +380,8 @@ static void testCodepage(void) ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "h"), "Didn't get expected type or value for property\n"); + PropVariantClear(&var); + /* now that a property's been set, you can't change the code page */ spec.ulKind = PRSPEC_PROPID; U(spec).propid = PID_CODEPAGE; diff --git a/rostests/winetests/ole32/storage32.c b/rostests/winetests/ole32/storage32.c index da2d0aa5a6e..7568a0ceafa 100644 --- a/rostests/winetests/ole32/storage32.c +++ b/rostests/winetests/ole32/storage32.c @@ -33,6 +33,28 @@ DEFINE_GUID( test_stg_cls, 0x88888888, 0x0425, 0x0000, 0,0,0,0,0,0,0,0); #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) +static CHAR filenameA[MAX_PATH]; +static WCHAR filename[MAX_PATH]; + +static const char file1_nameA[] = {'c','o','p','y','t','e','s','t','A',0}; +static const WCHAR file1_name[] = {'c','o','p','y','t','e','s','t','A',0}; +static const char file2_nameA[] = {'c','o','p','y','t','e','s','t','B',0}; +static const WCHAR file2_name[] = {'c','o','p','y','t','e','s','t','B',0}; +static const WCHAR stgA_name[] = {'S','t','o','r','a','g','e','A',0}; +static const WCHAR stgB_name[] = {'S','t','o','r','a','g','e','B',0}; +static const WCHAR strmA_name[] = {'S','t','r','e','a','m','A',0}; +static const WCHAR strmB_name[] = {'S','t','r','e','a','m','B',0}; +static const WCHAR strmC_name[] = {'S','t','r','e','a','m','C',0}; + +/* Win9x and WinMe don't have lstrcmpW */ +static int strcmp_ww(LPCWSTR strw1, LPCWSTR strw2) +{ + CHAR stra1[512], stra2[512]; + WideCharToMultiByte(CP_ACP, 0, strw1, -1, stra1, sizeof(stra1), NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, strw2, -1, stra2, sizeof(stra2), NULL, NULL); + return lstrcmpA(stra1, stra2); +} + static void test_hglobal_storage_stat(void) { ILockBytes *ilb = NULL; @@ -67,16 +89,10 @@ static void test_hglobal_storage_stat(void) static void test_create_storage_modes(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; HRESULT r; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* test with some invalid parameters */ r = StgCreateDocfile( NULL, 0, 0, &stg); @@ -144,7 +160,7 @@ static void test_create_storage_modes(void) ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n"); r = StgCreateDocfile( filename, STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READ, 0, &stg); ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); ok(r==S_OK, "StgCreateDocfile failed\n"); @@ -161,13 +177,13 @@ static void test_create_storage_modes(void) ok(r==S_OK, "StgCreateDocfile failed\n"); r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); r = StgCreateDocfile( filename, STGM_CREATE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); ok(r==S_OK, "StgCreateDocfile failed\n"); r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); /* test the way excel uses StgCreateDocFile */ r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_READWRITE, 0, &stg); @@ -176,7 +192,7 @@ static void test_create_storage_modes(void) { r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } /* and the way windows media uses it ... */ @@ -186,7 +202,7 @@ static void test_create_storage_modes(void) { r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } /* looks like we need STGM_TRANSACTED or STGM_CREATE */ @@ -196,7 +212,7 @@ static void test_create_storage_modes(void) { r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_WRITE, 0, &stg); @@ -205,7 +221,7 @@ static void test_create_storage_modes(void) { r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); @@ -214,7 +230,7 @@ static void test_create_storage_modes(void) { r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } /* test the way msi uses StgCreateDocfile */ @@ -222,19 +238,16 @@ static void test_create_storage_modes(void) ok(r==S_OK, "StgCreateDocFile failed\n"); r = IStorage_Release(stg); ok(r == 0, "storage not released\n"); - ok(DeleteFileW(filename), "failed to delete file\n"); + ok(DeleteFileA(filenameA), "failed to delete file\n"); } static void test_storage_stream(void) { static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; static const WCHAR longname[] = { 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; HRESULT r; IStream *stm = NULL; @@ -244,10 +257,7 @@ static void test_storage_stream(void) ULARGE_INTEGER p; unsigned char buffer[0x100]; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); ok(r==S_OK, "StgCreateDocfile failed\n"); @@ -345,15 +355,28 @@ static void test_storage_stream(void) r = IStorage_Release(stg); ok(r == 0, "wrong ref count\n"); - r = DeleteFileW(filename); + + /* try create some invalid streams */ + stg = NULL; + stm = NULL; + r = StgOpenStorage(filename, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + if (stg) + { + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm); + ok(r == STG_E_INVALIDFLAG, "IStorage->OpenStream should return STG_E_INVALIDFLAG instead of 0x%08x\n", r); + IStorage_Release(stg); + } + + r = DeleteFileA(filenameA); ok(r, "file should exist\n"); } -static BOOL touch_file(LPCWSTR filename) +static BOOL touch_file(LPCSTR filename) { HANDLE file; - file = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, + file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file==INVALID_HANDLE_VALUE) return FALSE; @@ -361,12 +384,12 @@ static BOOL touch_file(LPCWSTR filename) return TRUE; } -static BOOL is_zero_length(LPCWSTR filename) +static BOOL is_zero_length(LPCSTR filename) { HANDLE file; DWORD len; - file = CreateFileW(filename, GENERIC_READ, 0, NULL, + file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (file==INVALID_HANDLE_VALUE) return FALSE; @@ -375,11 +398,11 @@ static BOOL is_zero_length(LPCWSTR filename) return len == 0; } -static BOOL is_existing_file(LPCWSTR filename) +static BOOL is_existing_file(LPCSTR filename) { HANDLE file; - file = CreateFileW(filename, GENERIC_READ, 0, NULL, + file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (file==INVALID_HANDLE_VALUE) return FALSE; @@ -389,20 +412,14 @@ static BOOL is_existing_file(LPCWSTR filename) static void test_open_storage(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; static const WCHAR szNonExist[] = { 'n','o','n','e','x','i','s','t',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL, *stg2 = NULL; HRESULT r; DWORD stgm; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - /* try opening a zero length file - it should stay zero length */ - DeleteFileW(filename); - touch_file(filename); + DeleteFileA(filenameA); + touch_file(filenameA); stgm = STGM_NOSCRATCH | STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE; r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n"); @@ -410,17 +427,17 @@ static void test_open_storage(void) stgm = STGM_SHARE_EXCLUSIVE | STGM_READWRITE; r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n"); - ok(is_zero_length(filename), "file length changed\n"); + ok(is_zero_length(filenameA), "file length changed\n"); - DeleteFileW(filename); + DeleteFileA(filenameA); /* try opening a nonexistent file - it should not create it */ stgm = STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE; r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); ok(r!=S_OK, "StgOpenStorage failed: 0x%08x\n", r); if (r==S_OK) IStorage_Release(stg); - ok(!is_existing_file(filename), "StgOpenStorage should not create a file\n"); - DeleteFileW(filename); + ok(!is_existing_file(filenameA), "StgOpenStorage should not create a file\n"); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); @@ -558,24 +575,18 @@ static void test_open_storage(void) r = StgOpenStorage( filename, NULL, STGM_NOSNAPSHOT | STGM_PRIORITY, NULL, 0, &stg); ok(r == STG_E_INVALIDFLAG, "should fail\n"); - r = DeleteFileW(filename); + r = DeleteFileA(filenameA); ok(r, "file didn't exist\n"); } static void test_storage_suminfo(void) { - static const WCHAR szDot[] = { '.',0 }; - static const WCHAR szPrefix[] = { 's','t','g',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; IPropertySetStorage *propset = NULL; IPropertyStorage *ps = NULL; HRESULT r; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | @@ -673,14 +684,11 @@ static void test_storage_suminfo(void) r = IStorage_Release(stg); ok(r == 0, "ref count wrong\n"); - DeleteFileW(filename); + DeleteFileA(filenameA); } static void test_storage_refcount(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; IStorage *stgprio = NULL; HRESULT r; @@ -691,10 +699,7 @@ static void test_storage_refcount(void) STATSTG stat; char buffer[10]; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | @@ -774,7 +779,7 @@ static void test_storage_refcount(void) r = IStorage_Stat( stg2, &statstg, STATFLAG_DEFAULT ); ok(r == S_OK, "Stat should have succeded instead of returning 0x%08x\n", r); - ok(!lstrcmpW(statstg.pwcsName, stgname), + ok(!memcmp(statstg.pwcsName, stgname, sizeof(stgname)), "Statstg pwcsName should have been the name the storage was created with\n"); ok(statstg.type == STGTY_STORAGE, "Statstg type should have been STGTY_STORAGE instead of %d\n", statstg.type); ok(U(statstg.cbSize).LowPart == 0, "Statstg cbSize.LowPart should have been 0 instead of %d\n", U(statstg.cbSize).LowPart); @@ -800,22 +805,16 @@ static void test_storage_refcount(void) } IStorage_Release(stgprio); - DeleteFileW(filename); + DeleteFileA(filenameA); } static void test_writeclassstg(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; HRESULT r; CLSID temp_cls; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | @@ -850,26 +849,22 @@ static void test_writeclassstg(void) r = IStorage_Release( stg ); ok (r == 0, "storage not released\n"); - DeleteFileW(filename); + DeleteFileA(filenameA); } static void test_streamenum(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; IStorage *stg = NULL; HRESULT r; IStream *stm = NULL; static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'A','B','C','D','E','F','G','H','I',0 }; + static const WCHAR stmname3[] = { 'A','B','C','D','E','F','G','H','I','J',0 }; STATSTG stat; IEnumSTATSTG *ee = NULL; ULONG count; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | @@ -897,6 +892,9 @@ static void test_streamenum(void) ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); ok(count == 1, "count wrong\n"); + if (r == S_OK) + CoTaskMemFree(stat.pwcsName); + r = IEnumSTATSTG_Release(ee); /* second enum... destroy the stream before reading */ @@ -904,14 +902,12 @@ static void test_streamenum(void) ok(r==S_OK, "IStorage->EnumElements failed\n"); r = IStorage_DestroyElement(stg, stmname); - ok(r==S_OK, "IStorage->EnumElements failed\n"); + ok(r==S_OK, "IStorage->DestroyElement failed\n"); - todo_wine { count = 0xf00; r = IEnumSTATSTG_Next(ee, 1, &stat, &count); ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n"); ok(count == 0, "count wrong\n"); - } /* reset and try again */ r = IEnumSTATSTG_Reset(ee); @@ -922,49 +918,161 @@ static void test_streamenum(void) ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n"); ok(count == 0, "count wrong\n"); + /* add a stream before reading */ + r = IEnumSTATSTG_Reset(ee); + ok(r==S_OK, "IEnumSTATSTG->Reset failed\n"); + + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Release(stm); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + if (r == S_OK) + { + ok(lstrcmpiW(stat.pwcsName, stmname) == 0, "expected CONTENTS, got %s\n", wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + } + + r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Release(stm); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + if (r == S_OK) + { + ok(lstrcmpiW(stat.pwcsName, stmname2) == 0, "expected ABCDEFGHI, got %s\n", wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + } + + /* delete previous and next stream after reading */ + r = IStorage_CreateStream(stg, stmname3, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Release(stm); + + r = IEnumSTATSTG_Reset(ee); + ok(r==S_OK, "IEnumSTATSTG->Reset failed\n"); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + if (r == S_OK) + { + ok(lstrcmpiW(stat.pwcsName, stmname) == 0, "expected CONTENTS, got %s\n", wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + } + + r = IStorage_DestroyElement(stg, stmname); + ok(r==S_OK, "IStorage->DestroyElement failed\n"); + + r = IStorage_DestroyElement(stg, stmname2); + ok(r==S_OK, "IStorage->DestroyElement failed\n"); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + if (r == S_OK) + { + ok(lstrcmpiW(stat.pwcsName, stmname3) == 0, "expected ABCDEFGHIJ, got %s\n", wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + } + + r = IStorage_Release( stg ); + todo_wine ok (r == 0, "storage not released\n"); + + /* enumerator is still valid and working after the storage is released */ + r = IEnumSTATSTG_Reset(ee); + ok(r==S_OK, "IEnumSTATSTG->Reset failed\n"); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + if (r == S_OK) + { + ok(lstrcmpiW(stat.pwcsName, stmname3) == 0, "expected ABCDEFGHIJ, got %s\n", wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + } + + /* the storage is left open until the enumerator is freed */ + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, NULL, 0, &stg); + ok(r==STG_E_SHAREVIOLATION || + r==STG_E_LOCKVIOLATION, /* XP-SP2/W2K3-SP1 and below */ + "StgCreateDocfile failed, res=%x\n", r); + r = IEnumSTATSTG_Release(ee); ok (r == 0, "enum not released\n"); - r = IStorage_Release( stg ); - ok (r == 0, "storage not released\n"); - - DeleteFileW(filename); + DeleteFileA(filenameA); } static void test_transact(void) { - static const WCHAR szPrefix[] = { 's','t','g',0 }; - static const WCHAR szDot[] = { '.',0 }; - WCHAR filename[MAX_PATH]; - IStorage *stg = NULL, *stg2 = NULL; + IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL; HRESULT r; IStream *stm = NULL; static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; static const WCHAR stmname2[] = { 'F','O','O',0 }; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 }; - if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) - return; - - DeleteFileW(filename); + DeleteFileA(filenameA); /* create the file */ r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); ok(r==S_OK, "StgCreateDocfile failed\n"); - /* now create a stream, but don't commit it */ + /* commit a new stream and storage */ r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); ok(r==S_OK, "IStorage->CreateStream failed\n"); r = IStream_Write(stm, "this is stream 1\n", 16, NULL); ok(r==S_OK, "IStream->Write failed\n"); - r = IStream_Release(stm); + IStream_Release(stm); - r = IStorage_Commit(stg, 0); - ok(r==S_OK, "IStorage->Commit failed\n"); + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); - /* now create a stream, but don't commit it */ + if (r == S_OK) + { + /* Create two substorages but only commit one */ + r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + + r = IStorage_Commit(stg, 0); + ok(r==S_OK, "IStorage->Commit failed\n"); + + r = IStorage_CreateStorage(stg2, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + + IStorage_Release(stg2); + } + + /* now create a stream and storage, but don't commit them */ stm = NULL; r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); ok(r==S_OK, "IStorage->CreateStream failed\n"); @@ -972,10 +1080,17 @@ static void test_transact(void) r = IStream_Write(stm, "this is stream 2\n", 16, NULL); ok(r==S_OK, "IStream->Write failed\n"); + /* IStream::Commit does nothing for OLE storage streams */ r = IStream_Commit(stm, STGC_ONLYIFCURRENT | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE); ok(r==S_OK, "IStream->Commit failed\n"); - r = IStream_Release(stm); + r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stg2); + + IStream_Release(stm); IStorage_Release(stg); @@ -999,24 +1114,467 @@ static void test_transact(void) r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); - todo_wine { r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); - } - if (stm) - r = IStream_Release(stm); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); + if (r == S_OK) + IStorage_Release(stg2); r = IStorage_OpenStorage(stg, stmname2, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); - ok(r==S_OK, "IStorage->OpenStream should fail %08x\n", r); - if (stm) - r = IStream_Release(stm); + ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r); + if (r == S_OK) + { + r = IStorage_OpenStorage(stg2, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 ); + ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r); + if (r == S_OK) + IStorage_Release(stg3); + + r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); + if (r == S_OK) + IStorage_Release(stg3); + + IStorage_Release(stg2); + } IStorage_Release(stg); - r = DeleteFileW(filename); + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_substorage_share(void) +{ + IStorage *stg, *stg2, *stg3; + IStream *stm, *stm2; + HRESULT r; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR othername[] = { 'N','E','W','N','A','M','E',0 }; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a read/write storage and try to open it again */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage should fail %08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage should fail %08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + + /* cannot rename the storage while it's open */ + r = IStorage_RenameElement(stg, stgname, othername); + ok(r==STG_E_ACCESSDENIED, "IStorage->RenameElement should fail %08x\n", r); + if (SUCCEEDED(r)) IStorage_RenameElement(stg, othername, stgname); + + /* destroying an object while it's open invalidates it */ + r = IStorage_DestroyElement(stg, stgname); + ok(r==S_OK, "IStorage->DestroyElement failed, hr=%08x\n", r); + + r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(r==STG_E_REVERTED, "IStorage->CreateStream failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stm); + + IStorage_Release(stg2); + } + + /* create a read/write stream and try to open it again */ + r = IStorage_CreateStream(stg, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r); + + if (r == S_OK) + { + r = IStorage_OpenStream(stg, stmname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm2); + ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStream should fail %08x\n", r); + + if (r == S_OK) + IStorage_Release(stm2); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm2); + ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStream should fail %08x\n", r); + + if (r == S_OK) + IStorage_Release(stm2); + + /* cannot rename the stream while it's open */ + r = IStorage_RenameElement(stg, stmname, othername); + ok(r==STG_E_ACCESSDENIED, "IStorage->RenameElement should fail %08x\n", r); + if (SUCCEEDED(r)) IStorage_RenameElement(stg, othername, stmname); + + /* destroying an object while it's open invalidates it */ + r = IStorage_DestroyElement(stg, stmname); + ok(r==S_OK, "IStorage->DestroyElement failed, hr=%08x\n", r); + + r = IStream_Write(stm, "this shouldn't work\n", 20, NULL); + ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r); + + IStorage_Release(stm); + } + + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_revert(void) +{ + IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL; + HRESULT r; + IStream *stm = NULL, *stm2 = NULL; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'F','O','O',0 }; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 }; + STATSTG statstg; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* commit a new stream and storage */ + r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Write(stm, "this is stream 1\n", 16, NULL); + ok(r==S_OK, "IStream->Write failed\n"); + + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + /* Create two substorages but only commit one */ + r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + + r = IStorage_Commit(stg, 0); + ok(r==S_OK, "IStorage->Commit failed\n"); + + r = IStorage_CreateStorage(stg2, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + IStorage_Release(stg3); + } + + /* now create a stream and storage, then revert */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm2 ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Write(stm2, "this is stream 2\n", 16, NULL); + ok(r==S_OK, "IStream->Write failed\n"); + + r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + r = IStorage_Revert(stg); + + /* all open objects become invalid */ + r = IStream_Write(stm, "this shouldn't work\n", 20, NULL); + ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r); + + r = IStream_Write(stm2, "this shouldn't work\n", 20, NULL); + ok(r==STG_E_REVERTED, "IStream_Write should fail %08x\n", r); + + r = IStorage_Stat(stg2, &statstg, STATFLAG_NONAME); + ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r); + + r = IStorage_Stat(stg3, &statstg, STATFLAG_NONAME); + ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r); + + IStream_Release(stm); + IStream_Release(stm2); + IStorage_Release(stg2); + IStorage_Release(stg3); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_DENY_NONE|STGM_READ, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_DELETEONRELEASE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); + if (r == S_OK) + IStorage_Release(stg2); + + r = IStorage_OpenStorage(stg, stmname2, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r); + if (r == S_OK) + { + r = IStorage_OpenStorage(stg2, stgname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 ); + ok(r==S_OK, "IStorage->OpenStorage should succeed %08x\n", r); + if (r == S_OK) + IStorage_Release(stg3); + + r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); + if (r == S_OK) + IStorage_Release(stg3); + + IStorage_Release(stg2); + } + + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); + + /* Revert only invalidates objects in transacted mode */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStorage_Revert(stg); + ok(r==S_OK, "IStorage->Revert failed %08x\n", r); + + r = IStream_Write(stm, "this works\n", 11, NULL); + ok(r==S_OK, "IStream_Write should succeed %08x\n", r); + + IStream_Release(stm); + IStream_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_parent_free(void) +{ + IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL; + HRESULT r; + IStream *stm = NULL; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + ULONG ref; + STATSTG statstg; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a new storage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + /* now create a stream inside the new storage */ + r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + if (r == S_OK) + { + /* create a storage inside the new storage */ + r = IStorage_CreateStorage(stg2, stgname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg3 ); + ok(r==S_OK, "IStorage->CreateStorage failed\n"); + } + + /* free the parent */ + ref = IStorage_Release(stg2); + ok(ref == 0, "IStorage still has %u references\n", ref); + + /* child objects are invalid */ + if (r == S_OK) + { + r = IStream_Write(stm, "this should fail\n", 17, NULL); + ok(r==STG_E_REVERTED, "IStream->Write sould fail, hr=%x\n", r); + + IStream_Release(stm); + + r = IStorage_Stat(stg3, &statstg, STATFLAG_NONAME); + ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r); + + r = IStorage_SetStateBits(stg3, 1, 1); + ok(r==STG_E_REVERTED, "IStorage_Stat should fail %08x\n", r); + + IStorage_Release(stg3); + } + } + + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_nonroot_transacted(void) +{ + IStorage *stg = NULL, *stg2 = NULL, *stg3 = NULL; + HRESULT r; + IStream *stm = NULL; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'F','O','O',0 }; + + DeleteFileA(filenameA); + + /* create a transacted file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a transacted substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + /* create and commit stmname */ + r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Commit(stg2, 0); + + /* create and revert stmname2 */ + r = IStorage_CreateStream(stg2, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Revert(stg2); + + /* check that Commit and Revert really worked */ + r = IStorage_OpenStream(stg2, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Release(stg2); + } + + /* create a read-only transacted substorage */ + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, NULL, 0, &stg2); + ok(r==S_OK, "IStorage->OpenStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + /* The storage can be modified. */ + r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + if (r == S_OK) + IStream_Release(stg3); + + /* But changes cannot be committed. */ + r = IStorage_Commit(stg2, 0); + ok(r==STG_E_ACCESSDENIED, "IStorage->Commit should fail, hr=%08x\n", r); + + IStorage_Release(stg2); + } + + IStorage_Release(stg); + + /* create a non-transacted file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a transacted substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + if (r == S_OK) + { + /* create and commit stmname */ + r = IStorage_CreateStream(stg2, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Commit(stg2, 0); + + /* create and revert stmname2 */ + r = IStorage_CreateStream(stg2, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Revert(stg2); + + /* check that Commit and Revert really worked */ + r = IStorage_OpenStream(stg2, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==S_OK, "IStorage->OpenStream should succeed %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); + if (r == S_OK) + IStream_Release(stm); + + IStorage_Release(stg2); + } + + IStream_Release(stg); + + r = DeleteFileA(filenameA); ok( r == TRUE, "deleted file\n"); } @@ -1048,6 +1606,8 @@ static void test_ReadClassStm(void) hr = ReadClassStm(pStream, &clsid); ok_ole_success(hr, "ReadClassStm"); ok(IsEqualCLSID(&clsid, &test_stg_cls), "clsid should have been set to CLSID_WineTest\n"); + + IStream_Release(pStream); } struct access_res @@ -1267,8 +1827,1014 @@ static void test_access(void) DeleteFileA("winetest"); } +static void test_readonly(void) +{ + IStorage *stg, *stg2, *stg3; + IStream *stream; + HRESULT hr; + static const WCHAR fileW[] = {'w','i','n','e','t','e','s','t',0}; + static const WCHAR storageW[] = {'s','t','o','r','a','g','e',0}; + static const WCHAR streamW[] = {'s','t','r','e','a','m',0}; + + hr = StgCreateDocfile( fileW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + hr = IStorage_CreateStorage( stg, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg2 ); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + hr = IStorage_CreateStream( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stream ); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + IStream_Release(stream); + IStorage_Release(stg2); + } + IStorage_Release(stg); + } + + /* re-open read only */ + hr = StgOpenStorage( fileW, NULL, STGM_TRANSACTED | STGM_SHARE_DENY_NONE | STGM_READ, NULL, 0, &stg); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + hr = IStorage_OpenStorage( stg, storageW, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg2 ); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + /* CreateStream on read-only storage, name exists */ + hr = IStorage_CreateStream( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stream ); + ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr); + if (SUCCEEDED(hr)) + IStream_Release(stream); + + /* CreateStream on read-only storage, name does not exist */ + hr = IStorage_CreateStream( stg2, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stream ); + ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr); + if (SUCCEEDED(hr)) + IStream_Release(stream); + + /* CreateStorage on read-only storage, name exists */ + hr = IStorage_CreateStorage( stg2, streamW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stg3 ); + ok(hr == STG_E_FILEALREADYEXISTS, "should fail, res=%x\n", hr); + if (SUCCEEDED(hr)) + IStream_Release(stg3); + + /* CreateStorage on read-only storage, name does not exist */ + hr = IStorage_CreateStorage( stg2, storageW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, 0, 0, &stg3 ); + ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr); + if (SUCCEEDED(hr)) + IStream_Release(stg3); + + /* DestroyElement on read-only storage, name exists */ + hr = IStorage_DestroyElement( stg2, streamW ); + ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr); + + /* DestroyElement on read-only storage, name does not exist */ + hr = IStorage_DestroyElement( stg2, storageW ); + ok(hr == STG_E_ACCESSDENIED, "should fail, res=%x\n", hr); + + IStorage_Release(stg2); + } + + IStorage_Release(stg); + } + + DeleteFileA("winetest"); +} + +static void test_simple(void) +{ + /* Tests for STGM_SIMPLE mode */ + + IStorage *stg, *stg2; + HRESULT r; + IStream *stm; + static const WCHAR stgname[] = { 'S','t','g',0 }; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'S','m','a','l','l',0 }; + LARGE_INTEGER pos; + ULARGE_INTEGER upos; + DWORD count; + STATSTG stat; + + DeleteFileA(filenameA); + + r = StgCreateDocfile( filename, STGM_SIMPLE | STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); + ok(r == S_OK, "got %08x\n", r); + + r = IStorage_CreateStorage(stg, stgname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stg2); + ok(r == STG_E_INVALIDFUNCTION, "got %08x\n", r); + if (SUCCEEDED(r)) IStorage_Release(stg2); + + r = IStorage_CreateStream(stg, stmname, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm); + ok(r == STG_E_INVALIDFLAG, "got %08x\n", r); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm); + ok(r == S_OK, "got %08x\n", r); + + upos.QuadPart = 6000; + r = IStream_SetSize(stm, upos); + ok(r == S_OK, "got %08x\n", r); + + r = IStream_Write(stm, "foo", 3, &count); + ok(r == S_OK, "got %08x\n", r); + ok(count == 3, "got %d\n", count); + + pos.QuadPart = 0; + r = IStream_Seek(stm, pos, STREAM_SEEK_CUR, &upos); + ok(r == S_OK, "got %08x\n", r); + ok(upos.QuadPart == 3, "got %d\n", upos.u.LowPart); + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME); + ok(r == S_OK || + broken(r == STG_E_INVALIDFUNCTION), /* NT4 and below */ + "got %08x\n", r); + if (r == S_OK) + ok(stat.cbSize.QuadPart == 3, "got %d\n", stat.cbSize.u.LowPart); + + pos.QuadPart = 1; + r = IStream_Seek(stm, pos, STREAM_SEEK_SET, &upos); + ok(r == S_OK, "got %08x\n", r); + ok(upos.QuadPart == 1, "got %d\n", upos.u.LowPart); + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME); + ok(r == S_OK || + broken(r == STG_E_INVALIDFUNCTION), /* NT4 and below */ + "got %08x\n", r); + if (r == S_OK) + ok(stat.cbSize.QuadPart == 1, "got %d\n", stat.cbSize.u.LowPart); + + IStream_Release(stm); + + r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm); + ok(r == S_OK, "got %08x\n", r); + + upos.QuadPart = 100; + r = IStream_SetSize(stm, upos); + ok(r == S_OK, "got %08x\n", r); + + r = IStream_Write(stm, "foo", 3, &count); + ok(r == S_OK, "got %08x\n", r); + ok(count == 3, "got %d\n", count); + + IStream_Release(stm); + + IStorage_Commit(stg, STGC_DEFAULT); + IStorage_Release(stg); + + r = StgOpenStorage( filename, NULL, STGM_SIMPLE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg); + if (r == STG_E_INVALIDFLAG) + { + win_skip("Flag combination is not supported on NT4 and below\n"); + DeleteFileA(filenameA); + return; + } + ok(r == S_OK, "got %08x\n", r); + + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg2); + ok(r == STG_E_INVALIDFUNCTION, "got %08x\n", r); + if (SUCCEEDED(r)) IStorage_Release(stg2); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm); + ok(r == S_OK, "got %08x\n", r); + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME); + ok(r == S_OK, "got %08x\n", r); + ok(stat.cbSize.QuadPart == 6000, "got %d\n", stat.cbSize.u.LowPart); + + IStream_Release(stm); + + r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stm); + ok(r == S_OK, "got %08x\n", r); + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME); + ok(r == S_OK, "got %08x\n", r); + ok(stat.cbSize.QuadPart == 4096, "got %d\n", stat.cbSize.u.LowPart); + + IStream_Release(stm); + + + IStorage_Release(stg); + + DeleteFileA(filenameA); +} + +static void test_fmtusertypestg(void) +{ + IStorage *stg; + IEnumSTATSTG *stat; + HRESULT hr; + static const char fileA[] = {'f','m','t','t','e','s','t',0}; + static const WCHAR fileW[] = {'f','m','t','t','e','s','t',0}; + static WCHAR userTypeW[] = {'S','t','g','U','s','r','T','y','p','e',0}; + static WCHAR strmNameW[] = {1,'C','o','m','p','O','b','j',0}; + + hr = StgCreateDocfile( fileW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + + if (SUCCEEDED(hr)) + { + /* try to write the stream */ + hr = WriteFmtUserTypeStg(stg, 0, userTypeW); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + + /* check that the stream was created */ + hr = IStorage_EnumElements(stg, 0, NULL, 0, &stat); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + BOOL found = FALSE; + STATSTG statstg; + DWORD got; + while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1) + { + if (strcmp_ww(statstg.pwcsName, strmNameW) == 0) + found = TRUE; + else + ok(0, "found unexpected stream or storage\n"); + CoTaskMemFree(statstg.pwcsName); + } + ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n"); + IEnumSTATSTG_Release(stat); + } + + /* re-write the stream */ + hr = WriteFmtUserTypeStg(stg, 0, userTypeW); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + + /* check that the stream is still there */ + hr = IStorage_EnumElements(stg, 0, NULL, 0, &stat); + ok(hr == S_OK, "should succeed, res=%x\n", hr); + if (SUCCEEDED(hr)) + { + BOOL found = FALSE; + STATSTG statstg; + DWORD got; + while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1) + { + if (strcmp_ww(statstg.pwcsName, strmNameW) == 0) + found = TRUE; + else + ok(0, "found unexpected stream or storage\n"); + CoTaskMemFree(statstg.pwcsName); + } + ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n"); + IEnumSTATSTG_Release(stat); + } + + IStorage_Release(stg); + DeleteFileA( fileA ); + } +} + +static void test_references(void) +{ + IStorage *stg,*stg2; + HRESULT hr; + unsigned c1,c2; + static const WCHAR StorName[] = { 'D','a','t','a','S','p','a','c','e','I','n','f','o',0 }; + + DeleteFileA(filenameA); + + hr = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(hr==S_OK, "StgCreateDocfile failed\n"); + + if (SUCCEEDED(hr)) + { + IStorage_Release(stg); + + hr = StgOpenStorage( filename, NULL, STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &stg); + ok(hr==S_OK, "StgOpenStorage failed (result=%x)\n",hr); + + if (SUCCEEDED(hr)) + { + hr = IStorage_CreateStorage(stg,StorName,STGM_READWRITE | STGM_SHARE_EXCLUSIVE,0,0,&stg2); + ok(hr == S_OK, "IStorage_CreateStorage failed (result=%x)\n",hr); + + if (SUCCEEDED(hr)) + { + c1 = IStorage_AddRef(stg); + ok(c1 == 2, "creating internal storage added references to ancestor\n"); + c1 = IStorage_AddRef(stg); + IStorage_Release(stg2); + c2 = IStorage_AddRef(stg) - 1; + ok(c1 == c2, "releasing internal storage removed references to ancestor\n"); + } + c1 = IStorage_Release(stg); + while ( c1 ) c1 = IStorage_Release(stg); + } + } + + DeleteFileA(filenameA); +} + +/* dest + * |-StorageA + * | `StreamA: "StreamA" + * |-StorageB + * | `StreamB: "StreamB" + * `StreamC: "StreamC" + */ +static HRESULT create_test_file(IStorage *dest) +{ + IStorage *stgA = NULL, *stgB = NULL; + IStream *strmA = NULL, *strmB = NULL, *strmC = NULL; + const ULONG strmA_name_size = lstrlenW(strmA_name) * sizeof(WCHAR); + const ULONG strmB_name_size = lstrlenW(strmB_name) * sizeof(WCHAR); + const ULONG strmC_name_size = lstrlenW(strmC_name) * sizeof(WCHAR); + ULONG bytes; + HRESULT hr; + + hr = IStorage_CreateStorage(dest, stgA_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stgA); + ok(hr == S_OK, "IStorage_CreateStorage failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = IStorage_CreateStream(stgA, strmA_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmA); + ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = IStream_Write(strmA, strmA_name, strmA_name_size, &bytes); + ok(hr == S_OK && bytes == strmA_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmA_name_size); + + hr = IStorage_CreateStorage(dest, stgB_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stgB); + ok(hr == S_OK, "IStorage_CreateStorage failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = IStorage_CreateStream(stgB, strmB_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmB); + ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = IStream_Write(strmB, strmB_name, strmB_name_size, &bytes); + ok(hr == S_OK && bytes == strmB_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmB_name_size); + + hr = IStorage_CreateStream(dest, strmC_name, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &strmC); + ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = IStream_Write(strmC, strmC_name, strmC_name_size, &bytes); + ok(hr == S_OK && bytes == strmC_name_size, "IStream_Write failed: 0x%08x, %d of %d bytes written\n", hr, bytes, strmC_name_size); + +cleanup: + if(strmC) + IStream_Release(strmC); + if(strmB) + IStream_Release(strmB); + if(stgB) + IStorage_Release(stgB); + if(strmA) + IStream_Release(strmA); + if(stgA) + IStorage_Release(stgA); + + return hr; +} + +static void test_copyto(void) +{ + IStorage *file1 = NULL, *file2 = NULL, *stg_tmp; + IStream *strm_tmp; + WCHAR buf[64]; + HRESULT hr; + + /* create & populate file1 */ + hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = create_test_file(file1); + if(FAILED(hr)) + goto cleanup; + + /* create file2 */ + hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* copy file1 into file2 */ + hr = IStorage_CopyTo(file1, 0, NULL, NULL, NULL); + ok(hr == STG_E_INVALIDPOINTER, "CopyTo should give STG_E_INVALIDPONITER, gave: 0x%08x\n", hr); + + hr = IStorage_CopyTo(file1, 0, NULL, NULL, file2); + ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* verify that all of file1 was copied */ + hr = IStorage_OpenStorage(file2, stgA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + hr = IStorage_OpenStream(stg_tmp, strmA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + memset(buf, 0, sizeof(buf)); + hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL); + ok(hr == S_OK, "Read failed: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + ok(strcmp_ww(buf, strmA_name) == 0, + "Expected %s to be read, got %s\n", wine_dbgstr_w(strmA_name), wine_dbgstr_w(buf)); + + IStream_Release(strm_tmp); + } + + IStorage_Release(stg_tmp); + } + + hr = IStorage_OpenStorage(file2, stgB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + memset(buf, 0, sizeof(buf)); + hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL); + ok(hr == S_OK, "Read failed: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + ok(strcmp_ww(buf, strmB_name) == 0, + "Expected %s to be read, got %s\n", wine_dbgstr_w(strmB_name), wine_dbgstr_w(buf)); + + IStream_Release(strm_tmp); + } + + IStorage_Release(stg_tmp); + } + + hr = IStorage_OpenStream(file2, strmC_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + memset(buf, 0, sizeof(buf)); + hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL); + ok(hr == S_OK, "Read failed: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + ok(strcmp_ww(buf, strmC_name) == 0, + "Expected %s to be read, got %s\n", wine_dbgstr_w(strmC_name), wine_dbgstr_w(buf)); + + IStream_Release(strm_tmp); + } + +cleanup: + if(file1) + IStorage_Release(file1); + if(file2) + IStorage_Release(file2); + + DeleteFileA(file1_nameA); + DeleteFileA(file2_nameA); +} + +static void test_copyto_snbexclusions(void) +{ + static const WCHAR *snb_exclude[] = {stgA_name, strmB_name, strmC_name, 0}; + + IStorage *file1 = NULL, *file2 = NULL, *stg_tmp; + IStream *strm_tmp; + WCHAR buf[64]; + HRESULT hr; + + /* create & populate file1 */ + hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = create_test_file(file1); + if(FAILED(hr)) + goto cleanup; + + /* create file2 */ + hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* copy file1 to file2 with name exclusions */ + hr = IStorage_CopyTo(file1, 0, NULL, (SNB)snb_exclude, file2); + ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* verify that file1 copied over, respecting exclusions */ + hr = IStorage_OpenStorage(file2, stgA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStorage_Release(stg_tmp); + + hr = IStorage_OpenStream(file2, strmA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + + hr = IStorage_OpenStorage(file2, stgB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + memset(buf, 0, sizeof(buf)); + hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL); + ok(hr == S_OK, "Read failed: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + ok(strcmp_ww(buf, strmB_name) == 0, + "Expected %s to be read, got %s\n", wine_dbgstr_w(strmB_name), wine_dbgstr_w(buf)); + + IStream_Release(strm_tmp); + } + + IStorage_Release(stg_tmp); + } + + hr = IStorage_OpenStream(file2, strmC_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + +cleanup: + if(file1) + IStorage_Release(file1); + if(file2) + IStorage_Release(file2); + + DeleteFileA(file1_nameA); + DeleteFileA(file2_nameA); +} + +static void test_copyto_iidexclusions_storage(void) +{ + IStorage *file1 = NULL, *file2 = NULL, *stg_tmp; + IStream *strm_tmp; + WCHAR buf[64]; + HRESULT hr; + + /* create & populate file1 */ + hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = create_test_file(file1); + if(FAILED(hr)) + goto cleanup; + + /* create file2 */ + hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* copy file1 to file2 with iid exclusions */ + hr = IStorage_CopyTo(file1, 1, &IID_IStorage, NULL, file2); + ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* verify that file1 copied over, respecting exclusions */ + hr = IStorage_OpenStorage(file2, stgA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStorage_Release(stg_tmp); + + hr = IStorage_OpenStream(file2, strmA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + + hr = IStorage_OpenStorage(file2, stgB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStorage should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStorage_Release(stg_tmp); + + hr = IStorage_OpenStream(file2, strmB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + + hr = IStorage_OpenStream(file2, strmC_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == S_OK, "OpenStream failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + memset(buf, 0, sizeof(buf)); + hr = IStream_Read(strm_tmp, buf, sizeof(buf), NULL); + ok(hr == S_OK, "Read failed: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + ok(strcmp_ww(buf, strmC_name) == 0, + "Expected %s to be read, got %s\n", wine_dbgstr_w(strmC_name), wine_dbgstr_w(buf)); + + IStream_Release(strm_tmp); + } + +cleanup: + if(file1) + IStorage_Release(file1); + if(file2) + IStorage_Release(file2); + + DeleteFileA(file1_nameA); + DeleteFileA(file2_nameA); +} + +static void test_copyto_iidexclusions_stream(void) +{ + IStorage *file1 = NULL, *file2 = NULL, *stg_tmp; + IStream *strm_tmp; + HRESULT hr; + + /* create & populate file1 */ + hr = StgCreateDocfile(file1_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file1); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + hr = create_test_file(file1); + if(FAILED(hr)) + goto cleanup; + + /* create file2 */ + hr = StgCreateDocfile(file2_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &file2); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* copy file1 to file2 with iid exclusions */ + hr = IStorage_CopyTo(file1, 1, &IID_IStream, NULL, file2); + ok(hr == S_OK, "CopyTo failed: 0x%08x\n", hr); + if(FAILED(hr)) + goto cleanup; + + /* verify that file1 copied over, respecting exclusions */ + hr = IStorage_OpenStorage(file2, stgA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + hr = IStorage_OpenStream(stg_tmp, strmA_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + + IStorage_Release(stg_tmp); + } + + hr = IStorage_OpenStorage(file2, stgB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg_tmp); + ok(hr == S_OK, "OpenStorage failed: 0x%08x\n", hr); + + if(SUCCEEDED(hr)){ + hr = IStorage_OpenStream(stg_tmp, strmB_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + + IStorage_Release(stg_tmp); + } + + hr = IStorage_OpenStream(file2, strmC_name, NULL, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &strm_tmp); + ok(hr == STG_E_FILENOTFOUND, "OpenStream should give STG_E_FILENOTFOUND, gave: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + IStream_Release(strm_tmp); + +cleanup: + if(file1) + IStorage_Release(file1); + if(file2) + IStorage_Release(file2); + + DeleteFileA(file1_nameA); + DeleteFileA(file2_nameA); +} + +static void test_rename(void) +{ + IStorage *stg, *stg2; + IStream *stm; + HRESULT r; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + static const WCHAR stgname2[] = { 'S','T','G',0 }; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'E','N','T','S',0 }; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* create a stream in the substorage */ + r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r); + IStream_Release(stm); + + /* rename the stream */ + r = IStorage_RenameElement(stg2, stmname, stmname2); + ok(r==S_OK, "IStorage->RenameElement failed, hr=%08x\n", r); + + /* cannot open stream with old name */ + r = IStorage_OpenStream(stg2, stmname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(r==STG_E_FILENOTFOUND, "IStorage_OpenStream should fail, hr=%08x\n", r); + if (SUCCEEDED(r)) IStream_Release(stm); + + /* can open stream with new name */ + r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(r==S_OK, "IStorage_OpenStream failed, hr=%08x\n", r); + if (SUCCEEDED(r)) IStream_Release(stm); + + IStorage_Release(stg2); + + /* rename the storage */ + IStorage_RenameElement(stg, stgname, stgname2); + + /* cannot open storage with old name */ + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg2); + ok(r==STG_E_FILENOTFOUND, "IStorage_OpenStream should fail, hr=%08x\n", r); + if (SUCCEEDED(r)) IStorage_Release(stg2); + + /* can open storage with new name */ + r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg2); + ok(r==S_OK, "IStorage_OpenStream should fail, hr=%08x\n", r); + if (SUCCEEDED(r)) + { + /* opened storage still has the stream */ + r = IStorage_OpenStream(stg2, stmname2, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(r==S_OK, "IStorage_OpenStream failed, hr=%08x\n", r); + if (SUCCEEDED(r)) IStream_Release(stm); + + IStorage_Release(stg2); + } + + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_toplevel_stat(void) +{ + IStorage *stg = NULL; + HRESULT r; + STATSTG stat; + char prev_dir[MAX_PATH]; + char temp[MAX_PATH]; + char full_path[MAX_PATH]; + LPSTR rel_pathA; + WCHAR rel_path[MAX_PATH]; + + DeleteFileA(filenameA); + + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT ); + ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n", + wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + + IStorage_Release( stg ); + + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r); + + r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT ); + ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n", + wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + + IStorage_Release( stg ); + + DeleteFileA(filenameA); + + /* Stat always returns the full path, even for files opened with a relative path. */ + GetCurrentDirectoryA(MAX_PATH, prev_dir); + + GetTempPathA(MAX_PATH, temp); + + SetCurrentDirectoryA(temp); + + GetFullPathNameA(filenameA, MAX_PATH, full_path, &rel_pathA); + MultiByteToWideChar(CP_ACP, 0, rel_pathA, -1, rel_path, MAX_PATH); + + r = StgCreateDocfile( rel_path, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT ); + ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n", + wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + + IStorage_Release( stg ); + + r = StgOpenStorage( rel_path, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r); + + r = IStorage_Stat( stg, &stat, STATFLAG_DEFAULT ); + ok(!strcmp_ww(stat.pwcsName, filename), "expected %s, got %s\n", + wine_dbgstr_w(filename), wine_dbgstr_w(stat.pwcsName)); + CoTaskMemFree(stat.pwcsName); + + IStorage_Release( stg ); + + SetCurrentDirectoryA(prev_dir); + + DeleteFileA(filenameA); +} + +static void test_substorage_enum(void) +{ + IStorage *stg, *stg2; + IEnumSTATSTG *ee; + HRESULT r; + ULONG ref; + static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 }; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* create an enumelements */ + r = IStorage_EnumElements(stg2, 0, NULL, 0, &ee); + ok(r==S_OK, "IStorage->EnumElements failed, hr=%08x\n", r); + + /* release the substorage */ + ref = IStorage_Release(stg2); + todo_wine ok(ref==0, "storage not released\n"); + + /* reopening fails, because the substorage is really still open */ + r = IStorage_OpenStorage(stg, stgname, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==STG_E_ACCESSDENIED, "IStorage->OpenStorage failed, hr=%08x\n", r); + + /* destroying the storage invalidates the enumerator */ + r = IStorage_DestroyElement(stg, stgname); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + r = IEnumSTATSTG_Reset(ee); + ok(r==STG_E_REVERTED, "IEnumSTATSTG->Reset failed, hr=%08x\n", r); + + IEnumSTATSTG_Release(ee); + + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_copyto_locking(void) +{ + IStorage *stg, *stg2, *stg3, *stg4; + IStream *stm; + HRESULT r; + static const WCHAR stgname[] = { 'S','T','G','1',0 }; + static const WCHAR stgname2[] = { 'S','T','G','2',0 }; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* create another substorage */ + r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* add a stream, and leave it open */ + r = IStorage_CreateStream(stg2, stmname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(r==S_OK, "IStorage->CreateStream failed, hr=%08x\n", r); + + /* Try to copy the storage while the stream is open */ + r = IStorage_CopyTo(stg2, 0, NULL, NULL, stg3); + todo_wine ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r); + + IStream_Release(stm); + + /* create a substorage */ + r = IStorage_CreateStorage(stg2, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg4); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* Try to copy the storage while the substorage is open */ + r = IStorage_CopyTo(stg2, 0, NULL, NULL, stg3); + todo_wine ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r); + + IStorage_Release(stg4); + IStorage_Release(stg3); + IStorage_Release(stg2); + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + +static void test_copyto_recursive(void) +{ + IStorage *stg, *stg2, *stg3, *stg4; + HRESULT r; + static const WCHAR stgname[] = { 'S','T','G','1',0 }; + static const WCHAR stgname2[] = { 'S','T','G','2',0 }; + + DeleteFileA(filenameA); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* create a substorage */ + r = IStorage_CreateStorage(stg, stgname, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* copy the parent to the child */ + r = IStorage_CopyTo(stg, 0, NULL, NULL, stg2); + ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r); + + /* create a transacted substorage */ + r = IStorage_CreateStorage(stg, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg3); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* copy the parent to the transacted child */ + r = IStorage_CopyTo(stg, 0, NULL, NULL, stg2); + ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r); + + /* create a transacted subsubstorage */ + r = IStorage_CreateStorage(stg3, stgname2, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_TRANSACTED, 0, 0, &stg4); + ok(r==S_OK, "IStorage->CreateStorage failed, hr=%08x\n", r); + + /* copy the parent to the transacted child of the transacted child */ + r = IStorage_CopyTo(stg, 0, NULL, NULL, stg4); + ok(r==STG_E_ACCESSDENIED, "IStorage->CopyTo failed, hr=%08x\n", r); + + /* copy the parent but exclude storage objects */ + r = IStorage_CopyTo(stg, 1, &IID_IStorage, NULL, stg4); + ok(r==S_OK, "IStorage->CopyTo failed, hr=%08x\n", r); + + IStorage_Release(stg4); + IStorage_Release(stg3); + IStorage_Release(stg2); + IStorage_Release(stg); + + r = DeleteFileA(filenameA); + ok( r == TRUE, "deleted file\n"); +} + START_TEST(storage32) { + CHAR temp[MAX_PATH]; + + GetTempPathA(MAX_PATH, temp); + if(!GetTempFileNameA(temp, "stg", 0, filenameA)) + { + win_skip("Could not create temp file, %u\n", GetLastError()); + return; + } + MultiByteToWideChar(CP_ACP, 0, filenameA, -1, filename, MAX_PATH); + DeleteFileA(filenameA); + test_hglobal_storage_stat(); test_create_storage_modes(); test_storage_stream(); @@ -1277,7 +2843,24 @@ START_TEST(storage32) test_storage_refcount(); test_streamenum(); test_transact(); + test_substorage_share(); + test_revert(); + test_parent_free(); + test_nonroot_transacted(); test_ReadClassStm(); test_access(); test_writeclassstg(); + test_readonly(); + test_simple(); + test_fmtusertypestg(); + test_references(); + test_copyto(); + test_copyto_snbexclusions(); + test_copyto_iidexclusions_storage(); + test_copyto_iidexclusions_stream(); + test_rename(); + test_toplevel_stat(); + test_substorage_enum(); + test_copyto_locking(); + test_copyto_recursive(); } diff --git a/rostests/winetests/ole32/usrmarshal.c b/rostests/winetests/ole32/usrmarshal.c index 3411cc283b1..a6553977744 100644 --- a/rostests/winetests/ole32/usrmarshal.c +++ b/rostests/winetests/ole32/usrmarshal.c @@ -29,7 +29,7 @@ #include "wine/test.h" -ULONG __RPC_USER HMETAFILE_UserSize(ULONG *, unsigned long, HMETAFILE *); +ULONG __RPC_USER HMETAFILE_UserSize(ULONG *, ULONG, HMETAFILE *); unsigned char * __RPC_USER HMETAFILE_UserMarshal(ULONG *, unsigned char *, HMETAFILE *); unsigned char * __RPC_USER HMETAFILE_UserUnmarshal(ULONG *, unsigned char *, HMETAFILE *); void __RPC_USER HMETAFILE_UserFree(ULONG *, HMETAFILE *); @@ -145,7 +145,7 @@ static void test_marshal_HWND(void) HWND_UserMarshal(&umcb.Flags, buffer, &hwnd); wirehwnd = (wireHWND)buffer; ok(wirehwnd->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08x\n", wirehwnd->fContext); - ok(wirehwnd->u.hInproc == (LONG_PTR)hwnd, "Marshaled value should be %p instead of %p\n", hwnd, (HANDLE)wirehwnd->u.hRemote); + ok(wirehwnd->u.hInproc == (LONG_PTR)hwnd, "Marshaled value should be %p instead of %x\n", hwnd, wirehwnd->u.hRemote); init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL); HWND_UserUnmarshal(&umcb.Flags, buffer, &hwnd2); @@ -162,7 +162,7 @@ static void test_marshal_HGLOBAL(void) MIDL_STUB_MESSAGE stub_msg; RPC_MESSAGE rpc_msg; unsigned char *buffer; - ULONG size; + ULONG size, block_size; HGLOBAL hglobal; HGLOBAL hglobal2; unsigned char *wirehglobal; @@ -188,39 +188,52 @@ static void test_marshal_HGLOBAL(void) init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL); HGLOBAL_UserFree(&umcb.Flags, &hglobal2); - hglobal = GlobalAlloc(0, 4); - buffer = GlobalLock(hglobal); - for (i = 0; i < 4; i++) - buffer[i] = i; - GlobalUnlock(hglobal); - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL); - size = HGLOBAL_UserSize(&umcb.Flags, 0, &hglobal); - /* native is poorly programmed and allocates 4/8 bytes more than it needs to - * here - Wine doesn't have to emulate that */ - ok((size == 24) || broken(size == 28) || broken(size == 32), "Size should be 24, instead of %d\n", size); - buffer = HeapAlloc(GetProcessHeap(), 0, size); - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL); - HGLOBAL_UserMarshal(&umcb.Flags, buffer, &hglobal); - wirehglobal = buffer; - ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal); - wirehglobal += sizeof(ULONG); - ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0x4 should be HGLOBAL\n"); - wirehglobal += sizeof(ULONG); - ok(*(ULONG *)wirehglobal == 4, "buffer+0x8 should be size of HGLOBAL instead of %d\n", *(ULONG *)wirehglobal); - wirehglobal += sizeof(ULONG); - ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0xc should be HGLOBAL\n"); - wirehglobal += sizeof(ULONG); - ok(*(ULONG *)wirehglobal == 4, "buffer+0x10 should be size of HGLOBAL instead of %d\n", *(ULONG *)wirehglobal); - wirehglobal += sizeof(ULONG); - for (i = 0; i < 4; i++) - ok(wirehglobal[i] == i, "buffer+0x%x should be %d\n", 0x10 + i, i); - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL); - HGLOBAL_UserUnmarshal(&umcb.Flags, buffer, &hglobal2); - ok(hglobal2 != NULL, "Didn't unmarshal properly\n"); - HeapFree(GetProcessHeap(), 0, buffer); - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL); - HGLOBAL_UserFree(&umcb.Flags, &hglobal2); - GlobalFree(hglobal); + + for(block_size = 0; block_size <= 17; block_size++) + { + ULONG actual_size, expected_size; + + hglobal = GlobalAlloc(0, block_size); + buffer = GlobalLock(hglobal); + for (i = 0; i < block_size; i++) + buffer[i] = i; + GlobalUnlock(hglobal); + actual_size = GlobalSize(hglobal); + expected_size = actual_size + 5 * sizeof(DWORD); + trace("%d: actual size %d\n", block_size, actual_size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL); + size = HGLOBAL_UserSize(&umcb.Flags, 0, &hglobal); + /* native is poorly programmed and allocates 4/8 bytes more than it needs to + * here - Wine doesn't have to emulate that */ + ok(size == expected_size || + broken(size == expected_size + 4) || + broken(size == expected_size + 8), + "%d: got size %d\n", block_size, size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL); + HGLOBAL_UserMarshal(&umcb.Flags, buffer, &hglobal); + wirehglobal = buffer; + ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0x4 should be HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == actual_size, "%d: buffer+0x8 %08x\n", block_size, *(ULONG *)wirehglobal); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == (ULONG)(ULONG_PTR)hglobal, "buffer+0xc should be HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == actual_size, "%d: buffer+0x10 %08x\n", block_size, *(ULONG *)wirehglobal); + wirehglobal += sizeof(ULONG); + for (i = 0; i < block_size; i++) + ok(wirehglobal[i] == i, "buffer+0x%x should be %d\n", 0x10 + i, i); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_LOCAL); + HGLOBAL_UserUnmarshal(&umcb.Flags, buffer, &hglobal2); + ok(hglobal2 != NULL, "Didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_LOCAL); + HGLOBAL_UserFree(&umcb.Flags, &hglobal2); + GlobalFree(hglobal); + } } static HENHMETAFILE create_emf(void) @@ -495,14 +508,50 @@ static const IUnknownVtbl TestUnknown_Vtbl = Test_IUnknown_Release, }; +static HRESULT WINAPI Test_IStream_QueryInterface(IStream *iface, + REFIID riid, LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IStream)) + { + *ppvObj = iface; + IStream_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IStream_AddRef(IStream *iface) +{ + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IStream_Release(IStream *iface) +{ + return 1; /* non-heap-based object */ +} + +static const IStreamVtbl TestStream_Vtbl = +{ + Test_IStream_QueryInterface, + Test_IStream_AddRef, + Test_IStream_Release + /* the rest can be NULLs */ +}; + static IUnknown Test_Unknown = { &TestUnknown_Vtbl }; +static IStream Test_Stream = { &TestStream_Vtbl }; ULONG __RPC_USER WdtpInterfacePointer_UserSize(ULONG *, ULONG, ULONG, IUnknown *, REFIID); unsigned char * __RPC_USER WdtpInterfacePointer_UserMarshal(ULONG *, ULONG, unsigned char *, IUnknown *, REFIID); unsigned char * __RPC_USER WdtpInterfacePointer_UserUnmarshal(ULONG *, unsigned char *, IUnknown **, REFIID); void __RPC_USER WdtpInterfacePointer_UserFree(IUnknown *); -static void test_marshal_WdtpInterfacePointer(void) +static void marshal_WdtpInterfacePointer(DWORD umcb_ctx, DWORD ctx) { USER_MARSHAL_CB umcb; MIDL_STUB_MESSAGE stub_msg; @@ -512,64 +561,196 @@ static void test_marshal_WdtpInterfacePointer(void) IUnknown *unk; IUnknown *unk2; unsigned char *wireip; - const IID *iid; + HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, 0); + IStream *stm; + void *marshal_data; + LARGE_INTEGER zero; + ULARGE_INTEGER pos; + DWORD marshal_size; /* shows that the WdtpInterfacePointer functions don't marshal anything for * NULL pointers, so code using these functions must handle that case * itself */ + unk = NULL; - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC); - size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, umcb_ctx); + size = WdtpInterfacePointer_UserSize(&umcb.Flags, ctx, 0, unk, &IID_IUnknown); ok(size == 0, "size should be 0 bytes, not %d\n", size); buffer = HeapAlloc(GetProcessHeap(), 0, size); - buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown); + buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, ctx, buffer, unk, &IID_IUnknown); wireip = buffer; HeapFree(GetProcessHeap(), 0, buffer); + /* Now for a non-NULL pointer. The marshalled data are two size DWORDS and then + the result of CoMarshalInterface called with the LOWORD of the ctx */ + unk = &Test_Unknown; - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC); - size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown); - todo_wine - ok(size > 28, "size should be > 28 bytes, not %d\n", size); - trace("WdtpInterfacePointer_UserSize returned %d\n", size); + + CreateStreamOnHGlobal(h, TRUE, &stm); + CoMarshalInterface(stm, &IID_IUnknown, unk, LOWORD(ctx), NULL, MSHLFLAGS_NORMAL); + zero.QuadPart = 0; + IStream_Seek(stm, zero, STREAM_SEEK_CUR, &pos); + marshal_size = pos.u.LowPart; + marshal_data = GlobalLock(h); + trace("marshal_size %x\n", marshal_size); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, umcb_ctx); + size = WdtpInterfacePointer_UserSize(&umcb.Flags, ctx, 0, unk, &IID_IUnknown); + ok(size >= marshal_size + 2 * sizeof(DWORD), "marshal size %x got %x\n", marshal_size, size); + trace("WdtpInterfacePointer_UserSize returned %x\n", size); buffer = HeapAlloc(GetProcessHeap(), 0, size); - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC); - buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, umcb_ctx); + buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, ctx, buffer, unk, &IID_IUnknown); wireip = buffer; - if (size >= 28) - { - ok(*(DWORD *)wireip == 0x44, "wireip + 0x0 should be 0x44 instead of 0x%08x\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - ok(*(DWORD *)wireip == 0x44, "wireip + 0x4 should be 0x44 instead of 0x%08x\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - ok(*(DWORD *)wireip == 0x574f454d /* 'MEOW' */, "wireip + 0x8 should be 0x574f454d instead of 0x%08x\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - ok(*(DWORD *)wireip == 0x1, "wireip + 0xc should be 0x1 instead of 0x%08x\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - iid = (const IID *)wireip; - ok(IsEqualIID(iid, &IID_IUnknown), - "wireip + 0x10 should be IID_IUnknown instead of {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", - iid->Data1, iid->Data2, iid->Data3, - iid->Data4[0], iid->Data4[1], iid->Data4[2], iid->Data4[3], - iid->Data4[4], iid->Data4[5], iid->Data4[6], iid->Data4[7]); - wireip += sizeof(IID); - ok(*(DWORD *)wireip == 0, "wireip + 0x1c should be 0 instead of 0x%08x\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - ok(*(DWORD *)wireip == 5, "wireip + 0x20 should be 5 instead of %d\n", *(DWORD *)wireip); - wireip += sizeof(DWORD); - /* the rest is dynamic so can't really be tested */ - } + + ok(buffer_end == buffer + marshal_size + 2 * sizeof(DWORD), "buffer_end %p buffer %p (diff %x)\n", buffer_end, buffer, buffer_end - buffer); + + ok(*(DWORD *)wireip == marshal_size, "wireip + 0x0 should be %x instead of %x\n", marshal_size, *(DWORD *)wireip); + wireip += sizeof(DWORD); + ok(*(DWORD *)wireip == marshal_size, "wireip + 0x4 should be %x instead of %x\n", marshal_size, *(DWORD *)wireip); + wireip += sizeof(DWORD); + + ok(!memcmp(marshal_data, wireip, marshal_size), "buffer mismatch\n"); + GlobalUnlock(h); + zero.QuadPart = 0; + IStream_Seek(stm, zero, STREAM_SEEK_SET, NULL); + CoReleaseMarshalData(stm); + IStream_Release(stm); unk2 = NULL; - init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_INPROC); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, umcb_ctx); WdtpInterfacePointer_UserUnmarshal(&umcb.Flags, buffer, &unk2, &IID_IUnknown); - todo_wine ok(unk2 != NULL, "IUnknown object didn't unmarshal properly\n"); HeapFree(GetProcessHeap(), 0, buffer); init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC); WdtpInterfacePointer_UserFree(unk2); } +static void test_marshal_WdtpInterfacePointer(void) +{ + /* + * There are two places where we can pass the marshalling ctx: as + * part of the umcb and as a separate flag. The loword of that + * separate flag field is what matters. + */ + + /* All three are marshalled as inproc */ + marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_INPROC); + marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_INPROC); + marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_INPROC, 0xffff)); + + /* All three are marshalled as remote */ + marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE); + marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_DIFFERENTMACHINE); + marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_DIFFERENTMACHINE, 0xffff)); +} + +static void test_marshal_STGMEDIUM(void) +{ + USER_MARSHAL_CB umcb; + MIDL_STUB_MESSAGE stub_msg; + RPC_MESSAGE rpc_msg; + unsigned char *buffer, *buffer_end, *expect_buffer, *expect_buffer_end; + ULONG size, expect_size; + STGMEDIUM med, med2; + IUnknown *unk = &Test_Unknown; + IStream *stm = &Test_Stream; + + /* TYMED_NULL with pUnkForRelease */ + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 2 * sizeof(DWORD), unk, &IID_IUnknown); + expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE); + expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 2 * sizeof(DWORD), unk, &IID_IUnknown); + + med.tymed = TYMED_NULL; + U(med).pstg = NULL; + med.pUnkForRelease = unk; + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size = STGMEDIUM_UserSize(&umcb.Flags, 0, &med); + ok(size == expect_size, "size %d should be %d bytes\n", size, expect_size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + buffer_end = STGMEDIUM_UserMarshal(&umcb.Flags, buffer, &med); + ok(buffer_end - buffer == expect_buffer_end - expect_buffer, "buffer size mismatch\n"); + ok(*(DWORD*)buffer == TYMED_NULL, "got %08x\n", *(DWORD*)buffer); + ok(*((DWORD*)buffer+1) != 0, "got %08x\n", *((DWORD*)buffer+1)); + ok(!memcmp(buffer+8, expect_buffer + 8, expect_buffer_end - expect_buffer - 8), "buffer mismatch\n"); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + + /* native crashes if this is uninitialised, presumably because it + tries to release it */ + med2.tymed = TYMED_NULL; + U(med2).pstm = NULL; + med2.pUnkForRelease = NULL; + + STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2); + + ok(med2.tymed == TYMED_NULL, "got tymed %x\n", med2.tymed); + ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n"); + + HeapFree(GetProcessHeap(), 0, buffer); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + STGMEDIUM_UserFree(&umcb.Flags, &med2); + + HeapFree(GetProcessHeap(), 0, expect_buffer); + + /* TYMED_ISTREAM with pUnkForRelease */ + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream); + expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, expect_size, unk, &IID_IUnknown); + + expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size); + /* There may be a hole between the two interfaces so init the buffer to something */ + memset(expect_buffer, 0xcc, expect_size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE); + expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream); + expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer_end, unk, &IID_IUnknown); + + med.tymed = TYMED_ISTREAM; + U(med).pstm = stm; + med.pUnkForRelease = unk; + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + size = STGMEDIUM_UserSize(&umcb.Flags, 0, &med); + ok(size == expect_size, "size %d should be %d bytes\n", size, expect_size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + memset(buffer, 0xcc, size); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + buffer_end = STGMEDIUM_UserMarshal(&umcb.Flags, buffer, &med); + ok(buffer_end - buffer == expect_buffer_end - expect_buffer, "buffer size mismatch\n"); + ok(*(DWORD*)buffer == TYMED_ISTREAM, "got %08x\n", *(DWORD*)buffer); + ok(*((DWORD*)buffer+1) != 0, "got %08x\n", *((DWORD*)buffer+1)); + ok(*((DWORD*)buffer+2) != 0, "got %08x\n", *((DWORD*)buffer+2)); + ok(!memcmp(buffer + 12, expect_buffer + 12, (buffer_end - buffer) - 12), "buffer mismatch\n"); + + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE); + + /* native crashes if this is uninitialised, presumably because it + tries to release it */ + med2.tymed = TYMED_NULL; + U(med2).pstm = NULL; + med2.pUnkForRelease = NULL; + + STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2); + + ok(med2.tymed == TYMED_ISTREAM, "got tymed %x\n", med2.tymed); + ok(U(med2).pstm != NULL, "Incorrectly unmarshalled\n"); + ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n"); + + HeapFree(GetProcessHeap(), 0, buffer); + init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE); + STGMEDIUM_UserFree(&umcb.Flags, &med2); + + HeapFree(GetProcessHeap(), 0, expect_buffer); +} + START_TEST(usrmarshal) { CoInitialize(NULL); @@ -581,6 +762,7 @@ START_TEST(usrmarshal) test_marshal_HMETAFILE(); test_marshal_HMETAFILEPICT(); test_marshal_WdtpInterfacePointer(); + test_marshal_STGMEDIUM(); CoUninitialize(); }