From efadd5b8cfc31bc48640e6477a7d770cfc82e73a Mon Sep 17 00:00:00 2001 From: junkmd Date: Fri, 23 Jan 2026 22:20:53 +0900 Subject: [PATCH 1/5] refactor: Extract GDI-related definitions to `gdi_helper` module. Move GDI-related ctypes definitions from `test/test_stream.py` to a new dedicated module, `test/gdi_helper.py`. This improves modularity and reusability of GDI helper functions across tests. --- comtypes/test/gdi_helper.py | 80 ++++++++++++++++++++++++++++++++++ comtypes/test/test_stream.py | 83 ++++++------------------------------ 2 files changed, 92 insertions(+), 71 deletions(-) create mode 100644 comtypes/test/gdi_helper.py diff --git a/comtypes/test/gdi_helper.py b/comtypes/test/gdi_helper.py new file mode 100644 index 00000000..91d13d3d --- /dev/null +++ b/comtypes/test/gdi_helper.py @@ -0,0 +1,80 @@ +from ctypes import POINTER, Structure, WinDLL, c_void_p +from ctypes.wintypes import ( + BOOL, + DWORD, + HANDLE, + HDC, + HGDIOBJ, + HWND, + INT, + LONG, + UINT, + WORD, +) + +_user32 = WinDLL("user32") + +_GetDC = _user32.GetDC +_GetDC.argtypes = (HWND,) +_GetDC.restype = HDC + +_ReleaseDC = _user32.ReleaseDC +_ReleaseDC.argtypes = (HWND, HDC) +_ReleaseDC.restype = INT + +_gdi32 = WinDLL("gdi32") + +_CreateCompatibleDC = _gdi32.CreateCompatibleDC +_CreateCompatibleDC.argtypes = (HDC,) +_CreateCompatibleDC.restype = HDC + +_DeleteDC = _gdi32.DeleteDC +_DeleteDC.argtypes = (HDC,) +_DeleteDC.restype = BOOL + +_SelectObject = _gdi32.SelectObject +_SelectObject.argtypes = (HDC, HGDIOBJ) +_SelectObject.restype = HGDIOBJ + +_DeleteObject = _gdi32.DeleteObject +_DeleteObject.argtypes = (HGDIOBJ,) +_DeleteObject.restype = BOOL + +_GdiFlush = _gdi32.GdiFlush +_GdiFlush.argtypes = [] +_GdiFlush.restype = BOOL + + +class BITMAPINFOHEADER(Structure): + _fields_ = [ + ("biSize", DWORD), + ("biWidth", LONG), + ("biHeight", LONG), + ("biPlanes", WORD), + ("biBitCount", WORD), + ("biCompression", DWORD), + ("biSizeImage", DWORD), + ("biXPelsPerMeter", LONG), + ("biYPelsPerMeter", LONG), + ("biClrUsed", DWORD), + ("biClrImportant", DWORD), + ] + + +class BITMAPINFO(Structure): + _fields_ = [ + ("bmiHeader", BITMAPINFOHEADER), + ("bmiColors", DWORD * 1), # Placeholder for color table, not used for 32bpp + ] + + +_CreateDIBSection = _gdi32.CreateDIBSection +_CreateDIBSection.argtypes = ( + HDC, + POINTER(BITMAPINFO), + UINT, # DIB_RGB_COLORS + POINTER(c_void_p), # lplpBits + HANDLE, # hSection + DWORD, # dwOffset +) +_CreateDIBSection.restype = HGDIOBJ diff --git a/comtypes/test/test_stream.py b/comtypes/test/test_stream.py index 1e270f5a..9a1b3367 100644 --- a/comtypes/test/test_stream.py +++ b/comtypes/test/test_stream.py @@ -22,18 +22,14 @@ from ctypes.wintypes import ( BOOL, DWORD, - HANDLE, - HDC, HGDIOBJ, HGLOBAL, - HWND, INT, LONG, LPCWSTR, LPVOID, UINT, ULARGE_INTEGER, - WORD, ) from pathlib import Path from typing import Optional @@ -41,6 +37,18 @@ import comtypes.client from comtypes import hresult from comtypes.malloc import CoGetMalloc +from comtypes.test.gdi_helper import ( + BITMAPINFO, + BITMAPINFOHEADER, + _CreateCompatibleDC, + _CreateDIBSection, + _DeleteDC, + _DeleteObject, + _GdiFlush, + _GetDC, + _ReleaseDC, + _SelectObject, +) comtypes.client.GetModule("portabledeviceapi.dll") # The stdole module is generated automatically during the portabledeviceapi @@ -315,73 +323,6 @@ def test_can_lock_file_based_stream(self): # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-istream#methods -_user32 = WinDLL("user32") - -_GetDC = _user32.GetDC -_GetDC.argtypes = (HWND,) -_GetDC.restype = HDC - -_ReleaseDC = _user32.ReleaseDC -_ReleaseDC.argtypes = (HWND, HDC) -_ReleaseDC.restype = INT - -_gdi32 = WinDLL("gdi32") - -_CreateCompatibleDC = _gdi32.CreateCompatibleDC -_CreateCompatibleDC.argtypes = (HDC,) -_CreateCompatibleDC.restype = HDC - -_DeleteDC = _gdi32.DeleteDC -_DeleteDC.argtypes = (HDC,) -_DeleteDC.restype = BOOL - -_SelectObject = _gdi32.SelectObject -_SelectObject.argtypes = (HDC, HGDIOBJ) -_SelectObject.restype = HGDIOBJ - -_DeleteObject = _gdi32.DeleteObject -_DeleteObject.argtypes = (HGDIOBJ,) -_DeleteObject.restype = BOOL - -_GdiFlush = _gdi32.GdiFlush -_GdiFlush.argtypes = [] -_GdiFlush.restype = BOOL - - -class BITMAPINFOHEADER(Structure): - _fields_ = [ - ("biSize", DWORD), - ("biWidth", LONG), - ("biHeight", LONG), - ("biPlanes", WORD), - ("biBitCount", WORD), - ("biCompression", DWORD), - ("biSizeImage", DWORD), - ("biXPelsPerMeter", LONG), - ("biYPelsPerMeter", LONG), - ("biClrUsed", DWORD), - ("biClrImportant", DWORD), - ] - - -class BITMAPINFO(Structure): - _fields_ = [ - ("bmiHeader", BITMAPINFOHEADER), - ("bmiColors", DWORD * 1), # Placeholder for color table, not used for 32bpp - ] - - -_CreateDIBSection = _gdi32.CreateDIBSection -_CreateDIBSection.argtypes = ( - HDC, - POINTER(BITMAPINFO), - UINT, # DIB_RGB_COLORS - POINTER(c_void_p), # lplpBits - HANDLE, # hSection - DWORD, # dwOffset -) -_CreateDIBSection.restype = HGDIOBJ - _kernel32 = WinDLL("kernel32") _GlobalAlloc = _kernel32.GlobalAlloc From ad450de44ed78a85a5b4221313f4abe95da6251f Mon Sep 17 00:00:00 2001 From: junkmd Date: Fri, 23 Jan 2026 22:20:53 +0900 Subject: [PATCH 2/5] feat: Enhance GDI helper module with context managers and utilities. Further enhance `test/gdi_helper.py` by introducing context managers (`get_dc`, `create_compatible_dc`, `select_object`, `create_image_rendering_dc`) and utility functions and constants (`create_24bitmap_info`, `BI_RGB`, `DIB_RGB_COLORS`). These additions centralize GDI object management and simplify off-screen rendering setup for tests. --- comtypes/test/gdi_helper.py | 108 +++++++++++++++++++++++++++++++- comtypes/test/test_stream.py | 117 +---------------------------------- 2 files changed, 108 insertions(+), 117 deletions(-) diff --git a/comtypes/test/gdi_helper.py b/comtypes/test/gdi_helper.py index 91d13d3d..c2911471 100644 --- a/comtypes/test/gdi_helper.py +++ b/comtypes/test/gdi_helper.py @@ -1,4 +1,6 @@ -from ctypes import POINTER, Structure, WinDLL, c_void_p +import contextlib +from collections.abc import Iterator +from ctypes import POINTER, Structure, WinDLL, byref, c_void_p, sizeof from ctypes.wintypes import ( BOOL, DWORD, @@ -12,6 +14,9 @@ WORD, ) +BI_RGB = 0 # No compression +DIB_RGB_COLORS = 0 + _user32 = WinDLL("user32") _GetDC = _user32.GetDC @@ -78,3 +83,104 @@ class BITMAPINFO(Structure): DWORD, # dwOffset ) _CreateDIBSection.restype = HGDIOBJ + + +@contextlib.contextmanager +def get_dc(hwnd: int) -> Iterator[int]: + """Context manager to get and release a device context (DC).""" + dc = _GetDC(hwnd) + assert dc, "Failed to get device context." + try: + yield dc + finally: + # Release the device context + _ReleaseDC(hwnd, dc) + + +@contextlib.contextmanager +def create_compatible_dc(hdc: int) -> Iterator[int]: + """Context manager to create and delete a compatible device context.""" + mem_dc = _CreateCompatibleDC(hdc) + assert mem_dc, "Failed to create compatible memory DC." + try: + yield mem_dc + finally: + _DeleteDC(mem_dc) + + +@contextlib.contextmanager +def select_object(hdc: int, obj: int) -> Iterator[int]: + """Context manager to select a GDI object into a device context and restore + the original. + """ + old_obj = _SelectObject(hdc, obj) + assert old_obj, "Failed to select object into DC." + try: + yield obj + finally: + _SelectObject(hdc, old_obj) + + +def create_24bitmap_info(width: int, height: int) -> BITMAPINFO: + """Creates a BITMAPINFO structure for a 24bpp BGR DIB section.""" + bmi = BITMAPINFO() + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + bmi.bmiHeader.biWidth = width + bmi.bmiHeader.biHeight = -height # Negative for top-down DIB + bmi.bmiHeader.biPlanes = 1 + bmi.bmiHeader.biBitCount = 24 + bmi.bmiHeader.biCompression = BI_RGB + # width*height pixels * 3 bytes/pixel (BGR) + bmi.bmiHeader.biSizeImage = width * height * 3 + return bmi + + +@contextlib.contextmanager +def create_image_rendering_dc( + hwnd: int, + width: int, + height: int, + usage: int = DIB_RGB_COLORS, + hsection: int = 0, + dwoffset: int = 0, +) -> Iterator[tuple[int, c_void_p, BITMAPINFO, int]]: + """Context manager to create a device context for off-screen image rendering. + + This sets up a memory device context (DC) with a DIB section, allowing + GDI operations to render into a memory buffer. + + Args: + hwnd: Handle to the window (0 for desktop). + width: Width of the image buffer. + height: Height of the image buffer. + usage: The type of DIB. Default is DIB_RGB_COLORS (0). + hsection: A handle to a file-mapping object. If NULL (0), the system + allocates memory for the DIB. + dwoffset: The offset from the beginning of the file-mapping object + specified by `hsection` to where the DIB bitmap begins. + + Yields: + A tuple containing: + - mem_dc: The handle to the memory device context. + - bits: Pointer to the pixel data of the DIB section. + - bmi: The structure describing the DIB section. + - hbm: The handle to the created DIB section bitmap. + """ + # Get a screen DC to use as a reference for creating a compatible DC + with get_dc(hwnd) as screen_dc, create_compatible_dc(screen_dc) as mem_dc: + bits = c_void_p() + bmi = create_24bitmap_info(width, height) + try: + hbm = _CreateDIBSection( + mem_dc, + byref(bmi), + usage, + byref(bits), + hsection, + dwoffset, + ) + assert hbm, "Failed to create DIB section." + with select_object(mem_dc, hbm): + yield mem_dc, bits, bmi, hbm + finally: + _DeleteObject(hbm) diff --git a/comtypes/test/test_stream.py b/comtypes/test/test_stream.py index 9a1b3367..ff927e02 100644 --- a/comtypes/test/test_stream.py +++ b/comtypes/test/test_stream.py @@ -37,18 +37,7 @@ import comtypes.client from comtypes import hresult from comtypes.malloc import CoGetMalloc -from comtypes.test.gdi_helper import ( - BITMAPINFO, - BITMAPINFOHEADER, - _CreateCompatibleDC, - _CreateDIBSection, - _DeleteDC, - _DeleteObject, - _GdiFlush, - _GetDC, - _ReleaseDC, - _SelectObject, -) +from comtypes.test.gdi_helper import BI_RGB, _GdiFlush, create_image_rendering_dc comtypes.client.GetModule("portabledeviceapi.dll") # The stdole module is generated automatically during the portabledeviceapi @@ -435,21 +424,6 @@ class PICTDESC(Structure): GMEM_FIXED = 0x0000 GMEM_ZEROINIT = 0x0040 -BI_RGB = 0 # No compression -DIB_RGB_COLORS = 0 - - -@contextlib.contextmanager -def get_dc(hwnd: int) -> Iterator[int]: - """Context manager to get and release a device context (DC).""" - dc = _GetDC(hwnd) - assert dc, "Failed to get device context." - try: - yield dc - finally: - # Release the device context - _ReleaseDC(hwnd, dc) - @contextlib.contextmanager def global_alloc(uflags: int, dwbytes: int) -> Iterator[int]: @@ -473,44 +447,6 @@ def global_lock(handle: int) -> Iterator[int]: _GlobalUnlock(handle) -@contextlib.contextmanager -def create_compatible_dc(hdc: int) -> Iterator[int]: - """Context manager to create and delete a compatible device context.""" - mem_dc = _CreateCompatibleDC(hdc) - assert mem_dc, "Failed to create compatible memory DC." - try: - yield mem_dc - finally: - _DeleteDC(mem_dc) - - -@contextlib.contextmanager -def select_object(hdc: int, obj: int) -> Iterator[int]: - """Context manager to select a GDI object into a device context and restore - the original. - """ - old_obj = _SelectObject(hdc, obj) - assert old_obj, "Failed to select object into DC." - try: - yield obj - finally: - _SelectObject(hdc, old_obj) - - -def create_24bitmap_info(width: int, height: int) -> BITMAPINFO: - """Creates a BITMAPINFO structure for a 24bpp BGR DIB section.""" - bmi = BITMAPINFO() - bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) - bmi.bmiHeader.biWidth = width - bmi.bmiHeader.biHeight = height # positive for bottom-up DIB - bmi.bmiHeader.biPlanes = 1 - bmi.bmiHeader.biBitCount = 24 - bmi.bmiHeader.biCompression = BI_RGB - # width*height pixels * 3 bytes/pixel (BGR) - bmi.bmiHeader.biSizeImage = width * height * 3 - return bmi - - def create_24bit_pixel_data( red: int, green: int, @@ -565,57 +501,6 @@ def create_24bit_pixel_data( return bmp_header + info_header + pixel_data -@contextlib.contextmanager -def create_image_rendering_dc( - hwnd: int, - width: int, - height: int, - usage: int = DIB_RGB_COLORS, - hsection: int = 0, - dwoffset: int = 0, -) -> Iterator[tuple[int, c_void_p, BITMAPINFO, int]]: - """Context manager to create a device context for off-screen image rendering. - - This sets up a memory device context (DC) with a DIB section, allowing - GDI operations to render into a memory buffer. - - Args: - hwnd: Handle to the window (0 for desktop). - width: Width of the image buffer. - height: Height of the image buffer. - usage: The type of DIB. Default is DIB_RGB_COLORS (0). - hsection: A handle to a file-mapping object. If NULL (0), the system - allocates memory for the DIB. - dwoffset: The offset from the beginning of the file-mapping object - specified by `hsection` to where the DIB bitmap begins. - - Yields: - A tuple containing: - - mem_dc: The handle to the memory device context. - - bits: Pointer to the pixel data of the DIB section. - - bmi: The structure describing the DIB section. - - hbm: The handle to the created DIB section bitmap. - """ - # Get a screen DC to use as a reference for creating a compatible DC - with get_dc(hwnd) as screen_dc, create_compatible_dc(screen_dc) as mem_dc: - bits = c_void_p() - bmi = create_24bitmap_info(width, height) - try: - hbm = _CreateDIBSection( - mem_dc, - byref(bmi), - usage, - byref(bits), - hsection, - dwoffset, - ) - assert hbm, "Failed to create DIB section." - with select_object(mem_dc, hbm): - yield mem_dc, bits, bmi, hbm - finally: - _DeleteObject(hbm) - - class Test_Picture(ut.TestCase): def test_load_from_handle_stream(self): width, height = 1, 1 From ccbc50e784576d3a3935e33bd6a6f6a5fd6734d9 Mon Sep 17 00:00:00 2001 From: junkmd Date: Fri, 23 Jan 2026 22:20:53 +0900 Subject: [PATCH 3/5] refactor: Allow `Optional[int]` in `gdi_helper` context managers. Update type hints for `get_dc` and `create_compatible_dc` in `test/gdi_helper.py` to explicitly accept `Optional[int]`. --- comtypes/test/gdi_helper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/comtypes/test/gdi_helper.py b/comtypes/test/gdi_helper.py index c2911471..c91a1a96 100644 --- a/comtypes/test/gdi_helper.py +++ b/comtypes/test/gdi_helper.py @@ -13,6 +13,7 @@ UINT, WORD, ) +from typing import Optional BI_RGB = 0 # No compression DIB_RGB_COLORS = 0 @@ -86,7 +87,7 @@ class BITMAPINFO(Structure): @contextlib.contextmanager -def get_dc(hwnd: int) -> Iterator[int]: +def get_dc(hwnd: Optional[int]) -> Iterator[int]: """Context manager to get and release a device context (DC).""" dc = _GetDC(hwnd) assert dc, "Failed to get device context." @@ -98,7 +99,7 @@ def get_dc(hwnd: int) -> Iterator[int]: @contextlib.contextmanager -def create_compatible_dc(hdc: int) -> Iterator[int]: +def create_compatible_dc(hdc: Optional[int]) -> Iterator[int]: """Context manager to create and delete a compatible device context.""" mem_dc = _CreateCompatibleDC(hdc) assert mem_dc, "Failed to create compatible memory DC." From 650b5982c6586d1a2dbbd6c1d2098501c24400f6 Mon Sep 17 00:00:00 2001 From: junkmd Date: Fri, 23 Jan 2026 22:20:53 +0900 Subject: [PATCH 4/5] refactor: Extract `create_dib_section` context manager in `gdi_helper`. Introduce `create_dib_section` as a dedicated context manager in `test/gdi_helper.py` to handle the creation and destruction of DIB sections. Refactor `create_image_rendering_dc` to utilize this new manager, reducing nesting and improving readability. Additionally, update the return type hint of `create_image_rendering_dc` to use `int` for the bitmap bits pointer address, ensuring consistency with the handle types. --- comtypes/test/gdi_helper.py | 45 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/comtypes/test/gdi_helper.py b/comtypes/test/gdi_helper.py index c91a1a96..466cddbe 100644 --- a/comtypes/test/gdi_helper.py +++ b/comtypes/test/gdi_helper.py @@ -109,6 +109,33 @@ def create_compatible_dc(hdc: Optional[int]) -> Iterator[int]: _DeleteDC(mem_dc) +@contextlib.contextmanager +def create_dib_section( + hdc: int, bmi: BITMAPINFO, usage: int, hsection: int, dwoffset: int +) -> Iterator[tuple[int, int]]: + """Context manager to create and manage a DIB section. + + This function creates a device-independent bitmap (DIB) that applications + can write to directly. It provides a handle to the DIB and a pointer + address to its bitmap bits. + """ + bits = c_void_p() + try: + hbm = _CreateDIBSection( + hdc, + byref(bmi), + usage, + byref(bits), + hsection, + dwoffset, + ) + assert hbm, "Failed to create DIB section." + assert bits.value, "Failed to get the bitmap's bit value." + yield hbm, bits.value + finally: + _DeleteObject(hbm) + + @contextlib.contextmanager def select_object(hdc: int, obj: int) -> Iterator[int]: """Context manager to select a GDI object into a device context and restore @@ -144,7 +171,7 @@ def create_image_rendering_dc( usage: int = DIB_RGB_COLORS, hsection: int = 0, dwoffset: int = 0, -) -> Iterator[tuple[int, c_void_p, BITMAPINFO, int]]: +) -> Iterator[tuple[int, int, BITMAPINFO, int]]: """Context manager to create a device context for off-screen image rendering. This sets up a memory device context (DC) with a DIB section, allowing @@ -163,25 +190,13 @@ def create_image_rendering_dc( Yields: A tuple containing: - mem_dc: The handle to the memory device context. - - bits: Pointer to the pixel data of the DIB section. + - bits: Pointer address to the pixel data of the DIB section. - bmi: The structure describing the DIB section. - hbm: The handle to the created DIB section bitmap. """ # Get a screen DC to use as a reference for creating a compatible DC with get_dc(hwnd) as screen_dc, create_compatible_dc(screen_dc) as mem_dc: - bits = c_void_p() bmi = create_24bitmap_info(width, height) - try: - hbm = _CreateDIBSection( - mem_dc, - byref(bmi), - usage, - byref(bits), - hsection, - dwoffset, - ) - assert hbm, "Failed to create DIB section." + with create_dib_section(mem_dc, bmi, usage, hsection, dwoffset) as (hbm, bits): with select_object(mem_dc, hbm): yield mem_dc, bits, bmi, hbm - finally: - _DeleteObject(hbm) From d8eaa1c37a65409091b34e64a72770b95f1ac3f1 Mon Sep 17 00:00:00 2001 From: junkmd Date: Fri, 23 Jan 2026 22:20:53 +0900 Subject: [PATCH 5/5] test: Add test for `IViewObject::Draw` method. Introduce `test_Draw` in `test_viewobject.py` to validate the `Draw` method of the `IViewObject` interface. This test creates an off-screen rendering context using helpers from `gdi_helper.py`. An `InkEdit` COM object is created, placed into a running state via `OleRun`, and then instructed to draw itself onto the device context. The test verifies the operation's success by asserting that the pixel data in the resulting bitmap matches the color set on the object. --- comtypes/test/test_viewobject.py | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/comtypes/test/test_viewobject.py b/comtypes/test/test_viewobject.py index aa4803af..20a55222 100644 --- a/comtypes/test/test_viewobject.py +++ b/comtypes/test/test_viewobject.py @@ -1,9 +1,12 @@ import contextlib +import ctypes import unittest +from ctypes import POINTER, OleDLL from ctypes.wintypes import POINT, RECT, SIZEL import comtypes.client from comtypes import IUnknown +from comtypes.test.gdi_helper import _GdiFlush, create_image_rendering_dc from comtypes.viewobject import ( DVASPECT_CONTENT, IAdviseSink, @@ -12,9 +15,11 @@ IViewObjectEx, ) +comtypes.client.GetModule("inked.dll") with contextlib.redirect_stdout(None): # supress warnings comtypes.client.GetModule("mshtml.tlb") +import comtypes.gen.INKEDLib as inkedlib import comtypes.gen.MSHTML as mshtml @@ -22,6 +27,11 @@ def create_html_document() -> IUnknown: return comtypes.client.CreateObject(mshtml.HTMLDocument) +_ole32 = OleDLL("ole32") +_OleRun = _ole32.OleRun +_OleRun.argtypes = [POINTER(IUnknown)] + + class Test_IViewObject(unittest.TestCase): def test_Advise_GetAdvise(self): vo = create_html_document().QueryInterface(IViewObject) @@ -40,6 +50,35 @@ def test_Freeze_Unfreeze(self): self.assertIsInstance(cookie, int) vo.Unfreeze(cookie) + def test_Draw(self): + # https://learn.microsoft.com/en-us/windows/win32/api/ole/nf-ole-iviewobject-draw + # It is necessary to use a valid HDC for the `Draw` method. + ink_edit = comtypes.client.CreateObject( + inkedlib.InkEdit, interface=inkedlib.IInkEdit + ) + _OleRun(ink_edit) # Put InkEdit into running state + ink_edit.Text = "" + ink_edit.BackColor = 255 << 16 | 0 << 8 | 0 + vo = ink_edit.QueryInterface(IViewObject) + width, height = 1, 1 + with create_image_rendering_dc(0, width, height) as (hdc, bits, bmi, _): + vo.Draw( + DVASPECT_CONTENT, # dwDrawAspect + -1, # lindex + None, # pvAspect + None, # ptd + None, # hicTargetDev + hdc, # hdcDraw + RECT(left=0, top=0, right=width, bottom=height), # lprcBounds + None, # lprcWBounds + None, # pfnContinue + 0, # dwContinue + ) + _GdiFlush() # To ensure all drawing is complete + # Read the pixel data directly from the bits pointer. + gdi_data = ctypes.string_at(bits, bmi.bmiHeader.biSizeImage) + self.assertEqual(gdi_data, b"\xff\x00\x00") + class Test_IViewObject2(unittest.TestCase): def test_GetExtent(self):