Index: apitests/CMakeLists.txt =================================================================== --- apitests/CMakeLists.txt (revision 70992) +++ apitests/CMakeLists.txt (working copy) @@ -17,6 +17,7 @@ add_subdirectory(ole32) add_subdirectory(pefile) add_subdirectory(powrprof) +add_subdirectory(sdk) add_subdirectory(setupapi) add_subdirectory(shell32) add_subdirectory(psapi) Index: apitests/sdk/CMakeLists.txt =================================================================== --- apitests/sdk/CMakeLists.txt (revision 0) +++ apitests/sdk/CMakeLists.txt (working copy) @@ -0,0 +1,7 @@ + +add_executable(sdk_apitest delayimp.cpp testlist.c) +set_module_type(sdk_apitest win32cui) +target_link_libraries(sdk_apitest ${PSEH_LIB}) +add_importlibs(sdk_apitest msvcrt kernel32 ntdll) +add_delay_importlibs(sdk_apitest winmm) +add_cd_file(TARGET sdk_apitest DESTINATION reactos/bin FOR all) Index: apitests/sdk/delayimp.cpp =================================================================== --- apitests/sdk/delayimp.cpp (revision 0) +++ apitests/sdk/delayimp.cpp (working copy) @@ -0,0 +1,340 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for delayload + * PROGRAMMER: Mark Jansen + */ + +#include + +#include +#include +#include +extern "C" +{ +#include +} + +/* Compatibility with the MS defines */ + +#ifndef FACILITY_VISUALCPP +#define FACILITY_VISUALCPP ((LONG)0x6d) +#endif + +#ifndef VcppException +#define VcppException(sev,err) ((sev) | (FACILITY_VISUALCPP<<16) | err) +#endif + +#ifdef __REACTOS__ +#define WINMM_DLLNAME "winmm.dll" +#else +#define WINMM_DLLNAME "WINMM.dll" +#endif + +bool g_BreakFunctionName = false; +bool g_HasLoadedDll = false; +unsigned g_ExpectedNotify = (unsigned)-1; +const char* g_ExpectedDll = NULL; +const char* g_ExpectedName = NULL; + +struct UnProtect +{ + UnProtect(PVOID addr) + :mAddr(NULL), mProt(0) + { + if (IsBadWritePtr(addr, 1)) + { + mAddr = addr; + VirtualProtect(addr, 1, PAGE_EXECUTE_READWRITE, &mProt); + } + } + ~UnProtect() + { + DWORD dwOld; + if (mAddr) + VirtualProtect(mAddr, 1, mProt, &dwOld); + } + + PVOID mAddr; + DWORD mProt; +}; + + + +FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + ok(pdli && pdli->cb >= 36, "Expected a valid pointer with a struct that is big enough: %p, %lu\n", pdli, pdli ? pdli->cb : 0u); + if (!pdli || pdli->cb < 36) return NULL; + + if (g_BreakFunctionName && pdli->dlp.fImportByName) + { + g_BreakFunctionName = false; + UnProtect prot((PVOID)pdli->dlp.szProcName); + char c = pdli->dlp.szProcName[0]; + ((char*)pdli->dlp.szProcName)[0] = isupper(c) ? tolower(c) : toupper(c); + } + + /* When loading all functions automatically, we are not going to verify all parameters */ + if (!g_ExpectedDll) return NULL; + + ok(dliNotify == g_ExpectedNotify, "Expected notify to be %u, was %u\n", g_ExpectedNotify, dliNotify); + + ok(!strcmp(g_ExpectedDll, pdli->szDll), "Expected szDll to be '%s', but was: '%s'\n", g_ExpectedDll, pdli->szDll); + ok(pdli->dlp.fImportByName, "Expected import by name\n"); + if (pdli->dlp.fImportByName) + ok(!strcmp(g_ExpectedName, pdli->dlp.szProcName), "Expected szProcName to be '%s', but was: '%s'\n", g_ExpectedName, pdli->dlp.szProcName); + + switch(g_ExpectedNotify) + { + case dliStartProcessing: + if (!g_HasLoadedDll) + { + g_ExpectedNotify = dliNotePreLoadLibrary; + ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p\n", pdli->hmodCur); + } + else + { + g_ExpectedNotify = dliNotePreGetProcAddress; + ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p\n", pdli->hmodCur); + } + break; + case dliNotePreLoadLibrary: + g_ExpectedNotify = dliNotePreGetProcAddress; + ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p\n", pdli->hmodCur); + break; + case dliNotePreGetProcAddress: + g_ExpectedNotify = dliNoteEndProcessing; + ok(pdli->hmodCur != NULL, "Expected hmodCur to be valid, was NULL\n"); + break; + case dliNoteEndProcessing: + g_ExpectedNotify = (unsigned)-2; + ok(pdli->hmodCur != NULL, "Expected hmodCur to be valid, was NULL\n"); + if (g_ExpectedDll && g_ExpectedName) + { + FARPROC target = GetProcAddress(GetModuleHandleA(g_ExpectedDll), g_ExpectedName); + ok(target != NULL, "This should not happen, the function i need is unavail! (%s!%s)\n", + g_ExpectedDll, g_ExpectedName); + ok(target == pdli->pfnCur, "Expected pfnCur to be %p, was %p\n", target, pdli->pfnCur); + ok(pdli->ppfn && target == *pdli->ppfn, + "Expected ppfn to be valid and to result in %p, was: %p(%p)\n", + target, pdli->ppfn, pdli->ppfn ? *pdli->ppfn : NULL); + } + break; + default: + ok(0, "We should not end up here! (%u, %u)\n", g_ExpectedNotify, dliNotify); + break; + } + return NULL; +} + +unsigned g_ExpectedFail = (unsigned)-1; + +FARPROC WINAPI DliFailHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + ok(dliNotify == g_ExpectedFail, "Expected notify to be %u, was %u\n", g_ExpectedFail, dliNotify); + + switch(g_ExpectedFail) + { + case dliFailGetProc: + g_ExpectedFail = (unsigned)-2; + break; + default: + ok(0, "We should not end up here! (%u, %u)\n", g_ExpectedFail, dliNotify); + break; + } + return NULL; +} + + +LONG ExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo, ULONG ExceptionCode) +{ + DWORD expected = VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND); + ok(ExceptionCode == expected, "Expected code to be 0x%lx, was: 0x%lx\n", expected, ExceptionCode); + ok(ExceptionInfo != NULL, "Expected to get exception info\n"); + ok(ExceptionInfo->ExceptionRecord != NULL, "Expected to get a valid record info\n"); + + if (ExceptionCode != expected) + { + skip("Skipping other checks, this was not the exception we expected!\n"); + return EXCEPTION_EXECUTE_HANDLER; + } + + if (ExceptionInfo && ExceptionInfo->ExceptionRecord) + { + PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; + ok(ExceptionRecord->ExceptionCode == expected, "Expected ExceptionCode to be 0x%lx, was 0x%lx\n", + expected, ExceptionRecord->ExceptionCode); + /* We can still continue. */ + ok(ExceptionRecord->ExceptionFlags == 0, "Expected ExceptionFlags to be 0, was: 0x%lx\n", + ExceptionRecord->ExceptionFlags); + ok(ExceptionRecord->NumberParameters == 1, "Expected 1 parameter, got %lu\n", + ExceptionRecord->NumberParameters); + if (ExceptionRecord->NumberParameters == 1) + { + PDelayLoadInfo LoadInfo = (PDelayLoadInfo)ExceptionRecord->ExceptionInformation[0]; + ok(LoadInfo && LoadInfo->cb >= 36, "Expected a valid pointer with a struct that is big enough: %p, %lu\n", + LoadInfo, LoadInfo ? LoadInfo->cb : 0); + + if (g_ExpectedDll) + ok(!strcmp(g_ExpectedDll, LoadInfo->szDll), "Expected szDll to be '%s', but was: '%s'\n", + g_ExpectedDll, LoadInfo->szDll); + if (g_ExpectedName) + { + ok(LoadInfo->dlp.fImportByName, "Expected import by name\n"); + if (LoadInfo->dlp.fImportByName) + ok(!strcmp(g_ExpectedName, LoadInfo->dlp.szProcName), + "Expected szProcName to be '%s', but was: '%s'\n", g_ExpectedName, LoadInfo->dlp.szProcName); + } + } + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + +extern "C" +{ + PfnDliHook __pfnDliNotifyHook2 = DliHook; + PfnDliHook __pfnDliFailureHook2 = DliFailHook; +} + + +bool g_UsePointers = false; + +template +PTR Rva2Addr(PIMAGE_DOS_HEADER dos, RVA rva) +{ + /* Old delayload type */ + if (g_UsePointers) + return reinterpret_cast(rva); + return reinterpret_cast((reinterpret_cast(dos) + rva)); +} + +START_TEST(delayimp) +{ + +#if defined(_DELAY_IMP_VER) && _DELAY_IMP_VER == 2 && defined(DELAYLOAD_SUPPORTS_UNLOADING) + /* First, before mangling the delayload stuff, let's try some v2 functions */ + HMODULE mod = GetModuleHandleA(WINMM_DLLNAME); + ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod); + /* Now, a mistyped module (case sensitive!) */ + HRESULT hr = __HrLoadAllImportsForDll("WiNmM.DlL"); + ok(hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), "Expected hr to be HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), was %lu\n", hr); + mod = GetModuleHandleA(WINMM_DLLNAME); + ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod); + + /* Let's load it */ + hr = __HrLoadAllImportsForDll(WINMM_DLLNAME); + ok(hr == S_OK, "Expected hr to be S_OK, was %lu\n", hr); + mod = GetModuleHandleA(WINMM_DLLNAME); + ok(mod != NULL, "Expected mod to be valid, was NULL\n"); + + BOOL status = __FUnloadDelayLoadedDLL2(WINMM_DLLNAME); + ok(status == TRUE, "Expected __FUnloadDelayLoadedDLL2 to succeed\n"); + mod = GetModuleHandleA(WINMM_DLLNAME); + ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod); +#endif + + +#if 0 + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); + + ok(dos->e_magic == IMAGE_DOS_SIGNATURE, "Expected a DOS header\n"); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return; + + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PBYTE)dos + dos->e_lfanew); + PIMAGE_DATA_DIRECTORY delaydir = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT; + + ok(delaydir->Size > 0, "Expected to find a delayload dir\n"); + if (!delaydir->Size) + return; + + PImgDelayDescr pidd = Rva2Addr(dos, nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress); + int num = 0; + while (pidd->rvaDLLName) + { + g_UsePointers = (pidd->grAttrs & dlattrRva) == 0; + + LPCSTR DllName = Rva2Addr(dos, pidd->rvaDLLName); + ok(!strcmp(DllName, WINMM_DLLNAME), "Expected the dll to be named '%s', was: '%s'\n", WINMM_DLLNAME, DllName); + + /* iat holds the IAT entry (which points to the resolver stub), thunk holds the name entry */ + PImgThunkData iat = Rva2Addr(dos, pidd->rvaIAT); + PImgThunkData thunk = Rva2Addr(dos, pidd->rvaINT); + + while (iat->u1.Function) + { + if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal)) + { + ok(0, "Was not expecting an ordinal!\n"); + } + else + { + /* We need this one to fail */ + PIMAGE_IMPORT_BY_NAME imp = Rva2Addr(dos, thunk->u1.AddressOfData); + bool playSoundW = !strcmp((const char*)imp->Name, "PlaySoundW"); + + _SEH2_TRY + { + if (playSoundW) + imp->Name[0] = tolower(imp->Name[0]); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + trace("MSVC has this section writable, should we also?\n"); + if (playSoundW) + { + DWORD dwOld; + VirtualProtect(imp->Name, 1, PAGE_EXECUTE_READWRITE, &dwOld); + imp->Name[0] = tolower(imp->Name[0]); + VirtualProtect(imp->Name, 1, dwOld, &dwOld); + } + } + _SEH2_END; + num++; + } + iat++; thunk++; + } + pidd++; + } + + ok(num == 3, "Expected 3 delay imports, got: %d\n", num); +#endif + + + + + g_ExpectedNotify = dliStartProcessing; + g_ExpectedDll = WINMM_DLLNAME; + g_ExpectedName = "sndPlaySoundA"; + g_HasLoadedDll = false; + sndPlaySoundA(NULL, SND_NOWAIT | SND_NOSTOP); + ok(g_ExpectedNotify == (unsigned)-2, "Expected notify to be %u, was %u\n", (unsigned)-2, g_ExpectedNotify); + + + g_ExpectedNotify = dliStartProcessing; + g_ExpectedDll = WINMM_DLLNAME; + g_ExpectedName = "sndPlaySoundW"; + g_HasLoadedDll = true; + sndPlaySoundW(NULL, SND_NOWAIT | SND_NOSTOP); + ok(g_ExpectedNotify == (unsigned)-2, "Expected notify to be %u, was %u\n", (unsigned)-2, g_ExpectedNotify); + + + g_ExpectedNotify = dliStartProcessing; + g_ExpectedDll = WINMM_DLLNAME; + g_ExpectedName = "playSoundW"; + g_BreakFunctionName = true; + g_ExpectedFail = dliFailGetProc; + _SEH2_TRY + { + PlaySoundW(NULL, NULL, SND_NOWAIT | SND_NOSTOP); + } + _SEH2_EXCEPT(ExceptionFilter(_SEH2_GetExceptionInformation(), _SEH2_GetExceptionCode())) + { + ; + } + _SEH2_END; + ok(g_ExpectedFail == (unsigned)-2, "Expected fail to be %u, was %u\n", (unsigned)-2, g_ExpectedFail); + ok(g_BreakFunctionName == false, "Expected the functionname to be changed\n"); +} Index: apitests/sdk/testlist.c =================================================================== --- apitests/sdk/testlist.c (revision 0) +++ apitests/sdk/testlist.c (working copy) @@ -0,0 +1,12 @@ +#define __ROS_LONG64__ + +#define STANDALONE +#include + +extern void func_delayimp(void); + +const struct test winetest_testlist[] = +{ + { "delayimp", func_delayimp }, + { 0, 0 } +};