Index: apitests/CMakeLists.txt =================================================================== --- apitests/CMakeLists.txt (revision 67982) +++ apitests/CMakeLists.txt (working copy) @@ -13,6 +13,7 @@ endif() add_subdirectory(msvcrt) add_subdirectory(ntdll) +add_subdirectory(ole32) add_subdirectory(powrprof) add_subdirectory(setupapi) add_subdirectory(shell32) Index: apitests/include/unknownbase.h =================================================================== --- apitests/include/unknownbase.h (revision 0) +++ apitests/include/unknownbase.h (working copy) @@ -0,0 +1,47 @@ +#ifndef APITESTS_UNKNOWNBASE_H +#define APITESTS_UNKNOWNBASE_H + +template +class CUnknownBase : public Interface +{ + LONG m_lRef; + bool m_AutoDelete; +protected: + virtual const QITAB* GetQITab() = 0; +public: + + CUnknownBase(bool autoDelete, LONG initialRef) + : m_lRef(initialRef), + m_AutoDelete(autoDelete) + { + } + + ULONG STDMETHODCALLTYPE AddRef () + { + return InterlockedIncrement( &m_lRef ); + } + + ULONG STDMETHODCALLTYPE Release() + { + long newref = InterlockedDecrement( &m_lRef ); + if (m_AutoDelete && newref<=0) + { + delete this; + } + return newref; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) + { + return QISearch(this, GetQITab(), riid, ppv); + } + + virtual ~CUnknownBase() {} + + LONG GetRef() const + { + return m_lRef; + } +}; + +#endif // APITESTS_UNKNOWNBASE_H Index: apitests/ole32/CMakeLists.txt =================================================================== --- apitests/ole32/CMakeLists.txt (revision 0) +++ apitests/ole32/CMakeLists.txt (working copy) @@ -0,0 +1,7 @@ + +set_cpp(WITH_RUNTIME) +add_executable(ole32_apitest initializespy.cpp testlist.c) +target_link_libraries(ole32_apitest wine uuid) +set_module_type(ole32_apitest win32cui) +add_importlibs(ole32_apitest msvcrt kernel32 user32 gdi32 shell32 ole32 shlwapi) +add_cd_file(TARGET ole32_apitest DESTINATION reactos/bin FOR all) Index: apitests/ole32/initializespy.cpp =================================================================== --- apitests/ole32/initializespy.cpp (revision 0) +++ apitests/ole32/initializespy.cpp (working copy) @@ -0,0 +1,321 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: BSD - See COPYING.ARM in the top level directory + * PURPOSE: Tests for IInitializeSpy + * PROGRAMMERS: Mark Jansen + */ + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include +#include + +#include +#include + +#include +#include + +#define test_S_OK(hres, message) ok((hres) == S_OK, "%s (0x%lx instead of S_OK)\n", (message), (hres)) +#define test_HRES(hres, hresExpected, message) ok((hres) == (hresExpected), "%s (0x%lx instead of 0x%lx)\n", (message), (hres), (hresExpected)) +#define test_ref(spy, expectedRef) ok((spy)->GetRef() == (expectedRef), "unexpected refcount, %ld instead of %d\n", (spy)->GetRef(), (expectedRef)) + + +typedef HRESULT (WINAPI *pCoRegisterInitializeSpy_t)(_In_ LPINITIALIZESPY pSpy, _Out_ ULARGE_INTEGER *puliCookie); +typedef HRESULT (WINAPI *pCoRevokeInitializeSpy_t)(_In_ ULARGE_INTEGER uliCookie); +pCoRegisterInitializeSpy_t pCoRegisterInitializeSpy; +pCoRevokeInitializeSpy_t pCoRevokeInitializeSpy; + + +const DWORD INVALID_VALUE = 0xdeadbeef; + + +class CTestSpy : public CUnknownBase +{ +public: + HRESULT hr; + ULARGE_INTEGER Cookie; + + // expected values to check against + HRESULT m_hrCoInit; + DWORD m_CoInit; + DWORD m_CurAptRefs; + + // keeping count of the times called + LONG m_PreInitCalled; + LONG m_PostInitCalled; + LONG m_PreUninitCalled; + LONG m_PostUninitCalled; + + // fake out some + bool m_FailQueryInterface; + bool m_AlwaysReturnOK; + + CTestSpy() + : CUnknownBase( false, 0 ), + hr(0), + m_hrCoInit(0), + m_CoInit(0), + m_CurAptRefs(0), + m_FailQueryInterface(false), + m_AlwaysReturnOK(false) + { + Cookie.HighPart = Cookie.LowPart = INVALID_VALUE; + Clear(); + } + + ~CTestSpy() + { + // always try to revoke if we succeeded to register. + if (SUCCEEDED(hr)) + { + hr = pCoRevokeInitializeSpy(Cookie); + test_S_OK(hr, "CoRevokeInitializeSpy"); + } + // we should be done. + ok(GetRef() == 0, "Expected m_lRef to be 0, was: %ld\n", GetRef()); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) + { + if (m_FailQueryInterface) + { + return E_NOINTERFACE; + } + return CUnknownBase::QueryInterface(riid, ppv); + } + + const QITAB* GetQITab() + { + static const QITAB tab[] = { { &IID_IInitializeSpy, OFFSETOFCLASS(IInitializeSpy, CTestSpy) }, { 0 } }; + return tab; + } + + + HRESULT STDMETHODCALLTYPE PreInitialize(DWORD dwCoInit, DWORD dwCurThreadAptRefs) + { + InterlockedIncrement(&m_PreInitCalled); + ok(m_CoInit == dwCoInit, "Unexpected dwCoInit: got %lx, expected %lx\n", dwCoInit, m_CoInit); + DWORD expectApt = m_hrCoInit == RPC_E_CHANGED_MODE ? m_CurAptRefs : m_CurAptRefs -1; + ok(expectApt == dwCurThreadAptRefs, "Unexpected dwCurThreadAptRefs: got %lx, expected %lx\n", dwCurThreadAptRefs, expectApt); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE PostInitialize(HRESULT hrCoInit, DWORD dwCoInit, DWORD dwNewThreadAptRefs) + { + InterlockedIncrement(&m_PostInitCalled); + ok(m_PreInitCalled == m_PostInitCalled, "Expected balanced pre/post: %ld / %ld\n", m_PreInitCalled, m_PostInitCalled); + test_HRES(hrCoInit, m_hrCoInit, "Unexpected hrCoInit in PostInitialize"); + ok(m_CoInit == dwCoInit, "Unexpected dwCoInit: got %lx, expected %lx\n", dwCoInit, m_CoInit); + ok(m_CurAptRefs == dwNewThreadAptRefs, "Unexpected dwNewThreadAptRefs: got %lx, expected %lx\n", dwNewThreadAptRefs, m_CurAptRefs); + if (m_AlwaysReturnOK) + return S_OK; + return hrCoInit; + } + + HRESULT STDMETHODCALLTYPE PreUninitialize(DWORD dwCurThreadAptRefs) + { + InterlockedIncrement(&m_PreUninitCalled); + ok(m_CurAptRefs == dwCurThreadAptRefs, "Unexpected dwCurThreadAptRefs: got %lx, expected %lx\n", dwCurThreadAptRefs, m_CurAptRefs); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE PostUninitialize(DWORD dwNewThreadAptRefs) + { + InterlockedIncrement(&m_PostUninitCalled); + ok(m_PreUninitCalled == m_PostUninitCalled, "Expected balanced pre/post: %ld / %ld\n", m_PreUninitCalled, m_PostUninitCalled); + DWORD apt = m_CurAptRefs ? (m_CurAptRefs-1) : 0; + ok(apt == dwNewThreadAptRefs, "Unexpected dwNewThreadAptRefs: got %lx, expected %lx\n", dwNewThreadAptRefs, apt); + return S_OK; + } + + void Clear() + { + m_PreInitCalled = 0; + m_PostInitCalled = 0; + m_PreUninitCalled = 0; + m_PostUninitCalled = 0; + } + + void Expect(HRESULT hrCoInit, DWORD CoInit, DWORD CurAptRefs) + { + m_hrCoInit = hrCoInit; + m_CoInit = CoInit; + m_CurAptRefs = CurAptRefs; + } + + void Check(LONG PreInit, LONG PostInit, LONG PreUninit, LONG PostUninit) + { + ok(m_PreInitCalled == PreInit, "Expected PreInit to be %ld, was: %ld\n", PreInit, m_PreInitCalled); + ok(m_PostInitCalled == PostInit, "Expected PostInit to be %ld, was: %ld\n", PostInit, m_PostInitCalled); + ok(m_PreUninitCalled == PreUninit, "Expected PreUninit to be %ld, was: %ld\n", PreUninit, m_PreUninitCalled); + ok(m_PostUninitCalled == PostUninit, "Expected PostUninit to be %ld, was: %ld\n", PostUninit, m_PostUninitCalled); + } +}; + + +void test_IInitializeSpy_register2() +{ + CTestSpy spy, spy2; + + // first we register 2 spies + spy.hr = pCoRegisterInitializeSpy(&spy, &spy.Cookie); + test_S_OK(spy.hr, "CoRegisterInitializeSpy"); + test_ref(&spy, 1); + + spy2.hr = pCoRegisterInitializeSpy(&spy2, &spy2.Cookie); + test_S_OK(spy2.hr, "CoRegisterInitializeSpy"); + test_ref(&spy, 1); + + // tell them what we expect + spy.Expect(S_OK, COINIT_APARTMENTTHREADED, 1); + spy2.Expect(S_OK, COINIT_APARTMENTTHREADED, 1); + + // Call CoInitializeEx and validate the results + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + test_S_OK(hr, "CoInitializeEx"); + spy.Check(1, 1, 0, 0); + spy2.Check(1, 1, 0, 0); + + // Calling CoInit twice with the same apartment makes it return S_FALSE but still increment count + spy.Expect(S_FALSE, COINIT_APARTMENTTHREADED, 2); + spy2.Expect(S_FALSE, COINIT_APARTMENTTHREADED, 2); + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + test_HRES(hr, S_FALSE, "CoInitializeEx"); + spy.Check(2, 2, 0, 0); + spy2.Check(2, 2, 0, 0); + + /* the order we registered the spies in is important here. + we have the second one to forcibly return S_OK, which makes the first spy see + S_OK instead of S_FALSE.. */ + spy.Expect(S_OK, COINIT_APARTMENTTHREADED, 3); + spy2.m_AlwaysReturnOK = true; + spy2.Expect(S_FALSE, COINIT_APARTMENTTHREADED, 3); + + // and the S_OK also influences the returned value from CoInit. + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + test_S_OK(hr, "CoInitializeEx"); + spy.Check(3, 3, 0, 0); + spy2.Check(3, 3, 0, 0); + + CoUninitialize(); + spy.Check(3, 3, 1, 1); + spy2.Check(3, 3, 1, 1); + + spy.m_CurAptRefs = spy2.m_CurAptRefs = 2; + + CoUninitialize(); + spy.Check(3, 3, 2, 2); + spy2.Check(3, 3, 2, 2); + + spy.m_CurAptRefs = spy2.m_CurAptRefs = 1; + + CoUninitialize(); + spy.Check(3, 3, 3, 3); + spy2.Check(3, 3, 3, 3); + + spy.m_CurAptRefs = spy2.m_CurAptRefs = 0; + + CoUninitialize(); + spy.Check(3, 3, 4, 4); + spy2.Check(3, 3, 4, 4); +} + +void test_IInitializeSpy_switch_apt() +{ + CTestSpy spy; + + spy.hr = pCoRegisterInitializeSpy(&spy, &spy.Cookie); + test_S_OK(spy.hr, "CoRegisterInitializeSpy"); + test_ref(&spy, 1); + + spy.Expect(S_OK, COINIT_APARTMENTTHREADED, 1); + + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + test_S_OK(hr, "CoInitializeEx"); + spy.Check(1, 1, 0, 0); + + spy.Expect(RPC_E_CHANGED_MODE, COINIT_MULTITHREADED, 1); + + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + test_HRES(hr, RPC_E_CHANGED_MODE, "CoInitializeEx"); + spy.Check(2, 2, 0, 0); + + + CoUninitialize(); + spy.Check(2, 2, 1, 1); + + spy.m_CurAptRefs = 0; + + CoUninitialize(); + spy.Check(2, 2, 2, 2); + + CoUninitialize(); + spy.Check(2, 2, 3, 3); +} + +void test_IInitializeSpy_fail() +{ + CTestSpy spy; + + spy.m_FailQueryInterface = true; + + spy.hr = pCoRegisterInitializeSpy(&spy, &spy.Cookie); + test_HRES(spy.hr, E_NOINTERFACE, "Unexpected hr while registering invalid interface"); + test_ref(&spy, 0); + ok(spy.Cookie.HighPart == 0xffffffff, "Unexpected Cookie.HighPart, expected 0xffffffff got: 0x%08lx\n", spy.Cookie.HighPart); + ok(spy.Cookie.LowPart == 0xffffffff, "Unexpected Cookie.HighPart, expected 0xffffffff got: 0x%08lx\n", spy.Cookie.LowPart); + + spy.Cookie.HighPart = spy.Cookie.LowPart = 0xffffffff; + HRESULT hr = pCoRevokeInitializeSpy(spy.Cookie); + test_HRES(hr, E_INVALIDARG, "Unexpected hr while unregistering invalid interface"); + test_ref(&spy, 0); + + spy.Cookie.HighPart = spy.Cookie.LowPart = 0; + hr = pCoRevokeInitializeSpy(spy.Cookie); + test_HRES(hr, E_INVALIDARG, "Unexpected hr while unregistering invalid interface"); + test_ref(&spy, 0); + + /* we should not crash here, just return E_NOINTERFACE + do note the Cookie is not even being touched at all, compared to calling this with an interface + that does not respond to IID_IInitializeSpy */ + spy.Cookie.HighPart = spy.Cookie.LowPart = INVALID_VALUE; + hr = pCoRegisterInitializeSpy(NULL, &spy.Cookie); + test_HRES(spy.hr, E_NOINTERFACE, "Unexpected hr while registering NULL interface"); + ok(spy.Cookie.HighPart == INVALID_VALUE, "Unexpected Cookie.HighPart, expected 0xdeadbeef got: %lx\n", spy.Cookie.HighPart); + ok(spy.Cookie.LowPart == INVALID_VALUE, "Unexpected Cookie.HighPart, expected 0xdeadbeef got: %lx\n", spy.Cookie.LowPart); +} + +void test_IInitializeSpy_twice() +{ + CTestSpy spy; + + spy.hr = pCoRegisterInitializeSpy(&spy, &spy.Cookie); + test_S_OK(spy.hr, "CoRegisterInitializeSpy"); + test_ref(&spy, 1); + + ULARGE_INTEGER Cookie = { { INVALID_VALUE, INVALID_VALUE } }; + HRESULT hr = pCoRegisterInitializeSpy(&spy, &Cookie); + test_S_OK(hr, "CoRegisterInitializeSpy"); + test_ref(&spy, 2); + + hr = pCoRevokeInitializeSpy(Cookie); + test_S_OK(hr, "CoRevokeInitializeSpy"); + test_ref(&spy, 1); +} + + +START_TEST(initializespy) +{ + HMODULE ole32 = LoadLibraryA("ole32.dll"); + pCoRegisterInitializeSpy = (pCoRegisterInitializeSpy_t)GetProcAddress(ole32, "CoRegisterInitializeSpy"); + pCoRevokeInitializeSpy = (pCoRevokeInitializeSpy_t)GetProcAddress(ole32, "CoRevokeInitializeSpy"); + + test_IInitializeSpy_register2(); + test_IInitializeSpy_switch_apt(); + test_IInitializeSpy_fail(); + test_IInitializeSpy_twice(); +} Index: apitests/ole32/testlist.c =================================================================== --- apitests/ole32/testlist.c (revision 0) +++ apitests/ole32/testlist.c (working copy) @@ -0,0 +1,13 @@ +#define __ROS_LONG64__ + +#define STANDALONE +#include + +extern void func_initializespy(void); + +const struct test winetest_testlist[] = +{ + { "initializespy", func_initializespy }, + + { 0, 0 } +}; Index: apitests/shell32/menu.cpp =================================================================== --- apitests/shell32/menu.cpp (revision 67982) +++ apitests/shell32/menu.cpp (working copy) @@ -34,6 +34,7 @@ public: CDummyWindow(HWND hwnd) + :CUnknownBase( true, 0 ) { m_hwnd = hwnd; } @@ -220,6 +221,7 @@ public: CMenuCallback(struct _test_info *testResults, int testsCount) + :CUnknownBase( true, 0 ) { m_iTest = 0; m_iCallback = 0; Index: apitests/shell32/shelltest.h =================================================================== --- apitests/shell32/shelltest.h (revision 67982) +++ apitests/shell32/shelltest.h (working copy) @@ -27,38 +27,4 @@ DEFINE_GUID(CLSID_MenuBandSite, 0xE13EF4E4, 0xD2F2, 0x11D0, 0x98, 0x16, 0x00, 0xC0, 0x4F, 0xD9, 0x19, 0x72); -template -class CUnknownBase : public Interface -{ - LONG m_lRef; -protected: - virtual const QITAB* GetQITab() = 0; -public: - - CUnknownBase() - { - m_lRef = 0; - } - - ULONG STDMETHODCALLTYPE AddRef () - { - return InterlockedIncrement( &m_lRef ); - } - - ULONG STDMETHODCALLTYPE Release() - { - long newref = InterlockedDecrement( &m_lRef ); - if (newref<=0) delete this; - return newref; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) - { - HRESULT hresult = QISearch(this, GetQITab(), riid, ppv); - if(SUCCEEDED(hresult)) AddRef(); - return hresult; - } - - virtual ~CUnknownBase() {} -}; - +#include "unknownbase.h"