Creating a COM object for Classic ASP (Part 2)

Summary

In this blog post I’m going to expand on my last post about COM objects and design a COM “wrapper” for a dictionary.  This will demonstrate the use of properties, passing parameters and the indexer.

The MyDictionary Class

The code that I’m going to use in my COM module will be something like this:

public interface IMyDictionary
{
    object this[string key] { get; set; }
}

public class MyDictionary : IMyDictionary
{
    private Dictionary<string, object> Vars = new Dictionary<string, object>();

    public object this[string key]
    {
        get
        {
            return Vars[key.ToLower()];
        }
        set
        {
            Vars[key.ToLower()] = value;
        }
    }
}

As I mentioned in the summary, this is nothing more than a wrapper for the basic functionality of the Dictionary object using a string indexer and saving and returning an object data type.  Technically, there is already a dictionary COM object available for Classic ASP, but I wanted to demonstrate how this functions.

Next, follow the steps of the last blog post (or download the final version at the bottom of this blog post) and use it in ASP.

Here’s a sample ASP program:

<%
Dim MyComObject
Dim MyText

Set MyComObject = Server.CreateObject(“ComDictionary.MyDictionary”)

MyComObject(“testvar”) = “test data”

MyText = MyComObject(“testvar”)

%>
<html>
<head></head>
<body>
    <%=MyText %>
</body>
</html>

When you execute your program, you’ll see “test data” appear in the browser.


Adding Methods to Your Object

By now you’re probably getting the idea that setting up a COM object is the hardest part.  After that, it’s just C# code to add to your object and ASP code to call the object.  So let’s add a clear function to clear all the data in our dictionary:

public void Clear()
{
    Vars.Clear();
}

And add the interface definition to your interface section:

void Clear();

Restart your IIS and rebuild your COM solution.  Now change your ASP program to look like this:

<%
Dim MyComObject
Dim MyText

Set MyComObject = Server.CreateObject(“ComDictionary.MyDictionary”)

MyComObject(“testvar”) = “test data”

MyComObject.Clear

MyText = MyComObject(“testvar”)

%>
<html>
<head></head>
<body>
    <%=MyText %>
</body>
</html>

If you run it you’ll notice that it blows up.  Some of you probably already know what the “issue” is.  It’s the fact that an item is not in the dictionary, and it’s crashing at the “get” property.  So let’s fix that.  Chang your “get” property to look like this:

get
{
    if (Vars.ContainsKey(key.ToLower()))
    {
        return Vars[key.ToLower()];
    }
    else
    {
        return null;
    }
}


Now reset your IIS and rebuild your COM solution.  You’ll notice that there is no output.  That’s because you cleared the variable that was set in the dictionary and the subsequent read of that variable produced nothing.


Where to get the Code

You can visit my GitHub account and download this sample by clicking here.  I have included the home.asp file in the root directory.




 

 

Creating a COM Object for Classic ASP

Summary

This blog post will show you how to create a COM (Component Object Model) object in C# that you can use in your classic ASP code.  Why would anybody do that?  Amazing as it might seem, there is a lot of classic ASP code out there in the form of legacy applications.  It’s monstrously difficulty to replace this code due to the fact that classic ASP isn’t compatible with anything and it doesn’t play nice with any other language.  So I’m going to show the basic mechanics of how to create a COM object in C# and how to incorporate it in your ASP code.

Writing Your First COM Program

First, let’s write the simplest program possible: Hello World.  I am going to follow a similar path as this article (which is where I learned some of this from):

Creating a COM object from scratch with C# 

There are a couple of changes I’m going to make because VS 2012 and above have a built-in GUID generator and I want to show how you can build something with no parameters, just to keep it ultra-simple.

Step 1 – Create your C# program

Create a new C# class library project.  You’ll need to add a using statement for the interop services library to the top of your code:

using System.Runtime.InteropServices;

Step 2: Construct the Class

For my example, I’m going to make this just a simple class with one method:

public class Launcher
{
    public string Hello()
    {
        return Hello world“;
    }
}

I’m leaving out the constructor, since it’s not used anyway.

Step 3: Presenting our Class Through an Interface

In order to access any methods or classes inside the COM module, we need to provide an interface.

public interface ILauncher
{
    string Hello();
}

You’ll also need to add the interface name as the base class for the Launcher object.


Step 4: Making the Connection

We need to add an attribute to our class to make sure it doesn’t automatically generate another interface (we want to use the interface we typed up manually):

[ClassInterface(ClassInterfaceType.None)]
public class Launcher : ILauncher
{
    public string Hello()
    {
        return Hello world“;
    }
}


Next, I’m going to deviate from the original article and allow early and late binding by adding the InterfaceIsDual attribute:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ILauncher
{
    string Hello();
}

Step 5: Making them Unique

This is where I’m really going to deviate from the original article.  You’ll need to generate GUID’s for both the interface and the class. Now go to the “Tools” menu and select “Create GUID”:

Choose #5, and click on the “Copy” button.  Then paste it into your code:

