├── .github
├── FUNDING.yml
└── workflows
│ ├── autofmt.yml
│ └── autotest.yml
├── MANIFEST.in
├── comtypes
├── tools
│ ├── __init__.py
│ └── codegenerator
│ │ ├── __init__.py
│ │ ├── modulenamer.py
│ │ ├── packing.py
│ │ └── comments.py
├── gen
│ └── __init__.py
├── test
│ ├── mylib.tlb
│ ├── urlhist.tlb
│ ├── TestComServer.tlb
│ ├── TestDispServer.tlb
│ ├── runtests.py
│ ├── setup.py
│ ├── test_pump_events.py
│ ├── test_jscript.js
│ ├── test_subinterface.py
│ ├── README.md
│ ├── test_clear_cache.py
│ ├── test_ienum.py
│ ├── test_w_getopt.py
│ ├── test_casesensitivity.py
│ ├── test_showevents.py
│ ├── test_sapi.py
│ ├── test_DISPPARAMS.py
│ ├── test_BSTR.py
│ ├── test_avmc.py
│ ├── test_imfattributes.py
│ ├── mylib.idl
│ ├── test_GUID.py
│ ├── test_urlhistory.py
│ ├── TestDispServer.idl
│ ├── find_memleak.py
│ ├── test_wmi.py
│ ├── test_word.py
│ ├── test_QueryService.py
│ ├── test_persist.py
│ ├── TestComServer.idl
│ ├── test_recordinfo.py
│ ├── mytypelib.idl
│ ├── test_getactiveobj.py
│ ├── test_viewobject.py
│ ├── test_msscript.py
│ ├── test_client_dynamic.py
│ ├── test_findgendir.py
│ ├── test_hresult.py
│ ├── test_dyndispatch.py
│ ├── test_outparam.py
│ ├── test_monikers.py
│ ├── test_dispifc_safearrays.py
│ ├── TestDispServer.py
│ ├── test_dispifc_records.py
│ └── test_midl_safearray_create.py
├── _post_coinit
│ ├── instancemethod.py
│ ├── __init__.py
│ ├── bstr.py
│ └── activeobj.py
├── _tlib_version_checker.py
├── client
│ ├── __init__.py
│ └── _activeobj.py
├── server
│ ├── w_getopt.py
│ ├── __init__.py
│ ├── automation.py
│ └── localserver.py
├── messageloop.py
├── clear_cache.py
├── logutil.py
├── patcher.py
├── stream.py
├── git.py
├── GUID.py
└── _meta.py
├── source
├── AvmcIfc.suo
├── AvmcIfc.tlb
├── Debug
│ ├── AvmcIfc.dll
│ └── AvmcIfc_x64.dll
├── AvmcIfc.vcxproj.user
├── StdAfx.cpp
├── AvmcIfc.def
├── AvmcIfcps.def
├── build-dll.py
├── Avmc.rgs
├── resource.h
├── AvmcIfc.dsw
├── CppTestSrv
│ ├── UTIL.H
│ ├── REGISTRY.H
│ ├── SERVER.CPP
│ ├── CoComtypesDispSafearrayParamTest.h
│ ├── CoComtypesDispRecordParamTest.h
│ ├── UTIL.CPP
│ ├── MAKEFILE
│ ├── CUNKNOWN.CPP
│ ├── CUNKNOWN.H
│ ├── SERVER.IDL
│ └── CFACTORY.H
├── StdAfx.h
├── AvmcIfc.idl
├── AvmcIfc.cpp
├── AvmcIfc.vcxproj.filters
├── Avmc.h
├── AvmcIfc.sln
├── AvmcIfc.rc
└── Avmc.cpp
├── docs
├── requirements.txt
└── source
│ ├── threading.rst
│ ├── mytypelib.idl
│ ├── myserver.py
│ ├── index.rst
│ └── npsupport.rst
├── codecov.yml
├── .bumpversion.cfg
├── SECURITY.md
├── TODO
├── .readthedocs.yaml
├── appveyor.yml
├── .gitignore
├── LICENSE.txt
├── test_pip_install.py
├── admin
└── extract-comtypes-repo
└── pyproject.toml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | tidelift: pypi/comtypes
2 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt
2 | include README.md
3 |
--------------------------------------------------------------------------------
/comtypes/tools/__init__.py:
--------------------------------------------------------------------------------
1 | # the comtypes.tools package
2 |
--------------------------------------------------------------------------------
/comtypes/gen/__init__.py:
--------------------------------------------------------------------------------
1 | # comtypes.gen package, directory for generated files.
2 |
--------------------------------------------------------------------------------
/source/AvmcIfc.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/source/AvmcIfc.suo
--------------------------------------------------------------------------------
/source/AvmcIfc.tlb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/source/AvmcIfc.tlb
--------------------------------------------------------------------------------
/comtypes/test/mylib.tlb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/comtypes/test/mylib.tlb
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx==8.1.3
2 | sphinx-rtd-theme==3.0.2
3 | sphinx-notfound-page==1.0.2
4 |
--------------------------------------------------------------------------------
/source/Debug/AvmcIfc.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/source/Debug/AvmcIfc.dll
--------------------------------------------------------------------------------
/comtypes/test/urlhist.tlb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/comtypes/test/urlhist.tlb
--------------------------------------------------------------------------------
/source/Debug/AvmcIfc_x64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/source/Debug/AvmcIfc_x64.dll
--------------------------------------------------------------------------------
/comtypes/test/TestComServer.tlb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/comtypes/test/TestComServer.tlb
--------------------------------------------------------------------------------
/comtypes/test/TestDispServer.tlb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enthought/comtypes/HEAD/comtypes/test/TestDispServer.tlb
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | informational: true
6 | patch:
7 | default:
8 | informational: true
9 |
--------------------------------------------------------------------------------
/source/AvmcIfc.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | files = comtypes/__init__.py docs/source/index.rst docs/source/conf.py
3 | commit = True
4 | tag_name = {current_version}
5 | current_version = 1.1.4
6 |
7 |
--------------------------------------------------------------------------------
/comtypes/test/runtests.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | import comtypes.test
4 |
5 |
6 | def main():
7 | sys.exit(comtypes.test.run(sys.argv[1:]))
8 |
9 |
10 | if __name__ == "__main__":
11 | main()
12 |
--------------------------------------------------------------------------------
/comtypes/test/setup.py:
--------------------------------------------------------------------------------
1 | # all the unittests can be converted to exe-files.
2 | import glob
3 | from distutils.core import setup
4 |
5 | import py2exe
6 |
7 | setup(name="test_*", console=glob.glob("test_*.py"))
8 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | ## Security contact information
2 |
3 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
4 | Tidelift will coordinate the fix and disclosure.
5 |
--------------------------------------------------------------------------------
/source/StdAfx.cpp:
--------------------------------------------------------------------------------
1 | // stdafx.cpp : source file that includes just the standard includes
2 | // stdafx.pch will be the pre-compiled header
3 | // stdafx.obj will contain the pre-compiled type information
4 |
5 | #include "stdafx.h"
6 |
--------------------------------------------------------------------------------
/comtypes/tools/codegenerator/__init__.py:
--------------------------------------------------------------------------------
1 | from comtypes.tools.codegenerator.modulenamer import ( # noqa
2 | name_friendly_module,
3 | name_wrapper_module,
4 | )
5 | from comtypes.tools.codegenerator.codegenerator import CodeGenerator, version # noqa
6 |
--------------------------------------------------------------------------------
/source/AvmcIfc.def:
--------------------------------------------------------------------------------
1 | ; AvmcIfc.def : Declares the module parameters.
2 |
3 | LIBRARY "AvmcIfc.DLL"
4 |
5 | EXPORTS
6 | DllCanUnloadNow PRIVATE
7 | DllGetClassObject PRIVATE
8 | DllRegisterServer PRIVATE
9 | DllUnregisterServer PRIVATE
10 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | comtypes TODO-list
2 | ==================
3 |
4 | Bugs
5 | ----
6 |
7 | In test_comserver.py, TestLocalServer.test_get_typeinfo() fails with
8 | an access violation. No idea why.
9 |
10 | Features planned
11 | ----------------
12 |
13 | Provide a .cab file for easy installation on Windows CE.
14 |
--------------------------------------------------------------------------------
/source/AvmcIfcps.def:
--------------------------------------------------------------------------------
1 |
2 | LIBRARY "AvmcIfcPS"
3 |
4 | DESCRIPTION 'Proxy/Stub DLL'
5 |
6 | EXPORTS
7 | DllGetClassObject @1 PRIVATE
8 | DllCanUnloadNow @2 PRIVATE
9 | GetProxyDllInfo @3 PRIVATE
10 | DllRegisterServer @4 PRIVATE
11 | DllUnregisterServer @5 PRIVATE
12 |
--------------------------------------------------------------------------------
/docs/source/threading.rst:
--------------------------------------------------------------------------------
1 | #########
2 | Threading
3 | #########
4 |
5 | XXX mention single threaded apartments, multi threaded apartments.
6 | ``sys.coinit_flags``, ``CoInitialize``, ``CoUninitialize`` and so on.
7 | All this is pretty advanced stuff.
8 |
9 | XXX mention threading issues, message loops
10 |
11 |
12 | .. |comtypes| replace:: ``comtypes``
13 |
--------------------------------------------------------------------------------
/source/build-dll.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 |
3 | from jaraco.develop import python
4 | from jaraco.develop import vstudio
5 |
6 | vs = vstudio.VisualStudio.find()
7 | env = vs.get_vcvars_env()
8 | msbuild = python.find_in_path('msbuild.exe', env['Path'])
9 | cmd = [msbuild, 'AvmcIfc.sln', '/p:Configuration=Debug',
10 | '/p:Platform=x64']
11 | subprocess.check_call(cmd, env=env)
12 |
--------------------------------------------------------------------------------
/comtypes/_post_coinit/instancemethod.py:
--------------------------------------------------------------------------------
1 | from ctypes import py_object, pythonapi
2 |
3 | pythonapi.PyInstanceMethod_New.argtypes = [py_object]
4 | pythonapi.PyInstanceMethod_New.restype = py_object
5 | PyInstanceMethod_Type = type(pythonapi.PyInstanceMethod_New(id))
6 |
7 |
8 | def instancemethod(func, inst, cls):
9 | mth = PyInstanceMethod_Type(func)
10 | if inst is None:
11 | return mth
12 | return mth.__get__(inst)
13 |
--------------------------------------------------------------------------------
/comtypes/test/test_pump_events.py:
--------------------------------------------------------------------------------
1 | import gc
2 | import unittest
3 |
4 | from comtypes.client import PumpEvents
5 |
6 |
7 | class PumpEventsTest(unittest.TestCase):
8 | def test_pump_events_doesnt_leak_cycles(self):
9 | gc.collect()
10 | for i in range(3):
11 | PumpEvents(0.05)
12 | ncycles = gc.collect()
13 | self.assertEqual(ncycles, 0)
14 |
15 |
16 | if __name__ == "__main__":
17 | unittest.main()
18 |
--------------------------------------------------------------------------------
/comtypes/test/test_jscript.js:
--------------------------------------------------------------------------------
1 | var d = new ActiveXObject("TestDispServerLib.TestDispServer");
2 |
3 | //WScript.Echo("d.Name");
4 | if (d.Name != "spam, spam, spam")
5 | throw new Error(d.Name);
6 |
7 | //WScript.Echo("d.Name = 'foo'");
8 | d.Name = "foo";
9 |
10 | //WScript.Echo("d.Name");
11 | if (d.Name != "foo")
12 | throw new Error(d.Name);
13 |
14 | //WScript.Echo("d.Eval('1 + 2')");
15 | var result = d.Eval("1 + 2");
16 | if (result != 3)
17 | throw new Error(result);
18 |
--------------------------------------------------------------------------------
/comtypes/test/test_subinterface.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unittest
3 | from ctypes import *
4 |
5 | from comtypes import GUID, IUnknown
6 |
7 |
8 | def test_main():
9 | from test import test_support
10 |
11 | test_support.run_unittest(Test)
12 |
13 |
14 | class Test(unittest.TestCase):
15 | def test_subinterface(self):
16 | class ISub(IUnknown):
17 | pass
18 |
19 | def test_subclass(self):
20 | class X(c_void_p):
21 | pass
22 |
--------------------------------------------------------------------------------
/source/Avmc.rgs:
--------------------------------------------------------------------------------
1 | HKCR
2 | {
3 | AvmcIfc.Avmc.1 = s 'Avmc Class'
4 | {
5 | CLSID = s '{41BDBDFC-A848-4523-A149-ADD3AE1E6D84}'
6 | }
7 | AvmcIfc.Avmc = s 'Avmc Class'
8 | {
9 | CLSID = s '{41BDBDFC-A848-4523-A149-ADD3AE1E6D84}'
10 | }
11 | NoRemove CLSID
12 | {
13 | ForceRemove {41BDBDFC-A848-4523-A149-ADD3AE1E6D84} = s 'Avmc Class'
14 | {
15 | ProgID = s 'AvmcIfc.Avmc.1'
16 | VersionIndependentProgID = s 'AvmcIfc.Avmc'
17 | InprocServer32 = s '%MODULE%'
18 | {
19 | val ThreadingModel = s 'both'
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/docs/source/mytypelib.idl:
--------------------------------------------------------------------------------
1 | import "oaidl.idl";
2 | import "ocidl.idl";
3 |
4 | [
5 | uuid(11C65963-BD45-4B56-A06E-1610532B8613),
6 | dual,
7 | oleautomation
8 | ]
9 | interface IMyInterface : IDispatch {
10 | HRESULT MyMethod([in] INT a, [in] INT b, [out, retval] INT *presult);
11 | }
12 |
13 | [
14 | uuid(F0D8338A-BDC1-45D7-A14F-27D64E7BCA18)
15 | ]
16 | library MyTypeLib
17 | {
18 | importlib("stdole2.tlb");
19 |
20 | [uuid(FBA0A6D0-B775-40D7-924A-B593B6EFA091)]
21 | coclass MyObject {
22 | [default] interface IMyInterface;
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/source/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Developer Studio generated include file.
3 | // Used by AvmcIfc.rc
4 | //
5 | #define IDS_PROJNAME 100
6 | #define IDS_AVMC_DESC 101
7 | #define IDR_Avmc 102
8 |
9 | // Next default values for new objects
10 | //
11 | #ifdef APSTUDIO_INVOKED
12 | #ifndef APSTUDIO_READONLY_SYMBOLS
13 | #define _APS_NEXT_RESOURCE_VALUE 201
14 | #define _APS_NEXT_COMMAND_VALUE 32768
15 | #define _APS_NEXT_CONTROL_VALUE 201
16 | #define _APS_NEXT_SYMED_VALUE 103
17 | #endif
18 | #endif
19 |
--------------------------------------------------------------------------------
/source/AvmcIfc.dsw:
--------------------------------------------------------------------------------
1 | Microsoft Developer Studio Workspace File, Format Version 6.00
2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
3 |
4 | ###############################################################################
5 |
6 | Project: "AvmcIfc"=.\AvmcIfc.dsp - Package Owner=<4>
7 |
8 | Package=<5>
9 | {{{
10 | }}}
11 |
12 | Package=<4>
13 | {{{
14 | }}}
15 |
16 | ###############################################################################
17 |
18 | Global:
19 |
20 | Package=<5>
21 | {{{
22 | }}}
23 |
24 | Package=<3>
25 | {{{
26 | }}}
27 |
28 | ###############################################################################
29 |
30 |
--------------------------------------------------------------------------------
/comtypes/test/README.md:
--------------------------------------------------------------------------------
1 | Running tests
2 | -------------
3 | From the projects root directory, run:
4 |
5 | python -m unittest discover -s ./comtypes/test -t comtypes\test
6 |
7 | Or, from PROJECT_ROOT/comtypes/test:
8 |
9 | python -m unittest discover
10 |
11 | TODO
12 | ----
13 |
14 | - [ ] Look at every skipped test and see if it can be fixed and made runnable as a regular
15 | unit test.
16 | - [ ] Remove the custom test runner stuff. See `comtypes/test/__init__.py`
17 | and `. /settup.py` for details.
18 | - [ ] If python 2.whatever is going to be supported we need to set up tox or something
19 | to run the tests on python 3 and python 2.
20 |
--------------------------------------------------------------------------------
/source/CppTestSrv/UTIL.H:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | #ifndef __Util_h__
10 | #define __Util_h__
11 |
12 | //
13 | // Util.h - Shared utilities
14 | //
15 | #include
16 |
17 | namespace Util
18 | {
19 | void Trace(const char* szLabel, const char* szText, HRESULT hr) ;
20 |
21 | void ErrorMessage(HRESULT hr) ;
22 | } ;
23 |
24 |
25 | //
26 | // Overloaded insertion operator for converting from
27 | // Unicode (wchar_t) to non-Unicode.
28 | //
29 | std::ostream& operator<< ( std::ostream& os, const wchar_t* wsz ) ;
30 |
31 | #endif // __Util_h__
--------------------------------------------------------------------------------
/comtypes/_tlib_version_checker.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 |
5 | def _check_version(actual, tlib_cached_mtime=None):
6 | from comtypes.tools.codegenerator import version as required
7 |
8 | if actual != required:
9 | raise ImportError("Wrong version")
10 | if not hasattr(sys, "frozen"):
11 | g = sys._getframe(1).f_globals
12 | tlb_path = g.get("typelib_path")
13 | try:
14 | tlib_curr_mtime = os.stat(tlb_path).st_mtime
15 | except (OSError, TypeError):
16 | return
17 | if not tlib_cached_mtime or abs(tlib_curr_mtime - tlib_cached_mtime) >= 1:
18 | raise ImportError("Typelib different than module")
19 |
--------------------------------------------------------------------------------
/.github/workflows/autofmt.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request:
3 | branches: [main]
4 |
5 | jobs:
6 | formatter:
7 | name: auto-formatter
8 | runs-on: windows-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v4
12 | - name: Set up Python
13 | uses: actions/setup-python@v5
14 | with:
15 | python-version: 3.9
16 | - name: Install ruff
17 | run: pip install ruff==0.6.9
18 | - name: Check format
19 | run: python -m ruff format comtypes/. --check --diff
20 | - name: Check lint
21 | run: python -m ruff check --output-format=github comtypes/.
22 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS, Python version, and other tools you might need
8 | build:
9 | os: ubuntu-24.04
10 | tools:
11 | python: "3.13"
12 |
13 | # Build documentation in the "docs/" directory with Sphinx
14 | sphinx:
15 | configuration: docs/source/conf.py
16 |
17 | # TODO: Setup recommended configurations.
18 | # Optionally, but recommended,
19 | # declare the Python requirements required to build your documentation
20 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
21 | python:
22 | install:
23 | - requirements: docs/requirements.txt
24 |
--------------------------------------------------------------------------------
/comtypes/_post_coinit/__init__.py:
--------------------------------------------------------------------------------
1 | """comtypes._post_coinit
2 |
3 | This subpackage contains symbols that should be imported into `comtypes/__init__.py`
4 | after `CoInitializeEx` is defined and called, and symbols that can be defined in
5 | any order during the initialization of the `comtypes` package.
6 |
7 | These were previously defined in `comtypes/__init__.py`, but due to the codebase
8 | of the file becoming bloated, reducing the ease of changes and increasing
9 | cognitive load, they have been moved here.
10 |
11 | This subpackage is called simultaneously with the initialization of `comtypes`.
12 | So it is necessary to maintain minimal settings to keep the lightweight action
13 | when the package is initialized.
14 | """
15 |
16 | from comtypes._post_coinit.unknwn import _shutdown # noqa
17 |
--------------------------------------------------------------------------------
/docs/source/myserver.py:
--------------------------------------------------------------------------------
1 | import comtypes
2 | import comtypes.server.localserver
3 |
4 | from comtypes.client import GetModule
5 | GetModule("mytypelib.tlb")
6 |
7 | from comtypes.gen.MyTypeLib import MyObject
8 |
9 | class MyObjectImpl(MyObject):
10 | # registry entries
11 | _reg_threading_ = "Both"
12 | _reg_progid_ = "MyTypeLib.MyObject.1"
13 | _reg_novers_progid_ = "MyTypeLib.MyObject"
14 | _reg_desc_ = "Simple COM server for testing"
15 | _reg_clsctx_ = comtypes.CLSCTX_INPROC_SERVER | comtypes.CLSCTX_LOCAL_SERVER
16 | _regcls_ = comtypes.server.localserver.REGCLS_MULTIPLEUSE
17 |
18 | def MyMethod(self, a, b):
19 | return a + b
20 |
21 | if __name__ == "__main__":
22 | from comtypes.server.register import UseCommandLine
23 | UseCommandLine(MyObjectImpl)
24 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2022
2 | build: off
3 | max_jobs: 3
4 |
5 | init:
6 | - git config --global core.autocrlf input
7 |
8 | shallow_clone: true
9 |
10 | environment:
11 | matrix:
12 | - py: Python37
13 | - py: Python37-x64
14 | - py: Python38
15 | - py: Python38-x64
16 | - py: Python39
17 | - py: Python39-x64
18 | - py: Python310
19 | - py: Python310-x64
20 | - py: Python311
21 | - py: Python311-x64
22 | - py: Python312
23 | - py: Python312-x64
24 |
25 | test_script:
26 | - C:\%py%\Scripts\pip.exe install --upgrade setuptools
27 | - C:\%py%\python.exe -m pip install .
28 | - C:\%py%\Scripts\pip.exe uninstall comtypes -y
29 | - C:\%py%\python.exe test_pip_install.py
30 | - C:\%py%\python.exe -m unittest discover -v -s ./comtypes/test -t comtypes\test
31 |
--------------------------------------------------------------------------------
/comtypes/tools/codegenerator/modulenamer.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import comtypes
4 | from comtypes import typeinfo
5 |
6 |
7 | def name_wrapper_module(tlib: typeinfo.ITypeLib) -> str:
8 | """Determine the name of a typelib wrapper module"""
9 | libattr = tlib.GetLibAttr()
10 | guid = str(libattr.guid)[1:-1].replace("-", "_")
11 | modname = f"_{guid}_{libattr.lcid}_{libattr.wMajorVerNum}_{libattr.wMinorVerNum}"
12 | return f"comtypes.gen.{modname}"
13 |
14 |
15 | def name_friendly_module(tlib: typeinfo.ITypeLib) -> Optional[str]:
16 | """Determine the friendly-name of a typelib module.
17 | If cannot get friendly-name from typelib, returns `None`.
18 | """
19 | try:
20 | modulename = tlib.GetDocumentation(-1)[0]
21 | except comtypes.COMError:
22 | return
23 | return f"comtypes.gen.{modulename}"
24 |
--------------------------------------------------------------------------------
/comtypes/test/test_clear_cache.py:
--------------------------------------------------------------------------------
1 | """
2 | Test for the ``comtypes.clear_cache`` module.
3 | """
4 |
5 | import contextlib
6 | import runpy
7 | from unittest import TestCase
8 | from unittest.mock import call, patch
9 |
10 | from comtypes.client import _find_gen_dir
11 |
12 |
13 | class ClearCacheTestCase(TestCase):
14 | # we patch sys.stdout so unittest doesn't show the print statements
15 |
16 | @patch("sys.argv", ["clear_cache.py", "-y"])
17 | @patch("shutil.rmtree")
18 | def test_clear_cache(self, mock_rmtree):
19 | with contextlib.redirect_stdout(None):
20 | runpy.run_module("comtypes.clear_cache", {}, "__main__")
21 |
22 | # because we don't actually delete anything, _find_gen_dir() will
23 | # give the same answer every time we call it
24 | self.assertEqual(
25 | mock_rmtree.call_args_list, [call(_find_gen_dir()) for _ in range(2)]
26 | )
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # comtypes specific
2 | comtypes/__pycache__/*.pyc
3 | comtypes/client/__pycache__/*.pyc
4 | comtypes/gen/
5 | !comtypes/gen/__init__.py
6 | comtypes/tools/__pycache__/*.pyc
7 | comtypes.egg-info/*
8 | custom location/*
9 |
10 | # Byte-compiled / optimized / DLL files
11 | __pycache__/
12 | *.py[cod]
13 | *$py.class
14 |
15 | # PyCharm project data folder
16 | .idea
17 |
18 | # Distribution / packaging
19 | .Python
20 | env/
21 | .venv/
22 | build/
23 | develop-eggs/
24 | dist/
25 | downloads/
26 | eggs/
27 | .eggs/
28 | lib/
29 | lib64/
30 | parts/
31 | sdist/
32 | var/
33 | *.egg-info/
34 | .installed.cfg
35 | *.egg
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *,cover
50 |
51 |
52 | # Sphinx documentation
53 | docs/_build/
54 |
55 |
56 |
--------------------------------------------------------------------------------
/comtypes/test/test_ienum.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import unittest as ut
3 | from ctypes import POINTER
4 |
5 | import comtypes.client
6 | from comtypes import GUID
7 |
8 |
9 | class Test_IEnum(ut.TestCase):
10 | def test_ienum(self):
11 | with contextlib.redirect_stdout(None): # supress warnings, see test_client.py
12 | comtypes.client.GetModule("msvidctl.dll")
13 | from comtypes.gen import MSVidCtlLib as vidlib
14 |
15 | CLSID_AviSplitter = GUID("{1b544c20-fd0b-11ce-8c63-00aa0044b51e}")
16 |
17 | avisplitter = comtypes.client.CreateObject(
18 | CLSID_AviSplitter,
19 | interface=vidlib.IBaseFilter,
20 | )
21 | pinEnum = avisplitter.EnumPins()
22 | self.assertIsInstance(pinEnum, POINTER(vidlib.IEnumPins))
23 | # make sure pinEnum is iterable and non-empty
24 | pins = list(pinEnum)
25 | self.assertGreater(len(pins), 0)
26 |
27 |
28 | if __name__ == "__main__":
29 | ut.main()
30 |
--------------------------------------------------------------------------------
/source/StdAfx.h:
--------------------------------------------------------------------------------
1 | // stdafx.h : include file for standard system include files,
2 | // or project specific include files that are used frequently,
3 | // but are changed infrequently
4 |
5 | #if !defined(AFX_STDAFX_H__E3A0B645_B548_4701_B164_973567C5C219__INCLUDED_)
6 | #define AFX_STDAFX_H__E3A0B645_B548_4701_B164_973567C5C219__INCLUDED_
7 |
8 | #if _MSC_VER > 1000
9 | #pragma once
10 | #endif // _MSC_VER > 1000
11 |
12 | #define STRICT
13 | #ifndef _WIN32_WINNT
14 | #define _WIN32_WINNT 0x0501
15 | #endif
16 | #define _ATL_APARTMENT_THREADED
17 |
18 | #include
19 | //You may derive a class from CComModule and use it if you want to override
20 | //something, but do not change the name of _Module
21 | extern CComModule _Module;
22 | #include
23 |
24 | //{{AFX_INSERT_LOCATION}}
25 | // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
26 |
27 | #endif // !defined(AFX_STDAFX_H__E3A0B645_B548_4701_B164_973567C5C219__INCLUDED)
28 |
--------------------------------------------------------------------------------
/comtypes/test/test_w_getopt.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from comtypes.server.w_getopt import GetoptError, w_getopt
4 |
5 |
6 | class TestCase(unittest.TestCase):
7 | def test_1(self):
8 | args = "-embedding spam /RegServer foo /UnregSERVER blabla".split()
9 | opts, args = w_getopt(args, "regserver unregserver embedding".split())
10 | self.assertEqual(
11 | opts, [("embedding", ""), ("regserver", ""), ("unregserver", "")]
12 | )
13 | self.assertEqual(args, ["spam", "foo", "blabla"])
14 |
15 | def test_2(self):
16 | args = "/TLB Hello.Tlb HELLO.idl".split()
17 | opts, args = w_getopt(args, ["tlb:"])
18 | self.assertEqual(opts, [("tlb", "Hello.Tlb")])
19 | self.assertEqual(args, ["HELLO.idl"])
20 |
21 | def test_3(self):
22 | # Invalid option
23 | with self.assertRaises(GetoptError):
24 | w_getopt("/TLIB hello.tlb hello.idl".split(), ["tlb:"])
25 |
26 | def test_4(self):
27 | # Missing argument
28 | with self.assertRaises(GetoptError):
29 | w_getopt("/TLB".split(), ["tlb:"])
30 |
--------------------------------------------------------------------------------
/source/CppTestSrv/REGISTRY.H:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | #ifndef __Registry_H__
10 | #define __Registry_H__
11 | //
12 | // Registry.h
13 | // - Helper functions registering and unregistering a component.
14 | //
15 |
16 | // This function will register a component in the Registry.
17 | // The component calls this function from its DllRegisterServer function.
18 | HRESULT RegisterServer(HMODULE hModule,
19 | const CLSID& clsid,
20 | LPCWSTR szFriendlyName,
21 | LPCWSTR szVerIndProgID,
22 | LPCWSTR szProgID,
23 | const GUID& libid) ;
24 |
25 | // This function will unregister a component. Components
26 | // call this function from their DllUnregisterServer function.
27 | HRESULT UnregisterServer(const CLSID& clsid,
28 | LPCWSTR szVerIndProgID,
29 | LPCWSTR szProgID,
30 | const GUID* libid) ;
31 |
32 | #endif
--------------------------------------------------------------------------------
/comtypes/test/test_casesensitivity.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import unittest
3 |
4 | from comtypes.client import GetModule
5 |
6 | with contextlib.redirect_stdout(None): # supress warnings
7 | GetModule("msvidctl.dll")
8 | from comtypes.gen import MSVidCtlLib as msvidctl
9 |
10 |
11 | class TestCase(unittest.TestCase):
12 | def test(self):
13 | # IDispatch(IUnknown)
14 | # IMSVidDevice(IDispatch)
15 | # IMSVidInputDevice(IMSVidDevice)
16 | # IMSVidPlayback(IMSVidOutputDevice)
17 |
18 | self.assertTrue(issubclass(msvidctl.IMSVidPlayback, msvidctl.IMSVidInputDevice))
19 | self.assertTrue(issubclass(msvidctl.IMSVidInputDevice, msvidctl.IMSVidDevice))
20 |
21 | # names in the base class __map_case__ must also appear in the
22 | # subclass.
23 | for name in msvidctl.IMSVidDevice.__map_case__:
24 | self.assertIn(name, msvidctl.IMSVidInputDevice.__map_case__)
25 | self.assertIn(name, msvidctl.IMSVidPlayback.__map_case__)
26 |
27 | for name in msvidctl.IMSVidInputDevice.__map_case__:
28 | self.assertIn(name, msvidctl.IMSVidPlayback.__map_case__)
29 |
30 |
31 | if __name__ == "__main__":
32 | unittest.main()
33 |
--------------------------------------------------------------------------------
/comtypes/test/test_showevents.py:
--------------------------------------------------------------------------------
1 | import doctest
2 | import unittest
3 | from typing import Optional
4 |
5 |
6 | def load_tests(
7 | loader: unittest.TestLoader, tests: unittest.TestSuite, pattern: Optional[str]
8 | ) -> unittest.TestSuite:
9 | import comtypes.test.test_showevents
10 |
11 | tests.addTests(doctest.DocTestSuite(comtypes.test.test_showevents))
12 | return tests
13 |
14 |
15 | class ShowEventsExamples:
16 | def StdFont_ShowEvents(self):
17 | """
18 | >>> from comtypes.client import CreateObject, GetModule, ShowEvents, PumpEvents
19 | >>> _ = GetModule('scrrun.dll') # generating `Scripting` also generates `stdole`
20 | >>> from comtypes.gen import stdole
21 | >>> font = CreateObject(stdole.StdFont)
22 | >>> conn = ShowEvents(font)
23 | # event found: FontEvents_FontChanged
24 | >>> font.Name = 'Arial'
25 | Event FontEvents_FontChanged(None, 'Name')
26 | >>> font.Italic = True
27 | Event FontEvents_FontChanged(None, 'Italic')
28 | >>> PumpEvents(0.01) # just calling. assertion does in `test_pump_events.py`
29 | >>> conn.disconnect()
30 | """
31 | pass
32 |
33 |
34 | if __name__ == "__main__":
35 | unittest.main()
36 |
--------------------------------------------------------------------------------
/comtypes/test/test_sapi.py:
--------------------------------------------------------------------------------
1 | # http://www.microsoft.com/technet/scriptcenter/funzone/games/sapi.mspx
2 | # ../gen/_C866CA3A_32F7_11D2_9602_00C04F8EE628_0_5_0
3 | # http://thread.gmane.org/gmane.comp.python.ctypes.user/1485
4 |
5 | import os
6 | import tempfile
7 | import unittest
8 |
9 | from comtypes.client import CreateObject
10 |
11 |
12 | class Test(unittest.TestCase):
13 | def test(self, dynamic=False):
14 | engine = CreateObject("SAPI.SpVoice", dynamic=dynamic)
15 | stream = CreateObject("SAPI.SpFileStream", dynamic=dynamic)
16 | from comtypes.gen import SpeechLib
17 |
18 | fd, fname = tempfile.mkstemp(suffix=".wav")
19 | os.close(fd)
20 |
21 | stream.Open(fname, SpeechLib.SSFMCreateForWrite)
22 |
23 | # engine.AudioStream is a propputref property
24 | engine.AudioOutputStream = stream
25 | self.assertEqual(engine.AudioOutputStream, stream)
26 | engine.speak("Hello, World", 0)
27 | stream.Close()
28 | filesize = os.stat(fname).st_size
29 | self.assertTrue(filesize > 100, "filesize only %d bytes" % filesize)
30 | os.unlink(fname)
31 |
32 | def test_dyndisp(self):
33 | return self.test(dynamic=True)
34 |
35 |
36 | if __name__ == "__main__":
37 | unittest.main()
38 |
--------------------------------------------------------------------------------
/comtypes/test/test_DISPPARAMS.py:
--------------------------------------------------------------------------------
1 | import unittest as ut
2 |
3 |
4 | class TestCase(ut.TestCase):
5 | def test(self):
6 | from comtypes.automation import DISPPARAMS, VARIANT
7 |
8 | dp = DISPPARAMS()
9 | dp.rgvarg = (VARIANT * 3)()
10 |
11 | for i in range(3):
12 | self.assertEqual(dp.rgvarg[i].value, None)
13 |
14 | dp.rgvarg[0].value = 42
15 | dp.rgvarg[1].value = "spam"
16 | dp.rgvarg[2].value = "foo"
17 |
18 | # damn, there's still this old bug!
19 |
20 | self.assertEqual(dp.rgvarg[0].value, 42)
21 | # these fail:
22 | # self.failUnlessEqual(dp.rgvarg[1].value, "spam")
23 | # self.failUnlessEqual(dp.rgvarg[2].value, "foo")
24 |
25 | def X_test_2(self):
26 | # basically the same test as above
27 | from comtypes.automation import DISPPARAMS, VARIANT
28 |
29 | args = [42, None, "foo"]
30 |
31 | dp = DISPPARAMS()
32 | dp.rgvarg = (VARIANT * 3)(*list(map(VARIANT, args[::-1])))
33 |
34 | import gc
35 |
36 | gc.collect()
37 |
38 | self.assertEqual(dp.rgvarg[0].value, 42)
39 | self.assertEqual(dp.rgvarg[1].value, "spam")
40 | self.assertEqual(dp.rgvarg[2].value, "foo")
41 |
42 |
43 | if __name__ == "__main__":
44 | ut.main()
45 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | This software is OSI Certified Open Source Software.
2 | OSI Certified is a certification mark of the Open Source Initiative.
3 |
4 | Copyright (c) 2006-2013, Thomas Heller.
5 | Copyright (c) 2014, Comtypes Developers.
6 | All rights reserved.
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
--------------------------------------------------------------------------------
/comtypes/test/test_BSTR.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import WinDLL
3 | from ctypes.wintypes import UINT
4 |
5 | from comtypes import BSTR
6 | from comtypes.test.find_memleak import find_memleak
7 |
8 |
9 | class Test(unittest.TestCase):
10 | def check_leaks(self, func, limit=0):
11 | bytes = find_memleak(func)
12 | self.assertFalse(bytes > limit, f"Leaks {bytes} bytes")
13 |
14 | def test_creation(self):
15 | def doit():
16 | BSTR("abcdef" * 100)
17 |
18 | # It seems this test is unreliable. Sometimes it leaks 4096
19 | # bytes, sometimes not. Try to workaround that...
20 | self.check_leaks(doit, limit=4096)
21 |
22 | def test_from_param(self):
23 | def doit():
24 | BSTR.from_param("abcdef")
25 |
26 | self.check_leaks(doit)
27 |
28 | def test_inargs(self):
29 | oleaut32 = WinDLL("oleaut32")
30 |
31 | SysStringLen = oleaut32.SysStringLen
32 | SysStringLen.argtypes = [BSTR]
33 | SysStringLen.restype = UINT
34 |
35 | self.assertEqual(SysStringLen("abc xyz"), 7)
36 |
37 | def doit():
38 | SysStringLen("abc xyz")
39 | SysStringLen("abc xyz")
40 | SysStringLen(BSTR("abc def"))
41 |
42 | self.check_leaks(doit)
43 |
44 |
45 | if __name__ == "__main__":
46 | unittest.main()
47 |
--------------------------------------------------------------------------------
/comtypes/client/__init__.py:
--------------------------------------------------------------------------------
1 | """comtypes.client - High level client level COM support package."""
2 |
3 | import ctypes
4 | import logging
5 |
6 | from comtypes import RevokeActiveObject, automation # noqa
7 | from comtypes.client import dynamic, lazybind # noqa
8 | from comtypes.client._activeobj import RegisterActiveObject # noqa
9 | from comtypes.client._code_cache import _find_gen_dir
10 | from comtypes.client._constants import Constants # noqa
11 | from comtypes.client._events import GetEvents, PumpEvents, ShowEvents
12 | from comtypes.client._generate import GetModule
13 | from comtypes.client._managing import GetBestInterface, _manage, wrap_outparam # noqa
14 | from comtypes.hresult import * # noqa
15 |
16 | gen_dir = _find_gen_dir()
17 | import comtypes.gen # noqa
18 |
19 | ### for testing
20 | ##gen_dir = None
21 |
22 | logger = logging.getLogger(__name__)
23 |
24 |
25 | # backwards compatibility:
26 | wrap = GetBestInterface
27 |
28 | # Should we do this for POINTER(IUnknown) also?
29 | ctypes.POINTER(automation.IDispatch).__ctypes_from_outparam__ = wrap_outparam # type: ignore
30 |
31 | from comtypes.client._activeobj import GetActiveObject
32 | from comtypes.client._create import (
33 | CoGetObject,
34 | CreateObject,
35 | GetClassObject,
36 | )
37 |
38 | # fmt: off
39 | __all__ = [
40 | "CreateObject", "GetActiveObject", "CoGetObject", "GetEvents",
41 | "ShowEvents", "PumpEvents", "GetModule", "GetClassObject",
42 | ]
43 | # fmt: on
44 |
--------------------------------------------------------------------------------
/comtypes/_post_coinit/bstr.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Callable
2 | from ctypes import WinDLL, _SimpleCData
3 | from typing import TYPE_CHECKING, Any
4 |
5 | if TYPE_CHECKING:
6 | from comtypes import hints # type: ignore
7 |
8 |
9 | _oleaut32 = WinDLL("oleaut32")
10 |
11 | _SysFreeString = _oleaut32.SysFreeString
12 |
13 |
14 | class BSTR(_SimpleCData):
15 | """The windows BSTR data type"""
16 |
17 | _type_ = "X"
18 | _needsfree = False
19 |
20 | def __repr__(self) -> str:
21 | return f"{self.__class__.__name__}({self.value!r})"
22 |
23 | def __ctypes_from_outparam__(self) -> Any:
24 | self._needsfree = True
25 | return self.value
26 |
27 | def __del__(self, _free: Callable[["BSTR"], Any] = _SysFreeString) -> None:
28 | # Free the string if self owns the memory
29 | # or if instructed by __ctypes_from_outparam__.
30 | if self._b_base_ is None or self._needsfree:
31 | _free(self)
32 |
33 | @classmethod
34 | def from_param(cls, value: Any) -> "hints.Self":
35 | """Convert into a foreign function call parameter."""
36 | if isinstance(value, cls):
37 | return value
38 | # Although the builtin SimpleCData.from_param call does the
39 | # right thing, it doesn't ensure that SysFreeString is called
40 | # on destruction.
41 | return cls(value)
42 |
43 |
44 | _SysFreeString.argtypes = [BSTR]
45 | _SysFreeString.restype = None
46 |
--------------------------------------------------------------------------------
/comtypes/test/test_avmc.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from comtypes.client import CreateObject
4 | from comtypes.test.find_memleak import find_memleak
5 |
6 |
7 | @unittest.skip(
8 | "This test does not work. Apparently it's supposed to work with the 'avmc' stuff "
9 | "in comtypes/source, but it doesn't. It's not clear to me why."
10 | )
11 | class Test(unittest.TestCase):
12 | """Test COM records"""
13 |
14 | def test(self):
15 | # The ATL COM dll
16 | avmc = CreateObject("AvmcIfc.Avmc.1")
17 |
18 | # This returns an array (a list) of DeviceInfo records.
19 | devs = avmc.FindAllAvmc()
20 |
21 | self.assertEqual(devs[0].Flags, 12)
22 | self.assertEqual(devs[0].ID, 13)
23 | self.assertEqual(devs[0].LocId, 14)
24 | self.assertEqual(devs[0].Description, "Avmc")
25 | self.assertEqual(devs[0].SerialNumber, "1234")
26 |
27 | self.assertEqual(devs[1].Flags, 22)
28 | self.assertEqual(devs[1].ID, 23)
29 | self.assertEqual(devs[1].LocId, 24)
30 | self.assertEqual(devs[1].Description, "Avmc2")
31 | self.assertEqual(devs[1].SerialNumber, "5678")
32 |
33 | # # Leaks... where?
34 | # def doit():
35 | # avmc.FindAllAvmc()
36 | # self.check_leaks(doit)
37 |
38 | def check_leaks(self, func, limit=0):
39 | bytes = find_memleak(func)
40 | self.assertFalse(bytes > limit, f"Leaks {bytes} bytes")
41 |
42 |
43 | if __name__ == "__main__":
44 | unittest.main()
45 |
--------------------------------------------------------------------------------
/comtypes/test/test_imfattributes.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import unittest as ut
3 | from ctypes import HRESULT, POINTER, WinDLL, c_uint32, pointer
4 |
5 | import comtypes.client
6 | from comtypes import GUID
7 |
8 |
9 | class Test_IMFAttributes(ut.TestCase):
10 | def test_imfattributes(self):
11 | with contextlib.redirect_stdout(None): # supress warnings, see test_client.py
12 | comtypes.client.GetModule("msvidctl.dll")
13 | from comtypes.gen import MSVidCtlLib
14 |
15 | _mfplat = WinDLL("mfplat")
16 |
17 | UINT32 = c_uint32
18 | _MFCreateAttributes = _mfplat.MFCreateAttributes
19 | _MFCreateAttributes.argtypes = [
20 | POINTER(POINTER(MSVidCtlLib.IMFAttributes)),
21 | UINT32,
22 | ]
23 | _MFCreateAttributes.restype = HRESULT
24 |
25 | imf_attrs = POINTER(MSVidCtlLib.IMFAttributes)()
26 | hres = _MFCreateAttributes(pointer(imf_attrs), 2)
27 | self.assertEqual(hres, 0)
28 |
29 | MF_TRANSCODE_ADJUST_PROFILE = GUID("{9c37c21b-060f-487c-a690-80d7f50d1c72}")
30 | set_int_value = 1
31 | # IMFAttributes.SetUINT32() is an example of a function that has a parameter
32 | # without an `in` or `out` direction; see also test_inout_args.py
33 | imf_attrs.SetUINT32(MF_TRANSCODE_ADJUST_PROFILE, set_int_value)
34 | get_int_value = imf_attrs.GetUINT32(MF_TRANSCODE_ADJUST_PROFILE)
35 | self.assertEqual(set_int_value, get_int_value)
36 |
37 |
38 | if __name__ == "__main__":
39 | ut.main()
40 |
--------------------------------------------------------------------------------
/comtypes/server/w_getopt.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Sequence
2 |
3 |
4 | class GetoptError(Exception):
5 | pass
6 |
7 |
8 | def w_getopt(
9 | args: Sequence[str], options: str
10 | ) -> tuple[Sequence[tuple[str, str]], Sequence[str]]:
11 | """A getopt for Windows.
12 |
13 | Options may start with either '-' or '/', the option names may
14 | have more than one letter (/tlb or -RegServer), and option names
15 | are case insensitive.
16 |
17 | Returns two elements, just as getopt.getopt. The first is a list
18 | of (option, value) pairs in the same way getopt.getopt does, but
19 | there is no '-' or '/' prefix to the option name, and the option
20 | name is always lower case. The second is the list of arguments
21 | which do not belong to an option.
22 |
23 | Different from getopt.getopt, a single argument not belonging to an option
24 | does not terminate parsing.
25 | """
26 | opts = []
27 | arguments = []
28 | while args:
29 | if args[0][:1] in "/-":
30 | arg = args[0][1:] # strip the '-' or '/'
31 | arg = arg.lower()
32 |
33 | if arg + ":" in options:
34 | try:
35 | opts.append((arg, args[1]))
36 | except IndexError:
37 | raise GetoptError(f"option '{args[0]}' requires an argument")
38 | args = args[1:]
39 | elif arg in options:
40 | opts.append((arg, ""))
41 | else:
42 | raise GetoptError(f"invalid option '{args[0]}'")
43 | args = args[1:]
44 | else:
45 | arguments.append(args[0])
46 | args = args[1:]
47 |
48 | return opts, arguments
49 |
--------------------------------------------------------------------------------
/comtypes/test/mylib.idl:
--------------------------------------------------------------------------------
1 | import "oaidl.idl";
2 | import "ocidl.idl";
3 | [uuid(f4f74946-4546-44bd-a073-9ea6f9fe78cb)] library TestLib {
4 | [object,
5 | oleautomation,
6 | dual,
7 | uuid(ed978f5f-cc45-4fcc-a7a6-751ffa8dfedd)]
8 | interface IMyInterface : IDispatch {
9 | [id(100), propget] HRESULT Name([out, retval] BSTR *pname);
10 | [id(100), propput] HRESULT Name([in] BSTR name);
11 | [id(101)] HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);
12 | [id(102)] HRESULT MultiInOutArgs([in, out] int *pa, [in, out] int *pb);
13 | HRESULT MultiInOutArgs2([in, out] int *pa, [out] int *pb);
14 | HRESULT MultiInOutArgs3([out] int *pa, [out] int *pb);
15 | HRESULT MultiInOutArgs4([out] int *pa, [in, out] int *pb);
16 | HRESULT GetStackTrace([in] ULONG FrameOffset,
17 | [in, out] INT *Frames,
18 | [in] ULONG FramesSize,
19 | [out, optional] ULONG *FramesFilled);
20 | HRESULT dummy([in] SAFEARRAY(VARIANT *) foo);
21 | HRESULT DoSomething();
22 | HRESULT DoSomethingElse();
23 | }
24 |
25 | [object,
26 | oleautomation,
27 | dual,
28 | uuid(f7c48a90-64ea-4bb8-abf1-b3a3aa996848)]
29 | interface IMyEventInterface : IDispatch {
30 | [id(103)] HRESULT OnSomething();
31 | [id(104)] HRESULT OnSomethingElse([out, retval] int *px);
32 | }
33 |
34 |
35 | [uuid(fa9de8f4-20de-45fc-b079-648572428817)]
36 | coclass MyServer {
37 | [default] interface IMyInterface;
38 | [default, source] interface IMyEventInterface;
39 | };
40 | }
--------------------------------------------------------------------------------
/source/AvmcIfc.idl:
--------------------------------------------------------------------------------
1 | // AvmcIfc.idl : IDL source for AvmcIfc.dll
2 | //
3 |
4 | // This file will be processed by the MIDL tool to
5 | // produce the type library (AvmcIfc.tlb) and marshalling code.
6 |
7 | import "oaidl.idl";
8 | import "ocidl.idl";
9 |
10 | // 1129c4e1-f46a-4b61-a464-406d7df3e516
11 |
12 | [
13 | uuid(6C7A25CB-7938-4BE0-A285-12C616717FDD),
14 | version(1.0),
15 | helpstring("FTDI Device info node")
16 | ]
17 | typedef struct DeviceInfo {
18 | [helpstring("Special case variant")] VARIANT Special;
19 | [helpstring("Name of the variable")] BSTR Name;
20 | [helpstring("Value of the variable")] long Value;
21 | [helpstring("Flags")] long Flags; //ULONG
22 | [helpstring("Device Type")] long Type; //ULONG
23 | [helpstring("Device Id")] long ID; //ULONG
24 | [helpstring("Local Id")] long LocId; //DWORD
25 | [helpstring("Device's Serial Number")] BSTR SerialNumber;
26 | [helpstring("Device's Description")] BSTR Description;
27 | [helpstring("Device current handle")] long ftHandle; //ULONG
28 | } DeviceInfo;
29 |
30 | [
31 | object,
32 | uuid(6C7A25CC-7938-4BE0-A285-12C616717FDD),
33 | dual,
34 | helpstring("IAvmc Interface"),
35 | pointer_default(unique)
36 | ]
37 | interface IAvmc : IDispatch
38 | {
39 | [id(1), helpstring("method FindAllAvmc")] HRESULT FindAllAvmc([out] SAFEARRAY(DeviceInfo) *avmcList);
40 | };
41 |
42 | [
43 | uuid(70577167-ED71-4977-B719-2C40C6DD8E1D),
44 | version(1.0),
45 | helpstring("AvmcIfc 1.0 Type Library")
46 | ]
47 | library AVMCIFCLib
48 | {
49 | importlib("stdole32.tlb");
50 | importlib("stdole2.tlb");
51 |
52 |
53 | [
54 | uuid(41BDBDFC-A848-4523-A149-ADD3AE1E6D84),
55 | helpstring("Avmc Class")
56 | ]
57 | coclass Avmc
58 | {
59 | [default] interface IAvmc;
60 | };
61 | };
62 |
--------------------------------------------------------------------------------
/comtypes/messageloop.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | from ctypes import WinDLL, WinError, byref
3 | from ctypes.wintypes import MSG
4 | from typing import TYPE_CHECKING, SupportsIndex
5 |
6 | if TYPE_CHECKING:
7 | from collections.abc import Callable, Iterable
8 | from ctypes import _CArgObject
9 | from typing import Any
10 |
11 | _FilterCallable = Callable[["_CArgObject"], Iterable[Any]] # type: ignore
12 |
13 | _user32 = WinDLL("user32")
14 |
15 | GetMessage = _user32.GetMessageA
16 | GetMessage.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint]
17 | TranslateMessage = _user32.TranslateMessage
18 | DispatchMessage = _user32.DispatchMessageA
19 |
20 |
21 | class _MessageLoop:
22 | def __init__(self) -> None:
23 | self._filters: list["_FilterCallable"] = []
24 |
25 | def insert_filter(self, obj: "_FilterCallable", index: SupportsIndex = -1) -> None:
26 | self._filters.insert(index, obj)
27 |
28 | def remove_filter(self, obj: "_FilterCallable") -> None:
29 | self._filters.remove(obj)
30 |
31 | def run(self) -> None:
32 | msg = MSG()
33 | lpmsg = byref(msg)
34 | while 1:
35 | ret = GetMessage(lpmsg, 0, 0, 0)
36 | if ret == -1:
37 | raise WinError()
38 | elif ret == 0:
39 | return # got WM_QUIT
40 | if not self.filter_message(lpmsg):
41 | TranslateMessage(lpmsg)
42 | DispatchMessage(lpmsg)
43 |
44 | def filter_message(self, lpmsg: "_CArgObject") -> bool:
45 | return any(list(filter(lpmsg)) for filter in self._filters)
46 |
47 |
48 | _messageloop = _MessageLoop()
49 |
50 | run = _messageloop.run
51 | insert_filter = _messageloop.insert_filter
52 | remove_filter = _messageloop.remove_filter
53 |
54 | __all__ = ["run", "insert_filter", "remove_filter"]
55 |
--------------------------------------------------------------------------------
/comtypes/test/test_GUID.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from comtypes import GUID
4 |
5 |
6 | class Test(unittest.TestCase):
7 | def test_GUID_null(self):
8 | self.assertEqual(GUID(), GUID())
9 | self.assertEqual(
10 | str(GUID()),
11 | "{00000000-0000-0000-0000-000000000000}",
12 | )
13 |
14 | def test_dunder_eq(self):
15 | self.assertEqual(
16 | GUID("{00000000-0000-0000-C000-000000000046}"),
17 | GUID("{00000000-0000-0000-C000-000000000046}"),
18 | )
19 |
20 | def test_duner_str(self):
21 | self.assertEqual(
22 | str(GUID("{0002DF01-0000-0000-C000-000000000046}")),
23 | "{0002DF01-0000-0000-C000-000000000046}",
24 | )
25 |
26 | def test_dunder_repr(self):
27 | self.assertEqual(
28 | repr(GUID("{0002DF01-0000-0000-C000-000000000046}")),
29 | 'GUID("{0002DF01-0000-0000-C000-000000000046}")',
30 | )
31 |
32 | def test_invalid_constructor_arg(self):
33 | with self.assertRaises(WindowsError):
34 | GUID("abc")
35 |
36 | def test_from_progid(self):
37 | self.assertEqual(
38 | GUID.from_progid("Scripting.FileSystemObject"),
39 | GUID("{0D43FE01-F093-11CF-8940-00A0C9054228}"),
40 | )
41 | with self.assertRaises(WindowsError):
42 | GUID.from_progid("abc")
43 |
44 | def test_as_progid(self):
45 | self.assertEqual(
46 | GUID("{0D43FE01-F093-11CF-8940-00A0C9054228}").as_progid(),
47 | "Scripting.FileSystemObject",
48 | )
49 | with self.assertRaises(WindowsError):
50 | GUID("{00000000-0000-0000-C000-000000000046}").as_progid()
51 |
52 | def test_create_new(self):
53 | self.assertNotEqual(GUID.create_new(), GUID.create_new())
54 |
55 |
56 | if __name__ == "__main__":
57 | unittest.main()
58 |
--------------------------------------------------------------------------------
/comtypes/clear_cache.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import contextlib
3 | import os
4 | import sys
5 | from shutil import rmtree # TESTS ASSUME USE OF RMTREE
6 |
7 |
8 | # if supporting Py>=3.11 only, this might be `contextlib.chdir`.
9 | # https://docs.python.org/3/library/contextlib.html#contextlib.chdir
10 | @contextlib.contextmanager
11 | def chdir(path):
12 | """Context manager to change the current working directory."""
13 | work_dir = os.getcwd()
14 | os.chdir(path)
15 | yield
16 | os.chdir(work_dir)
17 |
18 |
19 | def main():
20 | parser = argparse.ArgumentParser(
21 | prog="py -m comtypes.clear_cache", description="Removes comtypes cache folders."
22 | )
23 | parser.add_argument(
24 | "-y", help="Pre-approve deleting all folders", action="store_true"
25 | )
26 | args = parser.parse_args()
27 |
28 | if not args.y:
29 | confirm = input("Remove comtypes cache directories? (y/n): ")
30 | if confirm.lower() != "y":
31 | print("Cache directories NOT removed")
32 | return
33 |
34 | # change cwd to avoid import from local folder during installation process
35 | with chdir(os.path.dirname(sys.executable)):
36 | try:
37 | import comtypes.client
38 | except ImportError:
39 | print("Could not import comtypes", file=sys.stderr)
40 | sys.exit(1)
41 |
42 | # there are two possible locations for the cache folder (in the comtypes
43 | # folder in site-packages if that is writable, otherwise in APPDATA)
44 | # fortunately, by deleting the first location returned by _find_gen_dir()
45 | # we make it un-writable, so calling it again gives us the APPDATA location
46 | for _ in range(2):
47 | dir_path = comtypes.client._find_gen_dir()
48 | rmtree(dir_path)
49 | print(f'Removed directory "{dir_path}"')
50 |
51 |
52 | if __name__ == "__main__":
53 | main()
54 |
--------------------------------------------------------------------------------
/test_pip_install.py:
--------------------------------------------------------------------------------
1 | """This test covers 'pip install' issue #155"""
2 | import os
3 | import sys
4 | import shutil
5 | import subprocess
6 | import unittest
7 |
8 | def read_version():
9 | # Determine the version number by reading it from the file
10 | # 'comtypes\__init__.py'. We cannot import this file (with py3,
11 | # at least) because it is in py2.x syntax.
12 | with open("comtypes/__init__.py") as ofi:
13 | for line in ofi:
14 | if line.startswith("__version__ = "):
15 | var, value = line.split('=')
16 | return value.strip().strip('"').strip("'")
17 | raise NotImplementedError("__version__ is not found in __init__.py")
18 |
19 |
20 | class TestPipInstall(unittest.TestCase):
21 |
22 | def setUp(self):
23 | """prepare the same package that is usually uploaded to PyPI"""
24 | subprocess.check_call([sys.executable, '-m', 'build', '--sdist'])
25 |
26 | filename_for_upload = 'comtypes-%s.tar.gz' % read_version()
27 | self.target_package = os.path.join(os.getcwd(), 'dist', filename_for_upload)
28 | self.pip_exe = os.path.join(os.path.dirname(sys.executable), 'Scripts', 'pip.exe')
29 |
30 | def test_pip_install(self):
31 | """Test that "pip install comtypes-x.y.z.tar.gz" works"""
32 | subprocess.check_call([self.pip_exe, 'install', self.target_package])
33 |
34 | def test_no_cache_dir_custom_location(self):
35 | """Test that 'pip install comtypes-x.y.z.tar.gz --no-cache-dir --target="...\custom location"' works"""
36 | custom_dir = os.path.join(os.getcwd(), 'custom location')
37 | if os.path.exists(custom_dir):
38 | shutil.rmtree(custom_dir)
39 | os.makedirs(custom_dir)
40 |
41 | # this test catches issue #158
42 | subprocess.check_call('{0} install {1} --no-cache-dir --target="{2}"' \
43 | ''.format(self.pip_exe, self.target_package, custom_dir))
44 |
45 |
46 | if __name__ == '__main__':
47 | unittest.main()
48 |
--------------------------------------------------------------------------------
/source/CppTestSrv/SERVER.CPP:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | #include "CFactory.h"
10 | #include "Iface.h"
11 | #include "CoComtypesDispRecordParamTest.h"
12 | #include "CoComtypesDispSafearrayParamTest.h"
13 |
14 |
15 | ///////////////////////////////////////////////////////////
16 | //
17 | // Server.cpp
18 | //
19 | // This file contains the component server code.
20 | // The FactoryDataArray contains the components that
21 | // can be served.
22 | //
23 |
24 | // Each component derived from CUnknown defines a static function
25 | // for creating the component with the following prototype.
26 | // HRESULT CreateInstance(IUnknown* pUnknownOuter,
27 | // CUnknown** ppNewComponent) ;
28 | // This function is used to create the component.
29 | //
30 |
31 | //
32 | // The following array contains the data used by CFactory
33 | // to create components. Each element in the array contains
34 | // the CLSID, the pointer to the creation function, and the name
35 | // of the component to place in the Registry.
36 | //
37 | CFactoryData g_FactoryDataArray[] =
38 | {
39 | {&CLSID_CoComtypesDispRecordParamTest, CA::CreateInstance,
40 | L"Comtypes component for dispinterface record parameter tests", // Friendly Name
41 | L"Comtypes.DispRecordParamTest.1", // ProgID
42 | L"Comtypes.DispRecordParamTest", // Version-independent ProgID
43 | &LIBID_ComtypesCppTestSrvLib, // Type Library ID
44 | NULL, 0},
45 | {&CLSID_CoComtypesDispSafearrayParamTest, CB::CreateInstance,
46 | L"Comtypes component for dispinterface Safearray parameter tests", // Friendly Name
47 | L"Comtypes.DispSafearrayParamTest.1", // ProgID
48 | L"Comtypes.DispSafearrayParamTest", // Version-independent ProgID
49 | &LIBID_ComtypesCppTestSrvLib, // Type Library ID
50 | NULL, 0}
51 | } ;
52 | int g_cFactoryDataEntries
53 | = sizeof(g_FactoryDataArray) / sizeof(CFactoryData) ;
54 |
--------------------------------------------------------------------------------
/comtypes/test/test_urlhistory.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from copy import copy
4 | from ctypes import *
5 |
6 | from comtypes.client import CreateObject, GetModule
7 | from comtypes.GUID import _CoTaskMemFree
8 | from comtypes.patcher import Patch
9 |
10 | # ./urlhist.tlb was downloaded somewhere from the internet (?)
11 |
12 | GetModule(os.path.join(os.path.dirname(__file__), "urlhist.tlb"))
13 | from comtypes.gen import urlhistLib
14 |
15 |
16 | # The pwcsTitle and pwcsUrl fields of the _STATURL structure must be
17 | # freed by the caller. The only way to do this without patching the
18 | # generated code directly is to monkey-patch the
19 | # _STATURL.__ctypes_from_outparam__ method like this.
20 | @Patch(urlhistLib._STATURL)
21 | class _:
22 | def __ctypes_from_outparam__(self):
23 | from comtypes.util import cast_field
24 |
25 | result = type(self)()
26 | for n, _ in self._fields_:
27 | setattr(result, n, getattr(self, n))
28 | url, title = self.pwcsUrl, self.pwcsTitle
29 | _CoTaskMemFree(cast_field(self, "pwcsUrl", c_void_p))
30 | _CoTaskMemFree(cast_field(self, "pwcsTitle", c_void_p))
31 | return result
32 |
33 |
34 | from comtypes.test.find_memleak import find_memleak
35 |
36 |
37 | class Test(unittest.TestCase):
38 | def check_leaks(self, func):
39 | bytes = find_memleak(func, (5, 10))
40 | self.assertFalse(bytes, "Leaks %d bytes" % bytes)
41 |
42 | @unittest.skip(
43 | "This fails with: `TypeError: iter() returned non-iterator of type 'POINTER(IEnumSTATURL)'`"
44 | )
45 | def test_creation(self):
46 | hist = CreateObject(urlhistLib.UrlHistory)
47 | for x in hist.EnumURLS():
48 | x.pwcsUrl, x.pwcsTitle
49 | # print (x.pwcsUrl, x.pwcsTitle)
50 | # print x
51 |
52 | def doit():
53 | for x in hist.EnumURLs():
54 | pass
55 |
56 | doit()
57 | self.check_leaks(doit)
58 |
59 |
60 | if __name__ == "__main__":
61 | unittest.main()
62 |
--------------------------------------------------------------------------------
/comtypes/test/TestDispServer.idl:
--------------------------------------------------------------------------------
1 | /*
2 | 2882fa40-2d69-4880-8073-e81fa29e1785
3 | 7ae4b0e3-5d92-4ab1-b5d0-2a95c1c3ba73
4 | f557bf87-3e3f-4c73-9bc1-7d633d83714b
5 | */
6 |
7 | import "oaidl.idl";
8 | import "ocidl.idl";
9 |
10 | [
11 | uuid(3b3b2a10-7fef-4bcc-90fe-43a221162b1b),
12 | helpstring("A custom event interface")
13 | ]
14 | dispinterface DTestDispServerEvents {
15 | properties:
16 |
17 | methods:
18 | [id(10)]
19 | void EvalStarted([in] BSTR what);
20 |
21 | [id(11)]
22 | void EvalCompleted([in] BSTR what, [in] VARIANT result);
23 | };
24 |
25 | [
26 | uuid(d44d11ba-aa1f-4e93-8f5a-8fa0a4715241),
27 | helpstring("DTestDispServer interface")
28 | ]
29 | dispinterface DTestDispServer {
30 | properties:
31 | [readonly, id(10), helpstring("the id of the server")]
32 | UINT id;
33 |
34 | [id(11), helpstring("the name of the server")]
35 | BSTR name;
36 |
37 | methods:
38 |
39 | [id(12), helpstring("a method that receives an BSTR [in] parameter")]
40 | void SetName([in] BSTR name);
41 |
42 | [id(13), helpstring("evaluate an expression and return the result")]
43 | VARIANT eval([in] BSTR what);
44 |
45 | [id(14), helpstring("evaluate an expression and return the result")]
46 | VARIANT eval2([in] BSTR what);
47 |
48 | [id(16), helpstring("execute a statement")]
49 | void Exec([in] BSTR what);
50 |
51 | [id(17), helpstring("execute a statement")]
52 | void Exec2([in] BSTR what);
53 |
54 | /* Some methods that use defaultvalues */
55 | [id(100)]
56 | void do_cy([in, defaultvalue(32.78)] CURRENCY *value);
57 |
58 | [id(101)]
59 | void do_date([in, defaultvalue(32)] DATE *value);
60 | };
61 |
62 | [
63 | uuid(6baa1c79-4ba0-47f2-9ad7-d2ffb1c0f3e3),
64 | version(1.0),
65 | helpstring("TestDispServer 1.0 Type library")
66 | ]
67 | library TestDispServerLib
68 | {
69 | importlib("stdole2.tlb");
70 |
71 | [
72 | uuid(bb2aba53-9d42-435b-acc3-ae2c274517b0),
73 | helpstring("TestDispServer class object")
74 | ]
75 | coclass TestDispServer {
76 | [default] dispinterface DTestDispServer;
77 | [default, source] dispinterface DTestDispServerEvents;
78 | };
79 | };
80 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | ########################
2 | The ``comtypes`` package
3 | ########################
4 |
5 | |comtypes| is a *pure Python* COM package based on the
6 | `ctypes `_ ffi
7 | foreign function library. |ctypes| is included in Python 2.5 and
8 | later, it is also available for Python 2.4 as separate download.
9 |
10 | While the `pywin32 `_ package
11 | contains superior client side support for *dispatch based* COM
12 | interfaces, it is not possible to access *custom* COM interfaces
13 | unless they are wrapped in C++-code.
14 |
15 | The |comtypes| package makes it easy to access and implement both
16 | custom and dispatch based COM interfaces.
17 |
18 | .. contents::
19 |
20 |
21 | Functionalities
22 | ***************
23 |
24 | .. toctree::
25 | :maxdepth: 1
26 |
27 | client
28 | server
29 | com_interfaces
30 | npsupport
31 | threading
32 |
33 |
34 | Links
35 | *****
36 |
37 | Kourovtsev, Yaroslav (2008). `"Working with Custom COM Interfaces from Python" `_
38 |
39 | This article describes how to use |comtypes| to access a custom
40 | COM object.
41 |
42 | Chen, Alicia (2012). `"Comtypes: How Dropbox learned to stop worrying and love the COM" `_
43 |
44 | This article describes Dropbox's experience using |comtypes| to
45 | interact with COM objects in a Windows application.
46 |
47 |
48 | Downloads
49 | *********
50 |
51 | The |comtypes| project is hosted on `GitHub `_.
52 | Releases can be downloaded from the `GitHub releases `_
53 | section.
54 |
55 |
56 | Installation
57 | ************
58 |
59 | |comtypes| is available on `PyPI `_ and
60 | can be installed with ``pip``:
61 |
62 | .. sourcecode:: shell
63 |
64 | pip install comtypes
65 |
66 |
67 | .. |comtypes| replace:: ``comtypes``
68 |
69 | .. |ctypes| replace:: ``ctypes``
70 |
--------------------------------------------------------------------------------
/comtypes/client/_activeobj.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Optional, TypeVar, overload
2 | from typing import Union as _UnionT
3 |
4 | import comtypes
5 | import comtypes.client.dynamic
6 | from comtypes import GUID, CoClass, IUnknown, automation
7 | from comtypes.client._managing import _manage
8 |
9 | _T_IUnknown = TypeVar("_T_IUnknown", bound=IUnknown)
10 |
11 |
12 | ################################################################
13 | #
14 | # Object creation
15 | #
16 | @overload
17 | def GetActiveObject(progid: _UnionT[str, type[CoClass], GUID]) -> Any: ...
18 | @overload
19 | def GetActiveObject(
20 | progid: _UnionT[str, type[CoClass], GUID], interface: type[_T_IUnknown]
21 | ) -> _T_IUnknown: ...
22 | def GetActiveObject(
23 | progid: _UnionT[str, type[CoClass], GUID],
24 | interface: Optional[type[IUnknown]] = None,
25 | dynamic: bool = False,
26 | ) -> Any:
27 | """Return a pointer to a running COM object that has been
28 | registered with COM.
29 |
30 | 'progid' may be a string like "Excel.Application",
31 | a string specifying a clsid, a GUID instance, or an object with
32 | a _clsid_ attribute which should be any of the above.
33 | 'interface' allows to force a certain interface.
34 | 'dynamic=True' will return a dynamic dispatch object.
35 | """
36 | clsid = GUID.from_progid(progid)
37 | if dynamic:
38 | if interface is not None:
39 | raise ValueError("interface and dynamic are mutually exclusive")
40 | interface = automation.IDispatch
41 | elif interface is None:
42 | interface = getattr(progid, "_com_interfaces_", [None])[0]
43 | obj = comtypes.GetActiveObject(clsid, interface=interface)
44 | if dynamic:
45 | return comtypes.client.dynamic.Dispatch(obj)
46 | return _manage(obj, clsid, interface=interface)
47 |
48 |
49 | def RegisterActiveObject(
50 | punk: IUnknown, progid: _UnionT[str, type[CoClass], GUID], weak: bool = True
51 | ) -> int:
52 | clsid = GUID.from_progid(progid)
53 | flags = comtypes.ACTIVEOBJECT_WEAK if weak else comtypes.ACTIVEOBJECT_STRONG
54 | return comtypes.RegisterActiveObject(punk, clsid, flags)
55 |
--------------------------------------------------------------------------------
/source/CppTestSrv/CoComtypesDispSafearrayParamTest.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | //
10 | // CoComtypesDispSafearrayParamTest.cpp - Component
11 | //
12 |
13 | #include "Iface.h"
14 | #include "CUnknown.h"
15 |
16 | ///////////////////////////////////////////////////////////
17 | //
18 | // Component B
19 | //
20 | class CB : public CUnknown,
21 | public IDualSafearrayParamTest
22 | {
23 | public:
24 | // Creation
25 | static HRESULT CreateInstance(IUnknown* pUnknownOuter,
26 | CUnknown** ppNewComponent ) ;
27 |
28 | private:
29 | // Declare the delegating IUnknown.
30 | DECLARE_IUNKNOWN
31 |
32 | // IUnknown
33 | virtual HRESULT __stdcall NondelegatingQueryInterface(const IID& iid,
34 | void** ppv) ;
35 |
36 | // IDispatch
37 | virtual HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo) ;
38 |
39 | virtual HRESULT __stdcall GetTypeInfo(
40 | UINT iTypeInfo,
41 | LCID, // Localization is not supported.
42 | ITypeInfo** ppITypeInfo) ;
43 |
44 | virtual HRESULT __stdcall GetIDsOfNames(
45 | const IID& iid,
46 | OLECHAR** arrayNames,
47 | UINT countNames,
48 | LCID, // Localization is not supported.
49 | DISPID* arrayDispIDs) ;
50 |
51 | virtual HRESULT __stdcall Invoke(
52 | DISPID dispidMember,
53 | const IID& iid,
54 | LCID, // Localization is not supported.
55 | WORD wFlags,
56 | DISPPARAMS* pDispParams,
57 | VARIANT* pvarResult,
58 | EXCEPINFO* pExcepInfo,
59 | UINT* pArgErr) ;
60 |
61 | // Interface IDualSafearrayParamTest
62 | virtual HRESULT __stdcall InitArray(SAFEARRAY* *test_array) ;
63 | virtual HRESULT __stdcall VerifyArray(
64 | SAFEARRAY *test_array,
65 | VARIANT_BOOL* result) ;
66 |
67 | // Initialization
68 | virtual HRESULT Init() ;
69 |
70 | // Constructor
71 | CB(IUnknown* pUnknownOuter) ;
72 |
73 | // Destructor
74 | ~CB() ;
75 |
76 | // Pointer to type information.
77 | ITypeInfo* m_pITypeInfo ;
78 | } ;
79 |
--------------------------------------------------------------------------------
/source/AvmcIfc.cpp:
--------------------------------------------------------------------------------
1 | // AvmcIfc.cpp : Implementation of DLL Exports.
2 |
3 |
4 | // Note: Proxy/Stub Information
5 | // To build a separate proxy/stub DLL,
6 | // run nmake -f AvmcIfcps.mk in the project directory.
7 |
8 | #include "stdafx.h"
9 | #include "resource.h"
10 | #include
11 | #include "AvmcIfc.h"
12 |
13 | #include "AvmcIfc_i.c"
14 | #include "Avmc.h"
15 |
16 |
17 | CComModule _Module;
18 |
19 | BEGIN_OBJECT_MAP(ObjectMap)
20 | OBJECT_ENTRY(CLSID_Avmc, Avmc)
21 | END_OBJECT_MAP()
22 |
23 | /////////////////////////////////////////////////////////////////////////////
24 | // DLL Entry Point
25 |
26 | extern "C"
27 | BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
28 | {
29 | if (dwReason == DLL_PROCESS_ATTACH)
30 | {
31 | _Module.Init(ObjectMap, hInstance, &LIBID_AVMCIFCLib);
32 | DisableThreadLibraryCalls(hInstance);
33 | }
34 | else if (dwReason == DLL_PROCESS_DETACH)
35 | _Module.Term();
36 | return TRUE; // ok
37 | }
38 |
39 | /////////////////////////////////////////////////////////////////////////////
40 | // Used to determine whether the DLL can be unloaded by OLE
41 |
42 | STDAPI DllCanUnloadNow(void)
43 | {
44 | return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
45 | }
46 |
47 | /////////////////////////////////////////////////////////////////////////////
48 | // Returns a class factory to create an object of the requested type
49 |
50 | STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
51 | {
52 | return _Module.GetClassObject(rclsid, riid, ppv);
53 | }
54 |
55 | /////////////////////////////////////////////////////////////////////////////
56 | // DllRegisterServer - Adds entries to the system registry
57 |
58 | STDAPI DllRegisterServer(void)
59 | {
60 | // registers object, typelib and all interfaces in typelib
61 | return _Module.RegisterServer(TRUE);
62 | }
63 |
64 | /////////////////////////////////////////////////////////////////////////////
65 | // DllUnregisterServer - Removes entries from the system registry
66 |
67 | STDAPI DllUnregisterServer(void)
68 | {
69 | return _Module.UnregisterServer(TRUE);
70 | }
71 |
72 |
73 |
--------------------------------------------------------------------------------
/source/CppTestSrv/CoComtypesDispRecordParamTest.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | //
10 | // CoComtypesDispRecordParamTest.cpp - Component
11 | //
12 |
13 | #include "Iface.h"
14 | #include "CUnknown.h"
15 |
16 | ///////////////////////////////////////////////////////////
17 | //
18 | // Component A
19 | //
20 | class CA : public CUnknown,
21 | public IDualRecordParamTest
22 | {
23 | public:
24 | // Creation
25 | static HRESULT CreateInstance(IUnknown* pUnknownOuter,
26 | CUnknown** ppNewComponent ) ;
27 |
28 | private:
29 | // Declare the delegating IUnknown.
30 | DECLARE_IUNKNOWN
31 |
32 | // IUnknown
33 | virtual HRESULT __stdcall NondelegatingQueryInterface(const IID& iid,
34 | void** ppv) ;
35 |
36 | // IDispatch
37 | virtual HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo) ;
38 |
39 | virtual HRESULT __stdcall GetTypeInfo(
40 | UINT iTypeInfo,
41 | LCID, // Localization is not supported.
42 | ITypeInfo** ppITypeInfo) ;
43 |
44 | virtual HRESULT __stdcall GetIDsOfNames(
45 | const IID& iid,
46 | OLECHAR** arrayNames,
47 | UINT countNames,
48 | LCID, // Localization is not supported.
49 | DISPID* arrayDispIDs) ;
50 |
51 | virtual HRESULT __stdcall Invoke(
52 | DISPID dispidMember,
53 | const IID& iid,
54 | LCID, // Localization is not supported.
55 | WORD wFlags,
56 | DISPPARAMS* pDispParams,
57 | VARIANT* pvarResult,
58 | EXCEPINFO* pExcepInfo,
59 | UINT* pArgErr) ;
60 |
61 | // Interface IDualRecordParamTest
62 | virtual HRESULT __stdcall InitRecord(StructRecordParamTest* test_record) ;
63 | virtual HRESULT __stdcall VerifyRecord(
64 | StructRecordParamTest* test_record,
65 | VARIANT_BOOL* result) ;
66 |
67 | // Initialization
68 | virtual HRESULT Init() ;
69 |
70 | // Constructor
71 | CA(IUnknown* pUnknownOuter) ;
72 |
73 | // Destructor
74 | ~CA() ;
75 |
76 | // Pointer to type information.
77 | ITypeInfo* m_pITypeInfo ;
78 | } ;
79 |
--------------------------------------------------------------------------------
/comtypes/_post_coinit/activeobj.py:
--------------------------------------------------------------------------------
1 | from ctypes import HRESULT, POINTER, OleDLL, byref, c_ulong, c_void_p
2 | from ctypes.wintypes import DWORD, LPVOID
3 | from typing import TYPE_CHECKING, Optional, TypeVar, overload
4 | from typing import Union as _UnionT
5 |
6 | from comtypes import GUID
7 | from comtypes._post_coinit.unknwn import IUnknown
8 | from comtypes.GUID import REFCLSID
9 |
10 | if TYPE_CHECKING:
11 | from comtypes import hints # type: ignore
12 |
13 |
14 | _T_IUnknown = TypeVar("_T_IUnknown", bound=IUnknown)
15 |
16 | ACTIVEOBJECT_STRONG = 0x0
17 | ACTIVEOBJECT_WEAK = 0x1
18 |
19 |
20 | def RegisterActiveObject(
21 | punk: "_UnionT[IUnknown, hints.LP_LP_Vtbl]", clsid: GUID, flags: int
22 | ) -> int:
23 | """Registers a pointer as the active object for its class and returns the handle."""
24 | handle = c_ulong()
25 | _RegisterActiveObject(punk, byref(clsid), flags, byref(handle))
26 | return handle.value
27 |
28 |
29 | def RevokeActiveObject(handle: int) -> None:
30 | """Ends a pointer's status as active."""
31 | _RevokeActiveObject(handle, None)
32 |
33 |
34 | @overload
35 | def GetActiveObject(clsid: GUID, interface: None = None) -> IUnknown: ...
36 | @overload
37 | def GetActiveObject(clsid: GUID, interface: type[_T_IUnknown]) -> _T_IUnknown: ...
38 | def GetActiveObject(
39 | clsid: GUID, interface: Optional[type[IUnknown]] = None
40 | ) -> IUnknown:
41 | """Retrieves a pointer to a running object"""
42 | p = POINTER(IUnknown)()
43 | _GetActiveObject(byref(clsid), None, byref(p))
44 | if interface is not None:
45 | p = p.QueryInterface(interface) # type: ignore
46 | return p # type: ignore
47 |
48 |
49 | _oleaut32 = OleDLL("oleaut32")
50 |
51 | _RegisterActiveObject = _oleaut32.RegisterActiveObject
52 | _RegisterActiveObject.argtypes = [c_void_p, REFCLSID, DWORD, POINTER(DWORD)]
53 | _RegisterActiveObject.restype = HRESULT
54 |
55 | _RevokeActiveObject = _oleaut32.RevokeActiveObject
56 | _RevokeActiveObject.argtypes = [DWORD, LPVOID]
57 | _RevokeActiveObject.restype = HRESULT
58 |
59 | _GetActiveObject = _oleaut32.GetActiveObject
60 | _GetActiveObject.argtypes = [REFCLSID, LPVOID, POINTER(POINTER(IUnknown))]
61 | _GetActiveObject.restype = HRESULT
62 |
--------------------------------------------------------------------------------
/comtypes/logutil.py:
--------------------------------------------------------------------------------
1 | # logutil.py
2 | import logging
3 | from ctypes import WinDLL
4 | from ctypes.wintypes import LPCSTR, LPCWSTR
5 |
6 | _kernel32 = WinDLL("kernel32")
7 |
8 | _OutputDebugStringA = _kernel32.OutputDebugStringA
9 | _OutputDebugStringA.argtypes = [LPCSTR]
10 | _OutputDebugStringA.restype = None
11 |
12 | _OutputDebugStringW = _kernel32.OutputDebugStringW
13 | _OutputDebugStringW.argtypes = [LPCWSTR]
14 | _OutputDebugStringW.restype = None
15 |
16 |
17 | class NTDebugHandler(logging.Handler):
18 | def emit(
19 | self,
20 | record,
21 | writeA=_OutputDebugStringA,
22 | writeW=_OutputDebugStringW,
23 | ):
24 | text = self.format(record)
25 | if isinstance(text, str):
26 | writeA(text + "\n")
27 | else:
28 | writeW(text + "\n")
29 |
30 |
31 | logging.NTDebugHandler = NTDebugHandler
32 |
33 |
34 | def setup_logging(*pathnames):
35 | import configparser
36 |
37 | parser = configparser.ConfigParser()
38 | parser.optionxform = str # use case sensitive option names!
39 |
40 | parser.read(pathnames)
41 |
42 | DEFAULTS = {
43 | "handler": "StreamHandler()",
44 | "format": "%(levelname)s:%(name)s:%(message)s",
45 | "level": "WARNING",
46 | }
47 |
48 | def get(section, option):
49 | try:
50 | return parser.get(section, option, True)
51 | except (configparser.NoOptionError, configparser.NoSectionError):
52 | return DEFAULTS[option]
53 |
54 | levelname = get("logging", "level")
55 | format = get("logging", "format")
56 | handlerclass = get("logging", "handler")
57 |
58 | # convert level name to level value
59 | level = getattr(logging, levelname)
60 | # create the handler instance
61 | handler = eval(handlerclass, vars(logging))
62 | formatter = logging.Formatter(format)
63 | handler.setFormatter(formatter)
64 | logging.root.addHandler(handler)
65 | logging.root.setLevel(level)
66 |
67 | try:
68 | for name, value in parser.items("logging.levels", True):
69 | value = getattr(logging, value)
70 | logging.getLogger(name).setLevel(value)
71 | except configparser.NoSectionError:
72 | pass
73 |
--------------------------------------------------------------------------------
/comtypes/patcher.py:
--------------------------------------------------------------------------------
1 | class Patch:
2 | """
3 | Implements a class decorator suitable for patching an existing class with
4 | a new namespace.
5 |
6 | For example, consider this trivial class (that your code doesn't own):
7 |
8 | >>> class MyClass:
9 | ... def __init__(self, param):
10 | ... self.param = param
11 | ... def bar(self):
12 | ... print("orig bar")
13 |
14 | To add attributes to MyClass, you can use Patch:
15 |
16 | >>> @Patch(MyClass)
17 | ... class JustANamespace:
18 | ... def print_param(self):
19 | ... print(self.param)
20 | >>> ob = MyClass('foo')
21 | >>> ob.print_param()
22 | foo
23 |
24 | The namespace is assigned None, so there's no mistaking the purpose
25 | >>> JustANamespace
26 |
27 | The patcher will replace the existing methods:
28 |
29 | >>> @Patch(MyClass)
30 | ... class SomeNamespace:
31 | ... def bar(self):
32 | ... print("replaced bar")
33 | >>> ob = MyClass('foo')
34 | >>> ob.bar()
35 | replaced bar
36 |
37 | But it will not replace methods if no_replace is indicated.
38 |
39 | >>> @Patch(MyClass)
40 | ... class AnotherNamespace:
41 | ... @no_replace
42 | ... def bar(self):
43 | ... print("candy bar")
44 | >>> ob = MyClass('foo')
45 | >>> ob.bar()
46 | replaced bar
47 |
48 | """
49 |
50 | def __init__(self, target):
51 | self.target = target
52 |
53 | def __call__(self, patches):
54 | for name, value in vars(patches).items():
55 | if name in vars(ReferenceEmptyClass):
56 | continue
57 | no_replace = getattr(value, "__no_replace", False)
58 | if no_replace and hasattr(self.target, name):
59 | continue
60 | setattr(self.target, name, value)
61 |
62 |
63 | def no_replace(f):
64 | """
65 | Method decorator to indicate that a method definition shall
66 | silently be ignored if it already exists in the target class.
67 | """
68 | f.__no_replace = True
69 | return f
70 |
71 |
72 | class ReferenceEmptyClass:
73 | """
74 | This empty class will serve as a reference for attributes present on
75 | any class.
76 | """
77 |
--------------------------------------------------------------------------------
/source/AvmcIfc.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {2e2eeffd-0787-4fc6-910e-67b3c0fd1abf}
6 | cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
7 |
8 |
9 | {c8e183eb-bc67-4cac-84c1-7ca6364c2415}
10 | h;hpp;hxx;hm;inl
11 |
12 |
13 | {7f7732c5-9729-4d48-83f7-406657514f1a}
14 | ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 |
29 |
30 | Source Files
31 |
32 |
33 | Resource Files
34 |
35 |
36 |
37 |
38 | Source Files
39 |
40 |
41 |
42 |
43 | Source Files
44 |
45 |
46 |
47 |
48 | Header Files
49 |
50 |
51 | Header Files
52 |
53 |
54 | Header Files
55 |
56 |
57 | Header Files
58 |
59 |
60 | Header Files
61 |
62 |
63 |
--------------------------------------------------------------------------------
/admin/extract-comtypes-repo:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This script extracts the comtypes repository from the svn.python.org repository.
4 |
5 | if test ! -d mirror
6 | then {
7 | svnadmin create mirror
8 | cat <<'EOF' > mirror/hooks/pre-revprop-change
9 | #!/bin/sh
10 | USER="$3"
11 |
12 | if [ "$USER" = "svnsync" ]; then exit 0; fi
13 |
14 | echo "Only the svnsync user can change revprops" >&2
15 | exit 1
16 | EOF
17 |
18 | chmod +x mirror/hooks/pre-revprop-change
19 | svnsync init --username svnsync file://`pwd`/mirror http://svn.python.org/projects/
20 | }
21 | fi
22 |
23 | svnsync sync file://`pwd`/mirror
24 |
25 | if test ! -f svn.python.org-dumpfile
26 | then {
27 | echo "Dumping svn.python.org SVN repository mirror, this may take an hour or so."
28 | svnadmin dump ./mirror -r 40000:HEAD > svn.python.org-dumpfile
29 | } fi
30 |
31 | if test ! -f ctypes-dumpfile
32 | then {
33 | rm -fr comtypes-dumpfile
34 | echo "Filtering ctypes out of the svn.python.org SVN repository dumpfile, this may take some minutes."
35 | # It is important that we use svndumpfilter first to create a smaller dumpfile (5 GB reduced to 120 MB),
36 | # otherwise svndumpfilter2 below will run out of memory.
37 | cat svn.python.org-dumpfile | svndumpfilter include ctypes >ctypes-dumpfile
38 | } fi
39 |
40 | if test ! -f comtypes-dumpfile
41 | then {
42 | rm -fr comtypes-repo
43 | echo "Filtering comtypes out of the ctypes dumpfile, this may take some minutes."
44 | # We include ctypes/trunk/comtypes, ctypes/branches/comtypes*, ctypes/tags/comtypes*
45 | # Then we strip off the ctypes/ prefix, and rewrite 'trunk/comtypes' into 'trunk'
46 | cat ctypes-dumpfile | ./svndumpfilter2 --drop-empty-revs --renumber-revs ./mirror ctypes/trunk/comtypes ctypes/branches/comtypes* ctypes/tags/comtypes* | sed "s/-path: ctypes\//-path: /g" | sed "s/-path: trunk\/comtypes/-path: trunk/g" >comtypes-dumpfile
47 | } fi
48 |
49 | if test ! -d comtypes-repo
50 | then {
51 | svnadmin create comtypes-repo
52 | svn mkdir file://`pwd`/comtypes-repo/branches -m "Create initial structure"
53 | svn mkdir file://`pwd`/comtypes-repo/tags -m "Create initial structure"
54 | svnadmin load comtypes-repo < comtypes-dumpfile
55 | } fi
56 |
57 | # Create the dumpfile
58 | svnadmin dump ./comtypes-repo | bzip2 > sfimportcomtypes.bz2
59 | # and test it.
60 | svnadmin create ./new-comtypes-repo
61 | bzcat sfimportcomtypes.bz2 | svnadmin load ./new-comtypes-repo
62 |
--------------------------------------------------------------------------------
/comtypes/test/find_memleak.py:
--------------------------------------------------------------------------------
1 | import gc
2 | from ctypes import POINTER, Structure, WinDLL, WinError, byref, c_size_t, sizeof
3 | from ctypes.wintypes import BOOL, DWORD, HANDLE
4 |
5 | ################################################################
6 |
7 |
8 | class PROCESS_MEMORY_COUNTERS(Structure):
9 | _fields_ = [
10 | ("cb", DWORD),
11 | ("PageFaultCount", DWORD),
12 | ("PeakWorkingSetSize", c_size_t),
13 | ("WorkingSetSize", c_size_t),
14 | ("QuotaPeakPagedPoolUsage", c_size_t),
15 | ("QuotaPagedPoolUsage", c_size_t),
16 | ("QuotaPeakNonPagedPoolUsage", c_size_t),
17 | ("QuotaNonPagedPoolUsage", c_size_t),
18 | ("PagefileUsage", c_size_t),
19 | ("PeakPagefileUsage", c_size_t),
20 | ]
21 |
22 | def __init__(self):
23 | self.cb = sizeof(self)
24 |
25 | def dump(self):
26 | for n, _ in self._fields_[2:]:
27 | print(n, getattr(self, n) / 1e6)
28 |
29 |
30 | _psapi = WinDLL("psapi")
31 |
32 | _GetProcessMemoryInfo = _psapi.GetProcessMemoryInfo
33 | _GetProcessMemoryInfo.argtypes = [HANDLE, POINTER(PROCESS_MEMORY_COUNTERS), DWORD]
34 | _GetProcessMemoryInfo.restype = BOOL
35 |
36 |
37 | def wss():
38 | # Return the working set size (memory used by process)
39 | pmi = PROCESS_MEMORY_COUNTERS()
40 | if not _GetProcessMemoryInfo(-1, byref(pmi), sizeof(pmi)):
41 | raise WinError()
42 | return pmi.WorkingSetSize
43 |
44 |
45 | LOOPS = 10, 1000
46 |
47 |
48 | def find_memleak(func, loops=LOOPS):
49 | # call 'func' several times, so that memory consumption
50 | # stabilizes:
51 | for j in range(loops[0]):
52 | for k in range(loops[1]):
53 | func()
54 | gc.collect()
55 | gc.collect()
56 | gc.collect()
57 | bytes = wss()
58 | # call 'func' several times, recording the difference in
59 | # memory consumption before and after the call. Repeat this a
60 | # few times, and return a list containing the memory
61 | # consumption differences.
62 | for j in range(loops[0]):
63 | for k in range(loops[1]):
64 | func()
65 | gc.collect()
66 | gc.collect()
67 | gc.collect()
68 | # return the increased in process size
69 | result = wss() - bytes
70 | # Sometimes the process size did decrease, we do not report leaks
71 | # in this case:
72 | return max(result, 0)
73 |
--------------------------------------------------------------------------------
/comtypes/test/test_wmi.py:
--------------------------------------------------------------------------------
1 | import unittest as ut
2 |
3 | from comtypes.client import CoGetObject
4 |
5 |
6 | # WMI has dual interfaces.
7 | # Some methods/properties have "[out] POINTER(VARIANT)" parameters.
8 | # This test checks that these parameters are returned as strings:
9 | # that's what VARIANT.__ctypes_from_outparam__ does.
10 | class Test(ut.TestCase):
11 | def test_wmi(self):
12 | wmi: "WbemScripting.ISWbemServices" = CoGetObject("winmgmts:")
13 | disks = wmi.InstancesOf("Win32_LogicalDisk")
14 |
15 | # There are different typelibs installed for WMI on win2k and winXP.
16 | # WbemScripting refers to their guid:
17 | # Win2k:
18 | # import comtypes.gen._565783C6_CB41_11D1_8B02_00600806D9B6_0_1_1 as mod
19 | # WinXP:
20 | # import comtypes.gen._565783C6_CB41_11D1_8B02_00600806D9B6_0_1_2 as mod
21 | # So, the one that's referenced onm WbemScripting will be used, whether the
22 | # actual typelib is available or not. XXX
23 | from comtypes.gen import WbemScripting
24 |
25 | WbemScripting.wbemPrivilegeCreateToken
26 |
27 | for item in disks:
28 | # obj[index] is forwarded to obj.Item(index)
29 | # .Value is a property with "[out] POINTER(VARIANT)" parameter.
30 | item: "WbemScripting.ISWbemObject"
31 | a = item.Properties_["Caption"].Value
32 | b = item.Properties_.Item("Caption").Value
33 | c = item.Properties_("Caption").Value
34 | self.assertEqual(a, b)
35 | self.assertEqual(a, c)
36 | self.assertTrue(isinstance(a, str))
37 | self.assertTrue(isinstance(b, str))
38 | self.assertTrue(isinstance(c, str))
39 | result = {}
40 | for prop in item.Properties_:
41 | prop: "WbemScripting.ISWbemProperty"
42 | self.assertTrue(isinstance(prop.Name, str))
43 | prop.Value
44 | result[prop.Name] = prop.Value
45 | # print "\t", (prop.Name, prop.Value)
46 | self.assertEqual(len(item.Properties_), item.Properties_.Count)
47 | self.assertEqual(len(item.Properties_), len(result))
48 | self.assertTrue(isinstance(item.Properties_["Description"].Value, str))
49 | # len(obj) is forwared to obj.Count
50 | self.assertEqual(len(disks), disks.Count)
51 |
52 |
53 | if __name__ == "__main__":
54 | ut.main()
55 |
--------------------------------------------------------------------------------
/source/Avmc.h:
--------------------------------------------------------------------------------
1 | // Avmc.h: Definition of the Avmc class
2 | //
3 | //////////////////////////////////////////////////////////////////////
4 |
5 | #if !defined(AFX_AVMC_H__14DBEE48_EB7D_48AD_8BEE_8E15B2D0A780__INCLUDED_)
6 | #define AFX_AVMC_H__14DBEE48_EB7D_48AD_8BEE_8E15B2D0A780__INCLUDED_
7 |
8 | #if _MSC_VER > 1000
9 | #pragma once
10 | #endif // _MSC_VER > 1000
11 |
12 | #include "resource.h" // main symbols
13 |
14 | /////////////////////////////////////////////////////////////////////////////
15 | // Avmc
16 |
17 | //#include "FTD2XX.h"
18 | typedef PVOID FT_HANDLE;
19 | typedef ULONG FT_STATUS;
20 |
21 | enum {
22 | FT_OK,
23 | FT_INVALID_HANDLE,
24 | FT_DEVICE_NOT_FOUND,
25 | FT_DEVICE_NOT_OPENED,
26 | FT_IO_ERROR,
27 | FT_INSUFFICIENT_RESOURCES,
28 | FT_INVALID_PARAMETER,
29 | FT_INVALID_BAUD_RATE,
30 |
31 | FT_DEVICE_NOT_OPENED_FOR_ERASE,
32 | FT_DEVICE_NOT_OPENED_FOR_WRITE,
33 | FT_FAILED_TO_WRITE_DEVICE,
34 | FT_EEPROM_READ_FAILED,
35 | FT_EEPROM_WRITE_FAILED,
36 | FT_EEPROM_ERASE_FAILED,
37 | FT_EEPROM_NOT_PRESENT,
38 | FT_EEPROM_NOT_PROGRAMMED,
39 | FT_INVALID_ARGS,
40 | FT_NOT_SUPPORTED,
41 | FT_OTHER_ERROR
42 | };
43 |
44 |
45 | //#define FT_SUCCESS(status) ((status) == FT_OK)
46 |
47 | typedef struct _ft_device_list_info_node {
48 | ULONG Flags;
49 | ULONG Type;
50 | ULONG ID;
51 | DWORD LocId;
52 | char SerialNumber[16];
53 | char Description[64];
54 | FT_HANDLE ftHandle;
55 | } FT_DEVICE_LIST_INFO_NODE;
56 |
57 | class Avmc :
58 | public IDispatchImpl,
59 | public ISupportErrorInfo,
60 | public CComObjectRoot,
61 | public CComCoClass
62 | {
63 | public:
64 | Avmc() {}
65 | BEGIN_COM_MAP(Avmc)
66 | COM_INTERFACE_ENTRY(IDispatch)
67 | COM_INTERFACE_ENTRY(IAvmc)
68 | COM_INTERFACE_ENTRY(ISupportErrorInfo)
69 | END_COM_MAP()
70 | //DECLARE_NOT_AGGREGATABLE(Avmc)
71 | // Remove the comment from the line above if you don't want your object to
72 | // support aggregation.
73 |
74 | DECLARE_REGISTRY_RESOURCEID(IDR_Avmc)
75 | // ISupportsErrorInfo
76 | STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
77 |
78 | // IAvmc
79 | public:
80 | STDMETHOD(FindAllAvmc)(/*[out]*/ SAFEARRAY **avmcList);
81 |
82 | private:
83 | void FinalizeCommand(char *command, int cmdLength);
84 | int ReadToSimpleArray(int devNum, char *arr);
85 |
86 | private:
87 | FT_DEVICE_LIST_INFO_NODE *devInfo;
88 | char mResArray[512]; // Inconsistant array - just to read results...
89 | };
90 |
91 | #endif // !defined(AFX_AVMC_H__14DBEE48_EB7D_48AD_8BEE_8E15B2D0A780__INCLUDED_)
92 |
--------------------------------------------------------------------------------
/source/CppTestSrv/UTIL.CPP:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | //
10 | //
11 | // util.cpp - Common utilities for printing out messages
12 | //
13 | //
14 | #include
15 | #include //sprintf
16 | #include
17 | #include
18 | // #include
19 |
20 | #include "util.h"
21 |
22 | // We are building a local server.
23 | // Listbox window handle
24 | extern HWND g_hWndListBox ;
25 |
26 | static inline void output(const char* sz)
27 | {
28 | size_t newsize = strlen(sz) + 1;
29 | wchar_t* wcstring = new wchar_t[newsize];
30 | size_t convertedChars = 0;
31 | mbstowcs_s(&convertedChars, wcstring, newsize, sz, _TRUNCATE);
32 | ::SendMessage(g_hWndListBox, LB_ADDSTRING, 0, (LPARAM)wcstring) ;
33 | delete []wcstring;
34 | }
35 |
36 | //
37 | // Utilities
38 | //
39 | namespace Util
40 | {
41 |
42 | //
43 | // Print out a message with a label.
44 | //
45 | void Trace(const char* szLabel, const char* szText, HRESULT hr)
46 | {
47 | char buf[256] ;
48 | sprintf(buf, "%s: \t%s", szLabel, szText) ;
49 | output(buf) ;
50 |
51 | if (FAILED(hr))
52 | {
53 | ErrorMessage(hr) ;
54 | }
55 | }
56 |
57 | //
58 | // Print out the COM/OLE error string for an HRESULT.
59 | //
60 | void ErrorMessage(HRESULT hr)
61 | {
62 | LPTSTR pMsgBuf = NULL;
63 |
64 | ::FormatMessage(
65 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
66 | NULL,
67 | hr,
68 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
69 | (LPTSTR)&pMsgBuf,
70 | 0,
71 | NULL
72 | ) ;
73 |
74 | char buf[256] ;
75 | int iLength = wcslen(pMsgBuf)+1 ;
76 | char* psz = new char[iLength] ;
77 | wcstombs(psz, pMsgBuf, iLength) ;
78 | sprintf(buf, "Error (%x): %s", hr, psz) ;
79 | output(buf) ;
80 |
81 | // Free the buffer.
82 | LocalFree(pMsgBuf) ;
83 | }
84 |
85 | } ; // End Namespace Util
86 |
87 |
88 | //
89 | // Overloaded ostream insertion operator
90 | // Converts from wchar_t to char
91 | //
92 | std::ostream& operator<< ( std::ostream& os, const wchar_t* wsz )
93 | {
94 | // Length of incoming string
95 | int iLength = wcslen(wsz)+1 ;
96 |
97 | // Allocate buffer for converted string.
98 | char* psz = new char[iLength] ;
99 |
100 | // Convert from wchar_t to char.
101 | wcstombs(psz, wsz, iLength) ;
102 |
103 | // Send it out.
104 | os << psz ;
105 |
106 | // cleanup
107 | delete [] psz ;
108 | return os ;
109 | }
110 |
--------------------------------------------------------------------------------
/source/AvmcIfc.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 11.00
2 | # Visual Studio 2010
3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AvmcIfc", "AvmcIfc.vcxproj", "{8B2CD355-6596-B892-F782-8DCDD607C496}"
4 | EndProject
5 | Global
6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
7 | Debug|Win32 = Debug|Win32
8 | Debug|x64 = Debug|x64
9 | Release MinDependency|Win32 = Release MinDependency|Win32
10 | Release MinDependency|x64 = Release MinDependency|x64
11 | Release MinSize|Win32 = Release MinSize|Win32
12 | Release MinSize|x64 = Release MinSize|x64
13 | Unicode Debug|Win32 = Unicode Debug|Win32
14 | Unicode Debug|x64 = Unicode Debug|x64
15 | Unicode Release MinDependency|Win32 = Unicode Release MinDependency|Win32
16 | Unicode Release MinDependency|x64 = Unicode Release MinDependency|x64
17 | Unicode Release MinSize|Win32 = Unicode Release MinSize|Win32
18 | Unicode Release MinSize|x64 = Unicode Release MinSize|x64
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Debug|Win32.ActiveCfg = Debug|Win32
22 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Debug|Win32.Build.0 = Debug|Win32
23 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Debug|x64.ActiveCfg = Debug|x64
24 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Debug|x64.Build.0 = Debug|x64
25 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Release MinDependency|Win32.ActiveCfg = Release MinDependency|Win32
26 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Release MinDependency|x64.ActiveCfg = Release MinDependency|x64
27 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Release MinSize|Win32.ActiveCfg = Release MinSize|Win32
28 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Release MinSize|x64.ActiveCfg = Release MinSize|x64
29 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
30 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Debug|x64.ActiveCfg = Unicode Debug|x64
31 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Release MinDependency|Win32.ActiveCfg = Unicode Release MinDependency|Win32
32 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Release MinDependency|x64.ActiveCfg = Unicode Release MinDependency|x64
33 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Release MinSize|Win32.ActiveCfg = Unicode Release MinSize|Win32
34 | {8B2CD355-6596-B892-F782-8DCDD607C496}.Unicode Release MinSize|x64.ActiveCfg = Unicode Release MinSize|x64
35 | EndGlobalSection
36 | GlobalSection(SolutionProperties) = preSolution
37 | HideSolutionNode = FALSE
38 | EndGlobalSection
39 | EndGlobal
40 |
--------------------------------------------------------------------------------
/comtypes/test/test_word.py:
--------------------------------------------------------------------------------
1 | import time
2 | import unittest
3 |
4 | import comtypes.client
5 | from comtypes import COMError
6 |
7 | try:
8 | comtypes.client.GetModule(
9 | ("{00020905-0000-0000-C000-000000000046}",)
10 | ) # Word libUUID
11 | from comtypes.gen import Word
12 |
13 | IMPORT_FAILED = False
14 | except (ImportError, OSError):
15 | IMPORT_FAILED = True
16 |
17 |
18 | ################################################################
19 | #
20 | # TODO:
21 | #
22 | # It seems bad that only external test like this
23 | # can verify the behavior of `comtypes` implementation.
24 | # Find a different built-in win32 API to use.
25 | #
26 | ################################################################
27 |
28 |
29 | class _Sink:
30 | def __init__(self):
31 | self.events = []
32 |
33 | # Word Application Event
34 | def DocumentChange(self, this, *args):
35 | self.events.append("DocumentChange")
36 |
37 |
38 | @unittest.skipIf(IMPORT_FAILED, "This depends on Word.")
39 | class Test(unittest.TestCase):
40 | def setUp(self):
41 | # create a word instance
42 | self.word = comtypes.client.CreateObject("Word.Application")
43 |
44 | def tearDown(self):
45 | self.word.Quit()
46 | del self.word
47 |
48 | def test(self):
49 | word = self.word
50 | # Get the instance again, and receive events from that
51 | w2 = comtypes.client.GetActiveObject("Word.Application")
52 | sink = _Sink()
53 | conn = comtypes.client.GetEvents(w2, sink=sink)
54 |
55 | word.Visible = 1
56 |
57 | doc = word.Documents.Add()
58 | wrange = doc.Range()
59 | for i in range(10):
60 | wrange.InsertAfter("Hello from comtypes %d\n" % i)
61 |
62 | for i, para in enumerate(doc.Paragraphs):
63 | f = para.Range.Font
64 | f.ColorIndex = i + 1
65 | f.Size = 12 + (2 * i)
66 |
67 | time.sleep(0.5)
68 |
69 | doc.Close(SaveChanges=Word.wdDoNotSaveChanges)
70 |
71 | del word, w2
72 |
73 | time.sleep(0.5)
74 | conn.disconnect()
75 |
76 | self.assertEqual(sink.events, ["DocumentChange", "DocumentChange"])
77 |
78 | def test_commandbar(self):
79 | word = self.word
80 | word.Visible = 1
81 | tb = word.CommandBars("Standard")
82 | btn = tb.Controls[1]
83 |
84 | # word does not allow programmatic access, so this does fail
85 | with self.assertRaises(COMError):
86 | word.VBE.Events.CommandBarEvents(btn)
87 |
88 | del word
89 |
90 |
91 | if __name__ == "__main__":
92 | unittest.main()
93 |
--------------------------------------------------------------------------------
/source/CppTestSrv/MAKEFILE:
--------------------------------------------------------------------------------
1 | #
2 | # Builds the out-of-proc (local server) version of component.
3 | # Call with: nmake -f makefile.
4 | #
5 | #
6 | !MESSAGE Building local out-of-proc server.
7 | TARGETS = server.exe
8 |
9 | #
10 | # Flags - Always compiles debug.
11 | #
12 | CPP_FLAGS = /c /MTd /Zi /Od /D_DEBUG /DUNICODE /EHsc
13 | EXE_LINK_FLAGS = /DEBUG /NODEFAULTLIB:LIBCMT
14 |
15 | LIBS = kernel32.lib uuid.lib advapi32.lib ole32.lib oleaut32.lib
16 |
17 | #################################################
18 | #
19 | # Targets
20 | #
21 |
22 | all : $(TARGETS)
23 |
24 | #################################################
25 | #
26 | # Proxy source files
27 | #
28 | iface.h server.tlb proxy.c guids.c dlldata.c : server.idl
29 | midl /h iface.h /iid guids.c /proxy proxy.c server.idl
30 |
31 | #################################################
32 | #
33 | # Shared source files
34 | #
35 |
36 | guids.obj : guids.c
37 | cl /c /DWIN32 /DUNICODE /DREGISTER_PROXY_DLL guids.c
38 |
39 | #################################################
40 | #
41 | # Component/server source files
42 | #
43 |
44 | server.obj : server.cpp cunknown.h cfactory.h iface.h
45 | cl $(CPP_FLAGS) server.cpp
46 |
47 | CoComtypesDispRecordParamTest.obj : CoComtypesDispRecordParamTest.cpp \
48 | CoComtypesDispRecordParamTest.h iface.h registry.h CUnknown.h
49 | cl $(CPP_FLAGS) CoComtypesDispRecordParamTest.cpp
50 |
51 | CoComtypesDispSafearrayParamTest.obj : CoComtypesDispSafearrayParamTest.cpp \
52 | CoComtypesDispSafearrayParamTest.h iface.h registry.h CUnknown.h
53 | cl $(CPP_FLAGS) CoComtypesDispSafearrayParamTest.cpp
54 |
55 | #
56 | # Helper classes
57 | #
58 |
59 | CUnknown.obj : CUnknown.cpp CUnknown.h
60 | cl $(CPP_FLAGS) CUnknown.cpp
61 |
62 | CFactory.obj : CFactory.cpp CFactory.h
63 | cl $(CPP_FLAGS) CFactory.cpp
64 |
65 | registry.obj : registry.cpp registry.h
66 | cl $(CPP_FLAGS) registry.cpp
67 |
68 | # util.cpp compiled for server.
69 | util.obj : util.cpp util.h
70 | cl $(CPP_FLAGS) util.cpp
71 |
72 | outproc.obj : outproc.cpp CFactory.h CUnknown.h
73 | cl $(CPP_FLAGS) outproc.cpp
74 |
75 |
76 | #################################################
77 | #
78 | # Link component - Automatically register component.
79 | #
80 |
81 | SERVER_OBJS = Server.obj \
82 | CoComtypesDispRecordParamTest.obj \
83 | CoComtypesDispSafearrayParamTest.obj \
84 | Registry.obj \
85 | Cfactory.obj \
86 | Cunknown.obj \
87 | Util.obj \
88 | Guids.obj
89 |
90 | Server.exe: $(SERVER_OBJS) outproc.obj
91 | link $(EXE_LINK_FLAGS) $(SERVER_OBJS) \
92 | outproc.obj libcmtd.lib \
93 | libcpmtd.lib $(LIBS) user32.lib gdi32.lib
94 |
--------------------------------------------------------------------------------
/comtypes/tools/codegenerator/packing.py:
--------------------------------------------------------------------------------
1 | from comtypes.tools import typedesc
2 |
3 |
4 | def _calc_packing(struct, fields, pack, isStruct):
5 | # Try a certain packing, raise PackingError if field offsets,
6 | # total size ot total alignment is wrong.
7 | if struct.size is None: # incomplete struct
8 | return -1
9 | if struct.name in dont_assert_size:
10 | return None
11 | if struct.bases:
12 | size = struct.bases[0].size
13 | total_align = struct.bases[0].align
14 | else:
15 | size = 0
16 | total_align = 8 # in bits
17 | for i, f in enumerate(fields):
18 | if f.bits: # this code cannot handle bit field sizes.
19 | # print "##XXX FIXME"
20 | return -2 # XXX FIXME
21 | s, a = storage(f.typ)
22 | if pack is not None:
23 | a = min(pack, a)
24 | if size % a:
25 | size += a - size % a
26 | if isStruct:
27 | if size != f.offset:
28 | raise PackingError(f"field {f.name} offset ({size}/{f.offset})")
29 | size += s
30 | else:
31 | size = max(size, s)
32 | total_align = max(total_align, a)
33 | if total_align != struct.align:
34 | raise PackingError(f"total alignment ({total_align}/{struct.align})")
35 | a = total_align
36 | if pack is not None:
37 | a = min(pack, a)
38 | if size % a:
39 | size += a - size % a
40 | if size != struct.size:
41 | raise PackingError(f"total size ({size}/{struct.size})")
42 |
43 |
44 | def calc_packing(struct, fields):
45 | # try several packings, starting with unspecified packing
46 | isStruct = isinstance(struct, typedesc.Structure)
47 | for pack in [None, 16 * 8, 8 * 8, 4 * 8, 2 * 8, 1 * 8]:
48 | try:
49 | _calc_packing(struct, fields, pack, isStruct)
50 | except PackingError as details:
51 | continue
52 | else:
53 | if pack is None:
54 | return None
55 |
56 | return int(pack / 8)
57 |
58 | raise PackingError(f"PACKING FAILED: {details}")
59 |
60 |
61 | class PackingError(Exception):
62 | pass
63 |
64 |
65 | # XXX These should be filtered out in gccxmlparser.
66 | dont_assert_size = set(
67 | [
68 | "__si_class_type_info_pseudo",
69 | "__class_type_info_pseudo",
70 | ]
71 | )
72 |
73 |
74 | def storage(t):
75 | # return the size and alignment of a type
76 | if isinstance(t, typedesc.Typedef):
77 | return storage(t.typ)
78 | elif isinstance(t, typedesc.ArrayType):
79 | s, a = storage(t.typ)
80 | return s * (int(t.max) - int(t.min) + 1), a
81 | return int(t.size), int(t.align)
82 |
--------------------------------------------------------------------------------
/comtypes/stream.py:
--------------------------------------------------------------------------------
1 | from ctypes import HRESULT, POINTER, c_ubyte, c_ulong, pointer
2 | from typing import TYPE_CHECKING
3 |
4 | from comtypes import COMMETHOD, GUID, IUnknown
5 |
6 | if TYPE_CHECKING:
7 | from ctypes import Array as _CArrayType
8 |
9 |
10 | class ISequentialStream(IUnknown):
11 | """Defines methods for the stream objects in sequence."""
12 |
13 | _iid_ = GUID("{0C733A30-2A1C-11CE-ADE5-00AA0044773D}")
14 | _idlflags_ = []
15 |
16 | _methods_ = [
17 | # Note that these functions are called `Read` and `Write` in Microsoft's documentation,
18 | # see https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-isequentialstream.
19 | # However, the comtypes code generation detects these as `RemoteRead` and `RemoteWrite`
20 | # for very subtle reasons, see e.g. https://stackoverflow.com/q/19820999/. We will not
21 | # rename these in this manual import for the sake of consistency.
22 | COMMETHOD(
23 | [],
24 | HRESULT,
25 | "RemoteRead",
26 | # This call only works if `pv` is pre-allocated with `cb` bytes,
27 | # which cannot be done by the high level function generated by metaclasses.
28 | # Therefore, we override the high level function to implement this behaviour
29 | # and then delegate the call the raw COM method.
30 | (["out"], POINTER(c_ubyte), "pv"),
31 | (["in"], c_ulong, "cb"),
32 | (["out"], POINTER(c_ulong), "pcbRead"),
33 | ),
34 | COMMETHOD(
35 | [],
36 | HRESULT,
37 | "RemoteWrite",
38 | (["in"], POINTER(c_ubyte), "pv"),
39 | (["in"], c_ulong, "cb"),
40 | (["out"], POINTER(c_ulong), "pcbWritten"),
41 | ),
42 | ]
43 |
44 | def RemoteRead(self, cb: int) -> tuple["_CArrayType[c_ubyte]", int]:
45 | """Reads a specified number of bytes from the stream object into memory
46 | starting at the current seek pointer.
47 | """
48 | # Behaves as if `pv` is pre-allocated with `cb` bytes by the high level func.
49 | pv = (c_ubyte * cb)()
50 | pcb_read = pointer(c_ulong(0))
51 | self.__com_RemoteRead(pv, c_ulong(cb), pcb_read) # type: ignore
52 | # return both `out` parameters
53 | return pv, pcb_read.contents.value
54 |
55 | if TYPE_CHECKING:
56 |
57 | def RemoteWrite(self, pv: "_CArrayType[c_ubyte]", cb: int) -> int:
58 | """Writes a specified number of bytes into the stream object starting at
59 | the current seek pointer.
60 | """
61 | ...
62 |
63 |
64 | # fmt: off
65 | __known_symbols__ = [
66 | 'ISequentialStream',
67 | ]
68 | # fmt: on
69 |
--------------------------------------------------------------------------------
/comtypes/server/__init__.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | from ctypes import HRESULT, POINTER, byref
3 | from typing import TYPE_CHECKING, Any, Optional
4 |
5 | import comtypes
6 | import comtypes.client
7 | import comtypes.client.dynamic
8 | from comtypes import GUID, STDMETHOD, IUnknown
9 | from comtypes import RevokeActiveObject as RevokeActiveObject
10 | from comtypes.automation import IDispatch
11 |
12 | if TYPE_CHECKING:
13 | from ctypes import _Pointer
14 |
15 | from comtypes import hints # type: ignore
16 |
17 |
18 | ################################################################
19 | # Interfaces
20 | class IClassFactory(IUnknown):
21 | _iid_ = GUID("{00000001-0000-0000-C000-000000000046}")
22 | _methods_ = [
23 | STDMETHOD(
24 | HRESULT,
25 | "CreateInstance",
26 | [POINTER(IUnknown), POINTER(GUID), POINTER(ctypes.c_void_p)],
27 | ),
28 | STDMETHOD(HRESULT, "LockServer", [ctypes.c_int]),
29 | ]
30 |
31 | def CreateInstance(
32 | self,
33 | punkouter: Optional[type["_Pointer[IUnknown]"]] = None,
34 | interface: Optional[type[IUnknown]] = None,
35 | dynamic: bool = False,
36 | ) -> Any:
37 | if dynamic:
38 | if interface is not None:
39 | raise ValueError("interface and dynamic are mutually exclusive")
40 | itf = IDispatch
41 | elif interface is None:
42 | itf = IUnknown
43 | else:
44 | itf = interface
45 | obj = POINTER(itf)()
46 | self.__com_CreateInstance(punkouter, itf._iid_, byref(obj)) # type: ignore
47 | if dynamic:
48 | return comtypes.client.dynamic.Dispatch(obj)
49 | elif interface is None:
50 | # An interface was not specified, so return the best.
51 | return comtypes.client.GetBestInterface(obj)
52 | # An interface was specified and obj is already that interface.
53 | return obj
54 |
55 | if TYPE_CHECKING:
56 |
57 | def LockServer(self, fLock: int) -> hints.Hresult: ...
58 |
59 |
60 | # class IExternalConnection(IUnknown):
61 | # _iid_ = GUID("{00000019-0000-0000-C000-000000000046}")
62 | # _methods_ = [
63 | # STDMETHOD(HRESULT, "AddConnection", [c_ulong, c_ulong]),
64 | # STDMETHOD(HRESULT, "ReleaseConnection", [c_ulong, c_ulong, c_ulong])]
65 |
66 |
67 | def RegisterActiveObject(comobj: comtypes.COMObject, weak: bool = True) -> int:
68 | """Registers a pointer as the active object for its class and returns the handle."""
69 | punk = comobj._com_pointers_[IUnknown._iid_]
70 | clsid = comobj._reg_clsid_
71 | flags = comtypes.ACTIVEOBJECT_WEAK if weak else comtypes.ACTIVEOBJECT_STRONG
72 | return comtypes.RegisterActiveObject(punk, clsid, flags)
73 |
--------------------------------------------------------------------------------
/comtypes/test/test_QueryService.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import time
3 | import unittest
4 | from ctypes import POINTER
5 |
6 | import comtypes
7 | from comtypes import GUID
8 | from comtypes.client import CreateObject, GetModule
9 |
10 | with contextlib.redirect_stdout(None): # supress warnings
11 | GetModule("mshtml.tlb")
12 | import comtypes.gen.MSHTML as mshtml
13 |
14 | SID_SHTMLEditServices = GUID("{3050F7F9-98B5-11CF-BB82-00AA00BDCE0B}")
15 |
16 |
17 | class TestCase(unittest.TestCase):
18 | def test(self):
19 | doc = CreateObject(mshtml.HTMLDocument, interface=mshtml.IHTMLDocument2)
20 | doc.designMode = "On"
21 | doc.write("Hello
")
22 | doc.close()
23 | while doc.readyState != "complete":
24 | time.sleep(0.01)
25 | sp = doc.QueryInterface(comtypes.IServiceProvider)
26 | # This behavior is described in Microsoft documentation:
27 | # https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa704048(v=vs.85)
28 | es = sp.QueryService(SID_SHTMLEditServices, mshtml.IHTMLEditServices)
29 | self.assertIsInstance(es, POINTER(mshtml.IHTMLEditServices))
30 | mc = doc.QueryInterface(mshtml.IMarkupContainer)
31 | ss = es.GetSelectionServices(mc)
32 | # QueryInterface for `IHTMLDocument3` to access `getElementById`.
33 | element = doc.QueryInterface(mshtml.IHTMLDocument3).getElementById("test")
34 | self.assertEqual(element.innerHTML, "Hello")
35 | # MarkupPointer related tests:
36 | ms = doc.QueryInterface(mshtml.IMarkupServices)
37 | p_start = ms.CreateMarkupPointer()
38 | p_end = ms.CreateMarkupPointer()
39 | # QueryInterface for `IHTMLBodyElement` to access `createTextRange`.
40 | rng = doc.body.QueryInterface(mshtml.IHTMLBodyElement).createTextRange()
41 | rng.moveToElementText(element)
42 | ms.MovePointersToRange(rng, p_start, p_end)
43 | self.assertTrue(p_start.IsLeftOf(p_end))
44 | self.assertTrue(p_end.IsRightOf(p_start))
45 | self.assertFalse(p_start.IsEqualTo(p_end))
46 | seg = ss.AddSegment(p_start, p_end)
47 | q_start = ms.CreateMarkupPointer()
48 | q_end = ms.CreateMarkupPointer()
49 | self.assertFalse(p_start.IsEqualTo(q_start))
50 | self.assertFalse(p_end.IsEqualTo(q_end))
51 | seg.GetPointers(q_start, q_end)
52 | self.assertTrue(p_start.IsEqualTo(q_start))
53 | self.assertTrue(p_end.IsEqualTo(q_end))
54 | ss.RemoveSegment(seg)
55 | # Verify state changes of `p_start` and `p_end`.
56 | p_start.MoveToPointer(p_end)
57 | self.assertTrue(p_start.IsEqualTo(p_end))
58 |
59 |
60 | if __name__ == "__main__":
61 | unittest.main()
62 |
--------------------------------------------------------------------------------
/comtypes/test/test_persist.py:
--------------------------------------------------------------------------------
1 | import tempfile
2 | import unittest as ut
3 | from _ctypes import COMError
4 | from pathlib import Path
5 |
6 | from comtypes import GUID, CoCreateInstance, IPersist, hresult, persist
7 | from comtypes.automation import VARIANT
8 |
9 | CLSID_ShellLink = GUID("{00021401-0000-0000-C000-000000000046}")
10 |
11 |
12 | class Test_IPersist(ut.TestCase):
13 | def test_GetClassID(self):
14 | p = CoCreateInstance(CLSID_ShellLink).QueryInterface(IPersist)
15 | self.assertEqual(p.GetClassID(), CLSID_ShellLink)
16 |
17 |
18 | class Test_IPersistFile(ut.TestCase):
19 | def setUp(self):
20 | td = tempfile.TemporaryDirectory()
21 | self.addCleanup(td.cleanup)
22 | self.tmp_dir = Path(td.name)
23 |
24 | def _create_pf(self) -> persist.IPersistFile:
25 | return CoCreateInstance(CLSID_ShellLink).QueryInterface(persist.IPersistFile)
26 |
27 | def test_load(self):
28 | pf = self._create_pf()
29 | tgt_file = (self.tmp_dir / "tgt.txt").resolve()
30 | tgt_file.touch()
31 | pf.Load(str(tgt_file), persist.STGM_DIRECT)
32 | self.assertEqual(pf.GetCurFile(), str(tgt_file))
33 |
34 | def test_save(self):
35 | pf = self._create_pf()
36 | tgt_file = self.tmp_dir / "tgt.txt"
37 | self.assertFalse(tgt_file.exists())
38 | pf.Save(str(tgt_file), True)
39 | self.assertEqual(pf.GetCurFile(), str(tgt_file))
40 | self.assertTrue(tgt_file.exists())
41 |
42 |
43 | class Test_DictPropertyBag(ut.TestCase):
44 | def create_itf_ptr(self) -> persist.IPropertyBag:
45 | # Create a DictPropertyBag instance with some initial values
46 | impl = persist.DictPropertyBag(Key1="value1", Key2=123, Key3=True)
47 | # Get the IPropertyBag interface pointer
48 | itf = impl.QueryInterface(persist.IPropertyBag)
49 | return itf
50 |
51 | def test_read_existing_properties(self):
52 | itf = self.create_itf_ptr()
53 | self.assertEqual(itf.Read("Key1", VARIANT(), None), "value1")
54 | self.assertEqual(itf.Read("Key2", VARIANT(), None), 123)
55 | self.assertEqual(itf.Read("Key3", VARIANT(), None), True)
56 |
57 | def test_write_new_property(self):
58 | itf = self.create_itf_ptr()
59 | itf.Write("Key4", "new_value")
60 | self.assertEqual(itf.Read("Key4", VARIANT(), None), "new_value")
61 |
62 | def test_update_existing_property(self):
63 | itf = self.create_itf_ptr()
64 | itf.Write("Key1", "updated_value")
65 | self.assertEqual(itf.Read("Key1", VARIANT(), None), "updated_value")
66 |
67 | def test_read_non_existent_property(self):
68 | itf = self.create_itf_ptr()
69 | with self.assertRaises(COMError) as cm:
70 | itf.Read("NonExistentProp", VARIANT(), None)
71 | self.assertEqual(cm.exception.hresult, hresult.E_INVALIDARG)
72 |
--------------------------------------------------------------------------------
/comtypes/test/TestComServer.idl:
--------------------------------------------------------------------------------
1 | /*
2 | b30428b4-cc67-48ba-81db-4c6a6b3db1a3
3 | */
4 |
5 | /*
6 |
7 | REMEMBER TO COMPILE A NEW .TLB FILE WHEN THIS CHANGES,
8 | AND ALSO TO REMOVE THE comtypes\gen DIRECTORY TO DELETE
9 | THE TYPELIB WRAPPERS!
10 | The TestServer.py should also be registered again.
11 |
12 | */
13 |
14 | import "oaidl.idl";
15 | import "ocidl.idl";
16 |
17 | [
18 | object,
19 | oleautomation,
20 | uuid(f0a241e2-25d1-4f6d-9461-c67bf262779f),
21 | helpstring("A custom event interface")
22 | ]
23 | interface ITestComServerEvents : IUnknown {
24 | [id(10)]
25 | HRESULT EvalStarted([in] BSTR what);
26 |
27 | [id(11)]
28 | HRESULT EvalCompleted([in] BSTR what, [in] VARIANT result);
29 | };
30 |
31 | [
32 | object,
33 | /*
34 | the oleautomation flag enables universal marshalling on non-dispatch
35 | interfaces. See Don Box, Page 220.
36 | */
37 | oleautomation,
38 | uuid(58955c76-60a9-4eeb-8b8a-8f92e90d0fe7),
39 | helpstring("ITestComServer interface")
40 | ]
41 | interface ITestComServer : IDispatch {
42 | [propget, id(10), helpstring("returns the id of the server")]
43 | HRESULT id([out, retval] UINT *pid);
44 |
45 | [propget, id(11), helpstring("the name of the server")]
46 | HRESULT name([out, retval] BSTR *pname);
47 |
48 | [propput, id(11), helpstring("the name of the server")]
49 | HRESULT name([in] BSTR name);
50 |
51 | [id(12), helpstring("a method that receives an BSTR [in] parameter")]
52 | HRESULT SetName([in] BSTR name);
53 |
54 | [id(13), helpstring("evaluate an expression and return the result")]
55 | HRESULT eval([in] BSTR what, [out, retval] VARIANT *presult);
56 |
57 | /* Some methods that use defaultvalues */
58 | [id(14)]
59 | HRESULT do_cy([in, defaultvalue(32.78)] CURRENCY *value);
60 |
61 | [id(15)]
62 | HRESULT do_date([in, defaultvalue(32)] DATE *value);
63 |
64 | [id(16), helpstring("execute a statement")]
65 | HRESULT Exec([in] BSTR what);
66 |
67 | [id(17), helpstring("execute a statement")]
68 | HRESULT Exec2([in] BSTR what);
69 |
70 | [id(18), helpstring("a method with [in] and [out] args in mixed order")]
71 | HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);
72 | };
73 |
74 | [
75 | uuid(5a3e1d1d-947a-44ac-9b03-5c37d5f5fffc),
76 | version(1.0),
77 | helpstring("TestComServer 1.0 Type library")
78 | ]
79 | library TestComServerLib
80 | {
81 | importlib("stdole2.tlb");
82 |
83 | typedef
84 | [
85 | uuid(086b7f11-aed0-4de0-b77a-f1998371da83)
86 | ]
87 | struct MYCOLOR {
88 | double red;
89 | double green;
90 | double blue;
91 | } MYCOLOR;
92 |
93 | [
94 | uuid(1fca61d1-a1a6-464c-b3a8-e9508b4ac8f7),
95 | helpstring("TestComServer class object")
96 | ]
97 | coclass TestComServer {
98 | [default] interface ITestComServer;
99 | [default, source] interface ITestComServerEvents;
100 | };
101 | };
102 |
--------------------------------------------------------------------------------
/comtypes/test/test_recordinfo.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import byref, pointer, sizeof
3 |
4 | from comtypes import typeinfo
5 | from comtypes.client import GetModule
6 |
7 | ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}"
8 |
9 | try:
10 | GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0))
11 | from comtypes.gen.ComtypesCppTestSrvLib import StructRecordParamTest
12 |
13 | IMPORT_FAILED = False
14 | except (ImportError, OSError):
15 | IMPORT_FAILED = True
16 |
17 |
18 | def _create_recordinfo() -> typeinfo.IRecordInfo:
19 | return typeinfo.GetRecordInfoFromGuids(*StructRecordParamTest._recordinfo_)
20 |
21 |
22 | def _create_record(
23 | question: str, answer: int, needs_clarification: bool
24 | ) -> "StructRecordParamTest":
25 | record = StructRecordParamTest()
26 | record.question = question
27 | record.answer = answer
28 | record.needs_clarification = needs_clarification
29 | return record
30 |
31 |
32 | @unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
33 | class Test_IRecordInfo(unittest.TestCase):
34 | def test_RecordCopy(self):
35 | dst_rec = StructRecordParamTest()
36 | ri = _create_recordinfo()
37 | ri.RecordCopy(pointer(_create_record("foo", 3, True)), byref(dst_rec))
38 | self.assertEqual(dst_rec.question, "foo")
39 | self.assertEqual(dst_rec.answer, 3)
40 | self.assertEqual(dst_rec.needs_clarification, True)
41 |
42 | def test_GetGuid(self):
43 | *_, expected_guid = StructRecordParamTest._recordinfo_
44 | self.assertEqual(str(_create_recordinfo().GetGuid()), expected_guid)
45 |
46 | def test_GetName(self):
47 | self.assertEqual(
48 | _create_recordinfo().GetName(),
49 | StructRecordParamTest.__qualname__,
50 | )
51 |
52 | def test_GetSize(self):
53 | self.assertEqual(
54 | _create_recordinfo().GetSize(),
55 | sizeof(StructRecordParamTest),
56 | )
57 |
58 | def test_GetTypeInfo(self):
59 | ta = _create_recordinfo().GetTypeInfo().GetTypeAttr()
60 | *_, expected_guid = StructRecordParamTest._recordinfo_
61 | self.assertEqual(str(ta.guid), expected_guid)
62 |
63 | def test_IsMatchingType(self):
64 | ri = _create_recordinfo()
65 | ti = ri.GetTypeInfo()
66 | self.assertTrue(typeinfo.GetRecordInfoFromTypeInfo(ti).IsMatchingType(ri))
67 |
68 | def test_RecordCreateCopy(self):
69 | ri = _create_recordinfo()
70 | actual = ri.RecordCreateCopy(byref(_create_record("foo", 3, True)))
71 | self.assertIsInstance(actual, int)
72 | dst_rec = StructRecordParamTest()
73 | ri.RecordCopy(actual, byref(dst_rec))
74 | ri.RecordDestroy(actual)
75 | self.assertEqual(dst_rec.question, "foo")
76 | self.assertEqual(dst_rec.answer, 3)
77 | self.assertEqual(dst_rec.needs_clarification, True)
78 |
--------------------------------------------------------------------------------
/comtypes/git.py:
--------------------------------------------------------------------------------
1 | """comtypes.git - access the process wide global interface table
2 |
3 | The global interface table provides a way to marshal interface pointers
4 | between different threading appartments.
5 | """
6 |
7 | from ctypes import *
8 | from ctypes.wintypes import DWORD
9 |
10 | from comtypes import (
11 | CLSCTX_INPROC_SERVER,
12 | COMMETHOD,
13 | GUID,
14 | HRESULT,
15 | STDMETHOD,
16 | CoCreateInstance,
17 | IUnknown,
18 | )
19 |
20 |
21 | class IGlobalInterfaceTable(IUnknown):
22 | _iid_ = GUID("{00000146-0000-0000-C000-000000000046}")
23 | _methods_ = [
24 | STDMETHOD(
25 | HRESULT,
26 | "RegisterInterfaceInGlobal",
27 | [POINTER(IUnknown), POINTER(GUID), POINTER(DWORD)],
28 | ),
29 | STDMETHOD(HRESULT, "RevokeInterfaceFromGlobal", [DWORD]),
30 | STDMETHOD(
31 | HRESULT,
32 | "GetInterfaceFromGlobal",
33 | [DWORD, POINTER(GUID), POINTER(POINTER(IUnknown))],
34 | ),
35 | ]
36 |
37 | def RegisterInterfaceInGlobal(self, obj, interface=IUnknown):
38 | cookie = DWORD()
39 | self.__com_RegisterInterfaceInGlobal(obj, interface._iid_, cookie)
40 | return cookie.value
41 |
42 | def GetInterfaceFromGlobal(self, cookie, interface=IUnknown):
43 | ptr = POINTER(interface)()
44 | self.__com_GetInterfaceFromGlobal(cookie, interface._iid_, ptr)
45 | return ptr
46 |
47 | def RevokeInterfaceFromGlobal(self, cookie):
48 | self.__com_RevokeInterfaceFromGlobal(cookie)
49 |
50 |
51 | # It was a pain to get this CLSID: it's neither in the registry, nor
52 | # in any header files. I had to compile a C program, and find it out
53 | # with the debugger. Apparently it is in uuid.lib.
54 | CLSID_StdGlobalInterfaceTable = GUID("{00000323-0000-0000-C000-000000000046}")
55 |
56 | git = CoCreateInstance(
57 | CLSID_StdGlobalInterfaceTable,
58 | interface=IGlobalInterfaceTable,
59 | clsctx=CLSCTX_INPROC_SERVER,
60 | )
61 |
62 | RevokeInterfaceFromGlobal = git.RevokeInterfaceFromGlobal
63 | RegisterInterfaceInGlobal = git.RegisterInterfaceInGlobal
64 | GetInterfaceFromGlobal = git.GetInterfaceFromGlobal
65 |
66 | # fmt: off
67 | __all__ = [
68 | "RegisterInterfaceInGlobal", "RevokeInterfaceFromGlobal",
69 | "GetInterfaceFromGlobal",
70 | ]
71 | # fmt: on
72 |
73 |
74 | if __name__ == "__main__":
75 | from comtypes.typeinfo import CreateTypeLib, ICreateTypeLib
76 |
77 | tlib = CreateTypeLib("foo.bar") # we don not save it later
78 | assert (tlib.AddRef(), tlib.Release()) == (2, 1)
79 |
80 | cookie = RegisterInterfaceInGlobal(tlib)
81 | assert (tlib.AddRef(), tlib.Release()) == (3, 2)
82 |
83 | GetInterfaceFromGlobal(cookie, ICreateTypeLib)
84 | GetInterfaceFromGlobal(cookie, ICreateTypeLib)
85 | GetInterfaceFromGlobal(cookie)
86 | assert (tlib.AddRef(), tlib.Release()) == (3, 2)
87 | RevokeInterfaceFromGlobal(cookie)
88 | assert (tlib.AddRef(), tlib.Release()) == (2, 1)
89 |
--------------------------------------------------------------------------------
/comtypes/test/mytypelib.idl:
--------------------------------------------------------------------------------
1 | import "oaidl.idl";
2 | import "ocidl.idl";
3 |
4 | typedef
5 | [
6 | uuid(0a411e93-aeb0-4b84-8722-b237a1b87ba1)
7 | ]
8 | struct Pair {
9 | double a;
10 | double b;
11 | } Pair;
12 |
13 | typedef
14 | [
15 | uuid(00b7e135-f7a3-42f8-b65b-ecd106b3c17d)
16 | ]
17 | struct Point {
18 | double x;
19 | double y;
20 | } Point;
21 |
22 | [
23 | object,
24 | /*
25 | the oleautomation flag enables universal marshalling on non-dispatch
26 | interfaces. See Don Box, Page 220.
27 | */
28 | oleautomation,
29 | uuid(368ce4db-5f87-4927-b134-2a955c1dea1f),
30 | ]
31 | interface IMyInterface : IUnknown {
32 | [propget, id(10), helpstring("returns the id of the server")]
33 | HRESULT id([out, retval] UINT *pid);
34 |
35 | [propget, id(11), helpstring("the name of the server")]
36 | HRESULT name([out, retval] BSTR *pname);
37 |
38 | [propput, id(11), helpstring("the name of the server")]
39 | HRESULT name([in] BSTR name);
40 |
41 | [id(12), helpstring("a method that receives an BSTR [in] parameter")]
42 | HRESULT SetName([in] BSTR name);
43 |
44 | [id(13), helpstring("evaluate an expression and return the result")]
45 | HRESULT eval([in] BSTR what, [out, retval] VARIANT *presult);
46 |
47 | /* Some methods that use defaultvalues */
48 | [id(14)]
49 | HRESULT do_cy([in, defaultvalue(32.78)] CURRENCY *value);
50 |
51 | [id(15)]
52 | HRESULT do_date([in, defaultvalue(32)] DATE *value);
53 |
54 | [id(16), helpstring("execute a statement")]
55 | HRESULT Exec([in] BSTR what);
56 |
57 | [helpstring("execute a statement")]
58 | HRESULT Exec2([in] BSTR what);
59 |
60 | [helpstring("a method with [in] and [out] args in mixed order")]
61 | HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);
62 |
63 | [helpstring("a method that receives and returns SAFEARRAYs of pairs")]
64 | HRESULT TestPairArray([in] SAFEARRAY(Pair) val, [out, retval] SAFEARRAY(Pair) *result);
65 |
66 | [helpstring("a method that receives and returns SAFEARRAYs of pairs")]
67 | HRESULT TestPairArray2([in] SAFEARRAY(Pair) val, [out, retval] SAFEARRAY(Pair) *result);
68 |
69 | [helpstring("a method that receives and returns SAFEARRAYs of points")]
70 | HRESULT TestPointArray([in] SAFEARRAY(Point) val, [out, retval] SAFEARRAY(Point) *result);
71 |
72 | [local, helpstring("...")]
73 | LONG Test([in] int value, [out, retval] int *result);
74 |
75 | HRESULT MultiInOutArgs([in, out] int *pa,
76 | [in, out] int *pb,
77 | [in, out] int *pc);
78 |
79 | HRESULT MultiOutArgs2([in, out] int *pa,
80 | [in, out] int *pb,
81 | [out, retval] int *pc);
82 |
83 | };
84 |
85 | [
86 | uuid(6a237363-015c-4ded-937e-7e4d80b0a6cf),
87 | version(1.0),
88 | ]
89 | library MyTypeLib
90 | {
91 | importlib("stdole2.tlb");
92 |
93 | [
94 | uuid(08420058-ef6b-4884-9c78-14e73dfaf767),
95 | ]
96 | coclass MyComServer {
97 | [default] interface IMyInterface;
98 | };
99 | };
100 |
--------------------------------------------------------------------------------
/comtypes/test/test_getactiveobj.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import time
3 | import unittest
4 |
5 | import comtypes
6 | import comtypes.client
7 |
8 | with contextlib.redirect_stdout(None): # supress warnings
9 | comtypes.client.GetModule("msvidctl.dll")
10 | from comtypes.gen import MSVidCtlLib as msvidctl
11 |
12 | try:
13 | # pass Word libUUID
14 | comtypes.client.GetModule(("{00020905-0000-0000-C000-000000000046}",))
15 | IMPORT_WORD_FAILED = False
16 | except (ImportError, OSError):
17 | IMPORT_WORD_FAILED = True
18 |
19 |
20 | ################################################################
21 | #
22 | # TODO:
23 | #
24 | # It seems bad that only external test like this
25 | # can verify the behavior of `comtypes` implementation.
26 | # Find a different built-in win32 API to use.
27 | #
28 | ################################################################
29 |
30 |
31 | @unittest.skipIf(IMPORT_WORD_FAILED, "This depends on Word.")
32 | class Test_Word(unittest.TestCase):
33 | def setUp(self):
34 | try:
35 | comtypes.client.GetActiveObject("Word.Application")
36 | except OSError:
37 | pass
38 | else:
39 | # seems word is running, we cannot test this.
40 | self.fail("MSWord is running, cannot test")
41 | # create a WORD instance
42 | self.w1 = comtypes.client.CreateObject("Word.Application")
43 |
44 | def tearDown(self):
45 | if hasattr(self, "w1"):
46 | self.w1.Quit()
47 | del self.w1
48 |
49 | def test(self):
50 | # connect to the running instance
51 | w1 = self.w1
52 | w2 = comtypes.client.GetActiveObject("Word.Application")
53 |
54 | # check if they are referring to the same object
55 | self.assertEqual(
56 | w1.QueryInterface(comtypes.IUnknown), w2.QueryInterface(comtypes.IUnknown)
57 | )
58 |
59 | w1.Quit()
60 | del self.w1
61 |
62 | time.sleep(1)
63 |
64 | with self.assertRaises(comtypes.COMError) as arc:
65 | w2.Visible
66 |
67 | err = arc.exception
68 | variables = err.hresult, err.text, err.details
69 | self.assertEqual(variables, err.args)
70 | with self.assertRaises(WindowsError):
71 | comtypes.client.GetActiveObject("Word.Application")
72 |
73 |
74 | class Test_MSVidCtlLib(unittest.TestCase):
75 | def test_register_and_revoke(self):
76 | vidctl = comtypes.client.CreateObject(msvidctl.MSVidCtl)
77 | with self.assertRaises(WindowsError):
78 | comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
79 | handle = comtypes.client.RegisterActiveObject(vidctl, msvidctl.MSVidCtl)
80 | activeobj = comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
81 | self.assertEqual(vidctl, activeobj)
82 | comtypes.client.RevokeActiveObject(handle)
83 | with self.assertRaises(WindowsError):
84 | comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
85 |
86 |
87 | if __name__ == "__main__":
88 | unittest.main()
89 |
--------------------------------------------------------------------------------
/comtypes/test/test_viewobject.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import unittest
3 | from ctypes.wintypes import POINT, RECT, SIZEL
4 |
5 | import comtypes.client
6 | from comtypes import IUnknown
7 | from comtypes.viewobject import (
8 | DVASPECT_CONTENT,
9 | IAdviseSink,
10 | IViewObject,
11 | IViewObject2,
12 | IViewObjectEx,
13 | )
14 |
15 | with contextlib.redirect_stdout(None): # supress warnings
16 | comtypes.client.GetModule("mshtml.tlb")
17 |
18 | import comtypes.gen.MSHTML as mshtml
19 |
20 |
21 | def create_html_document() -> IUnknown:
22 | return comtypes.client.CreateObject(mshtml.HTMLDocument)
23 |
24 |
25 | class Test_IViewObject(unittest.TestCase):
26 | def test_Advise_GetAdvise(self):
27 | vo = create_html_document().QueryInterface(IViewObject)
28 | # Test that we can clear any existing advise connection.
29 | vo.SetAdvise(DVASPECT_CONTENT, 0, None)
30 | # Verify that no advise connection is present.
31 | aspect, advf, sink = vo.GetAdvise()
32 | self.assertIsInstance(aspect, int)
33 | self.assertIsInstance(advf, int)
34 | self.assertIsInstance(sink, IAdviseSink)
35 | self.assertFalse(sink) # A NULL com pointer evaluates to False.
36 |
37 | def test_Freeze_Unfreeze(self):
38 | vo = create_html_document().QueryInterface(IViewObject)
39 | cookie = vo.Freeze(DVASPECT_CONTENT, -1, None)
40 | self.assertIsInstance(cookie, int)
41 | vo.Unfreeze(cookie)
42 |
43 |
44 | class Test_IViewObject2(unittest.TestCase):
45 | def test_GetExtent(self):
46 | vo = create_html_document().QueryInterface(IViewObject2)
47 | size = vo.GetExtent(DVASPECT_CONTENT, -1, None)
48 | self.assertTrue(size)
49 | self.assertIsInstance(size, SIZEL)
50 |
51 |
52 | class Test_IViewObjectEx(unittest.TestCase):
53 | def test_GetRect(self):
54 | vo = create_html_document().QueryInterface(IViewObjectEx)
55 | rect = vo.GetRect(DVASPECT_CONTENT)
56 | self.assertTrue(rect)
57 | self.assertIsInstance(rect, RECT)
58 |
59 | def test_GetViewStatus(self):
60 | vo = create_html_document().QueryInterface(IViewObjectEx)
61 | status = vo.GetViewStatus()
62 | self.assertIsInstance(status, int)
63 |
64 | def test_QueryHitPoint(self):
65 | vo = create_html_document().QueryInterface(IViewObjectEx)
66 | # It is assumed that the view is not transparent at the origin.
67 | bounds = RECT(left=0, top=0, right=100, bottom=100)
68 | loc = POINT(x=0, y=0)
69 | hit = vo.QueryHitPoint(DVASPECT_CONTENT, bounds, loc, 0)
70 | self.assertIsInstance(hit, int)
71 |
72 | def test_QueryHitRect(self):
73 | vo = create_html_document().QueryInterface(IViewObjectEx)
74 | # It is assumed that the view is not transparent at the origin.
75 | bounds = RECT(left=0, top=0, right=100, bottom=100)
76 | loc = RECT(left=0, top=0, right=1, bottom=1)
77 | hit = vo.QueryHitRect(DVASPECT_CONTENT, bounds, loc, 0)
78 | self.assertIsInstance(hit, int)
79 |
--------------------------------------------------------------------------------
/comtypes/test/test_msscript.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import POINTER
3 |
4 | from comtypes import GUID
5 | from comtypes.automation import IDispatch
6 | from comtypes.client import CreateObject
7 |
8 | ##from test import test_support
9 | ##from comtypes.unittests import support
10 |
11 | try:
12 | GUID.from_progid("MSScriptControl.ScriptControl")
13 | CreateObject("MSScriptControl.ScriptControl")
14 | except OSError:
15 | # doesn't exist on Windows CE or in 64-bit.
16 | pass
17 | else:
18 |
19 | class Test(unittest.TestCase):
20 | def test_jscript(self):
21 | engine = CreateObject("MSScriptControl.ScriptControl")
22 | engine.Language = "JScript"
23 | # strange.
24 | #
25 | # engine.Eval returns a VARIANT containing a dispatch pointer.
26 | #
27 | # The dispatch pointer exposes this typeinfo (the number of
28 | # dispproperties varies, depending on the length of the list we pass
29 | # to Eval):
30 | #
31 | # class JScriptTypeInfo(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IDispatch):
32 | # 'JScript Type Info'
33 | # _iid_ = GUID('{C59C6B12-F6C1-11CF-8835-00A0C911E8B2}')
34 | # _idlflags_ = []
35 | # _methods_ = []
36 | # JScriptTypeInfo._disp_methods_ = [
37 | # DISPPROPERTY([dispid(9522932)], VARIANT, '0'),
38 | # DISPPROPERTY([dispid(9522976)], VARIANT, '1'),
39 | # ]
40 | #
41 | # Although the exact interface members vary, the guid stays
42 | # the same. Don't think that's allowed by COM standards - is
43 | # this a bug in the MSScriptControl?
44 | #
45 | # What's even more strange is that the returned dispatch
46 | # pointer can't be QI'd for this interface! So it seems the
47 | # typeinfo is really a temporary thing.
48 |
49 | res = engine.Eval("[1, 2, 3, 4]")._comobj
50 |
51 | # comtypes.client works around this bug, by not trying to
52 | # high-level wrap the dispatch pointer because QI for the real
53 | # interface fails.
54 | self.assertEqual(type(res), POINTER(IDispatch))
55 |
56 | tinfo_1 = engine.Eval("[1, 2, 3]")._comobj.GetTypeInfo(0)
57 | tinfo_2 = engine.Eval("[1, 2, 3, 4]")._comobj.GetTypeInfo(0)
58 | tinfo_3 = engine.Eval("[1, 2, 3, 4, 5]")._comobj.GetTypeInfo(0)
59 |
60 | self.assertEqual(tinfo_1.GetTypeAttr().cVars, 3)
61 | self.assertEqual(tinfo_2.GetTypeAttr().cVars, 4)
62 | self.assertEqual(tinfo_3.GetTypeAttr().cVars, 5)
63 |
64 | # These tests simply describe the current behaviour ;-)
65 | self.assertEqual(tinfo_1.GetTypeAttr().guid, tinfo_1.GetTypeAttr().guid)
66 |
67 | ## print (res[0], res[1], res[2])
68 | ## print len(res)
69 |
70 | engine.Reset()
71 |
72 |
73 | if __name__ == "__main__":
74 | unittest.main()
75 |
--------------------------------------------------------------------------------
/comtypes/test/test_client_dynamic.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | import unittest as ut
3 | from unittest import mock
4 |
5 | from comtypes import COMError, IUnknown, automation
6 | from comtypes.client import CreateObject, GetModule, dynamic, lazybind
7 |
8 |
9 | class Test_Dispatch_Function(ut.TestCase):
10 | # It is difficult to cause intentionally errors "in the regular way".
11 | # So `mock` is used to cover conditional branches.
12 | def test_returns_dynamic_Dispatch_if_takes_dynamic_Dispatch(self):
13 | obj = mock.MagicMock(spec=dynamic._Dispatch)
14 | self.assertIs(dynamic.Dispatch(obj), obj)
15 |
16 | def test_returns_lazybind_Dispatch_if_takes_ptrIDispatch(self):
17 | # Conditional branches that return `lazybind.Dispatch` are also covered by
18 | # `test_dyndispatch` and others.
19 | obj = mock.MagicMock(spec=ctypes.POINTER(automation.IDispatch))
20 | self.assertIsInstance(dynamic.Dispatch(obj), lazybind.Dispatch)
21 |
22 | def test_returns_dynamic_Dispatch_if_takes_ptrIDispatch_and_raised_comerr(self):
23 | obj = mock.MagicMock(spec=ctypes.POINTER(automation.IDispatch))
24 | obj.GetTypeInfo.side_effect = COMError(0, "test", ("", "", "", 0, 0))
25 | self.assertIsInstance(dynamic.Dispatch(obj), dynamic._Dispatch)
26 |
27 | def test_returns_dynamic_Dispatch_if_takes_ptrIDispatch_and_raised_winerr(self):
28 | obj = mock.MagicMock(spec=ctypes.POINTER(automation.IDispatch))
29 | obj.GetTypeInfo.side_effect = OSError()
30 | self.assertIsInstance(dynamic.Dispatch(obj), dynamic._Dispatch)
31 |
32 | def test_returns_what_is_took_if_takes_other(self):
33 | obj = object()
34 | self.assertIs(dynamic.Dispatch(obj), obj)
35 |
36 |
37 | class Test_Dispatch_Class(ut.TestCase):
38 | # `MethodCaller` and `_Collection` are indirectly covered in this.
39 | def test_dict(self):
40 | # The following conditional branches are not covered;
41 | # - not `hresult in ERRORS_BAD_CONTEXT`
42 | # - not `0 != enum.Skip(index)`
43 | # - other than `COMError` raises in `__getattr__`
44 | orig = CreateObject("Scripting.Dictionary", interface=automation.IDispatch)
45 | d = dynamic._Dispatch(orig)
46 | d.CompareMode = 42
47 | d.Item["foo"] = 1
48 | d.Item["bar"] = "spam foo"
49 | d.Item["baz"] = 3.14
50 | self.assertEqual(d[0], "foo")
51 | self.assertEqual(d.Item["foo"], 1)
52 | self.assertEqual([k for k in iter(d)], ["foo", "bar", "baz"])
53 | self.assertIsInstance(hash(d), int)
54 | d._FlagAsMethod("_NewEnum")
55 | self.assertIs(type(d._NewEnum()), ctypes.POINTER(IUnknown))
56 | scrrun = GetModule("scrrun.dll")
57 | scr_dict = d.QueryInterface(scrrun.IDictionary)
58 | self.assertIsInstance(scr_dict, scrrun.IDictionary)
59 | d.Item["qux"] = scr_dict
60 | with self.assertRaises(IndexError):
61 | d[4]
62 | with self.assertRaises(AttributeError):
63 | d.__foo__
64 |
65 |
66 | if __name__ == "__main__":
67 | ut.main()
68 |
--------------------------------------------------------------------------------
/.github/workflows/autotest.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request:
3 | branches: [main]
4 | push:
5 | branches: [main]
6 |
7 | jobs:
8 | unit-tests:
9 | runs-on: ${{ matrix.os }}
10 | strategy:
11 | matrix:
12 | os: [windows-latest]
13 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
14 | architecture: ['x86', 'x64']
15 | support: ['with 3rd parties', 'without 3rd parties']
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: Set up Python
19 | uses: actions/setup-python@v5
20 | with:
21 | python-version: ${{ matrix.python-version }}
22 | architecture: ${{ matrix.architecture }}
23 | - name: Set up MSVC
24 | uses: ilammy/msvc-dev-cmd@v1
25 | - name: Build and register the OutProc COM server
26 | run: |
27 | cd source/CppTestSrv
28 | nmake /f Makefile
29 | ./server.exe /RegServer
30 | - name: unittest comtypes
31 | run: |
32 | if ("${{ matrix.support }}" -eq "with 3rd parties") {
33 | pip install numpy
34 | pip install pywin32
35 | }
36 | pip install coverage[toml]
37 | coverage run -m unittest discover -v -s comtypes\test -t comtypes\test
38 | - name: Upload coverage reports to Codecov
39 | uses: codecov/codecov-action@v5
40 | with:
41 | fail_ci_if_error: true
42 | network_filter: comtype
43 | token: ${{ secrets.CODECOV_TOKEN }}
44 | - name: Unregister the OutProc COM server
45 | run: |
46 | cd source/CppTestSrv
47 | ./server.exe /UnregServer
48 |
49 | install-tests:
50 | runs-on: ${{ matrix.os }}
51 | strategy:
52 | matrix:
53 | os: [windows-2025, windows-2022]
54 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
55 | architecture: ['x86', 'x64']
56 | steps:
57 | - uses: actions/checkout@v4
58 | - name: Set up Python
59 | uses: actions/setup-python@v5
60 | with:
61 | python-version: ${{ matrix.python-version }}
62 | architecture: ${{ matrix.architecture }}
63 | - name: install comtypes
64 | run: |
65 | pip install --upgrade build
66 | python -m pip install .
67 | pip uninstall comtypes -y
68 | python test_pip_install.py
69 |
70 | docs-source-doctest:
71 | runs-on: windows-latest
72 | steps:
73 | - uses: actions/checkout@v4
74 | - name: Set up Python
75 | uses: actions/setup-python@v5
76 | with:
77 | python-version: "3.13"
78 | architecture: x64
79 | - name: Install dependencies
80 | run: |
81 | python -m pip install --upgrade pip
82 | pip install -r docs/requirements.txt
83 | - name: Set up MSVC
84 | uses: ilammy/msvc-dev-cmd@v1
85 | - name: Compile IDL
86 | run: midl /out docs\source docs\source\mytypelib.idl
87 | - name: Run doctest
88 | run: sphinx-build -b doctest -d docs/build/doctrees docs/source docs/build/doctest
89 | working-directory: ./
90 |
--------------------------------------------------------------------------------
/comtypes/server/automation.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from ctypes import *
3 |
4 | from comtypes import COMObject, IUnknown
5 | from comtypes.automation import IDispatch, IEnumVARIANT
6 | from comtypes.hresult import *
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | # XXX When the COMCollection class is ready, insert it into __all__
11 | __all__ = ["VARIANTEnumerator"]
12 |
13 |
14 | class VARIANTEnumerator(COMObject):
15 | """A universal VARIANTEnumerator class. Instantiate it with a
16 | collection of items that support the IDispatch interface."""
17 |
18 | _com_interfaces_ = [IEnumVARIANT]
19 |
20 | def __init__(self, items):
21 | self.items = (
22 | items # keep, so that we can restore our iterator (in Reset, and Clone).
23 | )
24 | self.seq = iter(self.items)
25 | super().__init__()
26 |
27 | def Next(self, this, celt, rgVar, pCeltFetched):
28 | if not rgVar:
29 | return E_POINTER
30 | if not pCeltFetched:
31 | pCeltFetched = [None]
32 | pCeltFetched[0] = 0
33 | try:
34 | for index in range(celt):
35 | item = next(self.seq)
36 | p = item.QueryInterface(IDispatch)
37 | rgVar[index].value = p
38 | pCeltFetched[0] += 1
39 | except StopIteration:
40 | pass
41 | # except:
42 | # # ReportException? return E_FAIL?
43 | # import traceback
44 | # traceback.print_exc()
45 |
46 | if pCeltFetched[0] == celt:
47 | return S_OK
48 | return S_FALSE
49 |
50 | def Skip(self, this, celt):
51 | # skip some elements.
52 | try:
53 | for _ in range(celt):
54 | next(self.seq)
55 | except StopIteration:
56 | return S_FALSE
57 | return S_OK
58 |
59 | def Reset(self, this):
60 | self.seq = iter(self.items)
61 | return S_OK
62 |
63 | # Clone not implemented
64 |
65 |
66 | ################################################################
67 |
68 | # XXX Shouldn't this be a mixin class?
69 | # And isn't this class borked anyway?
70 |
71 |
72 | class COMCollection(COMObject):
73 | """Abstract base class which implements Count, Item, and _NewEnum."""
74 |
75 | def __init__(self, itemtype, collection):
76 | self.collection = collection
77 | self.itemtype = itemtype
78 | super().__init__()
79 |
80 | def _get_Item(self, this, pathname, pitem):
81 | if not pitem:
82 | return E_POINTER
83 | item = self.itemtype(pathname)
84 | return item.IUnknown_QueryInterface(None, pointer(pitem[0]._iid_), pitem)
85 |
86 | def _get_Count(self, this, pcount):
87 | if not pcount:
88 | return E_POINTER
89 | pcount[0] = len(self.collection)
90 | return S_OK
91 |
92 | def _get__NewEnum(self, this, penum):
93 | if not penum:
94 | return E_POINTER
95 | enum = VARIANTEnumerator(self.itemtype, self.collection)
96 | return enum.IUnknown_QueryInterface(None, pointer(IUnknown._iid_), penum)
97 |
--------------------------------------------------------------------------------
/source/CppTestSrv/CUNKNOWN.CPP:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | ///////////////////////////////////////////////////////////
10 | //
11 | // CUnknown.cpp
12 | //
13 | // Implementation of IUnknown Base class
14 | //
15 | #include "CUnknown.h"
16 | #include "CFactory.h"
17 | #include "Util.h"
18 |
19 | static inline void trace(char* msg)
20 | {Util::Trace("CUnknown", msg, S_OK) ;}
21 | static inline void trace(char* msg, HRESULT hr)
22 | {Util::Trace("CUnknown", msg, hr) ;}
23 |
24 | ///////////////////////////////////////////////////////////
25 | //
26 | // Count of active objects
27 | // - Use to determine if we can unload the DLL.
28 | //
29 | long CUnknown::s_cActiveComponents = 0 ;
30 |
31 |
32 | ///////////////////////////////////////////////////////////
33 | //
34 | // Constructor
35 | //
36 | CUnknown::CUnknown(IUnknown* pUnknownOuter)
37 | : m_cRef(1)
38 | {
39 | // Set m_pUnknownOuter pointer.
40 | if (pUnknownOuter == NULL)
41 | {
42 | trace("Not aggregating; delegate to nondelegating IUnknown.") ;
43 | m_pUnknownOuter = reinterpret_cast
44 | (static_cast
45 | (this)) ; // notice cast
46 | }
47 | else
48 | {
49 | trace("Aggregating; delegate to outer IUnknown.") ;
50 | m_pUnknownOuter = pUnknownOuter ;
51 | }
52 |
53 | // Increment count of active components.
54 | ::InterlockedIncrement(&s_cActiveComponents) ;
55 | }
56 |
57 | //
58 | // Destructor
59 | //
60 | CUnknown::~CUnknown()
61 | {
62 | ::InterlockedDecrement(&s_cActiveComponents) ;
63 |
64 | // If this is an EXE server, shut it down.
65 | CFactory::CloseExe() ;
66 | }
67 |
68 | //
69 | // FinalRelease - called by Release before it deletes the component
70 | //
71 | void CUnknown::FinalRelease()
72 | {
73 | trace("Increment reference count for final release.") ;
74 | m_cRef = 1 ;
75 | }
76 |
77 | //
78 | // Nondelegating IUnknown
79 | // - Override to handle custom interfaces.
80 | //
81 | HRESULT __stdcall
82 | CUnknown::NondelegatingQueryInterface(const IID& iid, void** ppv)
83 | {
84 | // CUnknown supports only IUnknown.
85 | if (iid == IID_IUnknown)
86 | {
87 | return FinishQI(reinterpret_cast
88 | (static_cast(this)),
89 | ppv) ;
90 | }
91 | else
92 | {
93 | *ppv = NULL ;
94 | return E_NOINTERFACE ;
95 | }
96 | }
97 |
98 | //
99 | // AddRef
100 | //
101 | ULONG __stdcall CUnknown::NondelegatingAddRef()
102 | {
103 | return InterlockedIncrement(&m_cRef) ;
104 | }
105 |
106 | //
107 | // Release
108 | //
109 | ULONG __stdcall CUnknown::NondelegatingRelease()
110 | {
111 | InterlockedDecrement(&m_cRef) ;
112 | if (m_cRef == 0)
113 | {
114 | FinalRelease() ;
115 | delete this ;
116 | return 0 ;
117 | }
118 | return m_cRef ;
119 | }
120 |
121 | //
122 | // FinishQI
123 | // - Helper function to simplify overriding
124 | // NondelegatingQueryInterface
125 | //
126 | HRESULT CUnknown::FinishQI(IUnknown* pI, void** ppv)
127 | {
128 | *ppv = pI ;
129 | pI->AddRef() ;
130 | return S_OK ;
131 | }
132 |
--------------------------------------------------------------------------------
/comtypes/test/test_findgendir.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import importlib
3 | import os
4 | import sys
5 | import tempfile
6 | import types
7 | import unittest
8 | from collections.abc import Iterator
9 | from unittest import mock
10 |
11 | import comtypes
12 | import comtypes.client
13 | import comtypes.gen
14 |
15 | IMGBASE = os.path.splitext(os.path.basename(sys.executable))[0]
16 |
17 |
18 | @contextlib.contextmanager
19 | def patch_empty_module_to_comtypes_gen() -> Iterator[types.ModuleType]:
20 | with mock.patch.dict(sys.modules):
21 | del sys.modules["comtypes.gen"]
22 | mod = sys.modules["comtypes.gen"] = types.ModuleType("comtypes.gen")
23 | mod.__path__ = []
24 | with mock.patch.object(comtypes, "gen", mod):
25 | try:
26 | yield mod
27 | finally:
28 | importlib.reload(comtypes.gen)
29 |
30 |
31 | class Test(unittest.TestCase):
32 | """Test the comtypes.client._find_gen_dir() function in several
33 | simulated environments.
34 | """
35 |
36 | def test_script(self):
37 | # %APPDATA%\Python\Python25\comtypes_cache
38 | ma, mi = sys.version_info[:2]
39 | cache = rf"$APPDATA\Python\Python{ma:d}{mi:d}\comtypes_cache"
40 | path = os.path.expandvars(cache)
41 | with patch_empty_module_to_comtypes_gen():
42 | gen_dir = comtypes.client._find_gen_dir()
43 | self.assertEqual(path, gen_dir)
44 |
45 | # patch py2exe-attributes to `sys` modules
46 | @mock.patch.object(sys, "frozen", "dll", create=True)
47 | @mock.patch.object(sys, "frozendllhandle", sys.dllhandle, create=True)
48 | def test_frozen_dll(self):
49 | # %TEMP%\comtypes_cache\25-25
50 | # the image is python25.dll
51 | ma, mi = sys.version_info[:2]
52 | cache = rf"comtypes_cache\{IMGBASE}{ma:d}{mi:d}-{ma:d}{mi:d}"
53 | path = os.path.join(tempfile.gettempdir(), cache)
54 | with patch_empty_module_to_comtypes_gen():
55 | gen_dir = comtypes.client._find_gen_dir()
56 | self.assertEqual(path, gen_dir)
57 |
58 | # patch py2exe-attributes to `sys` modules
59 | @mock.patch.object(sys, "frozen", "console_exe", create=True)
60 | def test_frozen_console_exe(self):
61 | # %TEMP%\comtypes_cache\-25
62 | ma, mi = sys.version_info[:2]
63 | cache = rf"comtypes_cache\{IMGBASE}-{ma:d}{mi:d}"
64 | path = os.path.join(tempfile.gettempdir(), cache)
65 | with patch_empty_module_to_comtypes_gen():
66 | gen_dir = comtypes.client._find_gen_dir()
67 | self.assertEqual(path, gen_dir)
68 |
69 | # patch py2exe-attributes to `sys` modules
70 | @mock.patch.object(sys, "frozen", "windows_exe", create=True)
71 | def test_frozen_windows_exe(self):
72 | # %TEMP%\comtypes_cache\-25
73 | ma, mi = sys.version_info[:2]
74 | cache = rf"comtypes_cache\{IMGBASE}-{ma:d}{mi:d}"
75 | path = os.path.join(tempfile.gettempdir(), cache)
76 | with patch_empty_module_to_comtypes_gen():
77 | gen_dir = comtypes.client._find_gen_dir()
78 | self.assertEqual(path, gen_dir)
79 |
80 |
81 | def main():
82 | unittest.main()
83 |
84 |
85 | if __name__ == "__main__":
86 | main()
87 |
--------------------------------------------------------------------------------
/source/CppTestSrv/CUNKNOWN.H:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | #ifndef __CUnknown_h__
10 | #define __CUnknown_h__
11 |
12 | #include
13 |
14 | ///////////////////////////////////////////////////////////
15 | //
16 | // Nondelegating IUnknown interface
17 | // - Nondelegating version of IUnknown
18 | //
19 | interface INondelegatingUnknown
20 | {
21 | virtual HRESULT __stdcall
22 | NondelegatingQueryInterface(const IID& iid, void** ppv) = 0 ;
23 | virtual ULONG __stdcall NondelegatingAddRef() = 0 ;
24 | virtual ULONG __stdcall NondelegatingRelease() = 0 ;
25 | } ;
26 |
27 |
28 | ///////////////////////////////////////////////////////////
29 | //
30 | // Declaration of CUnknown
31 | // - Base class for implementing IUnknown
32 | //
33 |
34 | class CUnknown : public INondelegatingUnknown
35 | {
36 | public:
37 | // Nondelegating IUnknown implementation
38 | virtual HRESULT __stdcall NondelegatingQueryInterface(const IID&,
39 | void**) ;
40 | virtual ULONG __stdcall NondelegatingAddRef() ;
41 | virtual ULONG __stdcall NondelegatingRelease() ;
42 |
43 | // Constructor
44 | CUnknown(IUnknown* pUnknownOuter) ;
45 |
46 | // Destructor
47 | virtual ~CUnknown() ;
48 |
49 | // Initialization (especially for aggregates)
50 | virtual HRESULT Init() { return S_OK ;}
51 |
52 | // Notification to derived classes that we are releasing
53 | virtual void FinalRelease() ;
54 |
55 | // Count of currently active components
56 | static long ActiveComponents()
57 | { return s_cActiveComponents ;}
58 |
59 | // Helper function
60 | HRESULT FinishQI(IUnknown* pI, void** ppv) ;
61 |
62 | protected:
63 | // Support for delegation
64 | IUnknown* GetOuterUnknown() const
65 | { return m_pUnknownOuter ;}
66 |
67 | private:
68 | // Reference count for this object
69 | long m_cRef ;
70 |
71 | // Pointer to (external) outer IUnknown
72 | IUnknown* m_pUnknownOuter ;
73 |
74 | // Count of all active instances
75 | static long s_cActiveComponents ;
76 | } ;
77 |
78 |
79 | ///////////////////////////////////////////////////////////
80 | //
81 | // Delegating IUnknown
82 | // - Delegates to the nondelegating IUnknown, or to the
83 | // outer IUnknown if the component is aggregated.
84 | //
85 | #define DECLARE_IUNKNOWN \
86 | virtual HRESULT __stdcall \
87 | QueryInterface(const IID& iid, void** ppv) \
88 | { \
89 | return GetOuterUnknown()->QueryInterface(iid,ppv) ; \
90 | } ; \
91 | virtual ULONG __stdcall AddRef() \
92 | { \
93 | return GetOuterUnknown()->AddRef() ; \
94 | } ; \
95 | virtual ULONG __stdcall Release() \
96 | { \
97 | return GetOuterUnknown()->Release() ; \
98 | } ;
99 |
100 |
101 | ///////////////////////////////////////////////////////////
102 |
103 |
104 | #endif
105 |
--------------------------------------------------------------------------------
/comtypes/test/test_hresult.py:
--------------------------------------------------------------------------------
1 | import doctest
2 | import unittest as ut
3 |
4 | import comtypes.hresult
5 |
6 |
7 | class Test_MAKE_HRESULT(ut.TestCase):
8 | def test(self):
9 | for sev, fac, code, hr in [
10 | (0x0000, 0x0000, 0x0000, 0),
11 | (0x0001, 0x0000, 0x0000, -2147483648),
12 | (0x0000, 0x0001, 0x0000, 65536),
13 | (0x0000, 0x0000, 0x0001, 1),
14 | (0x0001, 0xFFFF, 0xFFFF, -1),
15 | (0x0000, 0xFFFF, 0xFFFF, -1),
16 | (0x0001, 0x0000, 0x0001, -2147483647),
17 | (0x0001, 0x0001, 0x0001, -2147418111),
18 | (0x0000, 0x0001, 0xFFFF, 131071),
19 | (0x0001, 0xFFFF, 0x0000, -65536),
20 | (0x0001, 0x0000, 0xFFFF, -2147418113),
21 | ]:
22 | with self.subTest(sev=sev, fac=fac, code=code, hr=hr):
23 | self.assertEqual(comtypes.hresult.MAKE_HRESULT(sev, fac, code), hr)
24 |
25 |
26 | ERROR_OUTOFMEMORY = 14 # 0xE
27 | ERROR_INVALID_PARAMETER = 87 # 0x57
28 | RPC_S_SERVER_UNAVAILABLE = 1722 # 0x6BA
29 |
30 |
31 | class Test_HRESULT_FROM_WIN32(ut.TestCase):
32 | def test(self):
33 | for w32, hr in [
34 | (ERROR_OUTOFMEMORY, comtypes.hresult.E_OUTOFMEMORY),
35 | (ERROR_INVALID_PARAMETER, comtypes.hresult.E_INVALIDARG),
36 | (RPC_S_SERVER_UNAVAILABLE, comtypes.hresult.RPC_S_SERVER_UNAVAILABLE),
37 | (-1, -1),
38 | (0, -2147024896),
39 | (1, -2147024895),
40 | (0xFFFF - 3, -2146959364),
41 | (0xFFFF - 2, -2146959363),
42 | (0xFFFF - 1, -2146959362),
43 | (0xFFFF + 0, -2146959361),
44 | (0xFFFF + 1, -2147024896),
45 | (0xFFFF + 2, -2147024895),
46 | (0xFFFF + 3, -2147024894),
47 | ]:
48 | with self.subTest(w32=w32, hr=hr):
49 | self.assertEqual(comtypes.hresult.HRESULT_FROM_WIN32(w32), hr)
50 |
51 |
52 | class Test_signed32bithex_to_int(ut.TestCase):
53 | def test(self):
54 | for val, expected in [
55 | ("0x00000000", comtypes.hresult.S_OK),
56 | ("0x00000001", comtypes.hresult.S_FALSE),
57 | ("0x8000FFFF", comtypes.hresult.E_UNEXPECTED),
58 | ("0x80004002", comtypes.hresult.E_NOINTERFACE),
59 | # boundary values
60 | ("0x7FFFFFFF", 2147483647),
61 | ("0x80000000", -2147483648),
62 | ("0xFFFFFFFF", -1),
63 | ]:
64 | with self.subTest(val=val, expected=expected):
65 | self.assertEqual(comtypes.hresult.signed32bithex_to_int(val), expected)
66 |
67 |
68 | class Test_int_to_signed32bithex(ut.TestCase):
69 | def test(self):
70 | for val, expected in [
71 | (comtypes.hresult.S_OK, "0x00000000"),
72 | (comtypes.hresult.S_FALSE, "0x00000001"),
73 | (comtypes.hresult.E_UNEXPECTED, "0x8000FFFF"),
74 | (comtypes.hresult.E_NOINTERFACE, "0x80004002"),
75 | ]:
76 | with self.subTest(val=val, expected=expected):
77 | self.assertEqual(comtypes.hresult.int_to_signed32bithex(val), expected)
78 |
79 |
80 | class DocTest(ut.TestCase):
81 | def test(self):
82 | doctest.testmod(
83 | comtypes.hresult,
84 | verbose=False,
85 | optionflags=doctest.ELLIPSIS,
86 | raise_on_error=True,
87 | )
88 |
--------------------------------------------------------------------------------
/comtypes/test/test_dyndispatch.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from comtypes.automation import IDispatch
4 | from comtypes.client import CreateObject, GetModule
5 | from comtypes.client.lazybind import Dispatch
6 |
7 | # create the typelib wrapper and import it
8 | GetModule("scrrun.dll")
9 | from comtypes.gen.Scripting import IDictionary
10 |
11 |
12 | class Test(unittest.TestCase):
13 | def setUp(self):
14 | self.d = CreateObject("Scripting.Dictionary", dynamic=True)
15 |
16 | def tearDown(self):
17 | del self.d
18 |
19 | def test_type(self):
20 | self.assertTrue(isinstance(self.d, Dispatch))
21 |
22 | def test_index_setter(self):
23 | d = self.d
24 | d.CompareMode = 42
25 | d["foo"] = 1
26 | d["bar"] = "spam foo"
27 | d["baz"] = 3.14
28 | self.assertAccessInterface(d)
29 |
30 | def test_named_property_setter(self):
31 | d = self.d
32 | d.CompareMode = 42
33 | d.Item["foo"] = 1
34 | d.Item["bar"] = "spam foo"
35 | d.Item["baz"] = 3.14
36 | self.assertAccessInterface(d)
37 |
38 | def test_reference_passing(self):
39 | d = self.d
40 |
41 | # Check reference passing
42 | d["self"] = d
43 | d[0] = "something nontrivial"
44 | dself = d["self"]
45 | dself[1] = "something else nontrivial"
46 | self.assertEqual(d, dself)
47 | self.assertEqual(d[0], "something nontrivial")
48 | self.assertEqual(dself[0], d[0])
49 | self.assertEqual(d[1], "something else nontrivial")
50 | self.assertEqual(dself[1], d[1])
51 |
52 | def test_query_interface(self):
53 | d = self.d
54 | d.CompareMode = 42
55 | d.Item["foo"] = 1
56 | d.Item["bar"] = "spam foo"
57 | d.Item["baz"] = 3.14
58 |
59 | # This should cast the underlying com object to an IDispatch
60 | d2 = d.QueryInterface(IDispatch)
61 | # Which can be cast to the non-dynamic type
62 | d3 = d2.QueryInterface(IDictionary)
63 | self.assertEqual(d3.CompareMode, 42)
64 | self.assertEqual(d3.Item["foo"], 1)
65 | self.assertEqual(d3.Item["bar"], "spam foo")
66 | self.assertEqual(d3.Item["baz"], 3.14)
67 |
68 | def test_named_property_no_length(self):
69 | with self.assertRaises(TypeError):
70 | len(self.d.Item)
71 |
72 | def test_named_property_not_iterable(self):
73 | with self.assertRaises(TypeError):
74 | list(self.d.Item)
75 |
76 | def assertAccessInterface(self, d):
77 | """Asserts access via indexing and named property"""
78 | self.assertEqual(d.CompareMode, 42)
79 | self.assertEqual(d["foo"], 1)
80 | self.assertEqual(d.Item["foo"], d["foo"])
81 | self.assertEqual(d.Item("foo"), d["foo"])
82 | self.assertEqual(d["bar"], "spam foo")
83 | self.assertEqual(d.Item("bar"), "spam foo")
84 | self.assertEqual(d["baz"], 3.14)
85 | self.assertEqual(d.Item("baz"), d["baz"])
86 | self.assertIsNone(d["asdlfkj"])
87 | self.assertIsNone(d.Item["asdlfkj"])
88 | self.assertIsNone(d.Item("asdlfkj"))
89 |
90 | items = iter(d)
91 | self.assertEqual(items[0], "foo")
92 | self.assertEqual(items[1], "bar")
93 | self.assertEqual(items[2], "baz")
94 | self.assertEqual(items[3], "asdlfkj")
95 |
96 |
97 | if __name__ == "__main__":
98 | unittest.main()
99 |
--------------------------------------------------------------------------------
/comtypes/test/test_outparam.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import (
3 | HRESULT,
4 | POINTER,
5 | OleDLL,
6 | WinDLL,
7 | byref,
8 | c_int,
9 | c_size_t,
10 | c_ulong,
11 | c_void_p,
12 | c_wchar,
13 | c_wchar_p,
14 | cast,
15 | memmove,
16 | sizeof,
17 | wstring_at,
18 | )
19 | from ctypes.wintypes import DWORD, LPVOID
20 | from unittest.mock import patch
21 |
22 | from comtypes import COMMETHOD, GUID, IUnknown
23 | from comtypes.GUID import _CoTaskMemFree
24 |
25 | text_type = str
26 |
27 |
28 | class IMalloc(IUnknown):
29 | _iid_ = GUID("{00000002-0000-0000-C000-000000000046}")
30 | _methods_ = [
31 | COMMETHOD([], c_void_p, "Alloc", ([], c_ulong, "cb")),
32 | COMMETHOD([], c_void_p, "Realloc", ([], c_void_p, "pv"), ([], c_ulong, "cb")),
33 | COMMETHOD([], None, "Free", ([], c_void_p, "py")),
34 | COMMETHOD([], c_ulong, "GetSize", ([], c_void_p, "pv")),
35 | COMMETHOD([], c_int, "DidAlloc", ([], c_void_p, "pv")),
36 | COMMETHOD([], None, "HeapMinimize"), # 25
37 | ]
38 |
39 |
40 | _ole32 = OleDLL("ole32")
41 |
42 | _CoGetMalloc = _ole32.CoGetMalloc
43 | _CoGetMalloc.argtypes = [DWORD, POINTER(POINTER(IMalloc))]
44 | _CoGetMalloc.restype = HRESULT
45 |
46 | _ole32_nohresult = WinDLL("ole32")
47 |
48 | SIZE_T = c_size_t
49 | _CoTaskMemAlloc = _ole32_nohresult.CoTaskMemAlloc
50 | _CoTaskMemAlloc.argtypes = [SIZE_T]
51 | _CoTaskMemAlloc.restype = LPVOID
52 |
53 | malloc = POINTER(IMalloc)()
54 | _CoGetMalloc(1, byref(malloc))
55 | assert bool(malloc)
56 |
57 |
58 | def from_outparm(self):
59 | if not self:
60 | return None
61 | result = wstring_at(self)
62 | if not malloc.DidAlloc(self):
63 | raise ValueError("memory was NOT allocated by CoTaskMemAlloc")
64 | _CoTaskMemFree(self)
65 | return result
66 |
67 |
68 | def comstring(text, typ=c_wchar_p):
69 | text = text_type(text)
70 | size = (len(text) + 1) * sizeof(c_wchar)
71 | mem = _CoTaskMemAlloc(size)
72 | print("malloc'd 0x%x, %d bytes" % (mem, size))
73 | ptr = cast(mem, typ)
74 | memmove(mem, text, size)
75 | return ptr
76 |
77 |
78 | class Test(unittest.TestCase):
79 | @unittest.skip("This fails for reasons I don't understand yet")
80 | # TODO untested changes; this was modified because it had global effects on other tests
81 | @patch.object(c_wchar_p, "__ctypes_from_outparam__", from_outparm)
82 | def test_c_char(self):
83 | # ptr = c_wchar_p("abc")
84 | # self.failUnlessEqual(ptr.__ctypes_from_outparam__(),
85 | # "abc")
86 |
87 | # p = BSTR("foo bar spam")
88 |
89 | x = comstring("Hello, World")
90 | y = comstring("foo bar")
91 | z = comstring("spam, spam, and spam")
92 |
93 | # (x.__ctypes_from_outparam__(), x.__ctypes_from_outparam__())
94 | print((x.__ctypes_from_outparam__(), None)) # x.__ctypes_from_outparam__())
95 |
96 | # print comstring("Hello, World", c_wchar_p).__ctypes_from_outparam__()
97 | # print comstring("Hello, World", c_wchar_p).__ctypes_from_outparam__()
98 | # print comstring("Hello, World", c_wchar_p).__ctypes_from_outparam__()
99 | # print comstring("Hello, World", c_wchar_p).__ctypes_from_outparam__()
100 |
101 |
102 | if __name__ == "__main__":
103 | unittest.main()
104 |
--------------------------------------------------------------------------------
/comtypes/test/test_monikers.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import unittest
3 | from _ctypes import COMError
4 | from ctypes import HRESULT, POINTER, OleDLL, byref, c_wchar_p
5 | from ctypes.wintypes import DWORD
6 |
7 | from comtypes import GUID, hresult
8 | from comtypes.client import CreateObject, GetModule
9 |
10 | with contextlib.redirect_stdout(None): # supress warnings
11 | GetModule("msvidctl.dll")
12 | from comtypes.gen import MSVidCtlLib as msvidctl
13 | from comtypes.gen.MSVidCtlLib import IBindCtx, IMoniker, IRunningObjectTable
14 |
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 |
33 |
34 | def _create_item_moniker(delim: str, item: str) -> IMoniker:
35 | mon = POINTER(IMoniker)()
36 | _CreateItemMoniker(delim, item, byref(mon))
37 | return mon # type: ignore
38 |
39 |
40 | def _create_bctx() -> IBindCtx:
41 | bctx = POINTER(IBindCtx)()
42 | # The first parameter is reserved and must be 0.
43 | _CreateBindCtx(0, byref(bctx))
44 | return bctx # type: ignore
45 |
46 |
47 | def _create_rot() -> IRunningObjectTable:
48 | rot = POINTER(IRunningObjectTable)()
49 | # The first parameter is reserved and must be 0.
50 | _GetRunningObjectTable(0, byref(rot))
51 | return rot # type: ignore
52 |
53 |
54 | class Test_IMoniker(unittest.TestCase):
55 | def test_IsSystemMoniker(self):
56 | item_id = str(GUID.create_new())
57 | mon = _create_item_moniker("!", item_id)
58 | self.assertEqual(mon.IsSystemMoniker(), MKSYS_ITEMMONIKER)
59 |
60 |
61 | class Test_IBindCtx(unittest.TestCase):
62 | def test_EnumObjectParam(self):
63 | 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)
69 |
70 |
71 | class Test_IRunningObjectTable(unittest.TestCase):
72 | def test_register_and_revoke_item_moniker(self):
73 | vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl)
74 | item_id = str(GUID.create_new())
75 | mon = _create_item_moniker("!", item_id)
76 | rot = _create_rot()
77 | bctx = _create_bctx()
78 | self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE)
79 | dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon)
80 | 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)
83 | rot.Revoke(dw_reg)
84 | self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE)
85 |
--------------------------------------------------------------------------------
/source/CppTestSrv/SERVER.IDL:
--------------------------------------------------------------------------------
1 | //
2 | // Server.idl
3 | //
4 | // This file will be processed by the MIDL compiler to
5 | // produce the type library (server.tlb) and marshaling code.
6 | //
7 |
8 | import "oaidl.idl" ;
9 |
10 | // Simple structure used for tests related to IRecordInfo or GetRecordInfoFromGuids functionality.
11 | // If a new test would require other fields do NOT modify this structure but add a new structure instead.
12 | typedef [uuid(00FABB0F-5691-41A6-B7C1-11606671F8E5)]
13 | struct StructRecordParamTest {
14 | BSTR question ;
15 | long answer ;
16 | VARIANT_BOOL needs_clarification ;
17 | } StructRecordParamTest ;
18 |
19 |
20 | // Interface IDualRecordParamTest
21 | [
22 | odl,
23 | uuid(0C4E01E8-4625-46A2-BC4C-2E889A8DBBD6),
24 | dual,
25 | helpstring("Dual Interface for testing record parameters."),
26 | nonextensible,
27 | oleautomation
28 | ]
29 | interface IDualRecordParamTest : IDispatch
30 | {
31 | [id(0x00000001)]
32 | HRESULT InitRecord([in, out] StructRecordParamTest* test_record) ;
33 | [id(0x00000002)]
34 | HRESULT VerifyRecord(
35 | [in] StructRecordParamTest* test_record,
36 | [out, retval] VARIANT_BOOL* result);
37 | } ;
38 |
39 |
40 | // Interface IDispRecordParamTest
41 | [
42 | uuid(033E4C10-0A7F-4E93-8377-499AD4B6583A),
43 | helpstring("Dispinterface for testing record parameters.")
44 | ]
45 | dispinterface IDispRecordParamTest
46 | {
47 | interface IDualRecordParamTest;
48 | } ;
49 |
50 |
51 | // Interface IDualSafearrayParamTest
52 | [
53 | odl,
54 | uuid(1F4F3B8B-D07E-4BB6-8D2C-D79B375696DA),
55 | dual,
56 | helpstring("IDualSafearrayParamTest Interface"),
57 | nonextensible,
58 | oleautomation
59 | ]
60 | interface IDualSafearrayParamTest : IDispatch
61 | {
62 | [id(0x00000001)]
63 | HRESULT InitArray([in, out] SAFEARRAY(double)* test_array) ;
64 | [id(0x00000002)]
65 | HRESULT VerifyArray(
66 | [in] SAFEARRAY(double) test_array,
67 | [out, retval] VARIANT_BOOL* result);
68 | } ;
69 |
70 |
71 | // Interface IDispSafearrayParamTest
72 | [
73 | uuid(4097A6D0-A111-40E2-BD0B-177B775A9496)
74 | ]
75 | dispinterface IDispSafearrayParamTest
76 | {
77 | interface IDualSafearrayParamTest;
78 | } ;
79 |
80 |
81 | //
82 | // Component and type library descriptions
83 | //
84 | [
85 | uuid(07D2AEE5-1DF8-4D2C-953A-554ADFD25F99),
86 | version(1.0),
87 | helpstring("Comtypes C++ Test COM Server 1.0 Type Library.")
88 | ]
89 | library ComtypesCppTestSrvLib
90 | {
91 | // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
92 | importlib("stdole2.tlb") ;
93 |
94 | // CoComtypesDispRecordParamTest
95 | // Component that implements interfaces used for dispinterface record parameter tests.
96 | [
97 | uuid(5E78C9A8-4C19-4285-BCD6-3FFBBA5B17A8),
98 | helpstring("Comtypes component for dispinterface record parameter tests.")
99 | ]
100 | coclass CoComtypesDispRecordParamTest
101 | {
102 | interface IDualRecordParamTest ;
103 | dispinterface IDispRecordParamTest ;
104 | } ;
105 |
106 |
107 | // CoComtypesDispSafearrayParamTest
108 | // Component that implements interfaces used for dispinterface Safearray parameter tests.
109 | [
110 | uuid(091D762E-FF4B-4532-8B24-23807FE873C3),
111 | helpstring("Comtypes component for dispinterface Safearray parameter tests.")
112 | ]
113 | coclass CoComtypesDispSafearrayParamTest
114 | {
115 | interface IDualSafearrayParamTest ;
116 | dispinterface IDispSafearrayParamTest ;
117 | } ;
118 | } ;
119 |
--------------------------------------------------------------------------------
/comtypes/server/localserver.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import queue
3 | from collections.abc import Sequence
4 | from ctypes import HRESULT, OleDLL, byref, c_ulong, c_void_p
5 | from ctypes.wintypes import DWORD, LPDWORD
6 | from typing import TYPE_CHECKING, Any, Literal, Optional
7 |
8 | import comtypes
9 | from comtypes import GUID, COMObject, IUnknown, hresult
10 | from comtypes.GUID import REFCLSID
11 | from comtypes.server import IClassFactory
12 |
13 | if TYPE_CHECKING:
14 | from ctypes import _Pointer
15 |
16 |
17 | logger = logging.getLogger(__name__)
18 | _debug = logger.debug
19 |
20 | REGCLS_SINGLEUSE = 0 # class object only generates one instance
21 | REGCLS_MULTIPLEUSE = 1 # same class object genereates multiple inst.
22 | REGCLS_MULTI_SEPARATE = 2 # multiple use, but separate control over each
23 | REGCLS_SUSPENDED = 4 # register it as suspended, will be activated
24 | REGCLS_SURROGATE = 8 # must be used when a surrogate process
25 |
26 | _ole32 = OleDLL("ole32")
27 |
28 | _CoRegisterClassObject = _ole32.CoRegisterClassObject
29 | _CoRegisterClassObject.argtypes = [REFCLSID, c_void_p, DWORD, DWORD, LPDWORD]
30 | _CoRegisterClassObject.restype = HRESULT
31 |
32 | _CoRevokeClassObject = _ole32.CoRevokeClassObject
33 | _CoRevokeClassObject.argtypes = [DWORD]
34 | _CoRevokeClassObject.restype = HRESULT
35 |
36 |
37 | def run(classes: Sequence[type[COMObject]]) -> None:
38 | classobjects = [ClassFactory(cls) for cls in classes]
39 | COMObject.__run_localserver__(classobjects)
40 |
41 |
42 | class ClassFactory(COMObject):
43 | _com_interfaces_ = [IClassFactory]
44 | _locks: int = 0
45 | _queue: Optional[queue.Queue] = None
46 | regcls: int = REGCLS_MULTIPLEUSE
47 |
48 | def __init__(self, cls: type[COMObject], *args, **kw) -> None:
49 | super().__init__()
50 | self._cls = cls
51 | self._register_class()
52 | self._args = args
53 | self._kw = kw
54 |
55 | def IUnknown_AddRef(self, this: Any) -> int:
56 | return 2
57 |
58 | def IUnknown_Release(self, this: Any) -> int:
59 | return 1
60 |
61 | def _register_class(self) -> None:
62 | regcls = getattr(self._cls, "_regcls_", self.regcls)
63 | cookie = c_ulong()
64 | ptr = self._com_pointers_[IUnknown._iid_]
65 | clsctx = self._cls._reg_clsctx_ # type: ignore
66 | clsctx &= ~comtypes.CLSCTX_INPROC # reset the inproc flags
67 | _CoRegisterClassObject(
68 | byref(GUID(self._cls._reg_clsid_)),
69 | ptr,
70 | clsctx,
71 | regcls,
72 | byref(cookie),
73 | )
74 | self.cookie = cookie
75 |
76 | def _revoke_class(self) -> None:
77 | _CoRevokeClassObject(self.cookie)
78 |
79 | def CreateInstance(
80 | self,
81 | this: Any,
82 | punkOuter: Optional[type["_Pointer[IUnknown]"]],
83 | riid: "_Pointer[GUID]",
84 | ppv: c_void_p,
85 | ) -> int:
86 | _debug("ClassFactory.CreateInstance(%s)", riid[0])
87 | obj = self._cls(*self._args, **self._kw)
88 | result = obj.IUnknown_QueryInterface(None, riid, ppv)
89 | _debug("CreateInstance() -> %s", result)
90 | return result
91 |
92 | def LockServer(self, this: Any, fLock: bool) -> Literal[0]:
93 | assert COMObject.__server__ is not None, "The localserver is not running yet"
94 | if fLock:
95 | COMObject.__server__.Lock()
96 | else:
97 | COMObject.__server__.Unlock()
98 | return hresult.S_OK
99 |
--------------------------------------------------------------------------------
/comtypes/tools/codegenerator/comments.py:
--------------------------------------------------------------------------------
1 | import io
2 |
3 | from comtypes.tools import typedesc
4 |
5 |
6 | class ComInterfaceBodyImplCommentWriter:
7 | def __init__(self, stream: io.StringIO):
8 | self.stream = stream
9 |
10 | def write(self, body: typedesc.ComInterfaceBody) -> None:
11 | print(
12 | "################################################################",
13 | file=self.stream,
14 | )
15 | print(f"# code template for {body.itf.name} implementation", file=self.stream)
16 | print(f"# class {body.itf.name}_Impl(object):", file=self.stream)
17 |
18 | methods = {}
19 | for m in body.itf.members:
20 | if isinstance(m, typedesc.ComMethod):
21 | # m.arguments is a sequence of tuples:
22 | # (argtype, argname, idlflags, docstring)
23 | # Some typelibs have unnamed method parameters!
24 | inargs = [a[1] or "" for a in m.arguments if "out" not in a[2]]
25 | outargs = [a[1] or "" for a in m.arguments if "out" in a[2]]
26 | if "propget" in m.idlflags:
27 | methods.setdefault(m.name, [0, inargs, outargs, m.doc])[0] |= 1
28 | elif "propput" in m.idlflags:
29 | methods.setdefault(m.name, [0, inargs[:-1], inargs[-1:], m.doc])[
30 | 0
31 | ] |= 2
32 | else:
33 | methods[m.name] = [0, inargs, outargs, m.doc]
34 |
35 | for name, (typ, inargs, outargs, doc) in methods.items():
36 | if typ == 0: # method
37 | print(
38 | f"# def {name}({', '.join(['self'] + inargs)}):",
39 | file=self.stream,
40 | )
41 | print(f"# {(doc or '-no docstring-')!r}", file=self.stream)
42 | print(f"# #return {', '.join(outargs)}", file=self.stream)
43 | elif typ == 1: # propget
44 | print("# @property", file=self.stream)
45 | print(
46 | f"# def {name}({', '.join(['self'] + inargs)}):",
47 | file=self.stream,
48 | )
49 | print(f"# {(doc or '-no docstring-')!r}", file=self.stream)
50 | print(f"# #return {', '.join(outargs)}", file=self.stream)
51 | elif typ == 2: # propput
52 | print(
53 | f"# def _set({', '.join(['self'] + inargs + outargs)}):",
54 | file=self.stream,
55 | )
56 | print(f"# {(doc or '-no docstring-')!r}", file=self.stream)
57 | print(
58 | f"# {name} = property(fset = _set, doc = _set.__doc__)",
59 | file=self.stream,
60 | )
61 | elif typ == 3: # propget + propput
62 | print(
63 | f"# def _get({', '.join(['self'] + inargs)}):",
64 | file=self.stream,
65 | )
66 | print(f"# {(doc or '-no docstring-')!r}", file=self.stream)
67 | print(f"# #return {', '.join(outargs)}", file=self.stream)
68 | print(
69 | f"# def _set({', '.join(['self'] + inargs + outargs)}):",
70 | file=self.stream,
71 | )
72 | print(f"# {(doc or '-no docstring-')!r}", file=self.stream)
73 | print(
74 | f"# {name} = property(_get, _set, doc = _set.__doc__)",
75 | file=self.stream,
76 | )
77 | else:
78 | raise RuntimeError("BUG")
79 | print("#", file=self.stream)
80 |
--------------------------------------------------------------------------------
/source/AvmcIfc.rc:
--------------------------------------------------------------------------------
1 | //Microsoft Developer Studio generated resource script.
2 | //
3 | #include "resource.h"
4 |
5 | #define APSTUDIO_READONLY_SYMBOLS
6 | /////////////////////////////////////////////////////////////////////////////
7 | //
8 | // Generated from the TEXTINCLUDE 2 resource.
9 | //
10 | #include "winres.h"
11 |
12 | /////////////////////////////////////////////////////////////////////////////
13 | #undef APSTUDIO_READONLY_SYMBOLS
14 |
15 | /////////////////////////////////////////////////////////////////////////////
16 | // English (U.S.) resources
17 |
18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
19 | #ifdef _WIN32
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 | #pragma code_page(1252)
22 | #endif //_WIN32
23 |
24 | #ifdef APSTUDIO_INVOKED
25 | /////////////////////////////////////////////////////////////////////////////
26 | //
27 | // TEXTINCLUDE
28 | //
29 |
30 | 1 TEXTINCLUDE DISCARDABLE
31 | BEGIN
32 | "resource.h\0"
33 | END
34 |
35 | 2 TEXTINCLUDE DISCARDABLE
36 | BEGIN
37 | "#include ""winres.h""\r\n"
38 | "\0"
39 | END
40 |
41 | 3 TEXTINCLUDE DISCARDABLE
42 | BEGIN
43 | "1 TYPELIB ""AvmcIfc.tlb""\r\n"
44 | "\0"
45 | END
46 |
47 | #endif // APSTUDIO_INVOKED
48 |
49 |
50 | #ifndef _MAC
51 | /////////////////////////////////////////////////////////////////////////////
52 | //
53 | // Version
54 | //
55 |
56 | VS_VERSION_INFO VERSIONINFO
57 | FILEVERSION 1,0,0,1
58 | PRODUCTVERSION 1,0,0,1
59 | FILEFLAGSMASK 0x3fL
60 | #ifdef _DEBUG
61 | FILEFLAGS 0x1L
62 | #else
63 | FILEFLAGS 0x0L
64 | #endif
65 | FILEOS 0x4L
66 | FILETYPE 0x2L
67 | FILESUBTYPE 0x0L
68 | BEGIN
69 | BLOCK "StringFileInfo"
70 | BEGIN
71 | BLOCK "040904B0"
72 | BEGIN
73 | VALUE "CompanyName", "\0"
74 | VALUE "FileDescription", "AvmcIfc Module\0"
75 | VALUE "FileVersion", "1, 0, 0, 1\0"
76 | VALUE "InternalName", "AvmcIfc\0"
77 | VALUE "LegalCopyright", "Copyright 2006\0"
78 | VALUE "OriginalFilename", "AvmcIfc.DLL\0"
79 | VALUE "ProductName", "AvmcIfc Module\0"
80 | VALUE "ProductVersion", "1, 0, 0, 1\0"
81 | VALUE "OLESelfRegister", "\0"
82 | END
83 | END
84 | BLOCK "VarFileInfo"
85 | BEGIN
86 | VALUE "Translation", 0x409, 1200
87 | END
88 | END
89 |
90 | #endif // !_MAC
91 |
92 |
93 | /////////////////////////////////////////////////////////////////////////////
94 | //
95 | // String Table
96 | //
97 |
98 | STRINGTABLE DISCARDABLE
99 | BEGIN
100 | IDS_PROJNAME "AvmcIfc"
101 | IDS_AVMC_DESC "Avmc Class"
102 | END
103 |
104 | #endif // English (U.S.) resources
105 | /////////////////////////////////////////////////////////////////////////////
106 |
107 |
108 | /////////////////////////////////////////////////////////////////////////////
109 | // Unknown language: 0xD, 0x1 resources
110 |
111 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_HEB)
112 | #ifdef _WIN32
113 | LANGUAGE 0xD, 0x1
114 | #pragma code_page(1255)
115 | #endif //_WIN32
116 |
117 | /////////////////////////////////////////////////////////////////////////////
118 | //
119 | // REGISTRY
120 | //
121 |
122 | IDR_Avmc REGISTRY DISCARDABLE "Avmc.rgs"
123 | #endif // Unknown language: 0xD, 0x1 resources
124 | /////////////////////////////////////////////////////////////////////////////
125 |
126 |
127 |
128 | #ifndef APSTUDIO_INVOKED
129 | /////////////////////////////////////////////////////////////////////////////
130 | //
131 | // Generated from the TEXTINCLUDE 3 resource.
132 | //
133 | 1 TYPELIB "AvmcIfc.tlb"
134 |
135 | /////////////////////////////////////////////////////////////////////////////
136 | #endif // not APSTUDIO_INVOKED
137 |
138 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "comtypes"
3 | description = "Pure Python COM package"
4 | readme = "README.md"
5 | requires-python = ">=3.9"
6 | license = "MIT"
7 | license-files = ["LICENSE.txt"]
8 | authors = [
9 | { name = "Thomas Heller", email = "theller@python.net" },
10 | ]
11 | classifiers = [
12 | "Development Status :: 5 - Production/Stable",
13 | "Intended Audience :: Developers",
14 | "Operating System :: Microsoft :: Windows",
15 | "Programming Language :: Python",
16 | "Programming Language :: Python :: 3",
17 | "Topic :: Software Development :: Libraries :: Python Modules",
18 | ]
19 | dynamic = ["version"]
20 |
21 | [project.scripts]
22 | clear_comtypes_cache = "comtypes.clear_cache:main"
23 |
24 | [project.urls]
25 | Source = "https://github.com/enthought/comtypes"
26 | Download = "https://github.com/enthought/comtypes/releases"
27 | Issues = "https://github.com/enthought/comtypes/issues"
28 |
29 | [build-system]
30 | requires = ["setuptools>=77.0.0"]
31 | build-backend = "setuptools.build_meta"
32 |
33 | [tool.setuptools]
34 | packages = [
35 | "comtypes",
36 | "comtypes._post_coinit",
37 | "comtypes.client",
38 | "comtypes.server",
39 | "comtypes.tools",
40 | "comtypes.tools.codegenerator",
41 | "comtypes.test",
42 | ]
43 |
44 | [tool.setuptools.dynamic]
45 | version = { attr = "comtypes.__version__" }
46 |
47 | [tool.setuptools.package-data]
48 | "comtypes.test" = [
49 | "TestComServer.idl",
50 | "TestComServer.tlb",
51 | "TestDispServer.idl",
52 | "TestDispServer.tlb",
53 | "mytypelib.idl",
54 | "mylib.idl",
55 | "mylib.tlb",
56 | "urlhist.tlb",
57 | "test_jscript.js",
58 | ]
59 | "comtypes" = ["hints.pyi"]
60 |
61 | [tool.ruff.lint]
62 | extend-select = ["I"]
63 | ignore = ["E402"]
64 |
65 | [tool.ruff.lint.per-file-ignores]
66 | # production
67 | "comtypes/_npsupport.py" = ["F401"]
68 | "comtypes/_vtbl.py" = ["E722"]
69 | "comtypes/automation.py" = ["F401", "F403", "F405"]
70 | "comtypes/git.py" = ["F401", "F403", "F405"]
71 | "comtypes/viewobject.py" = ["F403", "F405"]
72 | "comtypes/client/_constants.py" = ["F401"]
73 | "comtypes/server/automation.py" = ["F403", "F405"]
74 | "comtypes/server/connectionpoints.py" = ["F401", "F403", "F405"]
75 | "comtypes/server/inprocserver.py" = ["E722"]
76 | "comtypes/server/register.py" = ["E713"]
77 | "comtypes/tools/codegenerator/packing.py" = ["F821", "F841"]
78 | "comtypes/tools/typedesc.py" = ["F403", "F405"]
79 | "comtypes/tools/typedesc_base.py" = ["F401"]
80 | # gen directory
81 | "comtypes/gen/*" = ["E", "F", "I"]
82 | # stub
83 | "comtypes/hints.pyi" = ["I"]
84 | # tests
85 | "comtypes/test/TestDispServer.py" = ["E401"]
86 | "comtypes/test/find_memleak.py" = ["E401", "F401", "F403", "F405"]
87 | "comtypes/test/setup.py" = ["F401"]
88 | "comtypes/test/test_agilent.py" = ["F401", "F841"]
89 | "comtypes/test/test_client.py" = ["F401"]
90 | "comtypes/test/test_dict.py" = ["F841"]
91 | "comtypes/test/test_eventinterface.py" = ["F841"]
92 | "comtypes/test/test_outparam.py" = ["F841"]
93 | "comtypes/test/test_sapi.py" = ["E401"]
94 | "comtypes/test/test_server.py" = ["F401", "F841"]
95 | "comtypes/test/test_subinterface.py" = ["E401", "F401", "F403", "F405"]
96 | "comtypes/test/test_urlhistory.py" = ["E401", "F401", "F403", "F405", "F841"]
97 | "comtypes/test/test_variant.py" = ["F401", "F821", "F841"]
98 |
99 | [tool.coverage.run]
100 | # Specify the source directory to avoid tracking temporary files created by "test_client_regenerate_modules.py".
101 | # Without this, coverage track these temporary files, leading to error when trying to generate a report.
102 | source = ["comtypes"]
103 | omit = ["comtypes/gen/*"]
104 |
105 | [tool.coverage.report]
106 | exclude_also = [
107 | "if __name__ == .__main__.:",
108 | "if TYPE_CHECKING:",
109 | ]
110 |
--------------------------------------------------------------------------------
/source/CppTestSrv/CFACTORY.H:
--------------------------------------------------------------------------------
1 | /*
2 | This code is based on example code to the book:
3 | Inside COM
4 | by Dale E. Rogerson
5 | Microsoft Press 1997
6 | ISBN 1-57231-349-8
7 | */
8 |
9 | #ifndef __CFactory_h__
10 | #define __CFactory_h__
11 |
12 | #include "CUnknown.h"
13 | ///////////////////////////////////////////////////////////
14 |
15 | // Forward reference
16 | class CFactoryData ;
17 |
18 | // Global data used by CFactory
19 | extern CFactoryData g_FactoryDataArray[] ;
20 | extern int g_cFactoryDataEntries ;
21 |
22 | //////////////////////////////////////////////////////////
23 | //
24 | // Component creation function
25 | //
26 | class CUnknown ;
27 |
28 | typedef HRESULT (*FPCREATEINSTANCE)(IUnknown*, CUnknown**) ;
29 |
30 | ///////////////////////////////////////////////////////////
31 | //
32 | // CFactoryData
33 | // - Information CFactory needs to create a component
34 | // supported by the DLL
35 | //
36 | class CFactoryData
37 | {
38 | public:
39 | // The class ID for the component
40 | const CLSID* m_pCLSID ;
41 |
42 | // Pointer to the function that creates it
43 | FPCREATEINSTANCE CreateInstance ;
44 |
45 | // Name of the component to register in the registry
46 | LPCWSTR m_RegistryName ;
47 |
48 | // ProgID
49 | LPCWSTR m_szProgID ;
50 |
51 | // Version-independent ProgID
52 | LPCWSTR m_szVerIndProgID ;
53 |
54 | // Helper function for finding the class ID
55 | BOOL IsClassID(const CLSID& clsid) const
56 | { return (*m_pCLSID == clsid) ;}
57 |
58 | // Type Library ID
59 | const GUID* m_pLIBID ;
60 |
61 | //
62 | // Out of process server support
63 | //
64 |
65 | // Pointer to running class factory for this component
66 | IClassFactory* m_pIClassFactory ;
67 |
68 | // Magic cookie to identify running object
69 | DWORD m_dwRegister ;
70 | } ;
71 |
72 |
73 | ///////////////////////////////////////////////////////////
74 | //
75 | // Class Factory
76 | //
77 | class CFactory : public IClassFactory
78 | {
79 | public:
80 | // IUnknown
81 | virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
82 | virtual ULONG __stdcall AddRef() ;
83 | virtual ULONG __stdcall Release() ;
84 |
85 | // IClassFactory
86 | virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
87 | const IID& iid,
88 | void** ppv) ;
89 | virtual HRESULT __stdcall LockServer(BOOL bLock) ;
90 |
91 | // Constructor - Pass pointer to data of component to create.
92 | CFactory(const CFactoryData* pFactoryData) ;
93 |
94 | // Destructor
95 | ~CFactory() { }
96 |
97 | //
98 | // Static FactoryData support functions
99 | //
100 |
101 | // DllGetClassObject support
102 | static HRESULT GetClassObject(const CLSID& clsid,
103 | const IID& iid,
104 | void** ppv) ;
105 |
106 | // Helper function for DllCanUnloadNow
107 | static BOOL IsLocked()
108 | { return (s_cServerLocks > 0) ;}
109 |
110 | // Functions to [un]register all components
111 | static HRESULT RegisterAll() ;
112 | static HRESULT UnregisterAll() ;
113 |
114 | // Function to determine if component can be unloaded
115 | static HRESULT CanUnloadNow() ;
116 |
117 |
118 | //
119 | // Out-of-process server support
120 | //
121 |
122 | static BOOL StartFactories() ;
123 | static void StopFactories() ;
124 |
125 | static DWORD s_dwThreadID ;
126 |
127 | // Shut down the application.
128 | static void CloseExe()
129 | {
130 | if (CanUnloadNow() == S_OK)
131 | {
132 | ::PostThreadMessage(s_dwThreadID, WM_QUIT, 0, 0) ;
133 | }
134 | }
135 |
136 | public:
137 | // Reference Count
138 | LONG m_cRef ;
139 |
140 | // Pointer to information about class this factory creates
141 | const CFactoryData* m_pFactoryData ;
142 |
143 | // Count of locks
144 | static LONG s_cServerLocks ;
145 |
146 | // Module handle
147 | static HMODULE s_hModule ;
148 | } ;
149 |
150 | #endif
151 |
--------------------------------------------------------------------------------
/comtypes/GUID.py:
--------------------------------------------------------------------------------
1 | """comtypes.GUID module"""
2 |
3 | from ctypes import HRESULT, POINTER, OleDLL, Structure, WinDLL, byref, c_wchar_p
4 | from ctypes.wintypes import BYTE, DWORD, LPVOID, WORD
5 | from typing import TYPE_CHECKING, Any
6 |
7 | if TYPE_CHECKING:
8 | from comtypes import hints # type: ignore
9 |
10 |
11 | def binary(obj: "GUID") -> bytes:
12 | return bytes(obj)
13 |
14 |
15 | # Note: Comparing GUID instances by comparing their buffers
16 | # is slightly faster than using ole32.IsEqualGUID.
17 |
18 |
19 | class GUID(Structure):
20 | """Globally unique identifier structure."""
21 |
22 | _fields_ = [("Data1", DWORD), ("Data2", WORD), ("Data3", WORD), ("Data4", BYTE * 8)]
23 |
24 | def __init__(self, name=None):
25 | if name is not None:
26 | _CLSIDFromString(str(name), byref(self))
27 |
28 | def __repr__(self):
29 | return f'GUID("{str(self)}")'
30 |
31 | def __str__(self) -> str:
32 | p = c_wchar_p()
33 | _StringFromCLSID(byref(self), byref(p))
34 | result = p.value
35 | _CoTaskMemFree(p)
36 | # stringified `GUID_null` would be '{00000000-0000-0000-0000-000000000000}'
37 | # Should we do `assert result is not None`?
38 | return result # type: ignore
39 |
40 | def __bool__(self) -> bool:
41 | return self != GUID_null
42 |
43 | def __eq__(self, other) -> bool:
44 | return isinstance(other, GUID) and binary(self) == binary(other)
45 |
46 | def __hash__(self) -> int:
47 | # We make GUID instances hashable, although they are mutable.
48 | return hash(binary(self))
49 |
50 | def copy(self) -> "GUID":
51 | return GUID(str(self))
52 |
53 | @classmethod
54 | def from_progid(cls, progid: Any) -> "hints.Self":
55 | """Get guid from progid, ..."""
56 | if hasattr(progid, "_reg_clsid_"):
57 | progid = progid._reg_clsid_
58 | if isinstance(progid, cls):
59 | return progid
60 | elif isinstance(progid, str):
61 | if progid.startswith("{"):
62 | return cls(progid)
63 | inst = cls()
64 | _CLSIDFromProgID(str(progid), byref(inst))
65 | return inst
66 | else:
67 | raise TypeError(f"Cannot construct guid from {progid!r}")
68 |
69 | def as_progid(self) -> str:
70 | """Convert a GUID into a progid"""
71 | progid = c_wchar_p()
72 | _ProgIDFromCLSID(byref(self), byref(progid))
73 | result = progid.value
74 | _CoTaskMemFree(progid)
75 | # Should we do `assert result is not None`?
76 | return result # type: ignore
77 |
78 | @classmethod
79 | def create_new(cls) -> "hints.Self":
80 | """Create a brand new guid"""
81 | guid = cls()
82 | _CoCreateGuid(byref(guid))
83 | return guid
84 |
85 |
86 | REFCLSID = POINTER(GUID)
87 | LPOLESTR = LPCOLESTR = c_wchar_p
88 | LPCLSID = POINTER(GUID)
89 |
90 | _ole32_nohresult = WinDLL("ole32")
91 | _ole32 = OleDLL("ole32")
92 |
93 | _StringFromCLSID = _ole32.StringFromCLSID
94 | _StringFromCLSID.argtypes = [REFCLSID, POINTER(LPOLESTR)]
95 | _StringFromCLSID.restype = HRESULT
96 |
97 | _CoTaskMemFree = _ole32_nohresult.CoTaskMemFree
98 | _CoTaskMemFree.argtypes = [LPVOID]
99 | _CoTaskMemFree.restype = None
100 |
101 | _ProgIDFromCLSID = _ole32.ProgIDFromCLSID
102 | _ProgIDFromCLSID.argtypes = [REFCLSID, POINTER(LPOLESTR)]
103 | _ProgIDFromCLSID.restype = HRESULT
104 |
105 | _CLSIDFromString = _ole32.CLSIDFromString
106 | _CLSIDFromString.argtypes = [LPCOLESTR, LPCLSID]
107 | _CLSIDFromString.restype = HRESULT
108 |
109 | _CLSIDFromProgID = _ole32.CLSIDFromProgID
110 | _CLSIDFromProgID.argtypes = [LPCOLESTR, LPCLSID]
111 | _CLSIDFromProgID.restype = HRESULT
112 |
113 | _CoCreateGuid = _ole32.CoCreateGuid
114 | _CoCreateGuid.argtypes = [POINTER(GUID)]
115 | _CoCreateGuid.restype = HRESULT
116 |
117 | GUID_null = GUID()
118 |
119 | __all__ = ["GUID"]
120 |
--------------------------------------------------------------------------------
/source/Avmc.cpp:
--------------------------------------------------------------------------------
1 | // Avmc.cpp : Implementation of CAvmcIfcApp and DLL registration.
2 |
3 | #include "stdafx.h"
4 | #include "AvmcIfc.h"
5 | #include "Avmc.h"
6 | #include
7 |
8 | /////////////////////////////////////////////////////////////////////////////
9 | //
10 |
11 | const IID DEVICE_INFO_IID = {0x6C7A25CB, 0x7938, 0x4BE0, {0xA2, 0x85, 0x12, 0xC6, 0x16, 0x71, 0x7F, 0xDD} };
12 |
13 | STDMETHODIMP Avmc::InterfaceSupportsErrorInfo(REFIID riid)
14 | {
15 | static const IID* arr[] =
16 | {
17 | &IID_IAvmc,
18 | };
19 |
20 | for (int i=0;iRelease(); // Release the interface
81 | if( *avmcList == NULL ) {
82 | HRESULT hr = Error( _T("Can not create array of Device Info structures") );
83 | return( hr );
84 | }
85 |
86 | ///////////////////////////////////////////////////////////////////
87 |
88 | #ifdef DEBUG_NOW
89 | strstream s2;
90 | s2.clear();
91 | for (i = 0; i < numDevs; i++) {
92 | s2 << "Dev " << i << endl;
93 | s2 << " Flags = 0x" << hex << devInfo[i].Flags << endl;
94 | s2 << " Type = 0x" << hex << devInfo[i].Type << endl;
95 | s2 << " ID = 0x" << hex << devInfo[i].ID << endl;
96 | s2 << " LocId = 0x" << hex << devInfo[i].LocId << endl;
97 | s2 << " SerialNumber = " << devInfo[i].SerialNumber << endl;
98 | s2 << " Description = " << devInfo[i].Description << endl;
99 | s2 << " ftHandle = 0x" << hex << devInfo[i].ftHandle << endl;
100 | s2 << "---" << endl;
101 | }
102 | s2 << ends;
103 | MessageBox (0, s2.str(), "Device List", 0);
104 | #endif
105 | DeviceInfo HUGEP *pD = NULL;
106 | hr = SafeArrayAccessData (*avmcList, (void HUGEP **)&pD);
107 |
108 | for (i = 0; i < numDevs; i++) {
109 | pD[i].Flags = (ULONG) devInfo[i].Flags;
110 | pD[i].Type = (ULONG) devInfo[i].Type;
111 | pD[i].ID = (ULONG) devInfo[i].ID;
112 | pD[i].LocId = (ULONG) devInfo[i].LocId;
113 | pD[i].SerialNumber = _com_util::ConvertStringToBSTR(devInfo[i].SerialNumber);
114 | pD[i].Description = _com_util::ConvertStringToBSTR(devInfo[i].Description);
115 | pD[i].ftHandle = (ULONG)devInfo[i].ftHandle;
116 | }
117 |
118 | hr = SafeArrayUnaccessData(*avmcList);
119 | }
120 |
121 | return S_OK;
122 | }
--------------------------------------------------------------------------------
/docs/source/npsupport.rst:
--------------------------------------------------------------------------------
1 | #############
2 | NumPy interop
3 | #############
4 |
5 | NumPy provides the *de facto* array standard for Python. Though NumPy
6 | is not required to use |comtypes|, |comtypes| provides various options for
7 | NumPy interoperability. NumPy version 1.7 or greater is required to access
8 | all of these features.
9 |
10 |
11 | .. contents::
12 |
13 | NumPy Arrays as Input Arguments
14 | *******************************
15 |
16 | NumPy arrays can be passed as ``VARIANT`` arrays arguments. The array is
17 | converted to a SAFEARRAY according to its type. The type conversion
18 | is defined by the ``numpy.ctypeslib`` module. The following table
19 | shows type conversions that can be performed quickly by (nearly) direct
20 | conversion of a numpy array to a SAFEARRAY. Arrays with type that do not
21 | appear in this table, including object arrays, can still be converted to
22 | SAFEARRAYs on an item-by-item basis.
23 |
24 | +------------------------------------------------+---------------+
25 | | NumPy type | VARIANT type |
26 | +================================================+===============+
27 | | ``int8`` | VT_I1 |
28 | +------------------------------------------------+---------------+
29 | | ``int16``, ``short`` | VT_I2 |
30 | +------------------------------------------------+---------------+
31 | | ``int32``, ``int``, ``intc``, ``int_`` | VT_I4 |
32 | +------------------------------------------------+---------------+
33 | | ``int64``, ``long``, ``longlong``, ``intp`` | VT_I8 |
34 | +------------------------------------------------+---------------+
35 | | ``uint8``, ``ubyte`` | VT_UI1 |
36 | +------------------------------------------------+---------------+
37 | | ``uint16``, ``ushort`` | VT_UI2 |
38 | +------------------------------------------------+---------------+
39 | | ``uint32``, ``uint``, ``uintc`` | VT_UI4 |
40 | +------------------------------------------------+---------------+
41 | | ``uint64``, ``ulonglong``, ``uintp`` | VT_UI8 |
42 | +------------------------------------------------+---------------+
43 | | ``float32`` | VT_R4 |
44 | +------------------------------------------------+---------------+
45 | | ``float64``, ``float_`` | VT_R8 |
46 | +------------------------------------------------+---------------+
47 | | ``datetime64`` | VT_DATE |
48 | +------------------------------------------------+---------------+
49 |
50 | NumPy Arrays as Output Arguments
51 | ********************************
52 |
53 | By default, |comtypes| converts SAFEARRAY output arguments to tuples of
54 | python objects on an item-by-item basis. When dealing with large
55 | SAFEARRAYs, this conversion can be costly. Comtypes provides a the
56 | ``safearray_as_ndarray`` context manager (from ``comtypes.safearray``)
57 | for modifying this behavior to return a NumPy array. This altered
58 | behavior is to put an ndarray over a copy of the SAFEARRAY's memory,
59 | which is faster than calling into python for each item. When this fails,
60 | a NumPy array can still be created on an item-by-item basis. The context
61 | manager is thread-safe, in that usage of the context manager on one
62 | thread does not affect behavior on other threads.
63 |
64 | This is a hypothetical example of using the context manager. The context
65 | manager can be used around any property or method call to retrieve a
66 | NumPy array rather than a tuple.
67 |
68 |
69 | .. sourcecode:: python
70 |
71 | """Sample demonstrating use of safearray_as_ndarray context manager """
72 |
73 | from comtypes.safearray import safearray_as_ndarray
74 |
75 | # Hypothetically, this returns a SAFEARRAY as a tuple
76 | data1 = some_interface.some_property
77 |
78 | # This will return a NumPy array, and will be faster for basic types.
79 | with safearray_as_ndarray:
80 | data2 = some_interface.some_property
81 |
82 |
83 | .. |comtypes| replace:: ``comtypes``
84 |
--------------------------------------------------------------------------------
/comtypes/test/test_dispifc_safearrays.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import byref, c_double, pointer
3 |
4 | import comtypes
5 | import comtypes.safearray
6 | from comtypes import CLSCTX_LOCAL_SERVER
7 | from comtypes.client import CreateObject, GetModule
8 |
9 | ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}"
10 |
11 | try:
12 | GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0))
13 | import comtypes.gen.ComtypesCppTestSrvLib as ComtypesCppTestSrvLib
14 |
15 | IMPORT_FAILED = False
16 | except (ImportError, OSError):
17 | IMPORT_FAILED = True
18 |
19 |
20 | @unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
21 | class Test_DispMethods(unittest.TestCase):
22 | """Test dispmethods with safearray and safearray pointer parameters."""
23 |
24 | UNPACKED_ZERO_VALS = tuple(0.0 for _ in range(10))
25 | UNPACKED_CONSECUTIVE_VALS = tuple(float(i) for i in range(10))
26 |
27 | def _create_dispifc(self) -> "ComtypesCppTestSrvLib.IDispSafearrayParamTest":
28 | # Explicitely ask for the dispinterface of the component.
29 | return CreateObject(
30 | "Comtypes.DispSafearrayParamTest",
31 | clsctx=CLSCTX_LOCAL_SERVER,
32 | interface=ComtypesCppTestSrvLib.IDispSafearrayParamTest,
33 | )
34 |
35 | def _create_zero_array(self):
36 | return comtypes.safearray._midlSAFEARRAY(c_double).create(
37 | [c_double() for _ in range(10)]
38 | )
39 |
40 | def _create_consecutive_array(self):
41 | return comtypes.safearray._midlSAFEARRAY(c_double).create(
42 | [c_double(i) for i in range(10)]
43 | )
44 |
45 | def test_inout_byref(self):
46 | dispifc = self._create_dispifc()
47 | # Passing a safearray by reference to a method that has declared the parameter
48 | # as [in, out] we expect modifications of the safearray on the server side to
49 | # also change the safearray on the client side.
50 | test_array = self._create_zero_array()
51 | self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS)
52 | dispifc.InitArray(byref(test_array))
53 | self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS)
54 |
55 | def test_inout_pointer(self):
56 | dispifc = self._create_dispifc()
57 | # Passing a safearray pointer to a method that has declared the parameter
58 | # as [in, out] we expect modifications of the safearray on the server side to
59 | # also change the safearray on the client side.
60 | test_array = self._create_zero_array()
61 | self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS)
62 | dispifc.InitArray(pointer(test_array))
63 | self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS)
64 |
65 | def test_in_safearray(self):
66 | # Passing a safearray to a method that has declared the parameter just as [in]
67 | # we expect modifications of the safearray on the server side NOT to change
68 | # the safearray on the client side.
69 | # We also need to test if the safearray gets properly passed to the method on
70 | # the server side. For this, the 'VerifyArray' method returns 'True' if
71 | # the safearray items have values equal to the initialization values
72 | # provided by 'InitArray'.
73 | for sa, expected, unpacked_content in [
74 | (self._create_consecutive_array(), True, self.UNPACKED_CONSECUTIVE_VALS),
75 | # Also perform the inverted test. For this, create a safearray with zeros.
76 | (self._create_zero_array(), False, self.UNPACKED_ZERO_VALS),
77 | ]:
78 | with self.subTest(expected=expected, unpacked_content=unpacked_content):
79 | # Perform the check on initialization values.
80 | self.assertEqual(self._create_dispifc().VerifyArray(sa), expected)
81 | # Check if the safearray is unchanged although the method
82 | # modifies the safearray on the server side.
83 | self.assertEqual(sa.unpack(), unpacked_content)
84 |
85 |
86 | if __name__ == "__main__":
87 | unittest.main()
88 |
--------------------------------------------------------------------------------
/comtypes/test/TestDispServer.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import sys
4 |
5 | logging.basicConfig()
6 | ##logging.basicConfig(level=logging.DEBUG)
7 | ##logger = logging.getLogger(__name__)
8 |
9 | # Add comtypes to sys.path (if this is run from a SVN checkout)
10 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), r"..\..")))
11 |
12 | import comtypes
13 | import comtypes.connectionpoints
14 | import comtypes.server.connectionpoints
15 | from comtypes.hresult import S_OK
16 |
17 | ################################################################
18 |
19 | # Create the wrapper in the comtypes.gen package, it will be named
20 | # TestComServerLib; the name is derived from the 'library ' statement
21 | # in the IDL file
22 | if not hasattr(sys, "frozen"):
23 | import comtypes.client
24 |
25 | # pathname of the type library file
26 | tlbfile = os.path.join(os.path.dirname(__file__), "TestDispServer.tlb")
27 | # if running as frozen app (dll or exe), the wrapper should be in
28 | # the library archive, so we don't need to generate it.
29 | comtypes.client.GetModule(tlbfile)
30 |
31 | # Import the wrapper
32 | from comtypes.gen import TestDispServerLib
33 |
34 | ################################################################
35 |
36 |
37 | # Implement the CoClass by defining a subclass of the
38 | # TestDispServerLib.TestDispServer class in the wrapper file. The
39 | # COMObject base class provides default implementations of the
40 | # IUnknown, IDispatch, IPersist, IProvideClassInfo,
41 | # IProvideClassInfo2, and ISupportErrorInfo interfaces.
42 | #
43 | # The ConnectableObjectMixin class provides connectionpoints (events).
44 | class TestDispServer(
45 | TestDispServerLib.TestDispServer, # the coclass from the typelib wrapper
46 | comtypes.server.connectionpoints.ConnectableObjectMixin,
47 | ):
48 | # The default interface from the typelib MUST be the first
49 | # interface, other interfaces can follow
50 |
51 | _com_interfaces_ = TestDispServerLib.TestDispServer._com_interfaces_ + [
52 | comtypes.connectionpoints.IConnectionPointContainer
53 | ]
54 |
55 | # registry entries
56 | _reg_threading_ = "Both"
57 | _reg_progid_ = "TestDispServerLib.TestDispServer.1"
58 | _reg_novers_progid_ = "TestDispServerLib.TestDispServer"
59 | _reg_desc_ = "comtypes COM server sample for testing"
60 | _reg_clsctx_ = comtypes.CLSCTX_INPROC_SERVER | comtypes.CLSCTX_LOCAL_SERVER
61 |
62 | ################################
63 | # DTestDispServer methods
64 |
65 | def DTestDispServer_eval(self, this, expr, presult):
66 | self.Fire_Event(0, "EvalStarted", expr)
67 | # The following two are equivalent, but the former is more generic:
68 | presult[0] = eval(expr)
69 | ##presult[0].value = eval(expr)
70 | self.Fire_Event(0, "EvalCompleted", expr, presult[0].value)
71 | return S_OK
72 |
73 | def DTestDispServer_eval2(self, expr):
74 | self.Fire_Event(0, "EvalStarted", expr)
75 | result = eval(expr)
76 | self.Fire_Event(0, "EvalCompleted", expr, result)
77 | return result
78 |
79 | def DTestDispServer__get_id(self, this, pid):
80 | pid[0] = id(self)
81 | return S_OK
82 |
83 | def DTestDispServer_Exec(self, this, what):
84 | exec(what)
85 | return S_OK
86 |
87 | def DTestDispServer_Exec2(self, what):
88 | exec(what)
89 |
90 | _name = "spam, spam, spam"
91 |
92 | # Implementation of the DTestDispServer::Name propget
93 | def DTestDispServer__get_name(self, this, pname):
94 | pname[0] = self._name
95 | return S_OK
96 |
97 | # Implementation of the DTestDispServer::Name propput
98 | def DTestDispServer__set_name(self, this, name):
99 | self._name = name
100 | return S_OK
101 |
102 | # Implementation of the DTestDispServer::SetName method
103 | def DTestDispServer_sEtNaMe(self, name):
104 | self._name = name
105 |
106 |
107 | if __name__ == "__main__":
108 | from comtypes.server.register import UseCommandLine
109 |
110 | UseCommandLine(TestDispServer)
111 |
--------------------------------------------------------------------------------
/comtypes/_meta.py:
--------------------------------------------------------------------------------
1 | # comtypes._meta helper module
2 | import sys
3 | from ctypes import POINTER, c_void_p, cast
4 |
5 | import comtypes
6 |
7 | ################################################################
8 | # metaclass for CoClass (in comtypes/__init__.py)
9 |
10 |
11 | def _wrap_coclass(self):
12 | # We are an IUnknown pointer, represented as a c_void_p instance,
13 | # but we really want this interface:
14 | itf = self._com_interfaces_[0]
15 | punk = cast(self, POINTER(itf))
16 | result = punk.QueryInterface(itf)
17 | result.__dict__["__clsid"] = str(self._reg_clsid_)
18 | return result
19 |
20 |
21 | def _coclass_from_param(cls, obj):
22 | if isinstance(obj, (cls._com_interfaces_[0], cls)):
23 | return obj
24 | raise TypeError(obj)
25 |
26 |
27 | #
28 | # The mro() of a POINTER(App) type, where class App is a subclass of CoClass:
29 | #
30 | # POINTER(App)
31 | # App
32 | # CoClass
33 | # c_void_p
34 | # _SimpleCData
35 | # _CData
36 | # object
37 |
38 |
39 | class _coclass_meta(type):
40 | # metaclass for CoClass
41 | #
42 | # When a CoClass subclass is created, create a POINTER(...) type
43 | # for that class, with bases and c_void_p. Also, the
44 | # POINTER(...) type gets a __ctypes_from_outparam__ method which
45 | # will QueryInterface for the default interface: the first one on
46 | # the coclass' _com_interfaces_ list.
47 | def __new__(cls, name, bases, namespace):
48 | self = type.__new__(cls, name, bases, namespace)
49 | if bases == (object,):
50 | # HACK: Could this conditional branch be removed since it is never reached?
51 | # Since definition is `class CoClass(COMObject, metaclass=_coclass_meta)`,
52 | # the `bases` parameter passed to the `_coclass_meta.__new__` would be
53 | # `(COMObject,)`.
54 | # Moreover, since the `COMObject` derives from `object` and does not specify
55 | # a metaclass, `(object,)` will not be passed as the `bases` parameter
56 | # to the `_coclass_meta.__new__`.
57 | # The reason for this implementation might be a remnant of the differences
58 | # in how metaclasses work between Python 3.x and Python 2.x.
59 | # If there are no problems with the versions of Python that `comtypes`
60 | # supports, this removal could make the process flow easier to understand.
61 | return self
62 | # XXX We should insist that a _reg_clsid_ is present.
63 | if "_reg_clsid_" in namespace:
64 | clsid = namespace["_reg_clsid_"]
65 | comtypes.com_coclass_registry[str(clsid)] = self # type: ignore
66 |
67 | # `_coclass_pointer_meta` is a subclass inherited from `_coclass_meta`.
68 | # In other words, when the `__new__` method of this metaclass is called, an
69 | # instance of `_coclass_pointer_meta` might be created and assigned to `self`.
70 | if isinstance(self, _coclass_pointer_meta):
71 | # `self` is the `_coclass_pointer_meta` type or a `POINTER(coclass)` type.
72 | # Prevent creating/registering a pointer to a pointer (to a pointer...),
73 | # or specifying the metaclass type instance in the `bases` parameter when
74 | # instantiating it, which would lead to infinite recursion.
75 | # Depending on a version or revision of Python, this may be essential.
76 | return self
77 |
78 | p = _coclass_pointer_meta(
79 | f"POINTER({self.__name__})",
80 | (self, c_void_p),
81 | {
82 | "__ctypes_from_outparam__": _wrap_coclass,
83 | "from_param": classmethod(_coclass_from_param),
84 | },
85 | )
86 | if sys.version_info >= (3, 14):
87 | self.__pointer_type__ = p
88 | else:
89 | from ctypes import _pointer_type_cache # type: ignore
90 |
91 | _pointer_type_cache[self] = p
92 |
93 | return self
94 |
95 |
96 | # will not work if we change the order of the two base classes!
97 | class _coclass_pointer_meta(type(c_void_p), _coclass_meta):
98 | # metaclass for CoClass pointer
99 |
100 | pass # no functionality, but needed to avoid a metaclass conflict
101 |
--------------------------------------------------------------------------------
/comtypes/test/test_dispifc_records.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import byref, pointer
3 |
4 | from comtypes import CLSCTX_LOCAL_SERVER
5 | from comtypes.client import CreateObject, GetModule
6 |
7 | ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}"
8 |
9 | try:
10 | GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0))
11 | import comtypes.gen.ComtypesCppTestSrvLib as ComtypesCppTestSrvLib
12 |
13 | IMPORT_FAILED = False
14 | except (ImportError, OSError):
15 | IMPORT_FAILED = True
16 |
17 |
18 | @unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
19 | class Test_DispMethods(unittest.TestCase):
20 | """Test dispmethods with record and record pointer parameters."""
21 |
22 | EXPECTED_INITED_QUESTIONS = "The meaning of life, the universe and everything?"
23 |
24 | def _create_dispifc(self) -> "ComtypesCppTestSrvLib.IDispRecordParamTest":
25 | # Explicitely ask for the dispinterface of the component.
26 | return CreateObject(
27 | "Comtypes.DispRecordParamTest",
28 | clsctx=CLSCTX_LOCAL_SERVER,
29 | interface=ComtypesCppTestSrvLib.IDispRecordParamTest,
30 | )
31 |
32 | def test_inout_byref(self):
33 | dispifc = self._create_dispifc()
34 | # Passing a record by reference to a method that has declared the parameter
35 | # as [in, out] we expect modifications of the record on the server side to
36 | # also change the record on the client side.
37 | test_record = ComtypesCppTestSrvLib.StructRecordParamTest()
38 | self.assertEqual(test_record.question, None)
39 | self.assertEqual(test_record.answer, 0)
40 | self.assertEqual(test_record.needs_clarification, False)
41 | dispifc.InitRecord(byref(test_record))
42 | self.assertEqual(test_record.question, self.EXPECTED_INITED_QUESTIONS)
43 | self.assertEqual(test_record.answer, 42)
44 | self.assertEqual(test_record.needs_clarification, True)
45 |
46 | def test_inout_pointer(self):
47 | dispifc = self._create_dispifc()
48 | # Passing a record pointer to a method that has declared the parameter
49 | # as [in, out] we expect modifications of the record on the server side to
50 | # also change the record on the client side.
51 | test_record = ComtypesCppTestSrvLib.StructRecordParamTest()
52 | self.assertEqual(test_record.question, None)
53 | self.assertEqual(test_record.answer, 0)
54 | self.assertEqual(test_record.needs_clarification, False)
55 | dispifc.InitRecord(pointer(test_record))
56 | self.assertEqual(test_record.question, self.EXPECTED_INITED_QUESTIONS)
57 | self.assertEqual(test_record.answer, 42)
58 | self.assertEqual(test_record.needs_clarification, True)
59 |
60 | def test_in_record(self):
61 | # Passing a record to a method that has declared the parameter just as [in]
62 | # we expect modifications of the record on the server side NOT to change
63 | # the record on the client side.
64 | # We also need to test if the record gets properly passed to the method on
65 | # the server side. For this, the 'VerifyRecord' method returns 'True' if
66 | # all record fields have values equivalent to the initialization values
67 | # provided by 'InitRecord'.
68 | inited_record = ComtypesCppTestSrvLib.StructRecordParamTest()
69 | inited_record.question = self.EXPECTED_INITED_QUESTIONS
70 | inited_record.answer = 42
71 | inited_record.needs_clarification = True
72 | for rec, expected, (q, a, nc) in [
73 | (inited_record, True, (self.EXPECTED_INITED_QUESTIONS, 42, True)),
74 | # Also perform the inverted test. For this, create a blank record.
75 | (ComtypesCppTestSrvLib.StructRecordParamTest(), False, (None, 0, False)),
76 | ]:
77 | with self.subTest(expected=expected, q=q, a=a, nc=nc):
78 | # Perform the check on initialization values.
79 | self.assertEqual(self._create_dispifc().VerifyRecord(rec), expected)
80 | self.assertEqual(rec.question, q)
81 | # Check if the 'answer' field is unchanged although the method
82 | # modifies this field on the server side.
83 | self.assertEqual(rec.answer, a)
84 | self.assertEqual(rec.needs_clarification, nc)
85 |
86 |
87 | if __name__ == "__main__":
88 | unittest.main()
89 |
--------------------------------------------------------------------------------
/comtypes/test/test_midl_safearray_create.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from ctypes import HRESULT, POINTER, c_int, pointer
3 |
4 | import comtypes
5 | import comtypes.safearray
6 | import comtypes.typeinfo
7 | from comtypes import CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER
8 | from comtypes.client import CreateObject, GetModule
9 |
10 | GetModule("UIAutomationCore.dll")
11 | from comtypes.gen.UIAutomationClient import CUIAutomation, IUIAutomation
12 |
13 | ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}"
14 |
15 | try:
16 | GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0))
17 | from comtypes.gen.ComtypesCppTestSrvLib import (
18 | IDispSafearrayParamTest,
19 | StructRecordParamTest,
20 | )
21 |
22 | IMPORT_FAILED = False
23 | except (ImportError, OSError):
24 | IMPORT_FAILED = True
25 |
26 |
27 | class Test_midlSAFEARRAY_create(unittest.TestCase):
28 | def test_iunk(self):
29 | extra = pointer(IUIAutomation._iid_)
30 | iuia = CreateObject(
31 | CUIAutomation().IPersist_GetClassID(),
32 | interface=IUIAutomation,
33 | clsctx=CLSCTX_INPROC_SERVER,
34 | )
35 | sa_type = comtypes.safearray._midlSAFEARRAY(POINTER(IUIAutomation))
36 | for ptn, sa in [
37 | ("with extra", sa_type.create([iuia], extra=extra)),
38 | ("without extra", sa_type.create([iuia])),
39 | ]:
40 | with self.subTest(ptn=ptn):
41 | (unpacked,) = sa.unpack()
42 | self.assertIsInstance(unpacked, POINTER(IUIAutomation))
43 |
44 | @unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
45 | def test_idisp(self):
46 | extra = pointer(IDispSafearrayParamTest._iid_)
47 | idisp = CreateObject(
48 | "Comtypes.DispSafearrayParamTest",
49 | clsctx=CLSCTX_LOCAL_SERVER,
50 | interface=IDispSafearrayParamTest,
51 | )
52 | sa_type = comtypes.safearray._midlSAFEARRAY(POINTER(IDispSafearrayParamTest))
53 | for ptn, sa in [
54 | ("with extra", sa_type.create([idisp], extra=extra)),
55 | ("without extra", sa_type.create([idisp])),
56 | ]:
57 | with self.subTest(ptn=ptn):
58 | (unpacked,) = sa.unpack()
59 | self.assertIsInstance(unpacked, POINTER(IDispSafearrayParamTest))
60 |
61 | @unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
62 | def test_record(self):
63 | extra = comtypes.typeinfo.GetRecordInfoFromGuids(
64 | *StructRecordParamTest._recordinfo_
65 | )
66 | record = StructRecordParamTest()
67 | record.question = "The meaning of life, the universe and everything?"
68 | record.answer = 42
69 | record.needs_clarification = True
70 | sa_type = comtypes.safearray._midlSAFEARRAY(StructRecordParamTest)
71 | for ptn, sa in [
72 | ("with extra", sa_type.create([record], extra=extra)),
73 | ("without extra", sa_type.create([record])),
74 | ]:
75 | with self.subTest(ptn=ptn):
76 | (unpacked,) = sa.unpack()
77 | self.assertIsInstance(unpacked, StructRecordParamTest)
78 | self.assertEqual(
79 | unpacked.question,
80 | "The meaning of life, the universe and everything?",
81 | )
82 | self.assertEqual(unpacked.answer, 42)
83 | self.assertEqual(unpacked.needs_clarification, True)
84 |
85 | def test_HRESULT(self):
86 | hr = HRESULT(1)
87 | sa_type = comtypes.safearray._midlSAFEARRAY(HRESULT)
88 | with self.assertRaises(TypeError):
89 | sa_type.create([hr], extra=None)
90 | with self.assertRaises(TypeError):
91 | sa_type.create([hr])
92 |
93 | def test_ctype(self):
94 | extra = None
95 | cdata = c_int(1)
96 | sa_type = comtypes.safearray._midlSAFEARRAY(c_int)
97 | for ptn, sa in [
98 | ("with extra", sa_type.create([cdata], extra=extra)),
99 | ("without extra", sa_type.create([cdata])),
100 | ]:
101 | with self.subTest(ptn=ptn):
102 | (unpacked,) = sa.unpack()
103 | self.assertIsInstance(unpacked, int)
104 | self.assertEqual(unpacked, 1)
105 |
106 |
107 | if __name__ == "__main__":
108 | unittest.main()
109 |
--------------------------------------------------------------------------------