When creating a reg-free managed COM object, how can I get the CLR to look in a directory different to the main application's executable?

StackOverflow https://stackoverflow.com/questions/13591131

Question

This is a kind of complex question, hitting on somewhat arcane areas relating to COM, the CLR, and reg-free COM.

First, my main application is written in python. As such, it's codebase (in development) is at, say, C:\mainapp\main.py.

Of course, on windows, it is C:\Python27\python.exe that executes the program.

I now want to use reg-free COM from python (using win32com) to talk (using IDispatch) to a COM object written in C# that I control, that is located at C:\mainapp\ManagedCOMObject.dll


Note: If you don't speak python / pythoncom, note that the call to Dispatch() eventually boils down to CoCreateInstance().


Attempt 1

#main.py:

import win32com.client
CLSID_ManagedComObject_MyClass = "{zzzzz....}" #This is correct
myclass = win32com.client.Dispatch(CLSID_ManagedComObject_MyClass)

Result

Failure, because the object is not in the registry (as expected), and I didn't mention anything about manifest files, and python.exe's manifest obviously doesn't know about my object.


Attempt 2

#main.py:

ac = ActivationContext("C:\mainapp\myapp.manifest", asm_dir="C:\mainapp")
with ac.activate():
    #The above two lines fill in a ACTCTX structure, call CreateActCtx, and call ActivateActCtx 
    import win32com.client
    CLSID_ManagedComObject_MyClass = "{zzzzz....}" #This is correct
    myclass = win32com.client.Dispatch(CLSID_ManagedComObject_MyClass)

-

#myapp.manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32" 
                name="My.Main.App" 
                version="1.0.0.0" 
                processorArchitecture="x86" 
    />
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                        name="ManagedComObject" 
                        version="1.0.0.0" 
                        processorArchitecture="msil" 
            />
        </dependentAssembly>
    </dependency>
</assembly>

Note that ManagedCOMObject has an embedded manifest, which declares the COM object using the clrClass tag.

Result

The ActicateActCtx call succeeds and correctly parses the manifests of myapp.manifest and ManagedComObject.dll - I verified this using sxstrace.exe.

The call to CoCreateInstance fails with FileNotFound, after probing for C:\Python27\ManagedComObject.dll. The fusion log claims that PrivatePath is not set (presumable because python.exe.config doesn't exist), and simply does not look for the C# object in C:\mainapp.

Questions

  1. Why is this failing? I believe it is because the CLR COM loader stub is unable to import my C# assembly. If it failed before this step, the CLR would not even load, so the very fact that it is probing and creating fusion logs leads me to belive it is because the CLR can't find ManagedCOMObject.dll.

  2. Note that the CLR is loaded - I believe this means that COM has successfully looked at the current activation context to find the registration. I don't know precisely what clrClass does in the manifest, but presumably it got the CLR loaded successfully.

  3. I assume now that the issue is the CLR is not paying attention to the ActCtx when loading assemblies. If I was writing managed code, I could hook into the AppDomain.CurrentDomain.AssemblyResolve event and find the DLL myself. Since instead i'm writing unmanaged code, and hosting the CLR only implicitly, can I somehow change my applications PrivatePath and/or how assemblies are probed?

Was it helpful?

Solution

I assume now that the issue is the CLR is not paying attention to the ActCtx when loading assemblies

Yes, that's correct. The CLR has its own strategy to locate assemblies that isn't otherwise affected by the Windows activation context. By default it only looks in the GAC and then in the private bin path of the EXE. You can see this at work with the Fuslogvw.exe utility.

You don't have many great options here. Installing the assembly in the GAC is the obvious solution and in general appropriate for [ComVisible] assemblies since it helps solve the COM DLL Hell problem. Only other thing you can do is copy it to C:\Python27 directory or write a python.exe.config file in that same directory that changes the probing path to a subdirectory of C:\Python27. Neither scales well. Hosting the CLR yourself or writing an AppDomain.AssemblyResolve event handler are off the table.

Using the GAC is the appropriate solution if you want to avoid Regasm.exe /codebase

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top