Skip to content

Commit

Permalink
Adding the source files for a simple out of process COM-server under
Browse files Browse the repository at this point in the history
'source/OutProcSrv'. The COM-server implements a dual interface and
the corresponding dispinterface with currently just a single method
'InitRecord' for testing record pointer parameters.
The added unittest 'test_dispifc_recordptr.py' passes a record by
reference and also as a record pointer to the 'InitRecord' method
of the dispinterface and checks the initialized records.
  • Loading branch information
geppi committed May 23, 2024
1 parent f7f461f commit 22daf9a
Show file tree
Hide file tree
Showing 15 changed files with 2,036 additions and 0 deletions.
58 changes: 58 additions & 0 deletions comtypes/test/test_dispifc_recordptr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# coding: utf-8

import unittest

from comtypes import CLSCTX_LOCAL_SERVER
from comtypes.client import CreateObject, GetModule
from ctypes import byref, pointer

ComtypesTestLib_GUID = "07D2AEE5-1DF8-4D2C-953A-554ADFD25F99"
ProgID = "ComtypesTest.COM.Server"

try:
GetModule([f"{{{ComtypesTestLib_GUID}}}", 1, 0, 0])
import comtypes.gen.ComtypesTestLib as ComtypesTestLib

IMPORT_FAILED = False
except (ImportError, OSError):
IMPORT_FAILED = True


@unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
class Test(unittest.TestCase):
"""Test dispmethods with record pointer parameters."""

def test(self):
# Explicitely ask for the dispinterface of the COM-server.
dispifc = CreateObject(
ProgID, clsctx=CLSCTX_LOCAL_SERVER, interface=ComtypesTestLib.IComtypesTest
)

# Test passing a record by reference.
test_record = ComtypesTestLib.T_TEST_RECORD()
self.assertEqual(test_record.question, None)
self.assertEqual(test_record.answer, 0)
self.assertEqual(test_record.needs_clarification, False)
dispifc.InitRecord(byref(test_record))
self.assertEqual(
test_record.question, "The meaning of life, the universe and everything?"
)
self.assertEqual(test_record.answer, 42)
self.assertEqual(test_record.needs_clarification, True)

# Test passing a record pointer.
test_record = ComtypesTestLib.T_TEST_RECORD()
self.assertEqual(test_record.question, None)
self.assertEqual(test_record.answer, 0)
self.assertEqual(test_record.needs_clarification, False)
test_record_pointer = pointer(test_record)
dispifc.InitRecord(test_record_pointer)
self.assertEqual(
test_record.question, "The meaning of life, the universe and everything?"
)
self.assertEqual(test_record.answer, 42)
self.assertEqual(test_record.needs_clarification, True)


if __name__ == "__main__":
unittest.main()
281 changes: 281 additions & 0 deletions source/OutProcSrv/CFACTORY.CPP
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/*
This code is based on example code to the book:
Inside COM
by Dale E. Rogerson
Microsoft Press 1997
ISBN 1-57231-349-8
*/

///////////////////////////////////////////////////////////
//
// CFactory
// - Base class for reusing a single class factory for
// all components in a DLL
//
#include <objbase.h>

#include "Registry.h"
#include "CFactory.h"

///////////////////////////////////////////////////////////
//
// Static variables
//
LONG CFactory::s_cServerLocks = 0 ; // Count of locks

HMODULE CFactory::s_hModule = NULL ; // DLL module handle

DWORD CFactory::s_dwThreadID = 0 ;

///////////////////////////////////////////////////////////
//
// CFactory implementation
//

CFactory::CFactory(const CFactoryData* pFactoryData)
: m_cRef(1)
{
m_pFactoryData = pFactoryData ;
}

//
// IUnknown implementation
//
HRESULT __stdcall CFactory::QueryInterface(REFIID iid, void** ppv)
{
IUnknown* pI ;
if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
{
pI = this ;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
pI->AddRef() ;
*ppv = pI ;
return S_OK;
}

ULONG __stdcall CFactory::AddRef()
{
return ::InterlockedIncrement(&m_cRef) ;
}

ULONG __stdcall CFactory::Release()
{
if (::InterlockedDecrement(&m_cRef) == 0)
{
delete this;
return 0 ;
}
return m_cRef;
}

//
// IClassFactory implementation
//

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
const IID& iid,
void** ppv)
{

// Aggregate only if the requested IID is IID_IUnknown.
if ((pUnknownOuter != NULL) && (iid != IID_IUnknown))
{
return CLASS_E_NOAGGREGATION ;
}

// Create the component.
CUnknown* pNewComponent ;
HRESULT hr = m_pFactoryData->CreateInstance(pUnknownOuter,
&pNewComponent) ;
if (FAILED(hr))
{
return hr ;
}

// Initialize the component.
hr = pNewComponent->Init();
if (FAILED(hr))
{
// Initialization failed. Release the component.
pNewComponent->NondelegatingRelease() ;
return hr ;
}

// Get the requested interface.
hr = pNewComponent->NondelegatingQueryInterface(iid, ppv) ;

// Release the reference held by the class factory.
pNewComponent->NondelegatingRelease() ;
return hr ;
}

// LockServer
HRESULT __stdcall CFactory::LockServer(BOOL bLock)
{
if (bLock)
{
::InterlockedIncrement(&s_cServerLocks) ;
}
else
{
::InterlockedDecrement(&s_cServerLocks) ;
}
// If this is an out-of-proc server, check to see
// whether we should shut down.
CloseExe() ; //@local

return S_OK ;
}


///////////////////////////////////////////////////////////
//
// GetClassObject
// - Create a class factory based on a CLSID.
//
HRESULT CFactory::GetClassObject(const CLSID& clsid,
const IID& iid,
void** ppv)
{
if ((iid != IID_IUnknown) && (iid != IID_IClassFactory))
{
return E_NOINTERFACE ;
}

// Traverse the array of data looking for this class ID.
for (int i = 0; i < g_cFactoryDataEntries; i++)
{
const CFactoryData* pData = &g_FactoryDataArray[i] ;
if (pData->IsClassID(clsid))
{

// Found the ClassID in the array of components we can
// create. So create a class factory for this component.
// Pass the CFactoryData structure to the class factory
// so that it knows what kind of components to create.
*ppv = (IUnknown*) new CFactory(pData) ;
if (*ppv == NULL)
{
return E_OUTOFMEMORY ;
}
return NOERROR ;
}
}
return CLASS_E_CLASSNOTAVAILABLE ;
}

//
// Determine if the component can be unloaded.
//
HRESULT CFactory::CanUnloadNow()
{
if (CUnknown::ActiveComponents() || IsLocked())
{
return S_FALSE ;
}
else
{
return S_OK ;
}
}

//
// Register all components.
//
HRESULT CFactory::RegisterAll()
{
for(int i = 0 ; i < g_cFactoryDataEntries ; i++)
{
RegisterServer(s_hModule,
*(g_FactoryDataArray[i].m_pCLSID),
g_FactoryDataArray[i].m_RegistryName,
g_FactoryDataArray[i].m_szVerIndProgID,
g_FactoryDataArray[i].m_szProgID,
*(g_FactoryDataArray[i].m_pLIBID)) ;
}
return S_OK ;
}

HRESULT CFactory::UnregisterAll()
{
for(int i = 0 ; i < g_cFactoryDataEntries ; i++)
{
UnregisterServer(*(g_FactoryDataArray[i].m_pCLSID),
g_FactoryDataArray[i].m_szVerIndProgID,
g_FactoryDataArray[i].m_szProgID) ;
}
return S_OK ;
}


//
// Start factories
//
BOOL CFactory::StartFactories()
{
CFactoryData* pStart = &g_FactoryDataArray[0] ;
const CFactoryData* pEnd =
&g_FactoryDataArray[g_cFactoryDataEntries - 1] ;

for(CFactoryData* pData = pStart ; pData <= pEnd ; pData++)
{
// Initialize the class factory pointer and cookie.
pData->m_pIClassFactory = NULL ;
pData->m_dwRegister = NULL ;

// Create the class factory for this component.
IClassFactory* pIFactory = new CFactory(pData) ;

// Register the class factory.
DWORD dwRegister ;
HRESULT hr = ::CoRegisterClassObject(
*pData->m_pCLSID,
static_cast<IUnknown*>(pIFactory),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
// REGCLS_MULTI_SEPARATE, //@Multi
&dwRegister) ;
if (FAILED(hr))
{
pIFactory->Release() ;
return FALSE ;
}

// Set the data.
pData->m_pIClassFactory = pIFactory ;
pData->m_dwRegister = dwRegister ;
}
return TRUE ;
}

//
// Stop factories
//
void CFactory::StopFactories()
{
CFactoryData* pStart = &g_FactoryDataArray[0] ;
const CFactoryData* pEnd =
&g_FactoryDataArray[g_cFactoryDataEntries - 1] ;

for (CFactoryData* pData = pStart ; pData <= pEnd ; pData++)
{
// Get the magic cookie and stop the factory from running.
DWORD dwRegister = pData->m_dwRegister ;
if (dwRegister != 0)
{
::CoRevokeClassObject(dwRegister) ;
}

// Release the class factory.
IClassFactory* pIFactory = pData->m_pIClassFactory ;
if (pIFactory != NULL)
{
pIFactory->Release() ;
}
}
}
Loading

0 comments on commit 22daf9a

Please sign in to comment.