Skip to content

Commit

Permalink
Merge pull request #19 from supersega/pycomserver
Browse files Browse the repository at this point in the history
Implement python COM Server
  • Loading branch information
Jøger Hansegård authored Dec 17, 2021
2 parents d78c9bb + 12b7186 commit 20e8fd4
Show file tree
Hide file tree
Showing 20 changed files with 196 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
Build/
packages/
*/Generated Files/
*/env/

*.tlb
*.user
8 changes: 8 additions & 0 deletions AtlFreeServer/AtlFreeServer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IRoyalPython_i.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IRoyalPython_p.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IDog_i.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
Expand Down
6 changes: 6 additions & 0 deletions AtlFreeServer/AtlFreeServer.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
<ClCompile Include="..\Build\Output\Include\Interfaces\dlldata.c">
<Filter>Proxy</Filter>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IRoyalPython_i.c">
<Filter>Proxy</Filter>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IRoyalPython_p.c">
<Filter>Proxy</Filter>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IDog_i.c">
<Filter>Proxy</Filter>
</ClCompile>
Expand Down
4 changes: 4 additions & 0 deletions ComSamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TutorialsAndTests", "Tutori
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinrtServer", "WinrtServer\WinrtServer.vcxproj", "{1D97F756-AEB2-4070-B845-0E9C868B4CEF}"
EndProject
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "PyComServer", "PyComServer\PyComServer.pyproj", "{B48DC892-E120-49F5-8E20-D497214811D5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -90,6 +92,8 @@ Global
{1D97F756-AEB2-4070-B845-0E9C868B4CEF}.Debug|x64.Build.0 = Debug|x64
{1D97F756-AEB2-4070-B845-0E9C868B4CEF}.Release|x64.ActiveCfg = Release|x64
{1D97F756-AEB2-4070-B845-0E9C868B4CEF}.Release|x64.Build.0 = Release|x64
{B48DC892-E120-49F5-8E20-D497214811D5}.Debug|x64.ActiveCfg = Debug|Any CPU
{B48DC892-E120-49F5-8E20-D497214811D5}.Release|x64.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 1 addition & 1 deletion ComUtility/ComApartment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ std::future<HRESULT> ComApartment::InvokeOnApartment(std::function<HRESULT()> ca
{
assert(m_threadId != 0); // To document that at this time, m_threadId is always non-zero.

std::packaged_task task([func = std::move(callable), this]
std::packaged_task<HRESULT()> task([func = std::move(callable), this]
{
return func();
});
Expand Down
13 changes: 13 additions & 0 deletions Interfaces/IRoyalPython.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "oaidl.idl";
import "ocidl.idl";

[
oleautomation,
object,
uuid(0A315D51-1316-4822-9DB8-0980E4AFC7E0),
pointer_default(unique)
]
interface IRoyalPython : IUnknown
{
HRESULT Eat();
};
2 changes: 2 additions & 0 deletions Interfaces/Interfaces.idl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "oaidl.idl";
import "ocidl.idl";
import "IRoyalPython.idl";
import "IHen.idl";
import "IDog.idl";
import "IPostman.idl";
Expand All @@ -11,6 +12,7 @@ import "IPetShop.idl";
]
library Interfaces
{
interface IRoyalPython;
interface IHen;
interface IAsyncCluckObserver;
interface IDog;
Expand Down
1 change: 1 addition & 0 deletions Interfaces/interfaces.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<Midl Include="IRoyalPython.idl" />
<Midl Include="IHen.idl" />
<Midl Include="Interfaces.idl" />
<Midl Include="IDog.idl" />
Expand Down
1 change: 1 addition & 0 deletions Interfaces/interfaces.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Midl Include="IDog.idl" />
<Midl Include="IPostman.idl" />
<Midl Include="IPetShop.idl" />
<Midl Include="IRoyalPython.idl" />
</ItemGroup>
<ItemGroup>
<None Include="readme.md" />
Expand Down
35 changes: 35 additions & 0 deletions PyComServer/PyComServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from ctypes import *
from comtypes import CLSCTX_LOCAL_SERVER, CLSCTX_INPROC_SERVER
from comtypes.server.localserver import REGCLS_MULTIPLEUSE
import sys

# Get wrapped code for RoyalPython.tlb,
# It was generated in RegPyComServer.bat file from RoyalPython.idl
from comtypes.client import GetModule
GetModule("RoyalPython.tlb")

# comtypes.gen.PyComServer was generated by GetModule above
# RoyalPython corresponds to the RoyalPython from RoyalPython.idl
from comtypes.gen.PyComServer import RoyalPython

# Implement COM server using auto generated type RoyalPython
class PyRoyalPython(RoyalPython):
_reg_threading_ = "Both"
_reg_progid_ = "PyComServer.RoyalPython.1"
_reg_novers_progid_ = "PyComServer.RoyalPython"
_reg_clsctx_ = CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER
_reg_desc_ = "Royal python eats mammals"
_regcls_ = REGCLS_MULTIPLEUSE

def Eat(self):
print("eat")

if __name__ == '__main__':
# If some arguments passed to the script we call UseCommandLine what register/unregister COM server.
if len(sys.argv) > 1:
from comtypes.server.register import UseCommandLine
UseCommandLine(PyRoyalPython)
else:
# Run the server.
from win32com.server import localserver
localserver.serve(['{F405A270-9088-4800-A5CD-A5E1DED02AB4}'])
55 changes: 55 additions & 0 deletions PyComServer/PyComServer.pyproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>b48dc892-e120-49f5-8e20-d497214811d5</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>PyComServer.py</StartupFile>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<Name>PyComServer</Name>
<RootNamespace>PyComServer</RootNamespace>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<CommandLineArguments>/regserver</CommandLineArguments>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="PyComServer.py" />
</ItemGroup>
<ItemGroup>
<Content Include="readme.md" />
<Content Include="RoyalPython.idl" />
<Content Include="requirements.txt" />
</ItemGroup>
<ItemGroup>
<Interpreter Include="env\">
<Id>env</Id>
<Version>3.9</Version>
<Description>env (Python 3.9 (64-bit))</Description>
<InterpreterPath>Scripts\python.exe</InterpreterPath>
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
<Architecture>X64</Architecture>
</Interpreter>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
<!-- Uncomment the CoreCompile target to enable the Build command in
Visual Studio and specify your pre- and post-build commands in
the BeforeBuild and AfterBuild targets below. -->
<!--<Target Name="CoreCompile" />-->
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
</Project>
15 changes: 15 additions & 0 deletions PyComServer/RegPyComServer.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
setlocal
pushd %~dp0

@echo on

py -3 -m venv ..\Build\buildenv || exit /b 1
call ..\Build\buildenv\Scripts\Activate.bat || exit /b 1

python -m pip install -r requirements.txt || exit /b 1

:: In C++ projects we do it automatically, but for python server handcrafted generation needed
midl RoyalPython.idl || exit /b 1
python PyComServer.py -regserver || exit /b 1

popd
16 changes: 16 additions & 0 deletions PyComServer/RoyalPython.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "oaidl.idl";
import "ocidl.idl";
import "..\Interfaces\IRoyalPython.idl";

[
uuid(F0D8338A-BDC1-45D7-A14F-27D64E7BCA18)
]
library PyComServer
{
importlib("stdole2.tlb");

[uuid(F405A270-9088-4800-A5CD-A5E1DED02AB4)]
coclass RoyalPython {
[default] interface IRoyalPython;
};
};
4 changes: 4 additions & 0 deletions PyComServer/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# PyComServer

This project contains a COM Server implemented with python, pywin32 and comtypes used to create COM Server.
To be able to register PyComServer we have to generate .tlb manually from .idl using midl compiler.
2 changes: 2 additions & 0 deletions PyComServer/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pywin32
comtypes==1.1.7
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ COM Examples contains sample implementations that use Microsoft COM technology.
* [AtlServer](AtlServer#atlhenlib): An ATL implementation of a COM server that provides hens
* [AtlFreeServer](AtlFreeServer#atlfreeserver): An COM server implemented without ATL. It provides dogs
* [ManagedServer](ManagedServer/): An COM server implemented in .NET. Here you can buy dogs.
* [PyComServer](PyComServer/): An Com server implemented in python. It provides snakes.
* [WinrtServer](WinrtServer/): An COM server implemented in winrt as an Universal Windows component. It provides programmers.
* [ComUtility](ComUtility/): COM related utilities used across the projects
* [TutorialsAndTests](TutorialsAndTests/): Tutorials and tests used to demonstrate how COM objects are used from C++
Expand All @@ -16,5 +17,6 @@ COM Examples contains sample implementations that use Microsoft COM technology.
## Building

Dependencies:
* Visual Studio 2019 with Universal Windows Platform development workload
* Visual Studio 2019 with Universal Windows Platform development workload and Python development tools
* [C++/WinRT templates and visualizer for VS2019 (Wsix)](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/intro-to-using-cpp-with-winrt#visual-studio-support-for-cwinrt-xaml-the-vsix-extension-and-the-nuget-package)
* Python 3.8
4 changes: 3 additions & 1 deletion TutorialsAndTests/RegisterDependencies.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
set OutDir=%1
regsvr32 /s %OutDir%AtlServer.dll || exit /b 1
regsvr32 /s %OutDir%AtlFreeServer.dll || exit /b 1
regsvr32 /s %OutDir%ManagedServer\ManagedServer.comhost.dll || exit /b 1
regsvr32 /s %OutDir%ManagedServer\ManagedServer.comhost.dll || exit /b 1

call ..\PyComServer\RegPyComServer.bat || exit /b 1
21 changes: 21 additions & 0 deletions TutorialsAndTests/Tests/PyComServerTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "../pch.h"
#include <gtest/gtest.h>
#include <Interfaces/IRoyalPython.h>
#include <ComUtility/Utility.h>
#include <wrl.h>

using namespace testing;
using Microsoft::WRL::ComPtr;

TEST(PyComServerTests,
RequireThat_CoCreateInstance_CreatesPythonServer_WhenCalledWithGuidToPythonServer)
{
CLSID classid{};
HR(CLSIDFromProgID(L"PyComServer.RoyalPython.1", &classid));

ComPtr<IRoyalPython> py;
HR(CoCreateInstance(classid, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(IRoyalPython), &py));
EXPECT_NE(py, nullptr);

py->Eat();
}
1 change: 1 addition & 0 deletions TutorialsAndTests/TutorialsAndTests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<ClCompile Include="Tests\AtlHenTests.cpp" />
<ClCompile Include="Tests\ComFactoryTests.cpp" />
<ClCompile Include="Tests\ManagedServerTests.cpp" />
<ClCompile Include="Tests\PyComServerTests.cpp" />
<ClCompile Include="Tests\UtilityTests.cpp" />
<ClCompile Include="Tests\WinrtServerTests.cpp" />
<ClCompile Include="Tutorials\CreatingComObjectsWithCoCreateInstance.cpp" />
Expand Down
2 changes: 2 additions & 0 deletions TutorialsAndTests/TutorialsAndTests.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
</ClCompile>
<ClCompile Include="Tests\WinrtServerTests.cpp">
<Filter>Tests</Filter>
<ClCompile Include="Tests\PyComServerTests.cpp">
<Filter>Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
Expand Down

0 comments on commit 20e8fd4

Please sign in to comment.