From 5e48fff2becf80f53def20c2a37cc67c08274815 Mon Sep 17 00:00:00 2001 From: junkmd Date: Wed, 11 Feb 2026 13:46:38 +0900 Subject: [PATCH 1/5] test: Add pointer-moniker behavior and composition tests to `test_moniker.py`. --- comtypes/test/monikers_helper.py | 6 ++++++ comtypes/test/test_moniker.py | 35 +++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py index 376472c7..c4532f46 100644 --- a/comtypes/test/monikers_helper.py +++ b/comtypes/test/monikers_helper.py @@ -8,11 +8,13 @@ MKSYS_FILEMONIKER = 2 MKSYS_ANTIMONIKER = 3 MKSYS_ITEMMONIKER = 4 +MKSYS_POINTERMONIKER = 5 CLSID_CompositeMoniker = GUID("{00000309-0000-0000-c000-000000000046}") CLSID_FileMoniker = GUID("{00000303-0000-0000-C000-000000000046}") CLSID_AntiMoniker = GUID("{00000305-0000-0000-c000-000000000046}") CLSID_ItemMoniker = GUID("{00000304-0000-0000-c000-000000000046}") +CLSID_PointerMoniker = GUID("{00000306-0000-0000-c000-000000000046}") ROTFLAGS_ALLOWANYCLIENT = 1 @@ -40,6 +42,10 @@ _CreateItemMoniker.argtypes = [LPCOLESTR, LPCOLESTR, POINTER(POINTER(IUnknown))] _CreateItemMoniker.restype = HRESULT +_CreatePointerMoniker = _ole32.CreatePointerMoniker +_CreatePointerMoniker.argtypes = [POINTER(IUnknown), POINTER(POINTER(IUnknown))] +_CreatePointerMoniker.restype = HRESULT + _CreateBindCtx = _ole32.CreateBindCtx _CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IUnknown))] _CreateBindCtx.restype = HRESULT diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index f1cfc924..4a0e1910 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -9,7 +9,7 @@ from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR, MAX_PATH from pathlib import Path -from comtypes import GUID, hresult +from comtypes import GUID, IUnknown, hresult from comtypes.client import CreateObject, GetModule from comtypes.persist import IPersistFile from comtypes.test.monikers_helper import ( @@ -20,16 +20,19 @@ MKSYS_FILEMONIKER, MKSYS_GENERICCOMPOSITE, MKSYS_ITEMMONIKER, + MKSYS_POINTERMONIKER, ROTFLAGS_ALLOWANYCLIENT, CLSID_AntiMoniker, CLSID_CompositeMoniker, CLSID_FileMoniker, CLSID_ItemMoniker, + CLSID_PointerMoniker, _CreateAntiMoniker, _CreateBindCtx, _CreateFileMoniker, _CreateGenericComposite, _CreateItemMoniker, + _CreatePointerMoniker, _GetRunningObjectTable, ) from comtypes.test.time_structs_helper import CompareFileTime @@ -82,6 +85,13 @@ def _create_item_moniker(delim: str, item: str) -> IMoniker: return mon # type: ignore +def _create_pointer_moniker(punk: IUnknown) -> IMoniker: + mon = POINTER(IMoniker)() + # `punk` must be an instance of `POINTER(IUnknown)`. + _CreatePointerMoniker(punk, byref(mon)) + return mon # type: ignore + + def _create_bctx() -> IBindCtx: bctx = POINTER(IBindCtx)() # The first parameter is reserved and must be 0. @@ -146,6 +156,17 @@ def test_item(self): self.assertEqual(mon.GetClassID(), CLSID_ItemMoniker) self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + def test_pointer(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + mon = _create_pointer_moniker(vidctl) + self.assertEqual(mon.IsSystemMoniker(), MKSYS_POINTERMONIKER) + bctx = _create_bctx() + with self.assertRaises(COMError) as cm: + mon.GetDisplayName(bctx, None) + self.assertEqual(cm.exception.hresult, hresult.E_NOTIMPL) + self.assertEqual(mon.GetClassID(), CLSID_PointerMoniker) + self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + class Test_ComposeWith(unittest.TestCase): def test_file_with_same_type(self): @@ -222,6 +243,18 @@ def test_item_with_same_type(self): left_mon.ComposeWith(right_mon, True) self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_pointer_with_same_type(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + left_mon = _create_pointer_moniker(vidctl) + right_mon = _create_pointer_moniker(vidctl) + self.assertEqual( + left_mon.ComposeWith(right_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + left_mon.ComposeWith(right_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + class Test_IsEqual(unittest.TestCase): def test_item(self): From 667c3706c345f42a0bf2a5e685981fb78ed77e5d Mon Sep 17 00:00:00 2001 From: junkmd Date: Wed, 11 Feb 2026 13:46:38 +0900 Subject: [PATCH 2/5] test: Add class-moniker behavior and composition tests to `test_moniker.py`. --- comtypes/test/monikers_helper.py | 6 ++++++ comtypes/test/test_moniker.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py index c4532f46..555c5672 100644 --- a/comtypes/test/monikers_helper.py +++ b/comtypes/test/monikers_helper.py @@ -9,12 +9,14 @@ MKSYS_ANTIMONIKER = 3 MKSYS_ITEMMONIKER = 4 MKSYS_POINTERMONIKER = 5 +MKSYS_CLASSMONIKER = 7 CLSID_CompositeMoniker = GUID("{00000309-0000-0000-c000-000000000046}") CLSID_FileMoniker = GUID("{00000303-0000-0000-C000-000000000046}") CLSID_AntiMoniker = GUID("{00000305-0000-0000-c000-000000000046}") CLSID_ItemMoniker = GUID("{00000304-0000-0000-c000-000000000046}") CLSID_PointerMoniker = GUID("{00000306-0000-0000-c000-000000000046}") +CLSID_ClassMoniker = GUID("{0000031A-0000-0000-C000-000000000046}") ROTFLAGS_ALLOWANYCLIENT = 1 @@ -46,6 +48,10 @@ _CreatePointerMoniker.argtypes = [POINTER(IUnknown), POINTER(POINTER(IUnknown))] _CreatePointerMoniker.restype = HRESULT +_CreateClassMoniker = _ole32.CreateClassMoniker +_CreateClassMoniker.argtypes = [POINTER(GUID), POINTER(POINTER(IUnknown))] +_CreateClassMoniker.restype = HRESULT + _CreateBindCtx = _ole32.CreateBindCtx _CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IUnknown))] _CreateBindCtx.restype = HRESULT diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index 4a0e1910..b5c8a2f1 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -17,18 +17,21 @@ MK_E_NOINVERSE, MK_E_SYNTAX, MKSYS_ANTIMONIKER, + MKSYS_CLASSMONIKER, MKSYS_FILEMONIKER, MKSYS_GENERICCOMPOSITE, MKSYS_ITEMMONIKER, MKSYS_POINTERMONIKER, ROTFLAGS_ALLOWANYCLIENT, CLSID_AntiMoniker, + CLSID_ClassMoniker, CLSID_CompositeMoniker, CLSID_FileMoniker, CLSID_ItemMoniker, CLSID_PointerMoniker, _CreateAntiMoniker, _CreateBindCtx, + _CreateClassMoniker, _CreateFileMoniker, _CreateGenericComposite, _CreateItemMoniker, @@ -92,6 +95,12 @@ def _create_pointer_moniker(punk: IUnknown) -> IMoniker: return mon # type: ignore +def _create_class_moniker(clsid: GUID) -> IMoniker: + mon = POINTER(IMoniker)() + _CreateClassMoniker(byref(clsid), byref(mon)) + return mon # type: ignore + + def _create_bctx() -> IBindCtx: bctx = POINTER(IBindCtx)() # The first parameter is reserved and must be 0. @@ -167,6 +176,17 @@ def test_pointer(self): self.assertEqual(mon.GetClassID(), CLSID_PointerMoniker) self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + def test_class(self): + clsid = GUID.create_new() + mon = _create_class_moniker(clsid) + self.assertEqual(mon.IsSystemMoniker(), MKSYS_CLASSMONIKER) + bctx = _create_bctx() + self.assertEqual( + mon.GetDisplayName(bctx, None), f"clsid:{str(clsid).strip('{}')}:" + ) + self.assertEqual(mon.GetClassID(), CLSID_ClassMoniker) + self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + class Test_ComposeWith(unittest.TestCase): def test_file_with_same_type(self): @@ -255,6 +275,18 @@ def test_pointer_with_same_type(self): left_mon.ComposeWith(right_mon, True) self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_class_with_same_type(self): + clsid = GUID.create_new() + left_mon = _create_class_moniker(clsid) + right_mon = _create_class_moniker(GUID.create_new()) + self.assertEqual( + left_mon.ComposeWith(right_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + left_mon.ComposeWith(right_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + class Test_IsEqual(unittest.TestCase): def test_item(self): From a27ba2ed9854ebe338c51fbd7a301399982f598c Mon Sep 17 00:00:00 2001 From: junkmd Date: Wed, 11 Feb 2026 13:46:38 +0900 Subject: [PATCH 3/5] test: Add objref-moniker behavior and composition tests to `test_moniker.py`. --- comtypes/test/monikers_helper.py | 6 ++++++ comtypes/test/test_moniker.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py index 555c5672..3b1658d7 100644 --- a/comtypes/test/monikers_helper.py +++ b/comtypes/test/monikers_helper.py @@ -10,6 +10,7 @@ MKSYS_ITEMMONIKER = 4 MKSYS_POINTERMONIKER = 5 MKSYS_CLASSMONIKER = 7 +MKSYS_OBJREFMONIKER = 8 CLSID_CompositeMoniker = GUID("{00000309-0000-0000-c000-000000000046}") CLSID_FileMoniker = GUID("{00000303-0000-0000-C000-000000000046}") @@ -17,6 +18,7 @@ CLSID_ItemMoniker = GUID("{00000304-0000-0000-c000-000000000046}") CLSID_PointerMoniker = GUID("{00000306-0000-0000-c000-000000000046}") CLSID_ClassMoniker = GUID("{0000031A-0000-0000-C000-000000000046}") +CLSID_ObjrefMoniker = GUID("{00000327-0000-0000-c000-000000000046}") ROTFLAGS_ALLOWANYCLIENT = 1 @@ -52,6 +54,10 @@ _CreateClassMoniker.argtypes = [POINTER(GUID), POINTER(POINTER(IUnknown))] _CreateClassMoniker.restype = HRESULT +_CreateObjrefMoniker = _ole32.CreateObjrefMoniker +_CreateObjrefMoniker.argtypes = [POINTER(IUnknown), POINTER(POINTER(IUnknown))] +_CreateObjrefMoniker.restype = HRESULT + _CreateBindCtx = _ole32.CreateBindCtx _CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IUnknown))] _CreateBindCtx.restype = HRESULT diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index b5c8a2f1..013cca6d 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -21,6 +21,7 @@ MKSYS_FILEMONIKER, MKSYS_GENERICCOMPOSITE, MKSYS_ITEMMONIKER, + MKSYS_OBJREFMONIKER, MKSYS_POINTERMONIKER, ROTFLAGS_ALLOWANYCLIENT, CLSID_AntiMoniker, @@ -28,6 +29,7 @@ CLSID_CompositeMoniker, CLSID_FileMoniker, CLSID_ItemMoniker, + CLSID_ObjrefMoniker, CLSID_PointerMoniker, _CreateAntiMoniker, _CreateBindCtx, @@ -35,6 +37,7 @@ _CreateFileMoniker, _CreateGenericComposite, _CreateItemMoniker, + _CreateObjrefMoniker, _CreatePointerMoniker, _GetRunningObjectTable, ) @@ -101,6 +104,12 @@ def _create_class_moniker(clsid: GUID) -> IMoniker: return mon # type: ignore +def _create_objref_moniker(punk: IUnknown) -> IMoniker: + mon = POINTER(IMoniker)() + _CreateObjrefMoniker(punk, byref(mon)) + return mon # type: ignore + + def _create_bctx() -> IBindCtx: bctx = POINTER(IBindCtx)() # The first parameter is reserved and must be 0. @@ -187,6 +196,15 @@ def test_class(self): self.assertEqual(mon.GetClassID(), CLSID_ClassMoniker) self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + def test_objref(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + mon = _create_objref_moniker(vidctl) + self.assertEqual(mon.IsSystemMoniker(), MKSYS_OBJREFMONIKER) + bctx = _create_bctx() + self.assertTrue(mon.GetDisplayName(bctx, None).startswith("objref:")) + self.assertEqual(mon.GetClassID(), CLSID_ObjrefMoniker) + self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker) + class Test_ComposeWith(unittest.TestCase): def test_file_with_same_type(self): @@ -287,6 +305,18 @@ def test_class_with_same_type(self): left_mon.ComposeWith(right_mon, True) self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_objref_with_same_type(self): + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + left_mon = _create_objref_moniker(vidctl) + right_mon = _create_objref_moniker(vidctl) + self.assertEqual( + left_mon.ComposeWith(right_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + left_mon.ComposeWith(right_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + class Test_IsEqual(unittest.TestCase): def test_item(self): From eed0ead69e6ef0c592497cd5a415d92544945ff3 Mon Sep 17 00:00:00 2001 From: junkmd Date: Wed, 11 Feb 2026 13:46:38 +0900 Subject: [PATCH 4/5] test: Add more Anti-Moniker behavior and composition tests. --- comtypes/test/test_moniker.py | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index 013cca6d..15ba136b 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -266,6 +266,47 @@ def test_anti_with_item(self): self.assertFalse(item_mon.ComposeWith(anti_mon, False)) self.assertFalse(item_mon.ComposeWith(anti_mon, True)) + def test_anti_with_pointer(self): + anti_mon = _create_anti_moniker() + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + pointer_mon = _create_pointer_moniker(vidctl) + self.assertFalse(pointer_mon.ComposeWith(anti_mon, False)) + self.assertFalse(pointer_mon.ComposeWith(anti_mon, True)) + self.assertEqual( + anti_mon.ComposeWith(pointer_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + anti_mon.ComposeWith(pointer_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + + def test_anti_with_class(self): + anti_mon = _create_anti_moniker() + class_mon = _create_class_moniker(GUID.create_new()) + self.assertFalse(class_mon.ComposeWith(anti_mon, False)) + self.assertFalse(class_mon.ComposeWith(anti_mon, True)) + self.assertEqual( + anti_mon.ComposeWith(class_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + anti_mon.ComposeWith(class_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + + def test_anti_with_objref(self): + anti_mon = _create_anti_moniker() + vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl) + objref_mon = _create_objref_moniker(vidctl) + self.assertFalse(objref_mon.ComposeWith(anti_mon, False)) + self.assertFalse(objref_mon.ComposeWith(anti_mon, True)) + self.assertEqual( + anti_mon.ComposeWith(objref_mon, False).GetClassID(), + CLSID_CompositeMoniker, + ) + with self.assertRaises(COMError) as cm: + anti_mon.ComposeWith(objref_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_item_with_same_type(self): left_id = str(GUID.create_new()) left_mon = _create_item_moniker("!", left_id) From a7aa3326af09f77831cc71b9f0b3160b3e37d346 Mon Sep 17 00:00:00 2001 From: junkmd Date: Wed, 11 Feb 2026 13:46:38 +0900 Subject: [PATCH 5/5] test: Add comprehensive moniker composition tests. This commit introduces new test cases for the `ComposeWith` method. The tests cover various composition scenarios, including: - Composing generic composite monikers with identical types. - Composing generic composite monikers with item monikers. - Composing anti-monikers with generic composite monikers. --- comtypes/test/test_moniker.py | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/comtypes/test/test_moniker.py b/comtypes/test/test_moniker.py index 15ba136b..5a81684d 100644 --- a/comtypes/test/test_moniker.py +++ b/comtypes/test/test_moniker.py @@ -207,6 +207,58 @@ def test_objref(self): class Test_ComposeWith(unittest.TestCase): + def test_generic_composite_with_same_type(self): + item_id1 = str(GUID.create_new()) + item_id2 = str(GUID.create_new()) + item_id3 = str(GUID.create_new()) + item_id4 = str(GUID.create_new()) + left_mon = _create_generic_composite( + _create_item_moniker("!", item_id1), + _create_item_moniker("!", item_id2), + ) + right_mon = _create_generic_composite( + _create_item_moniker("!", item_id3), + _create_item_moniker("!", item_id4), + ) + comp_mon = left_mon.ComposeWith(right_mon, False) + self.assertEqual(comp_mon.GetClassID(), CLSID_CompositeMoniker) + self.assertEqual( + comp_mon.GetDisplayName(_create_bctx(), None), + f"!{item_id1}!{item_id2}!{item_id3}!{item_id4}", + ) + with self.assertRaises(COMError) as cm: + left_mon.ComposeWith(right_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + + def test_generic_composite_with_item(self): + item_id1 = str(GUID.create_new()) + item_id2 = str(GUID.create_new()) + item_id3 = str(GUID.create_new()) + orig_mon = _create_generic_composite( + _create_item_moniker("!", item_id1), + _create_item_moniker("!", item_id2), + ) + item_mon = _create_item_moniker("!", item_id3) + bctx = _create_bctx() + comp_with_item = orig_mon.ComposeWith(item_mon, False) + self.assertEqual(comp_with_item.GetClassID(), CLSID_CompositeMoniker) + self.assertEqual( + comp_with_item.GetDisplayName(bctx, None), + f"!{item_id1}!{item_id2}!{item_id3}", + ) + item_with_comp = item_mon.ComposeWith(orig_mon, False) + self.assertEqual(item_with_comp.GetClassID(), CLSID_CompositeMoniker) + self.assertEqual( + item_with_comp.GetDisplayName(bctx, None), + f"!{item_id3}!{item_id1}!{item_id2}", + ) + with self.assertRaises(COMError) as cm: + orig_mon.ComposeWith(item_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + with self.assertRaises(COMError) as cm: + item_mon.ComposeWith(orig_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_file_with_same_type(self): with tempfile.TemporaryDirectory() as t: tmpdir = Path(t) @@ -234,6 +286,29 @@ def test_anti_with_same_type(self): left_mon.ComposeWith(right_mon, True) self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_anti_with_generic_composite(self): + item_id1 = str(GUID.create_new()) + item_id2 = str(GUID.create_new()) + orig_mon = _create_generic_composite( + _create_item_moniker("!", item_id1), + _create_item_moniker("!", item_id2), + ) + bctx = _create_bctx() + comp_with_anti = orig_mon.ComposeWith(_create_anti_moniker(), False) + self.assertEqual(comp_with_anti.GetClassID(), CLSID_ItemMoniker) + self.assertEqual(comp_with_anti.GetDisplayName(bctx, None), f"!{item_id1}") + anti_with_comp = _create_anti_moniker().ComposeWith(orig_mon, False) + self.assertEqual(anti_with_comp.GetClassID(), CLSID_CompositeMoniker) + self.assertEqual( + anti_with_comp.GetDisplayName(bctx, None), f"\\..!{item_id1}!{item_id2}" + ) + with self.assertRaises(COMError) as cm: + orig_mon.ComposeWith(_create_anti_moniker(), True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + with self.assertRaises(COMError) as cm: + _create_anti_moniker().ComposeWith(orig_mon, True) + self.assertEqual(cm.exception.hresult, MK_E_NEEDGENERIC) + def test_anti_with_file(self): anti_mon = _create_anti_moniker() with tempfile.TemporaryDirectory() as t: