COM is a good technology, but using COM with .NET can become a hair hazard as you rip it out in frustration. Too many COM components use Variant data types. Oh, how I hate Variants. I never found Variants acceptable in Visual Basic and they are just plain problematic with strongly typed languages. I like strongly typed languages; I want to know exactly what data types I am working with. I believe that Variants were a big mistake, a mistake that is now difficult to work with, especially since COM components are still widely used.
This article provides tips when using Variants with COM Interops in C#. I needed to interact with a COM component to a proprietary database. This COM component was only supported on Visual Basic 6.0. Because my application required the use of Web Services, I did not want to implement the Web Services in Visual Basic. So, I tried to force the COM to work in C#. To my amazement, it worked extremely well. The problem was not the technology, but rather the documentation.
Prior to working on the project that led me to write this article, every time I saw a project that could use COM and .NET I either tried really hard to avoid COM or pumped extra money into the budget on handling COM. Now, after working on that project, COM and .NET do not scare me as much anymore. Hopefully, after you read this article, you will not have the same fear of COM and .NET that I had and you will not make the same mistakes that I did. I really hope that some of these tips will work for you.
The easiest method of creating your COM Interops is to let Visual Studio do it for you. These instructions are for Visual Studio 2003. They may be slightly different on newer versions of the IDE.
- Create a new project.
- From Solution Explorer, right click on References and select Add Reference...
- The dialog box below will appear. Click on the COM tab.
- Go through the COM components, double click the ones that you want and then click OK.
- In Solution Explorer, you will now see the COM Interop libraries under References.
That was easy, wasn't it? Now all you need to do to use those Interop libraries is to employ the
using statement in your classes. Microsoft did this right.
The COM component that I was using came with documentation. It was good documentation if you were using Visual Basic, but a bit frustrating if using C#. This was their documentation (without the bulk of the function description):
RecipeVar.GetRecipeHistory(NumVersions, Dates, Authors, Comments)
Parameters Data Type Description
NumVersions Integer Number of recipe versions available.
Dates Variant Array of dates for each recipe version.
Authors Variant Array of authors for each recipe version.
Comments Variant Array of comments for each recipe version.
The Interop's method through Object Browser was defined as:
GetRecipeHistory(ref short, ref object, ref object, ref object)
Microsoft's .NET Framework SDK Documentation states that:
An argument passed to a ref parameter must first be initialized.
Compare this to an out parameter, whose argument does not have to
be explicitly initialized before being passed to an out parameter.
The documentation stated that when using
ref, the parameter needed to be initialized as compared to
out, which does not need to be explicitly initialized. The problem is that these objects are arrays and the exact data type cannot be pre-defined. It will compile, but when you run the application, an exception will be generated stating that the wrong data type was referenced. Where I believe the documentation fails is that you can initialize the variable to
null. Initializing a variable to
null is considered initialization. I guess that seeing the words "explicitly initialized" in the sentence right after "initialized" made me think that the memory for the variable needed to be allocated through an instantiation. Thus, the usage is:
int numVersions = 0;
object dates = null;
object authors = null;
object comments = null;
bool err = recipe.GetRecipeHistory(ref numVersions,
ref dates, ref authors, ref comments);
Now that I've got the data, how can I use it? Well, the COM documentation stated that it was an array, so I can use the array to get the data out.
string arr = new string[numVersions];
Array.Copy(comments, arr, numVersions);
With the power of .NET, I can also use some other components
string arr = new string[numVersions];
System.Collections.ICollection collection =
int i = 0;
foreach(object o in collection)
arr[i++] = "" + i + "\t" + o.ToString();
This article illustrated some methods for using Variants with C#. I spent many hours on the web Googling all sorts of combinations of Variants, COM, .NET and C# and I could not find an answer. The solution that I showed here may not work with your COM components, but giving them a go may be worth a try and save you some time. Happy coding.
Michael Carey is the Head of Development for an Automation Integrator in Philadelphia, PA. Michael specializes in Batch Automation, Process History, and Factory to Enterprise Integration.