Skip to content

Commit 3ff9669

Browse files
authored
Enhance and refactor COM moniker and binding context tests (#902)
* 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`. * test: Copy the existing test module. * 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.
1 parent 1f5c5d5 commit 3ff9669

File tree

4 files changed

+181
-36
lines changed

4 files changed

+181
-36
lines changed

comtypes/test/monikers_helper.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from ctypes import HRESULT, POINTER, OleDLL, c_wchar_p
2+
from ctypes.wintypes import DWORD
3+
4+
from comtypes import GUID, IUnknown
5+
6+
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-mksys
7+
MKSYS_ITEMMONIKER = 4
8+
9+
CLSID_AntiMoniker = GUID("{00000305-0000-0000-c000-000000000046}")
10+
CLSID_ItemMoniker = GUID("{00000304-0000-0000-c000-000000000046}")
11+
12+
ROTFLAGS_ALLOWANYCLIENT = 1
13+
14+
LPOLESTR = LPCOLESTR = c_wchar_p
15+
16+
_ole32 = OleDLL("ole32")
17+
18+
_CreateItemMoniker = _ole32.CreateItemMoniker
19+
_CreateItemMoniker.argtypes = [LPCOLESTR, LPCOLESTR, POINTER(POINTER(IUnknown))]
20+
_CreateItemMoniker.restype = HRESULT
21+
22+
_CreateBindCtx = _ole32.CreateBindCtx
23+
_CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IUnknown))]
24+
_CreateBindCtx.restype = HRESULT
25+
26+
_GetRunningObjectTable = _ole32.GetRunningObjectTable
27+
_GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IUnknown))]
28+
_GetRunningObjectTable.restype = HRESULT
29+
30+
# Common COM Errors from Moniker/Binding Context operations
31+
MK_E_UNAVAILABLE = -2147221021 # 0x800401E3

comtypes/test/test_bctx.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import contextlib
2+
import unittest
3+
from _ctypes import COMError
4+
from ctypes import POINTER, byref
5+
6+
from comtypes import GUID, hresult
7+
from comtypes.client import CreateObject, GetModule
8+
from comtypes.test.monikers_helper import (
9+
ROTFLAGS_ALLOWANYCLIENT,
10+
_CreateBindCtx,
11+
_CreateItemMoniker,
12+
_GetRunningObjectTable,
13+
)
14+
15+
with contextlib.redirect_stdout(None): # supress warnings
16+
GetModule("msvidctl.dll")
17+
from comtypes.gen import MSVidCtlLib as msvidctl
18+
from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable
19+
20+
21+
def _create_item_moniker(delim: str, item: str) -> IMoniker:
22+
mon = POINTER(IMoniker)()
23+
_CreateItemMoniker(delim, item, byref(mon))
24+
return mon # type: ignore
25+
26+
27+
def _create_bctx() -> IBindCtx:
28+
bctx = POINTER(IBindCtx)()
29+
# The first parameter is reserved and must be 0.
30+
_CreateBindCtx(0, byref(bctx))
31+
return bctx # type: ignore
32+
33+
34+
def _create_rot() -> IRunningObjectTable:
35+
rot = POINTER(IRunningObjectTable)()
36+
# The first parameter is reserved and must be 0.
37+
_GetRunningObjectTable(0, byref(rot))
38+
return rot # type: ignore
39+
40+
41+
class Test_EnumObjectParam(unittest.TestCase):
42+
def test_cannot_call(self):
43+
bctx = _create_bctx()
44+
with self.assertRaises(COMError) as cm:
45+
# calling `EnumObjectParam` results in a return value of E_NOTIMPL.
46+
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers
47+
bctx.EnumObjectParam()
48+
self.assertEqual(cm.exception.hresult, hresult.E_NOTIMPL)
49+
50+
51+
class Test_GetRunningObjectTable(unittest.TestCase):
52+
def test_returns_rot(self):
53+
vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl)
54+
item_id = str(GUID.create_new())
55+
mon = _create_item_moniker("!", item_id)
56+
bctx = _create_bctx()
57+
# Before registering: should NOT be running
58+
rot_from_bctx = bctx.GetRunningObjectTable()
59+
self.assertIsInstance(rot_from_bctx, IRunningObjectTable)
60+
rot_from_func = _create_rot()
61+
dw_reg = rot_from_func.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon)
62+
# After registering: should be running
63+
self.assertEqual(rot_from_bctx.IsRunning(mon), hresult.S_OK)
64+
rot_from_func.Revoke(dw_reg)
65+
# After revoking: should NOT be running again
66+
self.assertEqual(rot_from_bctx, rot_from_func)
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
11
import contextlib
22
import unittest
3-
from _ctypes import COMError
4-
from ctypes import HRESULT, POINTER, OleDLL, byref, c_wchar_p
5-
from ctypes.wintypes import DWORD
3+
from ctypes import POINTER, byref
64

75
from comtypes import GUID, hresult
86
from comtypes.client import CreateObject, GetModule
7+
from comtypes.test.monikers_helper import (
8+
MKSYS_ITEMMONIKER,
9+
ROTFLAGS_ALLOWANYCLIENT,
10+
CLSID_AntiMoniker,
11+
CLSID_ItemMoniker,
12+
_CreateBindCtx,
13+
_CreateItemMoniker,
14+
_GetRunningObjectTable,
15+
)
916

1017
with contextlib.redirect_stdout(None): # supress warnings
1118
GetModule("msvidctl.dll")
1219
from comtypes.gen import MSVidCtlLib as msvidctl
1320
from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable
1421

15-
MKSYS_ITEMMONIKER = 4
16-
ROTFLAGS_ALLOWANYCLIENT = 1
17-
LPOLESTR = LPCOLESTR = c_wchar_p
18-
19-
_ole32 = OleDLL("ole32")
20-
21-
_CreateItemMoniker = _ole32.CreateItemMoniker
22-
_CreateItemMoniker.argtypes = [LPCOLESTR, LPCOLESTR, POINTER(POINTER(IMoniker))]
23-
_CreateItemMoniker.restype = HRESULT
24-
25-
_CreateBindCtx = _ole32.CreateBindCtx
26-
_CreateBindCtx.argtypes = [DWORD, POINTER(POINTER(IBindCtx))]
27-
_CreateBindCtx.restype = HRESULT
28-
29-
_GetRunningObjectTable = _ole32.GetRunningObjectTable
30-
_GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IRunningObjectTable))]
31-
_GetRunningObjectTable.restype = HRESULT
32-
3322

3423
def _create_item_moniker(delim: str, item: str) -> IMoniker:
3524
mon = POINTER(IMoniker)()
@@ -51,34 +40,29 @@ def _create_rot() -> IRunningObjectTable:
5140
return rot # type: ignore
5241

5342

54-
class Test_IMoniker(unittest.TestCase):
55-
def test_IsSystemMoniker(self):
43+
class Test_IsSystemMoniker_GetDisplayName_Inverse(unittest.TestCase):
44+
def test_item(self):
5645
item_id = str(GUID.create_new())
5746
mon = _create_item_moniker("!", item_id)
5847
self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER)
59-
60-
61-
class Test_IBindCtx(unittest.TestCase):
62-
def test_EnumObjectParam(self):
6348
bctx = _create_bctx()
64-
with self.assertRaises(COMError) as err_ctx:
65-
# calling `EnumObjectParam` results in a return value of E_NOTIMPL.
66-
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ibindctx-enumobjectparam#notes-to-callers
67-
bctx.EnumObjectParam()
68-
self.assertEqual(err_ctx.exception.hresult, hresult.E_NOTIMPL)
49+
self.assertEqual(mon.GetDisplayName(bctx, None), f"!{item_id}")
50+
self.assertEqual(mon.GetClassID(), CLSID_ItemMoniker)
51+
self.assertEqual(mon.Inverse().GetClassID(), CLSID_AntiMoniker)
6952

7053

71-
class Test_IRunningObjectTable(unittest.TestCase):
72-
def test_register_and_revoke_item_moniker(self):
54+
class Test_IsRunning(unittest.TestCase):
55+
def test_item(self):
7356
vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl)
7457
item_id = str(GUID.create_new())
7558
mon = _create_item_moniker("!", item_id)
7659
rot = _create_rot()
7760
bctx = _create_bctx()
61+
# Before registering: should NOT be running
7862
self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE)
7963
dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon)
64+
# After registering: should be running
8065
self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK)
81-
self.assertEqual(f"!{item_id}", mon.GetDisplayName(bctx, None))
82-
self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl)
8366
rot.Revoke(dw_reg)
67+
# After revoking: should NOT be running again
8468
self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE)

comtypes/test/test_rot.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import contextlib
2+
import unittest
3+
from _ctypes import COMError
4+
from ctypes import POINTER, byref
5+
6+
from comtypes import GUID, hresult
7+
from comtypes.client import CreateObject, GetModule
8+
from comtypes.test.monikers_helper import (
9+
MK_E_UNAVAILABLE,
10+
ROTFLAGS_ALLOWANYCLIENT,
11+
_CreateBindCtx,
12+
_CreateItemMoniker,
13+
_GetRunningObjectTable,
14+
)
15+
16+
with contextlib.redirect_stdout(None): # supress warnings
17+
GetModule("msvidctl.dll")
18+
from comtypes.gen import MSVidCtlLib as msvidctl
19+
from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable
20+
21+
22+
def _create_item_moniker(delim: str, item: str) -> IMoniker:
23+
mon = POINTER(IMoniker)()
24+
_CreateItemMoniker(delim, item, byref(mon))
25+
return mon # type: ignore
26+
27+
28+
def _create_bctx() -> IBindCtx:
29+
bctx = POINTER(IBindCtx)()
30+
# The first parameter is reserved and must be 0.
31+
_CreateBindCtx(0, byref(bctx))
32+
return bctx # type: ignore
33+
34+
35+
def _create_rot() -> IRunningObjectTable:
36+
rot = POINTER(IRunningObjectTable)()
37+
# The first parameter is reserved and must be 0.
38+
_GetRunningObjectTable(0, byref(rot))
39+
return rot # type: ignore
40+
41+
42+
class Test_Register_Revoke_GetObject_IsRunning(unittest.TestCase):
43+
def test_item(self):
44+
vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl)
45+
item_id = str(GUID.create_new())
46+
mon = _create_item_moniker("!", item_id)
47+
rot = _create_rot()
48+
bctx = _create_bctx()
49+
# Before registering: should NOT be running
50+
with self.assertRaises(COMError) as cm:
51+
rot.GetObject(mon)
52+
self.assertEqual(cm.exception.hresult, MK_E_UNAVAILABLE)
53+
self.assertEqual(rot.IsRunning(mon), hresult.S_FALSE)
54+
# After registering: should be running
55+
dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon)
56+
self.assertEqual(rot.IsRunning(mon), hresult.S_OK)
57+
self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_OK)
58+
self.assertEqual(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl)
59+
rot.Revoke(dw_reg)
60+
# After revoking: should NOT be running again
61+
self.assertEqual(rot.IsRunning(mon), hresult.S_FALSE)
62+
with self.assertRaises(COMError) as cm:
63+
rot.GetObject(mon)
64+
self.assertEqual(cm.exception.hresult, MK_E_UNAVAILABLE)

0 commit comments

Comments
 (0)