diff --git a/comtypes/test/monikers_helper.py b/comtypes/test/monikers_helper.py new file mode 100644 index 000000000..6ad3671b5 --- /dev/null +++ b/comtypes/test/monikers_helper.py @@ -0,0 +1,31 @@ +from ctypes import HRESULT, POINTER, OleDLL, c_wchar_p +from ctypes.wintypes import DWORD + +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 + +_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 + +# 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 new file mode 100644 index 000000000..adb88ba89 --- /dev/null +++ b/comtypes/test/test_bctx.py @@ -0,0 +1,66 @@ +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 ( + 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_EnumObjectParam(unittest.TestCase): + def test_cannot_call(self): + bctx = _create_bctx() + 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(cm.exception.hresult, hresult.E_NOTIMPL) + + +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) + bctx = _create_bctx() + # 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_monikers.py b/comtypes/test/test_moniker.py similarity index 52% rename from comtypes/test/test_monikers.py rename to comtypes/test/test_moniker.py index 6910e0840..f4999c745 100644 --- a/comtypes/test/test_monikers.py +++ b/comtypes/test/test_moniker.py @@ -1,35 +1,24 @@ 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, + CLSID_AntiMoniker, + CLSID_ItemMoniker, + _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)() @@ -51,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 new file mode 100644 index 000000000..88813ad11 --- /dev/null +++ b/comtypes/test/test_rot.py @@ -0,0 +1,64 @@ +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 ( + MK_E_UNAVAILABLE, + 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_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() + # 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(rot.GetObject(mon).QueryInterface(msvidctl.IMSVidCtl), vidctl) + rot.Revoke(dw_reg) + # 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)