From 3225f3f3d9656df2dbfd88c1868b8bd84b22093f Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 17 Jan 2026 10:11:10 +0900 Subject: [PATCH 1/3] refactor: Extract common moniker definitions to `monikers_helper.py` Moves shared constants, functions, and imports related to moniker testing from `test_monikers.py` to a new helper module `monikers_helper.py`. --- comtypes/test/monikers_helper.py | 24 ++++++++++++++++++++++++ comtypes/test/test_monikers.py | 28 ++++++++-------------------- 2 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 comtypes/test/monikers_helper.py diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py new file mode 100644 index 000000000..49e39b63d --- /dev/null +++ b/comtypes/test/monikers_helper.py @@ -0,0 +1,24 @@ +from ctypes import HRESULT, POINTER, OleDLL, c_wchar_p +from ctypes.wintypes import DWORD + +from comtypes import IUnknown + +MKSYS_ITEMMONIKER = 4 + +ROTFLAGS_ALLOWANYCLIENT = 1 + +LPOLESTR = LPCOLESTR = c_wchar_p + +_ole32 = OleDLL("ole32") + +_CreateItemMoniker = _ole32.CreateItemMoniker +_CreateItemMoniker.argtypes = [LPCOLESTR, LPCOLESTR, POINTER(POINTER(IUnknown))] +_CreateItemMoniker.restype = HRESULT + +_CreateBindCtx = _ole32.CreateBindCtx +_CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IUnknown))] +_CreateBindCtx.restype = HRESULT + +_GetRunningObjectTable = _ole32.GetRunningObjectTable +_GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IUnknown))] +_GetRunningObjectTable.restype = HRESULT diff --git a/comtypes/test/test_monikers.py b/comtypes/test/test_monikers.py index 6910e0840..3a07673c7 100644 --- a/comtypes/test/test_monikers.py +++ b/comtypes/test/test_monikers.py @@ -1,35 +1,23 @@ import contextlib import unittest from _ctypes import COMError -from ctypes import HRESULT, POINTER, OleDLL, byref, c_wchar_p -from ctypes.wintypes import DWORD +from ctypes import POINTER, byref from comtypes import GUID, hresult from comtypes.client import CreateObject, GetModule +from comtypes.test.monikers_helper import ( + MKSYS_ITEMMONIKER, + ROTFLAGS_ALLOWANYCLIENT, + _CreateBindCtx, + _CreateItemMoniker, + _GetRunningObjectTable, +) with contextlib.redirect_stdout(None): # supress warnings GetModule("msvidctl.dll") from comtypes.gen import MSVidCtlLib as msvidctl from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable -MKSYS_ITEMMONIKER = 4 -ROTFLAGS_ALLOWANYCLIENT = 1 -LPOLESTR = LPCOLESTR = c_wchar_p - -_ole32 = OleDLL("ole32") - -_CreateItemMoniker = _ole32.CreateItemMoniker -_CreateItemMoniker.argtypes = [LPCOLESTR, LPCOLESTR, POINTER(POINTER(IMoniker))] -_CreateItemMoniker.restype = HRESULT - -_CreateBindCtx = _ole32.CreateBindCtx -_CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IBindCtx))] -_CreateBindCtx.restype = HRESULT - -_GetRunningObjectTable = _ole32.GetRunningObjectTable -_GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IRunningObjectTable))] -_GetRunningObjectTable.restype = HRESULT - def _create_item_moniker(delim: str, item: str) -> IMoniker: mon = POINTER(IMoniker)() From e75412d7b1395113120c86bf545ecdedb83e6444 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 17 Jan 2026 10:11:10 +0900 Subject: [PATCH 2/3] test: Copy the existing test module. --- comtypes/test/test_bctx.py | 73 +++++++++++++++++++ .../{test_monikers.py => test_moniker.py} | 0 comtypes/test/test_rot.py | 73 +++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 comtypes/test/test_bctx.py rename comtypes/test/{test_monikers.py => test_moniker.py} (100%) create mode 100644 comtypes/test/test_rot.py diff --git a/comtypes/test/test_bctx.py b/comtypes/test/test_bctx.py new file mode 100644 index 000000000..1a6b3cefe --- /dev/null +++ b/comtypes/test/test_bctx.py @@ -0,0 +1,73 @@ +import contextlib +import unittest +from _ctypes import COMError +from ctypes import POINTER, byref + +from comtypes import GUID, hresult +from comtypes.client import CreateObject, GetModule +from comtypes.test.monikers_helper import ( + MKSYS_ITEMMONIKER, + ROTFLAGS_ALLOWANYCLIENT, + _CreateBindCtx, + _CreateItemMoniker, + _GetRunningObjectTable, +) + +with contextlib.redirect_stdout(None): # supress warnings + GetModule("msvidctl.dll") +from comtypes.gen import MSVidCtlLib as msvidctl +from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable + + +def _create_item_moniker(delim: str, item: str) -> IMoniker: + mon = POINTER(IMoniker)() + _CreateItemMoniker(delim, item, byref(mon)) + return mon # type: ignore + + +def _create_bctx() -> IBindCtx: + bctx = POINTER(IBindCtx)() + # The first parameter is reserved and must be 0. + _CreateBindCtx(0, byref(bctx)) + return bctx # type: ignore + + +def _create_rot() -> IRunningObjectTable: + rot = POINTER(IRunningObjectTable)() + # The first parameter is reserved and must be 0. + _GetRunningObjectTable(0, byref(rot)) + return rot # type: ignore + + +class Test_IMoniker(unittest.TestCase): + def test_IsSystemMoniker(self): + item_id = str(GUID.create_new()) + mon = _create_item_moniker("!", item_id) + self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER) + + +# TODO: Isolate this test case. +class Test_IBindCtx(unittest.TestCase): + def test_EnumObjectParam(self): + bctx = _create_bctx() + with self.assertRaises(COMError) as err_ctx: + # calling `EnumObjectParam` results in a return value of E_NOTIMPL. + # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers + bctx.EnumObjectParam() + self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL) + + +class Test_IRunningObjectTable(unittest.TestCase): + def test_register_and_revoke_item_moniker(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + item_id = str(GUID.create_new()) + mon = _create_item_moniker("!", item_id) + rot = _create_rot() + bctx = _create_bctx() + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) + dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK) + self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None)) + self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) + rot.Revoke(dw_reg) + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) diff --git a/comtypes/test/test_monikers.py b/comtypes/test/test_moniker.py similarity index 100% rename from comtypes/test/test_monikers.py rename to comtypes/test/test_moniker.py diff --git a/comtypes/test/test_rot.py b/comtypes/test/test_rot.py new file mode 100644 index 000000000..fd492c56e --- /dev/null +++ b/comtypes/test/test_rot.py @@ -0,0 +1,73 @@ +import contextlib +import unittest +from _ctypes import COMError +from ctypes import POINTER, byref + +from comtypes import GUID, hresult +from comtypes.client import CreateObject, GetModule +from comtypes.test.monikers_helper import ( + MKSYS_ITEMMONIKER, + ROTFLAGS_ALLOWANYCLIENT, + _CreateBindCtx, + _CreateItemMoniker, + _GetRunningObjectTable, +) + +with contextlib.redirect_stdout(None): # supress warnings + GetModule("msvidctl.dll") +from comtypes.gen import MSVidCtlLib as msvidctl +from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable + + +def _create_item_moniker(delim: str, item: str) -> IMoniker: + mon = POINTER(IMoniker)() + _CreateItemMoniker(delim, item, byref(mon)) + return mon # type: ignore + + +def _create_bctx() -> IBindCtx: + bctx = POINTER(IBindCtx)() + # The first parameter is reserved and must be 0. + _CreateBindCtx(0, byref(bctx)) + return bctx # type: ignore + + +def _create_rot() -> IRunningObjectTable: + rot = POINTER(IRunningObjectTable)() + # The first parameter is reserved and must be 0. + _GetRunningObjectTable(0, byref(rot)) + return rot # type: ignore + + +class Test_IMoniker(unittest.TestCase): + def test_IsSystemMoniker(self): + item_id = str(GUID.create_new()) + mon = _create_item_moniker("!", item_id) + self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER) + + +class Test_IBindCtx(unittest.TestCase): + def test_EnumObjectParam(self): + bctx = _create_bctx() + with self.assertRaises(COMError) as err_ctx: + # calling `EnumObjectParam` results in a return value of E_NOTIMPL. + # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers + bctx.EnumObjectParam() + self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL) + + +# TODO: Isolate this test case. +class Test_IRunningObjectTable(unittest.TestCase): + def test_register_and_revoke_item_moniker(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + item_id = str(GUID.create_new()) + mon = _create_item_moniker("!", item_id) + rot = _create_rot() + bctx = _create_bctx() + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) + dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK) + self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None)) + self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) + rot.Revoke(dw_reg) + self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) From 4cc59830784f5bf5f78b52c4d01401eb1bca872c Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 17 Jan 2026 10:11:10 +0900 Subject: [PATCH 3/3] refactor: Restructure moniker-related tests and helper utilities - Extract `CLSID_AntiMoniker`, `CLSID_ItemMoniker`, `MK_E_UNAVAILABLE` into `monikers_helper.py`. - Refactor test classes in `test_bctx.py`, `test_moniker.py`, and `test_rot.py` for clarity. - Improve test coverage and correctness for moniker-related functions. --- comtypes/test/monikers_helper.py | 9 +++++++- comtypes/test/test_bctx.py | 39 +++++++++++++------------------- comtypes/test/test_moniker.py | 28 ++++++++++------------- comtypes/test/test_rot.py | 39 ++++++++++++-------------------- 4 files changed, 51 insertions(+), 64 deletions(-) diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py index 49e39b63d..6ad3671b5 100644 --- a/comtypes/test/monikers_helper.py +++ b/comtypes/test/monikers_helper.py @@ -1,10 +1,14 @@ from ctypes import HRESULT, POINTER, OleDLL, c_wchar_p from ctypes.wintypes import DWORD -from comtypes import IUnknown +from comtypes import GUID, IUnknown +# https://learn.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-mksys MKSYS_ITEMMONIKER = 4 +CLSID_AntiMoniker = GUID("{00000305-0000-0000-c000-000000000046}") +CLSID_ItemMoniker = GUID("{00000304-0000-0000-c000-000000000046}") + ROTFLAGS_ALLOWANYCLIENT = 1 LPOLESTR = LPCOLESTR = c_wchar_p @@ -22,3 +26,6 @@ _GetRunningObjectTable = _ole32.GetRunningObjectTable _GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IUnknown))] _GetRunningObjectTable.restype = HRESULT + +# Common COM Errors from Moniker/Binding Context operations +MK_E_UNAVAILABLE = -2147221021 # 0x800401E3 diff --git a/comtypes/test/test_bctx.py b/comtypes/test/test_bctx.py index 1a6b3cefe..adb88ba89 100644 --- a/comtypes/test/test_bctx.py +++ b/comtypes/test/test_bctx.py @@ -6,7 +6,6 @@ from comtypes import GUID, hresult from comtypes.client import CreateObject, GetModule from comtypes.test.monikers_helper import ( - MKSYS_ITEMMONIKER, ROTFLAGS_ALLOWANYCLIENT, _CreateBindCtx, _CreateItemMoniker, @@ -39,35 +38,29 @@ def _create_rot() -> IRunningObjectTable: return rot # type: ignore -class Test_IMoniker(unittest.TestCase): - def test_IsSystemMoniker(self): - item_id = str(GUID.create_new()) - mon = _create_item_moniker("!", item_id) - self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER) - - -# TODO: Isolate this test case. -class Test_IBindCtx(unittest.TestCase): - def test_EnumObjectParam(self): +class Test_EnumObjectParam(unittest.TestCase): + def test_cannot_call(self): bctx = _create_bctx() - with self.assertRaises(COMError) as err_ctx: + with self.assertRaises(COMError) as cm: # calling `EnumObjectParam` results in a return value of E_NOTIMPL. # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers bctx.EnumObjectParam() - self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL) + self.assertEqual(cm.exception.hresult, hresult.E_NOTIMPL) -class Test_IRunningObjectTable(unittest.TestCase): - def test_register_and_revoke_item_moniker(self): +class Test_GetRunningObjectTable(unittest.TestCase): + def test_returns_rot(self): vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) item_id = str(GUID.create_new()) mon = _create_item_moniker("!", item_id) - rot = _create_rot() bctx = _create_bctx() - self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) - dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) - self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK) - self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None)) - self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) - rot.Revoke(dw_reg) - self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) + # Before registering: should NOT be running + rot_from_bctx = bctx.GetRunningObjectTable() + self.assertIsInstance(rot_from_bctx, IRunningObjectTable) + rot_from_func = _create_rot() + dw_reg = rot_from_func.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) + # After registering: should be running + self.assertEqual(rot_from_bctx.IsRunning(mon), hresult.S_OK) + rot_from_func.Revoke(dw_reg) + # After revoking: should NOT be running again + self.assertEqual(rot_from_bctx, rot_from_func) diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index 3a07673c7..f4999c745 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -1,6 +1,5 @@ import contextlib import unittest -from _ctypes import COMError from ctypes import POINTER, byref from comtypes import GUID, hresult @@ -8,6 +7,8 @@ from comtypes.test.monikers_helper import ( MKSYS_ITEMMONIKER, ROTFLAGS_ALLOWANYCLIENT, + CLSID_AntiMoniker, + CLSID_ItemMoniker, _CreateBindCtx, _CreateItemMoniker, _GetRunningObjectTable, @@ -39,34 +40,29 @@ def _create_rot() -> IRunningObjectTable: return rot # type: ignore -class Test_IMoniker(unittest.TestCase): - def test_IsSystemMoniker(self): +class Test_IsSystemMoniker_GetDisplayName_Inverse(unittest.TestCase): + def test_item(self): item_id = str(GUID.create_new()) mon = _create_item_moniker("!", item_id) self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER) - - -class Test_IBindCtx(unittest.TestCase): - def test_EnumObjectParam(self): bctx = _create_bctx() - with self.assertRaises(COMError) as err_ctx: - # calling `EnumObjectParam` results in a return value of E_NOTIMPL. - # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers - bctx.EnumObjectParam() - self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL) + self.assertEqual(mon.GetDisplayName(bctx, None), f"!{item_id}") + self.assertEqual(mon.GetClassID(), CLSID_ItemMoniker) + self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) -class Test_IRunningObjectTable(unittest.TestCase): - def test_register_and_revoke_item_moniker(self): +class Test_IsRunning(unittest.TestCase): + def test_item(self): vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) item_id = str(GUID.create_new()) mon = _create_item_moniker("!", item_id) rot = _create_rot() bctx = _create_bctx() + # Before registering: should NOT be running self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) + # After registering: should be running self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK) - self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None)) - self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) rot.Revoke(dw_reg) + # After revoking: should NOT be running again self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) diff --git a/comtypes/test/test_rot.py b/comtypes/test/test_rot.py index fd492c56e..88813ad11 100644 --- a/comtypes/test/test_rot.py +++ b/comtypes/test/test_rot.py @@ -6,7 +6,7 @@ from comtypes import GUID, hresult from comtypes.client import CreateObject, GetModule from comtypes.test.monikers_helper import ( - MKSYS_ITEMMONIKER, + MK_E_UNAVAILABLE, ROTFLAGS_ALLOWANYCLIENT, _CreateBindCtx, _CreateItemMoniker, @@ -39,35 +39,26 @@ def _create_rot() -> IRunningObjectTable: return rot # type: ignore -class Test_IMoniker(unittest.TestCase): - def test_IsSystemMoniker(self): - item_id = str(GUID.create_new()) - mon = _create_item_moniker("!", item_id) - self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER) - - -class Test_IBindCtx(unittest.TestCase): - def test_EnumObjectParam(self): - bctx = _create_bctx() - with self.assertRaises(COMError) as err_ctx: - # calling `EnumObjectParam` results in a return value of E_NOTIMPL. - # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers - bctx.EnumObjectParam() - self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL) - - -# TODO: Isolate this test case. -class Test_IRunningObjectTable(unittest.TestCase): - def test_register_and_revoke_item_moniker(self): +class Test_Register_Revoke_GetObject_IsRunning(unittest.TestCase): + def test_item(self): vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) item_id = str(GUID.create_new()) mon = _create_item_moniker("!", item_id) rot = _create_rot() bctx = _create_bctx() - self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) + # Before registering: should NOT be running + with self.assertRaises(COMError) as cm: + rot.GetObject(mon) + self.assertEqual(cm.exception.hresult, MK_E_UNAVAILABLE) + self.assertEqual(rot.IsRunning(mon), hresult.S_FALSE) + # After registering: should be running dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon) + self.assertEqual(rot.IsRunning(mon), hresult.S_OK) self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK) - self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None)) self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) rot.Revoke(dw_reg) - self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE) + # After revoking: should NOT be running again + self.assertEqual(rot.IsRunning(mon), hresult.S_FALSE) + with self.assertRaises(COMError) as cm: + rot.GetObject(mon) + self.assertEqual(cm.exception.hresult, MK_E_UNAVAILABLE)