I'm struggling trying to modify an existing set of program that uses a COM object instance for communication.
The COM server is actually and (optional) extension for the main application. Both are deployed to target systems by simple copy.
Currently, both applications are deployed only locally to our offices (although the main application, the COM client, is deployed more widely at our customer's) and we manually register the server. We need to redesign that for a deployment on a cloud service and therefore, I'm looking into registration-free COM.
So far, I have tried:
- Writing manifests for the client and the server. Unfortunately, we cannot deploy this solution since the client is then strongly linked with the server app and cannot be deployed separately (which is the case at our customers).
- Creating a new activation context and reading the server's manifest from the resource. This works but attempting to instantiate the object results in a "
Error in the Dll
" OLE exception. Some googling tells me this is becauseDllgetClassObject
export is missing. - Exporting the
DllGetClassObject
from the executable (simply adding the export clause to the project source, using the built-in implementation fromSystem.Win.ComServ
. This results in an Access Violation when called (either directly or through the activation context). I have not been able to figure out where the AV occurs .
here is the manifests I used (I left the various attempts in the code in form of comments):
Server application manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
name="wgaticket.exe"
type="Win32"
version="1.0.0.0"
/>
<file name = "wgaticket.exe">
<comClass
clsid="{E33A1F59-CEA2-463E-97B2-1CCDA66DA984}"
/>
<!-- comClass
clsid="{E33A1F59-CEA2-463E-97B2-1CCDA66DA984}"
threadingModel = "Apartment"
/-->
<typelib tlbid="{414AE7FB-3025-40D8-B14C-2A29B6E42C29}"
version="1.0" helpdir=""/>
</file>
<!--comInterfaceExternalProxyStub
name="INewTicket"
iid="{740BF585-3246-483E-9146-B6A8E49400B5}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{414AE7FB-3025-40D8-B14C-2A29B6E42C29}" /-->
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
The client code is a bit more complex because I'm using the Spring4D framework but here are the main elements:
Client activation context creation
function getActivationContext: IActivationContext;
var
actCtx: TActCtx;
begin
result := TActivationContext.Create;
zeroMemory(@actCtx, SizeOf(actCtx));
actCtx.cbSize := SizeOf(actCtx);
actCtx.lpSource := 'wgaticket.exe';
actCtx.lpResourceName := MakeIntResource(1);
actCtx.lpAssemblyDirectory := PChar(ModulePath);
actCtx.dwFlags := ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID or ACTCTX_FLAG_RESOURCE_NAME_VALID;
result.Handle := CreateActCtx(actCtx);
if result.Handle = INVALID_HANDLE_VALUE then
begin
RaiseLastOSError;
end;
result.Cookie := 0;
end;
Client activation context activation
procedure TActivationContext.Activate;
begin
CriticalSection.Enter;
try
if (FHandle <> INVALID_HANDLE_VALUE) and (Cookie = 0) then
begin
if not ActivateActCtx(FHandle, FCookie) then
RaiseLastOSError;
end;
finally
CriticalSection.Leave;
end;
end;
New instance creation in client
class function CoNewTicket.CreateAsClient: INewTicket;
begin
// GActContext is a global, lazy interface variable. It will be auto-created the first time GActContext.Value is referenced
GActContext.Value.Activate;
result := CreateComObject(CLASS_NewTicket) as INewTicket;
end;