As an addendum to my previous post, I just struggled through troubleshooting a couple of problems with using the .NET COM interop with regasm.
All members of your exposed classes must only reference other exposed classes. For instance, I added an internal class to my assembly and then created a field that stored an instance of it in my public object that was to be exposed during COM. This resulted in another obscure error code in Server.CreateObject: 8013150a.
Note that this only applies to instances of classes exposed through properties or fields in your exposed object, even if those fields/properties are private, protected, or internal. Objects instantiated inside methods and stored as local variables do not appear to cause any problems.
Debug.WriteLine will cause NullReferenceException to be thrown when a debugger is not attached. This one drove me nuts for a while; I kept getting a NullReferenceException in an odd place that didn't make any sense when I tried to call one of my methods from COM (it worked just fine calling it from .NET). I attached WinDbg to the w3wp.exe process to trace what was going on, and suddenly it stopped throwing the NullReferenceException. I unattached the debugger, and it came right back!
I pondered a moment what difference the debugger would be making, and on a whim I went through my object and removed any references to the Debug class. Apparently that did the trick, as now I have no more problems with a mysterious NullReferenceException being thrown.
Showing posts with label COM. Show all posts
Showing posts with label COM. Show all posts
Tuesday, April 10, 2007
Monday, April 9, 2007
Using .NET objects via COM
Recently I created an object in .NET that I needed to use both from ASP.NET applications and classic ASP. I really didn't want to create a web service interface for just this one little object, so I delved into the wonderful world of COM again.
If you want to instantiate your .NET object via COM, there are some basic rules you have to follow.
First, let's look at the code in C#.
There's several things going on in the above example. First of all, it's a pretty simple class. We have an overloaded set of methods, one property, and one constructor. You'll notice it's decorated with three attributes:
ComVisibleAttribute: Necessary so that your class will be exported to COM. You can also set this one at the assembly level.
GuidAttribute: This allows you to explicitly set a GUID for the generated COM object. If you don't specify one, the steps below will create one automatically, but it's best if you keep a consistent GUID in each installation when working with the same object, since you never know who will try to reference you by GUID.
ProgIdAttribute: This is only necessary if you want to override the ProgId of your COM object. By default, the ProgId will be <namespace>.<classname>.
So once you've got that worked out, compile your object into a class library. The next thing you need to is register your assembly with COM.
In your .NET framework folder you'll find a handy tool called regasm. regasm lets you register .NET assemblies as COM objects and does all the magic behind-the-scenes work for you.
There are two ways you can set this up:
The choice is up to you based on your particular needs, but I recommend using the GAC. This is because you might want to use the object again elsewhere, and it's a pain in the butt to unregister and then re-register COM objects due to locking issues.
Now that you've done that, you can create an instance of your COM object from classic ASP like normal:
And it's that easy. You can now access your nice managed .NET object in classic ASP/VBScript as if it were a COM object. And, of course, this is not limited to ASP... you can do this anywhere that you would normally be able to instantiate COM objects. Although, make sure you remember that your COM object is actually .NET... it would be worrisome to forget and sometime down the road import it into a .NET project with a redundant Interop assembly.
You may notice in the example code above, the overload to PrintBoolField has been renamed to PrintBoolField_2. That didn't exist in the original code, did it? You're right, it didn't.
COM doesn't support method overloads. When regasm builds its COM magic, it automatically appends a suffix of _2, _3, etc. to any overloaded methods. If you get confused about which overload is which, you can use a tool like OLE View® to help you figure it out.
One last note: Make sure that any base classes or interfaces your objects implement are also COM visible as well. Otherwise, you will get a mysterious error code 80131509 whenever you try to create your object.
If you want to instantiate your .NET object via COM, there are some basic rules you have to follow.
- The object must implement a public, parameterless constructor.
- Only your instance methods and properties will be visible via COM (i.e. no static stuff).
- You have to take care when using overloaded methods.
First, let's look at the code in C#.
using System;
using System.Runtime.InteropServices; // for the custom attributes below
namespace MyCompany
{
[ComVisible(true)]
[Guid("4663D268-454F-4338-B6BB-44537F5A0266")]
[ProgId("MyCompany.MyProject.MyClass")]
public class MyClass
{
private bool boolfield;
public MyClass()
{
boolfield = false;
}
public bool BoolField
{ get { return boolfield; } set { boolfield = value; } }
public string PrintBoolField()
{
return "BoolField is currently " + BoolField;
}
public string PrintBoolField(bool newValue)
{
BoolField = newValue;
return PrintBoolField();
}
}
}
There's several things going on in the above example. First of all, it's a pretty simple class. We have an overloaded set of methods, one property, and one constructor. You'll notice it's decorated with three attributes:
ComVisibleAttribute: Necessary so that your class will be exported to COM. You can also set this one at the assembly level.
GuidAttribute: This allows you to explicitly set a GUID for the generated COM object. If you don't specify one, the steps below will create one automatically, but it's best if you keep a consistent GUID in each installation when working with the same object, since you never know who will try to reference you by GUID.
ProgIdAttribute: This is only necessary if you want to override the ProgId of your COM object. By default, the ProgId will be <namespace>.<classname>.
So once you've got that worked out, compile your object into a class library. The next thing you need to is register your assembly with COM.
In your .NET framework folder you'll find a handy tool called regasm. regasm lets you register .NET assemblies as COM objects and does all the magic behind-the-scenes work for you.
There are two ways you can set this up:
- Put the assembly in the global assembly cache (GAC), and then register with regasm. This is good if you want your COM object to be globally accessible.
- Put the assembly in the application folder of the client application you want to be able to call the object. Register with regasm using the /codebase flag. The COM object will only be accessible to that application.
The choice is up to you based on your particular needs, but I recommend using the GAC. This is because you might want to use the object again elsewhere, and it's a pain in the butt to unregister and then re-register COM objects due to locking issues.
Now that you've done that, you can create an instance of your COM object from classic ASP like normal:
<html>
<body>
<p>
<%
Dim myobj : Set myobj = Server.CreateObject("MyCompany.MyProject.MyClass")
myobj.BoolField = True
' Should print: BoolField is currently True
Response.Write myobj.PrintBoolField()
Response.Write "<br>"
' Should print: BoolField is currently False
Response.Write myobj.PrintBoolField_2(False)
%>
</p>
</body>
</html>
And it's that easy. You can now access your nice managed .NET object in classic ASP/VBScript as if it were a COM object. And, of course, this is not limited to ASP... you can do this anywhere that you would normally be able to instantiate COM objects. Although, make sure you remember that your COM object is actually .NET... it would be worrisome to forget and sometime down the road import it into a .NET project with a redundant Interop assembly.
You may notice in the example code above, the overload to PrintBoolField has been renamed to PrintBoolField_2. That didn't exist in the original code, did it? You're right, it didn't.
COM doesn't support method overloads. When regasm builds its COM magic, it automatically appends a suffix of _2, _3, etc. to any overloaded methods. If you get confused about which overload is which, you can use a tool like OLE View® to help you figure it out.
One last note: Make sure that any base classes or interfaces your objects implement are also COM visible as well. Otherwise, you will get a mysterious error code 80131509 whenever you try to create your object.
Subscribe to:
Posts (Atom)