Calling Managed Code from Unmanaged Code

One significant part of developers (including myself) are doing some kind of automation of Microsoft Office that was already treated by Mike Gold. So I will concentrate on opposing side of problem, how to use managed assembly from MS Office or VB script. It  is a good idea to check your .NET Framework SDK Documentation, especially Interoperating with Unmanaged Code. For a start we will first check managed side of story. To expose managed types, properties, fields, or events to COM they must be public. Types can't be abstract and they must have default constructor. It's recommendable to define interface explicitly but it is not necessary, tools provided with .NET Platform are capable of creating one for you. Let's try it.

using System;
public class T
{
public string A(string s)
{
return s+" from C#";
}
}

Copy and paste code in notepad and save it as str.cs. Open command line and execute the following:

csc /t:library str.cs
tlbexp str.dll /
out:str.tlb
regasm str.dll

Copy str.dll and str.tlb to MS Office directory, typically it is "C:\Program Files\Microsoft Office\Office" Now open your favorite MS Office application i.e. Excel and open Visual Basic Editor (Alt + F11). To check early binding add reference to your library, browse to and select str.tlb. Insert new module into project and paste the following in code editor:

Sub Test()
Dim o As New T
MsgBox(o.A("Hello"), , "VBA test")
End Sub
Sub
LBTest()
Dim o
o = CreateObject("T")
MsgBox(o.A("Hello"), , "VBA late binding test")
End Sub

If you like to do the same from VB script copy str.dll and str.tlb to "C:\WINNT\system32" because wscript.exe will need these to execute your script. Create new .VBS file and paste into it the following:

Dim o
Set o = CreateObject("T")
MsgBox o.A("Hello"), , "VBS test"

When you are done with testing don't forget to unregister your str.dll, to do that execute from command line "regasm str.dll /unregister". Also deleting str.dll and str.tlb copies from "C:\Program Files\Microsoft Office\Office" and "C:\WINNT\system32" is a good idea. But some developers will be interested to examine in closer details what is in str.tlb.
They will discover the following:

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: str.tlb
[
uuid(AD7B6A7C-4F96-3710-B1AC-5170E611BA57),
version(1.0),
custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, str, Version=0.0.0.0, Culture=neutral, PublicKeyToken=
null)
]
library str
{
// TLib : // TLib : Common Language Runtime Library : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
// Forward declare all types defined in this typelib
interface _T;
[
uuid(4996A037-02BD-3839-A363-A9A321885D2C),
version(1.0),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, T)
]
coclass T
{
[
default] interface _T;
interface _Object;
};
[
odl,
uuid(742F958A-4C27-399F-89DD-5368588B63B7),
hidden,
dual,
oleautomation,
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, T)
]
interface _T : IDispatch
{
};
};

What is wrong there? Our public string A method is nowhere to be seen, also familiar methods belonging to Object are hidden.

img1.gif

That means that VBA code editor won't be able to help in a way which we would like to show as methods and properties of the class. So what could be done to improve that kind of situation? Help comes from attributes. Change your C# code to this:

using System;
using System.Runtime.InteropServices;
[ClassInterface(ClassInterfaceType.AutoDual)]
public class T
{
public string A(string s)
{
return s+" from C#";
}
}
 

Repeat steps to compile, get tlb and register library, copy str.dll and str.tlb to MS Office directory "C:\Program Files\Microsoft Office\Office" and try early binding. Everything will work fine.

CallingManagedCodeVBIDE2.gif

Again we will check what things look like in OLE/COM Object Viewer.

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: str.tlb
[
uuid(AD7B6A7C-4F96-3710-B1AC-5170E611BA57),
version(1.0),
custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, str, Version=0.0.0.0, Culture=neutral, PublicKeyToken=
null)
]
library str
{
// TLib : // TLib : Common Language Runtime Library : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
// Forward declare all types defined in this typelib
interface _T;
[
uuid(4996A037-02BD-3839-A363-A9A321885D2C),
version(1.0),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, T)
]
coclass T
{
[
default] interface _T;
interface _Object;
};
[
odl,
uuid(C145FF57-9C23-355C-B068-BADECB60531B),
hidden,
dual,
nonextensible,
oleautomation,
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, T)
]
interface _T : IDispatch
{
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
HRESULT ToString([
out, retval] BSTR* pRetVal);
[id(0x60020001)]
HRESULT Equals(
[
in] VARIANT obj,
[
out, retval] VARIANT_BOOL* pRetVal);
[id(0x60020002)]
HRESULT GetHashCode([
out, retval] long* pRetVal);
[id(0x60020003)]
HRESULT GetType([
out, retval] _Type** pRetVal);
[id(0x60020004)]
HRESULT A(
[
in] BSTR s,
[
out, retval] BSTR* pRetVal);
};
};

If we compare it with old IDL now our class T is described with interface _T as last time but there are all methods (some of them are inherited from Object).

If you like it's possible to do things slightly differently, you can generate tlb in one go using command regasm str.dll /tlb:str.tlb or you can generate registry file and do registration manually using regasm str.dll /regfile:str.reg which looks like this

REGEDIT4
[HKEY_CLASSES_ROOT\T]
@="T"
[HKEY_CLASSES_ROOT\T\CLSID]
@="{4996A037-02BD-3839-A363-A9A321885D2C}"
[HKEY_CLASSES_ROOT\CLSID\{4996A037-02BD-3839-A363-A9A321885D2C}]
@="T"
[HKEY_CLASSES_ROOT\CLSID\{4996A037-02BD-3839-A363-A9A321885D2C}\InprocServer32]
@="C:\WINNT\System32\mscoree.dll"
"ThreadingModel"="Both"
"Class"="T"
"Assembly"="str, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v1.0.3705"
[HKEY_CLASSES_ROOT\CLSID\{4996A037-02BD-3839-A363-A9A321885D2C}\ProgId]
@="T"
[HKEY_CLASSES_ROOT\CLSID\{4996A037-02BD-3839-A363-A9A321885D2C}\Implemented Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]

Up Next
    Ebook Download
    View all
    Learn
    View all