diff --git a/modules/rostests/apitests/user32/CMakeLists.txt b/modules/rostests/apitests/user32/CMakeLists.txt index ccfecb9a340..e49fad457b2 100644 --- a/modules/rostests/apitests/user32/CMakeLists.txt +++ b/modules/rostests/apitests/user32/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(makeimg) list(APPEND SOURCE AttachThreadInput.c @@ -30,6 +31,7 @@ list(APPEND SOURCE KbdLayout.c keybd_event.c LoadImage.c + LoadImageGCC.c LookupIconIdFromDirectoryEx.c MessageStateAnalyzer.c NextDlgItem.c diff --git a/modules/rostests/apitests/user32/LoadImageGCC.c b/modules/rostests/apitests/user32/LoadImageGCC.c new file mode 100644 index 00000000000..393965aa0ec --- /dev/null +++ b/modules/rostests/apitests/user32/LoadImageGCC.c @@ -0,0 +1,151 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Test for LoadImageW using DLL compiled with MSVC + * COPYRIGHT: Copyright 2024 Doug Lyons + * + * NOTES: + * Works on ReactOS, but not on Windows 2003 Server SP2. + */ + +#include "precomp.h" +#include "resource.h" +#include +#include + +BOOL FileExistsW(PCWSTR FileName) +{ + DWORD Attribute = GetFileAttributesW(FileName); + + return (Attribute != INVALID_FILE_ATTRIBUTES && + !(Attribute & FILE_ATTRIBUTE_DIRECTORY)); +} + +BOOL ResourceToFileW(INT i, PCWSTR FileName) +{ + FILE *fout; + HGLOBAL hData; + HRSRC hRes; + PVOID pResLock; + UINT iSize; + + if (FileExistsW(FileName)) + { + /* We should only be using %temp% paths, so deleting here should be OK */ + printf("Deleting '%S' that already exists.\n", FileName); + DeleteFileW(FileName); + } + + hRes = FindResourceW(NULL, MAKEINTRESOURCEW(i), MAKEINTRESOURCEW(RT_RCDATA)); + if (hRes == NULL) + { + skip("Could not locate resource (%d). Exiting now\n", i); + return FALSE; + } + + iSize = SizeofResource(NULL, hRes); + + hData = LoadResource(NULL, hRes); + if (hData == NULL) + { + skip("Could not load resource (%d). Exiting now\n", i); + return FALSE; + } + + // Lock the resource into global memory. + pResLock = LockResource(hData); + if (pResLock == NULL) + { + skip("Could not lock resource (%d). Exiting now\n", i); + return FALSE; + } + + fout = _wfopen(FileName, L"wb"); + fwrite(pResLock, iSize, 1, fout); + fclose(fout); + return TRUE; +} + +static struct +{ + PCWSTR FileName; + INT ResourceId; +} DataFiles[] = +{ + {L"%SystemRoot%\\bin\\image.dll", IDR_DLL_NORMAL}, +}; + + +START_TEST(LoadImageGCC) +{ + UINT i; + WCHAR PathBuffer[MAX_PATH]; + static HBITMAP hBmp; + HANDLE handle; + HDC hdcMem; + BITMAP bitmap; + BITMAPINFO bmi; + HGLOBAL hMem; + LPVOID lpBits; + CHAR img[8] = { 0 }; + UINT size; + + /* Windows 2003 cannot run this test. Testman shows CRASH, so skip it. */ + if (!IsReactOS()) + return; + + /* Extract Data Files */ + for (i = 0; i < _countof(DataFiles); ++i) + { + ExpandEnvironmentStringsW(DataFiles[i].FileName, PathBuffer, _countof(PathBuffer)); + + if (!ResourceToFileW(DataFiles[i].ResourceId, PathBuffer)) + { + printf("ResourceToFile Failed. Exiting now\n"); + goto Cleanup; + } + } + + handle = LoadLibraryExW(PathBuffer, NULL, LOAD_LIBRARY_AS_DATAFILE); + hBmp = (HBITMAP)LoadImage(handle, MAKEINTRESOURCE(130), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + + hdcMem = CreateCompatibleDC(NULL); + SelectObject(hdcMem, hBmp); + GetObject(hBmp, sizeof(BITMAP), &bitmap); + + memset(&bmi, 0, sizeof(bmi)); + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bitmap.bmWidth; + bmi.bmiHeader.biHeight = bitmap.bmHeight; + bmi.bmiHeader.biPlanes = bitmap.bmPlanes; + bmi.bmiHeader.biBitCount = bitmap.bmBitsPixel; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + + size = ((bitmap.bmWidth * bmi.bmiHeader.biBitCount + 31) / 32) * 4 * bitmap.bmHeight; + + hMem = GlobalAlloc(GMEM_MOVEABLE, size); + lpBits = GlobalLock(hMem); + GetDIBits(hdcMem, hBmp, 0, bitmap.bmHeight, lpBits, &bmi, DIB_RGB_COLORS); + + /* Get first 8 bytes of second row of bits from bitmap */ + memcpy(img, (VOID *)((INT_PTR)lpBits + 4 * bitmap.bmWidth), 8); + + ok(img[0] == 0, "Byte 0 Bad. Got 0x%02x, expected 0\n", img[0] & 0xff); + ok(img[1] == 0, "Byte 1 Bad. Got 0x%02x, expected 0\n", img[1] & 0xff); + ok(img[2] == 0, "Byte 2 Bad. Got 0x%02x, expected 0\n", img[2] & 0xff); + ok(img[3] == 0, "Byte 3 Bad. Got 0x%02x, expected 0\n", img[3] & 0xff); + + GlobalUnlock(hMem); + GlobalFree(hMem); + + DeleteDC(hdcMem); + +Cleanup: + for (i = 0; i < _countof(DataFiles); ++i) + { + ExpandEnvironmentStringsW(DataFiles[i].FileName, PathBuffer, _countof(PathBuffer)); + DeleteFileW(PathBuffer); + } +} diff --git a/modules/rostests/apitests/user32/image.dll b/modules/rostests/apitests/user32/image.dll new file mode 100644 index 00000000000..8dc8d9d2b3a Binary files /dev/null and b/modules/rostests/apitests/user32/image.dll differ diff --git a/modules/rostests/apitests/user32/makeimg/CMakeLists.txt b/modules/rostests/apitests/user32/makeimg/CMakeLists.txt new file mode 100644 index 00000000000..7bd91b91146 --- /dev/null +++ b/modules/rostests/apitests/user32/makeimg/CMakeLists.txt @@ -0,0 +1,13 @@ +PROJECT(MAKEIMG) + +add_executable(showimg showimg.c) + +set_module_type(showimg win32gui UNICODE) +add_importlibs(showimg gdi32 user32 shell32 msvcrt kernel32 ntdll) + +add_library(image MODULE image.rc) +set_module_type(image win32dll ENTRYPOINT 0) + + +add_rostests_file(TARGET showimg) +add_rostests_file(TARGET image) diff --git a/modules/rostests/apitests/user32/makeimg/image.rc b/modules/rostests/apitests/user32/makeimg/image.rc new file mode 100644 index 00000000000..b683331dfb3 --- /dev/null +++ b/modules/rostests/apitests/user32/makeimg/image.rc @@ -0,0 +1,4 @@ +#define IDB_TEST 130 + +IDB_TEST BITMAP "test.bmp" + diff --git a/modules/rostests/apitests/user32/makeimg/readme.txt b/modules/rostests/apitests/user32/makeimg/readme.txt new file mode 100644 index 00000000000..425a17c262a --- /dev/null +++ b/modules/rostests/apitests/user32/makeimg/readme.txt @@ -0,0 +1,14 @@ +For reference see https://jira.reactos.org/browse/CORE-17005 + +This should be compiled using MSVC to create the image.dll file for testing. +Also, it creates an interactive showimg.exe program that can be used to verify this file. +After the creation of the image.dll file using MSVC it can be used to create user32_apitest:LoadImageGCC. +Simply copy the MSVC created 'image.dll' into the 'modules\rostests\apitests\user32' subdirectory to use it. +This file already exists there so that this step is not necessary, but only presents another option. + +Unfortunately, this test will not work correctly using Windows 2003 Server SP2. +I have not looked into the details of why this is the case, but it works under ReactOS. +This is what is important for now in any case. + +Doug Lyons +April 16, 2024 diff --git a/modules/rostests/apitests/user32/makeimg/showimg.c b/modules/rostests/apitests/user32/makeimg/showimg.c new file mode 100644 index 00000000000..ae87f28ef4c --- /dev/null +++ b/modules/rostests/apitests/user32/makeimg/showimg.c @@ -0,0 +1,150 @@ +// Released to the Public Domain by Doug Lyons on April 16th, 2024. + +#include +#include + +WCHAR szWindowClass[] = L"testclass"; + +static LRESULT CALLBACK +WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HBITMAP hBmp; + HANDLE handle; + CHAR buffer[32]; + + switch (message) + { + case WM_CREATE: + { + handle = LoadLibraryExW(L"image.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); + sprintf(buffer, "%p", handle); + MessageBoxA(NULL, buffer, "handle", 0); + hBmp = (HBITMAP)LoadImage(handle, MAKEINTRESOURCE(130), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + sprintf(buffer, "%p", hBmp); + MessageBoxA(NULL, buffer, "Bmp", 0); + sprintf(buffer, "%ld", GetLastError()); + MessageBoxA(NULL, buffer, "LastError", 0); + break; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc, hdcMem; + BITMAP bitmap; + BITMAPINFO bmi; + hdc = BeginPaint(hWnd, &ps); + HGLOBAL hMem; + LPVOID lpBits; + + hdcMem = CreateCompatibleDC(hdc); + SelectObject(hdcMem, hBmp); + GetObject(hBmp, sizeof(BITMAP), &bitmap); + + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bitmap.bmWidth; + bmi.bmiHeader.biHeight = bitmap.bmHeight; + bmi.bmiHeader.biPlanes = bitmap.bmPlanes; + bmi.bmiHeader.biBitCount = bitmap.bmBitsPixel; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + + hMem = GlobalAlloc(GMEM_MOVEABLE, ((bitmap.bmWidth * + bmi.bmiHeader.biBitCount + 31) / 32) * 4 * bitmap.bmHeight); + lpBits = GlobalLock(hMem); + GetDIBits(hdc, hBmp, 0, bitmap.bmHeight, lpBits, &bmi, DIB_RGB_COLORS); + + // increasing the multiplier makes the image larger + StretchDIBits(hdc, 0, 0, bitmap.bmWidth * 3, bitmap.bmHeight * 3, 0, 0, + bitmap.bmWidth, bitmap.bmHeight,lpBits, &bmi, DIB_RGB_COLORS, SRCCOPY); + GlobalUnlock(hMem); + GlobalFree(hMem); + + DeleteDC(hdcMem); + + EndPaint(hWnd, &ps); + break; + } + + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + + +static ATOM +MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = NULL; + + return RegisterClassEx(&wcex); +} + + +static BOOL +InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + HWND hWnd; + + hWnd = CreateWindowEx(0, + szWindowClass, + L"Bmp test", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + 300, + 120, + NULL, + NULL, + hInstance, + NULL); + + if (!hWnd) + return FALSE; + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +int WINAPI +wWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + + MyRegisterClass(hInstance); + + if (!InitInstance(hInstance, nCmdShow)) + return FALSE; + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return (int)msg.wParam; +} diff --git a/modules/rostests/apitests/user32/makeimg/test.bmp b/modules/rostests/apitests/user32/makeimg/test.bmp new file mode 100644 index 00000000000..1ee7626cdf6 Binary files /dev/null and b/modules/rostests/apitests/user32/makeimg/test.bmp differ diff --git a/modules/rostests/apitests/user32/resource.h b/modules/rostests/apitests/user32/resource.h index 33972fde705..87412d3cdb5 100644 --- a/modules/rostests/apitests/user32/resource.h +++ b/modules/rostests/apitests/user32/resource.h @@ -4,3 +4,4 @@ #define IDR_ICONS_PNG 2000 #define IDR_ICONS_NORMAL 2001 #define IDR_EXE_NORMAL 2002 +#define IDR_DLL_NORMAL 2003 diff --git a/modules/rostests/apitests/user32/testlist.c b/modules/rostests/apitests/user32/testlist.c index 101ec0a3892..3e90ebfcfbd 100644 --- a/modules/rostests/apitests/user32/testlist.c +++ b/modules/rostests/apitests/user32/testlist.c @@ -32,6 +32,7 @@ extern void func_InitializeLpkHooks(void); extern void func_KbdLayout(void); extern void func_keybd_event(void); extern void func_LoadImage(void); +extern void func_LoadImageGCC(void); extern void func_LookupIconIdFromDirectoryEx(void); extern void func_MessageStateAnalyzer(void); extern void func_NextDlgItem(void); @@ -91,6 +92,7 @@ const struct test winetest_testlist[] = { "KbdLayout", func_KbdLayout }, { "keybd_event", func_keybd_event }, { "LoadImage", func_LoadImage }, + { "LoadImageGCC", func_LoadImageGCC }, { "LookupIconIdFromDirectoryEx", func_LookupIconIdFromDirectoryEx }, { "MessageStateAnalyzer", func_MessageStateAnalyzer }, { "NextDlgItem", func_NextDlgItem }, diff --git a/modules/rostests/apitests/user32/user32_apitest.rc b/modules/rostests/apitests/user32/user32_apitest.rc index 155390fc217..6279d19db16 100644 --- a/modules/rostests/apitests/user32/user32_apitest.rc +++ b/modules/rostests/apitests/user32/user32_apitest.rc @@ -11,6 +11,7 @@ TESTCURSOR CURSOR "test.cur" IDR_ICONS_PNG RCDATA "ROS.ico" IDR_ICONS_NORMAL RCDATA "sysicon.ico" IDR_EXE_NORMAL RCDATA "cpimg2e.exe" +IDR_DLL_NORMAL RCDATA "image.dll" TESTDIALOG DIALOG 0,0, 200,200 CLASS "TestDialogClass"