RegFree COM working from C#, NOT working from VBA
Asked Answered
F

2

10

I'm curently trying to get registration-free COM working with Excel as the client, and a .NET dll as the server. Currently I'm simply trying to get a proof-of-concept working but am having trouble.

Obviously, as I am using Excel, I can't simply use a client manifest living alongside the executable, so I'm using Microsoft.Windows.ActCtx (link)

I have the client manifest, assembly manifest, and dll all in the same location.

Unfortunately, what works in C# doesn't appear to work in Excel / VBA and I'm stumped as to the reason. While the C# test client works perfectly, VBA gives a 80070002 error, with message Method 'CreateObject' of object 'IActCtx' failed.

I have a .NET dll (COMTestService.dll) exposing a single class / interface to COM (COMTestObject / ICOMTestObject ), as here:

[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("EEE50CDF-D8EC-4F38-B986-C231EC45171E")]
public interface ICOMTestObject
{
    [ComVisible(true)]
    string GetString(int number);
}

[ComVisible(true), ClassInterfaceAttribute(ClassInterfaceType.None), ComDefaultInterface(typeof(ICOMTestObject))]
[Guid("6E54611B-8B56-49E0-9415-E59B0774A4BE")]
public class COMTestObject : ICOMTestObject
{
    public COMTestObject()
    {
    }

    public string GetString(int number)
    {
        return string.Format("The number is: {0}", number);
    }
}

The client manifest (COMTestService_Client.manifest):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly 
    manifestVersion="1.0" 
    xmlns="urn:schemas-microsoft-com:asm.v1" >
    <assemblyIdentity
        name="client"
        version="1.0.0.0" />
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                name="COMTestService"
                version="1.0.0.0" 
                processorArchitecture="msil" />
        </dependentAssembly>
    </dependency>
</assembly>

The assembly manifest (COMTestService.manifest):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly 
    manifestVersion="1.0" 
    xmlns="urn:schemas-microsoft-com:asm.v1" >
    <assemblyIdentity 
        name="COMTestService" 
        version="1.0.0.0" 
        processorArchitecture="msil" />
    <clrClass 
        clsid="{6E54611B-8B56-49E0-9415-E59B0774A4BE}" 
        progid="COMTestService.COMTestObject" 
        threadingModel="Both" 
        name="COMTestService.COMTestObject"
        runtimeVersion="v4.0.30319">
    </clrClass>
    <file 
        name="COMTestService.dll"
        hashalg="SHA1">        
    </file>
</assembly>

The VBA client code:

Dim actCtx As Object
Set actCtx = CreateObject("Microsoft.Windows.ActCtx")
actCtx.Manifest = "...\COMTestService_Client.manifest"

Dim testObject As Object
Set testObject = actCtx.CreateObject("COMTestService.COMTestObject") 'This line throws... 

Dim text As String
text = thing.GetString(42)

Debug.Print text

The C# client code:

var actCtxType = System.Type.GetTypeFromProgID("Microsoft.Windows.ActCtx");
dynamic actCtx = System.Activator.CreateInstance(actCtxType);
actCtx.Manifest = @"...\COMTestService_Client.manifest";

var type = System.Type.GetTypeFromProgID("COMTestService.COMTestObject");
dynamic obj = System.Activator.CreateInstance(type);
dynamic s = obj.GetString(42);

EDIT

The plot thickens... Just for fun, I wrote a quick COM-visible, REGISTERED helper class to do the object creation in C#, then pass it back, using a method along the lines of public object CreateObject(string manifestPath, string typeName) Now, calling this from a C# exe works fine, but calling it from VBA fails (80070002 again, message: The system cannot find the file specified.). Now I'm even more confused...

Thanks in advance for any help, and if I need to supply any more info just let me know and I'll be glad to oblige!

Freshwater answered 21/3, 2012 at 13:5 Comment(2)
The Manifest path you pass is invalid, can't use three dots. And you definitely need a full path, you can't predict what the current working directory will be.Alberik
Sorry to confuse, that was just a placeholder. The path is there in full, and is correct.Freshwater
M
2

Using Process Monitor, I discovered that the dll was being searched for in C:/Program Files/Microsoft Office/Office11/. I moved all my dlls over into there and the error was gone.

On a sidenote, if anybody knows how to tell Excel not to look there for my files but instead where the .tlb was found, I'm interested. (I already tried adding the path to the PATH environment variable)

Monarchy answered 19/2, 2013 at 11:32 Comment(0)
U
0

I can't say I've ever tried to use that technique and I've done a lot of installs over the years. I have to wonder, why not just register your .NET COM Visible server? Regasm tends to create very clean and light weight COM registration in the registry. Far cleaner then the old unmanaged DllRegisterServer() stuff.

Upwards answered 26/3, 2012 at 20:12 Comment(2)
Unfortunately this is being deployed without the possibility of administrator rights, so regasm is out the window...Freshwater
InstallShield MSI projects have a ".NET COM Visible" attribute that invokes RegAsm at build time to extract the COM meta data. In the case of a Per-User installation the COM keys are written to HKCU\Software\Classes instead of HKLM\Software\Classes. I don't see how RegAsm style COM registration is out of the question.Upwards

© 2022 - 2024 — McMap. All rights reserved.