[Guid(“6216B529-3E4A-4FE8-8F7A-BE99C33DB1F1“),
 InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ILauncher
{
    string Hello();
}

[Guid(41027763-2E19-415A-8525-F48F604EE810),
 ClassInterface(ClassInterfaceType.None)]
public class Launcher : ILauncher
{
    public string Hello()
    {
        return Hello world“;
    }
}


You might have to remove the square brackets to make the syntax correct (as shown above).


Step 6: Going Underground with Cryptography

This is where you’ll have to drag out the “sn” utility and give your assembly a strong name.  You’ll have to search your hard drive for the sn utility and there are probably more than one version.  I found mine at this path:

“C:Program Files (x86)Microsoft SDKsWindowsv8.1AbinNETFX 4.5.1 Toolssn”

I chose the latest version that was installed on my PC.  You’ll need to open a console window (go to your start menu and type “command” in the search box.  Then navigate to the project directory of your application.  Then execute the sn utility with the “-k key.snk” parameter:

“C:Program Files (x86)Microsoft SDKsWindowsv8.1AbinNETFX 4.5.1 Toolssn” -k key.snk

You’ll need to keep the path name quoted since it has spaces in it.  Once you execute this command there will be a file generated in your project directory named “key.snk”.  Now you need to add this file to your project as a link.  So right-click on your project and select “Add -> Existing Item”.  Don’t hit the “add” button, on the add button there is a down-arrow that allows you to select a different option:

Select “Add As Link”.  To use this file, we need to go to the project properties (right-click on your project and select “Properties”).  Then select the “Signing” tab.  Now click on the “Sign the assembly” check box and choose the “key.snk” file from the drop-down list:


Save your changes.


Step 7: Wrapping it All Up

Before building your solution we need to modify some settings.  In Deutschzuid’s article that I cited at the beginning of this blog post, you are directed to change your target build version at this point.  I’m going to leave it at “any” (the default).    Go back to your project properties and select the “Application” tab.  Click on the “Assembly Information” button.  Check the “Make Assembly COM-Visible” check box.

Now switch to the “Build” tab and check the “Register for COM interop” check box.


Step 8: Compiling it and Taking it for a Walk

Now you can build the solution.

First, let’s try this in ASP.  Make sure you have IIS installed and running on your PC.  Create a sub-directory in your C:inetpub directory named “aspcom”.  You can startup the IIS control panel (go to your start menu and type “iis” in the search box) and create a new application under your default website.  Name your application “aspcom” and point the directory to the new directory you just created.  You’ll need to click on “Edit permissions” and the directory properties window will pop-up.  Click on the “Security” tab.  Click “Edit” then “Add”, then type in “IUSR” and click “Check Names”.  Then click “OK”, then check the “allow full control” check box.  Then “OK”, 

Then repeat: “Edit”, “Add”, type “IIS_IUSRS” and click “Check Names”.  You might need to change your domain to the local PC domain for this to check right.  Then “OK”, then “allow full access”, then “OK” and last “OK” to close the properties window.

Now you’ll need to add those same two rights to the directory that contains your COM object project.  Just navigate to your project directory and right-click on it.  Then repeat the steps above to give full rights to “IUSR” and IIS_IUSRS”.

Restart IIS.

Now create a text file inside the C:inetpubaspcom directory named “home.asp” and type (or copy) this in:

<%
Dim MyComObject
Dim MyText

Set MyComObject = Server.CreateObject(“Launcher.Launcher”)

MyText = MyComObject.Hello()

%>
<html>
<head></head>
<body>
    <%=MyText %>
</body>
</html>

In the IIS control panel is a link called “browse” with a url on it.  You can click this link to get to the ASP directory, but you’ll have to add home.asp to the end of the path.  Then you should see “hello world” printed on your browser.

Next, create a text file on your desktop and name it “test.html”.  Now type or copy this into it:

<HTML>
<HEAD>
<SCRIPT language=”javascript”>
function Test() {
var objTest = new ActiveXObject(“Launcher.Launcher”);
alert(objTest.Hello());
}
</SCRIPT>
</HEAD>
<BODY>
<INPUT Type=”button” onClick=”Test()” Value=”Click here to test!”>
</BODY>
</HTML>


Now save it and double-click on the html file.  You’ll have to allow blocked content then click the button.  Then answer yes to the active-x control warning and you’ll see a text box displaying “hello world” in it.


Step 9: Deploying the dll on Another Machine

If you want to deploy the dll on another machine, you’ll have to add your dll to the registry by using the regasm utility.  First, copy your dll to the target machine (just the dll).  Then you need to find the regasm program on your pc, just like you did with the sn utility above.  Choose the latest version.  Navigate to your dll’s location and use this command:

regasm ComClassExample.dll /codebase /tlb /nologo

For testing purposes, you don’t need to use regasm.


Troubleshooting Tricks

OK, now there is an issue if you are attempting to change your COM program after you have executed it from an ASP program.  That issue is that IIS has the DLL locked and you can’t re-compile.  So you’ll need to reset your IIS before you re-build your application.  Then you can run your ASP program again.

Next, you are going to want to know what is going on while you are troubleshooting this project.  Especially if it doesn’t work the first time.  So in the IIS console, click on your application (aspcom), double-click on the ASP icon:


Expand your “Debugging Properties” section and change “Enable Client-side Debugging” to true and also change “Enable Server-side Debugging” to true.  Click on “apply”.  

Now, if something blows up, you can attach a debugger and see where it breaks in Visual Studio.  You can also get a good error message, like “permission denied”.

Don’t forget about the IIS log files.  They are located in the C:inetpublogs directory.  These are usually pretty cryptic, but sometimes they can shine some light on what’s wrong.

You can also open your home.asp page in Visual Studio and put a break-point on it.  Then choose “Debug -> Attach to Process…”, then select w3wp.exe and click the “attach” button.  Refresh the web page and the breakpoint should be hit.  You cannot get tool tip help in asp, but you can put a variable in your watch list and see what is contained in the variable.

One other annoying thing you might run into.  If you click the “Restart” button in the IIS control panel and your IIS does’t restart (it just sits there scanning, like it’s waiting for something) that means that there is a w3wp.exe process holding it up.  Just CTRL-ALT-Delete and start task manager.  Then find all w3wp.exe processes and “end process”.  You’ll notice that your IIS control panel will suddenly respond.


Download the Source

You can download the source code from my GitHub account by clicking here.