http://qs1969.pair.com?node_id=392275

This is a tutorial for calling a .NET assembly via Win32::OLE. I was looking for such a tutorial on the web and previously at PerlMonks but didn't find anything, so I elected to research the issue, get a quick example working, and submit it.

Basically what you want to be able to do is:

perl -e "use Win32::OLE; $hl = new Win32::OLE('HelloDot.HelloWorld') o +r die $!; $hl->SayHello();"
Where HelloDot.HelloWorld is a .NET assembly registered in the system to look/act/smell like a regular COM component. The following are preliminary setup steps that do not require explanation:

  • Step 1: You will have to install both the .NET Framework and the .NET SDK on your machine (type .net framework/sdk download in Google)
  • Step 2: The installer does not set your path, so you will have to add the following to be able to reach gacutil.exe, regasm.exe, csc.exe (my cheezy example was written in C#), etc.: C:\Program Files\Microsoft.NET\SDK\v1.1\Bin\;C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\

    After mucking with your PATH, you should create a key file like this:

    sn -k personality.snk
    This creates a 'key pair' file which contains a public and a private key for entering the .dll we are about to create into the GAC, the Global Assembly Cache. Think of it as a Windows registry for .NET assemblies. There is a way to separate the public key part from the private one (using sn -p) in case you are interested. (Note: I'm not sure why this thing didn't offer to allow me a choice of ciphers or allow me to choose my own entropy engine. I remember reading that it is using SHA1 or something, but I don't know.)

    The first think you will notice when you run an example with the .NET framework is that it is terribly slow. A simple 'Hello World' takes about three seconds to run every time. It seems obvious to me that this thing was not written for quick one-liner-like execution, so be warned. Copy the following hello.cs file and compile it:

    using System; using System.Reflection; [assembly:AssemblyVersion("1.0.0.0")] [assembly:AssemblyKeyFile("personality.snk")] namespace HelloDot { public class HelloWorld { public HelloWorld(){ //no param constructor for COM } public void SayHello(){ Console.WriteLine("Hello from DOT NET"); } } }
    You need a constructor with no params for the .NET assembly to be called like a regular COM component (as with Win32::OLE). There is some kind of wrapper that M$soft creates to allow COM components to interact with .NET assemblies and the constructor is necessary to make that happen. If you are interested on the why, more information on that is here. The only problem with this is that a third party .dll is probably not going to have this constructor, so at that point you will have to create a wrapper class that does have it before you try this.

    Anyway, you need to compile this into a .dll like so:

    csc /t:library /out:hellodot.dll hello.cs
    If you go back to to the hello.cs code, you will notice the funny lines before the namespace that stand out like a sore thumb: assembly:AssemblyVersion...yadda,yadda. These instructions tell the compiler that you are creating an assembly you are intending to share. If you remove the lines and recompile you will notice that the .dll becomes a little smaller. Remember to put it back in and recompile before you continue this tutorial.

    If the .dll compiled okay you now have to register it:

    gacutil /i hellodot.dll
    There is one more thing to do. Your .NET assembly still needs to look and act like a regular COM component. Normal COM components (i.e. Excel.Application, Microsoft.XML, etc.) are registered with regsvr32. This puts their GUIDs in the Window's Registry (see HKEY_LOCAL_MACHINE/Software/Classes for the ones you've got). Not so in this case. You need to use regasm.exe in order to 'register' your .NET .dll to be usable by everybody outside of the .NET camp:
    regasm hellodot.dll
    With all of this being done, you should be able to call the following:
    perl -e "use Win32::OLE; $hl = new Win32::OLE('HelloDot.HelloWorld') o +r die $!; $hl->SayHello();"

    You may read from various online sources that it is unncessary to load the assembly into the GAC for you to call it as a COM component, but this does not (at least for me) seem to work. Without registering in the GAC I had to copy the .dll to the Windows/System32 directory just to get it to work for the Window's Scripting Host. Win32::OLE simply could not find the bugger until I registered it in the GAC.

    Summary:
     
    Are you are asking yourself, "why is any of this important?" Well, it seems that the only other way to get Perl on Windows to interact with .NET is to pony up cash for Visual Studio.NET and Visual Perl. The above tutorial offers a (relatively) quick and (depending on your threshold) painless way to interoperate with .NET components.

  • Replies are listed 'Best First'.
    Re: Calling .NET Assemblies with Win32::OLE
    by dawook (Initiate) on Dec 07, 2007 at 22:52 UTC
      Thanks for this, its clear and explained well, the example works. I am guessing from my experimentation though that there are some serious limitations to this method. I have a dll, that is comprised of multiple classes, and uses Reflection and and System.Diagnostics to gather information from the calling program, it also uses the IDisposable interface. Needless to say I could not quite get it to work with perl...yet at least. If you have any examples of more comples C# dll's I would appreciate the information. Thanks again great post :-)
        There is no need to put the assembly in GAC for perl to use it. While registering using regasm.exe use the option /codebase.
        e.g.  regasm hello.dll /codebase
        Then perl will take the library from the path where it is.
          Thanks that worked...!!!
    Re: Calling .NET Assemblies with Win32::OLE
    by xtan (Initiate) on Apr 09, 2010 at 16:10 UTC
      Good article. I got a problem when using the method to call a static method in a .NET class. The error message is Win32::OLE(0.1703) error 0x8002000e: "Invalid number of parameters" in METHOD/PROPERTYGET", apparently it can't locate the static method in an object. Is there a workaround for this issue?
    Re: Calling .NET Assemblies with Win32::OLE
    by Anonymous Monk on Mar 15, 2011 at 16:42 UTC
      Hello,

      your example works fine. My problem was, that I could not load the assemblies to the cache(my .dlls have no key-file) so I was searching for another solution. I found that:

      http://search.cpan.org/~yamato/Win32-CLR-0.03/lib/Win32/CLR.pm

      Gacutil or regasm is not needed.

      Greeds
      Thomas B.
        I tried CLR.pm, looks it could only deal with .net original assemblies, may I ask how you managed to make it work with your own assembly?