Showing posts with label COM. Show all posts
Showing posts with label COM. Show all posts

Friday, 28 September 2012

A quick guide to Registration-Free COM in .Net–and how to Unit Test it

A couple of times recently I’ve needed to set up a .Net application to use Registration-Free COM, and each time I’ve had to hunt around to recall the details. Further, just this week I needed to write some unit tests that involve instantiating these un-registered COM objects, and that wasn’t straight forward. So, as much for the benefit of my future self as for you, my loyal reader, I’m going to summarise my know-how in quick blog post before it becomes used-to-know-how.

What is Registration-Free COM?

If you’re still reading, I’ll assume you know all about COM, Microsoft’s ancient technology for enabling components written in different languages to talk to each other (I wrote a little about it here, with some links to introductory articles). You are probably also aware of DLL Hell. That isn’t a place where bad executables are sent when they are terminated. Rather, it was a pain inflicted on developers by the necessity of registering COM components (and other DLLs) in a central place in the OS. Since all components were dumped into the same pool, one application could cause all kinds of hell for others by registering different versions of shared DLLs. The OS doesn’t police this pool, and it certainly doesn’t enforce compatibility, so much unexpected weird and wonderful behaviour was the result.

Starting with Windows XP, it has been possible to more-or-less escape this hell by not registering components in a central location, and instead using Registration-Free COM. This makes it much easier to deploy applications, because you can just copy a bunch of files – RegSvr32 is not involved, and there are no Registry keys to be written. You can be confident that your application will have no impact on others once installed.

It is all done using manifests.

Individual Manifest Files

For each dll, or ocx file (or ax files in my case – I’m working with DirectShow filters) containing COM components you need to create a manifest.

Suppose your dll is called MyCOMComponent.dll. Your manifest file should be called MyCOMComponent.sxs.manifest, and it should contain the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity
    type="win32"
    name="MyCOMComponent.sxs"
    version="1.0.0.0" />

<file name="MyCOMComponent.dll">
    <comClass
        description="MyCOMComponent"
        clsid="{AB12C3D4-567D-4156-802B-40A1387ADE61}"
        threadingModel="Both" />
</file>
</assembly>

Obviously you need to make sure that the clsid inside comClass is correct for your component. If you have more than one COM object in your dll you can add multiple comClass elements. For those not wanting to generate these manifests by hand, a StackOverflow answer lists some tools that might help.

About Deployment

When you deploy your application you should deploy both the dll/ocx/ax file and its manifest into the same directory as your .Net exe/dlls. When developing in Visual Studio, I customise the build process to make sure all these dlls get copied into the correct place for running and debugging the application. I stole the technique for doing this from the way ASP.Net MVC applications manage their dlls.

Put all the dlls and manifests into a folder called _bin_deployableAssemblies alongside the rest of your source code. Then modify your csproj file and add the following Target at the end of it:

<!--
  ============================================================
  CopyBinDeployableAssemblies

  This target copies the contents of ProjectDir\_bin_deployableAssemblies to the bin
  folder, preserving the relative paths
  ============================================================
  -->
<Target Name="CopyBinDeployableAssemblies" Condition="Exists('$(MSBuildProjectDirectory)\_bin_deployableAssemblies')">
  <CreateItem Include="$(MSBuildProjectDirectory)\_bin_deployableAssemblies\**\*.*" Condition="Exists('$(MSBuildProjectDirectory)\_bin_deployableAssemblies')">
    <Output ItemName="_binDeployableAssemblies" TaskParameter="Include" />
  </CreateItem>
  <Copy SourceFiles="@(_binDeployableAssemblies)" DestinationFolder="$(OutDir)\%(RecursiveDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
</Target>

To make sure that target is called when you build, update the AfterBuild target (uncomment it first if you’re not currently using it):

 <Target Name="AfterBuild" DependsOnTargets="MyOtherTarget;CopyBinDeployableAssemblies" />

The Application Manifest

Now you need to make sure your application declares its dependencies.

First add an app.manifest file to your project, if you haven’t already got one. To do this in Visual Studio, right click the project, select Add –> New Item … and then choose Application Manifest File. Having added the manifest, you need to ensure it is compiled into your executable. You do this by right-clicking the project, choosing Properties, then going to the Application tab. In the resources section you’ll see a Manifest textbox: make sure your app.manifest file is selected.

image

Now you need to add a section to the app.manifest file for each dependency.

By default your app.manifest file will probably already have a dependency for the Windows Common Controls. After that (so, nested directly inside the root element) you should add the following for each of the manifest files you created earlier:

<dependency>
  <dependentAssembly>
    <assemblyIdentity
        type="win32"
        name="MyCOMComponent.sxs"
        version="1.0.0.0" />
  </dependentAssembly>
</dependency>

Notice that we drop the “.manifest” off the end of the manifest file name when we refer to it here. The other important thing is that the version number here and the one in the manifest file should exactly match, though I don’t think there’s any reason to change it from 1.0.0.0.

Disabling the Visual Studio Hosting Process

There’s just one more thing to do before you try running your application, and that is to turn off the Visual Studio hosting process. The hosting process apparently helps improve debugging performance, amongst other things (though I’ve not noticed greatly decreased performance with it disabled). The problem is that, when enabled, application executables are not loaded directly- rather, they are loaded by an intermediary executable with a name ending .vshost.exe. The upshot is that the manifest embedded in your exe is ignored, and COM components are not loaded.

Disabling the hosting process is simple:  go to the Debug tab of your project’s Properties and uncheck “Enable the Visual Studio hosting process

image

With everything set up, you’ll want to try running your application. If you got everything right first time, everything will go smoothly. If not you might see an error like this:

image

If you do, check Windows’ Application event log for errors coming from SideBySide. These are usually pretty helpful in telling you which part of your configuration has a problem.

Summary

To re-cap briefly, here are the steps to enabling Registration-Free COM for you application:

  1. Create a manifest file for each COM dll
  2. Make sure both COM dlls and manifest files are deployed alongside your main executable
  3. Add a manifest file to your executable which references each individual manifest file
  4. Make sure you turn off the Visual Studio hosting process before debugging

Unit Testing and Registration-Free COM

And now, as promised, a word about running Unit Tests when Registration-Free COM is involved.

If you have a Unit Test which tries to create a Registration-Free COM object you’ll probably get an exception like

Retrieving the COM class factory for component with CLSID {1C123B56-3774-4EE4-A482-512B3AB7CABB} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).

If you don’t get this error, it’s probably because the component is still registered centrally on your machine. Running regsvr32 /u [Path_to_your_dll] will unregister it.

Why do Unit Tests fail, when the application works? It is for the same reason that the Visual Studio hosting process breaks Registration-Free COM: your unit tests are actually being run in a different process (for example, the Resharper.TaskRunner), and the manifest file which you so carefully crafted for your exe is being ignored. Only the manifest on the entry executable is taken into account, and since that’s a generic unit test runner it says nothing about your COM dependencies.

But there’s a workaround. Win32 has some APIs –the Activation Context APIs- which allow you to manually load up a manifest for each thread which needs to create COM components. Spike McLarty has written some code to make these easy to use from .Net, and I’ll show you a technique to incorporate this into your code so that it works correctly whether called from unit tests or not.

Here’s Spike’s code, with a few minor modifications of my own:

/// <remarks>
/// Code from http://www.atalasoft.com/blogs/spikemclarty/february-2012/dynamically-testing-an-activex-control-from-c-and
/// </remarks>
class ActivationContext
{
    static public void UsingManifestDo(string manifest, Action action)
    {
        UnsafeNativeMethods.ACTCTX context = new UnsafeNativeMethods.ACTCTX();
        context.cbSize = Marshal.SizeOf(typeof(UnsafeNativeMethods.ACTCTX));
        if (context.cbSize != 0x20)
        {
            throw new Exception("ACTCTX.cbSize is wrong");
        }
        context.lpSource = manifest;

        IntPtr hActCtx = UnsafeNativeMethods.CreateActCtx(ref context);
        if (hActCtx == (IntPtr)(-1))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        try // with valid hActCtx
        {
            IntPtr cookie = IntPtr.Zero;
            if (!UnsafeNativeMethods.ActivateActCtx(hActCtx, out cookie))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            try // with activated context
            {
                action();
            }
            finally
            {
                UnsafeNativeMethods.DeactivateActCtx(0, cookie);
            }
        }
        finally
        {
            UnsafeNativeMethods.ReleaseActCtx(hActCtx);
        }
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class UnsafeNativeMethods
    {
        // Activation Context API Functions
        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "CreateActCtxW")]
        internal extern static IntPtr CreateActCtx(ref ACTCTX actctx);

        [DllImport("Kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);

        [DllImport("Kernel32.dll", SetLastError = true)]
        internal static extern void ReleaseActCtx(IntPtr hActCtx);

        // Activation context structure
        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
        internal struct ACTCTX
        {
            public Int32 cbSize;
            public UInt32 dwFlags;
            public string lpSource;
            public UInt16 wProcessorArchitecture;
            public UInt16 wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
            public IntPtr hModule;
        }

    }
}

The method UsingManifestDo allows you to run any code of your choosing with an Activation Context loaded from a manifest file. Clearly we only need to invoke this when our code is being called from a Unit Test. But how do we structure code elegantly so that it uses the activation context when necessary, but not otherwise? Here’s my solution:

public static class COMFactory
{
   private static Func<Func<object>, object> _creationWrapper = function => function();

   public static T CreateComObject<T>() where T:new()
   {
       var instance = (T)_creationWrapper(() => new T());
       return instance;
   }

   public static object CreateComObject(Guid guid)
   {
       Type type = Type.GetTypeFromCLSID(guid);
       var instance = _creationWrapper(() => Activator.CreateInstance(type));

       return instance;
   }

   public static void UseManifestForCreation(string manifest)
   {
       _creationWrapper = function =>
           {
               object result = null;
               ActivationContext.UsingManifestDo(manifest, () => result = function());
               return result;
           };
   }
}

Whenever I need to create a COM Object in my production code, I do it by calling COMFactory.CreateCOMObject. By default this will create the COM objects directly, relying on the manifest which is embedded in the executable.

But in my Test project, before running any tests I call COMFactory.UseManifestForCreation and pass in the path to the manifest file. This ensures that the manifest gets loaded up before we try to create any COM objects in the tests.

To avoid duplicating the manifest file, I share the same file between my Test project and main executable project. You can do this right clicking your test project, choosing Add->Existing Item… then app.manifest in your main project. Finally, click the down arrow on the Add split button, and choose Add as Link.

If you’ve got any tips to share on using Registration-Free COM, whether in Unit Tests or just in applications, please do leave a comment.

Wednesday, 7 September 2011

Microsoft’s Build Windows Conference is Next Week – What’s in Store?

It’s less than a week to go until Microsoft’s Build conference. According to tradition, about now I should be speculating on what the future holds for us faithful Microsoft developers. But there’s still no official session list. Fortunately, we do have a few official hints, and a couple of very unofficial leaks to whet our appetites for what’s in store.

Windows 8

Read the Build homepage, and you’ll see that the conference is focused very clearly on Windows 8. We’ve already had a preview of the new touch-centric, Windows Phone 7-like UI. And over on the Building 8 blog, Steven Sinofsky and others on the Windows team have started announcing some of the to-be-expected features like USB 3.0 support.

Here are some of the other things that we know already:

And now comes the controversial part. Those of you who have not entirely delegated their memories to Google will recall that when Microsoft first showed the new Metro desktop for Windows 8, they announced that developers would be able to use HTML5 and JavaScript for building these new-fangled immersive apps. But they said nothing about the presence of .Net, WPF or Silverlight in this brave new world, stirring up an instant furore in the blogosphere.

But there’s no need to panic. It’s all under control. I think.

Direct UI

This is where we turn to the leaks. Leaked Windows 8 builds, that is.

According to Peter Bright over on Ars Technica, Microsoft are creating a new Windows Runtime (or WinRT), which is intended to be the successor to the Win32 API. It will be a native-code API, but shaped in a way that is pleasing to the eye of a managed-code developer, and what is more, said .Net developers will be able to access WinRT through a new (hopefully painless) version of COM for which support is being built into .Net 4.5.

Included in WinRT is a new UI framework called Direct UI which appears to be a lean-and-mean version of WPF/Silverlight (and also uses XAML – remember how part of the XAML team got moved to the Windows division!). With the official details coming out in less than a week, there’s little point on me elaborating further here, but you can get a taster of the new APIs by reading these two forum threads which dissect some of the leaked Windows 8 builds.

What I look forward to hearing is where WPF fits into the picture. One thing we know with a high degree of confidence, given Microsoft’s backwards-compatibility track record: current WPF applications will continue to work. The question is: will there be further development to WPF? As I reported last year, we know there are some new features coming, including fixing the airspace issues when hosting Hwnds inside WPF controls, and enabling hosting of Silverlight controls. But will there be anything beyond that? And will there be interop between WPF and Direct UI? Watch this space.

Silverlight

Remember that Microsoft stirred up another firestorm at PDC 2010 by staying mum about Silverlight? They put that right a few weeks later by announcing Silverlight 5, which was quite distinctly not a maintenance release, since it included a whole raft of new features like a 3d API, vector printing support, and P/Invoke for Trusted Applications. They’ve now made good on that, with the Silverlight 5 Release Candidate coming out just last week. It will be interesting to learn Microsoft’s vision for how Silverlight, WPF and Direct UI align.

C# 5.0

We already know the headline feature for C# 5: asynchronous methods. We’ve had a CTP. Here’s hoping for a beta release during the conference. One thing Anders did say at his talk last year is that async won’t be the only new feature in C# 5.0. So I wonder what else he has up his sleeves? It would be nice if it was the Roslyn project (Compiler as a Service).

Visual Studio

It sounds like Microsoft are preparing to release a new preview build of Visual Studio at Build. Many of the features that were previously released as PowerToys for VS 2010 are going to part of vNext. But the more interesting news to me is that Microsoft have been taking note of data coming back from PerfWatson to make some big performance improvements. Visual Studio vNext is going to make more use of multi-core machines, and will reduce memory usage in some cases by doing builds out-of-process for C# projects.

Stay Tuned

My new boss has kindly given me some time next week to follow the Build conference from the comfort of my office. And as in previous years, I’ll be reporting back the choicest titbits as I find them. Follow me on twitter to hear it as it happens.

Now, over to you. What are you looking forward to? Have you heard any rumours that I’ve not picked up on?

Wednesday, 14 July 2010

C# 4 broke my code! The Pitfalls of COM Interop and Extension methods

A couple of weeks back I got the go-ahead from my boss to upgrade to Visual Studio 2010. What with Microsoft’s legendary commitment to backwards compatibility, and the extended beta period for the 2010 release, I wasn’t expecting any problems. The Upgrade Wizard worked its magic without hiccup, the code compiled, everything seemed fine.

Banana Skin - Image: Chris Sharp / FreeDigitalPhotos.netThen I ran our suite of unit tests. 6145 passed. 15 failed.

When I perused the logs of the failing tests they all had this exception in common:

COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))

How could that happen? Nothing should have changed: though I was compiling in VS 2010, I was still targeting .Net 3.5. The runtime behaviour should be identical. Yet it looked like COM Interop itself was broken. Surely not?

A footprint in the flowerbed

I dug deeper. In every case, I was calling an extension method on a COM object – an extension method provided by Microsoft’s VSTO Power Tools, no less.

These Power Tools are painkillers that Microsoft made available for courageous souls programming against the Office Object Model using C#. Up until C# 4, calling a method with optional parameters required copious use of Type.Missing – one use for every optional parameter you didn’t wish to specify. Here’s an example (you might want to shield your eyes):

var workbook = application.Workbooks.Add(Missing.Value);
workbook.SaveAs(
    "Test.xls", 
    Missing.Value, 
    Missing.Value, 
    Missing.Value, 
    Missing.Value, 
    Missing.Value, 
    XlSaveAsAccessMode.xlNoChange, 
    Missing.Value, 
    Missing.Value, 
    Missing.Value, 
    Missing.Value, 
    Missing.Value);

The Power Tools library provides extension methods that hide the optional parameters:

using Microsoft.Office.Interop.Excel.Extensions;

var workbook = application.Workbooks.Add();
workbook.SaveAs(
    new WorkbookSaveAsArgs { FileName = "Test.xls"} );

Can you see what the problem is yet? I couldn’t. So I thought I’d try a work-around.

A big part of C# 4 is playing catch-up with VB.Net improving support for COM Interop. The headline feature is that fancy new dynamic keyword that makes it possible to do late-bound COM calls, but the one that’s relevant here is the support for optional and named parameters. The nice thing is that, whereas using the dynamic keyword requires you to target .Net 4.0, optional and named parameters are a compiler feature, so you can make use of them even if you are targeting .Net 3.5.

That meant that I didn’t need Power Tools any more. In C# 4 I can just rewrite my code like this:

var workbook = application.Workbooks.Add();
workbook.SaveAs( Filename: "Test.xls" );

And that worked. COM Interop clearly wasn’t broken. But why was the code involving extension methods failing?

Looking again over the stack traces of the exceptions in my failed tests I noticed something very interesting. The extension methods didn’t appear. The call was going directly from my method into the COM Interop layer. I was running a Debug build, so I knew that the extension method wasn’t being optimised out.

The big reveal

Then light dawned.

Take a look at the signature of the SaveAs method as revealed by Reflector:

void SaveAs(
   [In, Optional, MarshalAs(UnmanagedType.Struct)] object Filename,
    /* 11 other parameters snipped */)

Notice in particular how Filename is typed as object rather than string. This always happens to optional parameters in COM Interop to allow the Type.Missing value to be passed through if you opt out of supplying a real value.

Now when C# 3.0 was compiling my code and deciding what I meant by

workbook.SaveAs(new WorkbookSaveAsArgs { ... } );

it had to choose between a call to the SaveAs instance method with 12 parameters or a call to the SaveAs extension method with a single parameter. The extension method wins hands down.

But the landscape changes in C# 4.0. Now the compiler knows about optional parameters. It’s as if a whole bunch of overloads have been added to the SaveAs method on Workbook with 0 through to 12 parameters. Which method does the compiler pick this time? Remember that it will always go for an instance method over an extension method. Since new WorkbookSaveAsArgs { … } is a perfectly good object the compiler chooses the SaveAs instance method, completely ignoring the extension method.

All seems well until we hit Run and Excel is handed the WorkbookSaveAsArgs instance by COM Interop. Not knowing how to handle it, it spits it back out in the form of a Type Mismatch exception.

Mystery solved.

The moral

So watch out if your code uses extension methods on COM objects and you’re planning on upgrading to VS 2010. Don’t assume that your code works the same as before just because it compiled OK. Unit tests rule!

Update: Microsoft have confirmed that this is indeed a breaking change that they didn’t spot before shipping.

Thursday, 20 May 2010

A Primer on exposing .Net to COM

Four days ago, I jumped in a time machine, dialled back two millennia, and emerged in the medieval period of programming history, sometime during the reign of King COM. Now, returning, I present to you my rough notes on how you, a citizen of the brave new .Net utopia can communicate with the denizens of the dark ages.

Brace yourself

You’ll want to start by understanding the basic principles on which COM is built. There is a series of articles on CodeProject which form a good introduction. Here are links to Part 1 and Part 2. A post on StackOverflow provides an index to the remainder. Reading those articles, I am, once again, amazed at the ingenuity of the ancients!

Consuming COM in .Net

Consuming COM in .Net - well “it’s trivial” (as my old Maths lecturer used to say, having written a partial differential equation on the blackboard, looking to us expectantly for a solution). And in .Net 4.0 it has just got trivialler thanks to dynamic types and No PIA. You just right click on your project, click  Add Reference…, then select the appropriate library in the COM tab. All the types will then be available to you as if they were regular .Net types

Exporting .Net to COM

Where it gets interesting is when you want to make a .Net type available to COM. Here’s what I’ve learnt.

  • Create yourself a new Class library project. In the AssemblyInfo file, make sure that the ComVisible attribute is given the value false – you can then be selective in what is visible. Also, make sure that a Guid has been created.
  • Start by defining an interface defining the methods that you want your object to expose to COM. Decorate this interface with the attribute ComVisible(true).
  • Implement the interface in your object, and decorate the class with [ComVisible(true)] and [ClassInterface(ClassInterfaceType.None)]
  • Open the project properties, go to the Build tab, and tick Register for COM interop. This will ensure that the the necessary Type Library (.tlb) file gets created in the /bin directory
  • To check how your type will appear to COM, you can browse the tlb file in the Visual Studio Object Browser – but note that this will lock the tlb file, so you won’t be able to rebuild your project whilst it remains open in the browser.
  • I’ve noticed in VS2010 that the tlb file sometimes doesn’t seem to get updated: a Clean and a Rebuild soon fixed that.

An example:

using System;
using System.Runtime.InteropServices;

namespace ComLibrary
{
    [ComVisible(true)]
    public interface IMainType
    {
        int GetInt();

        void StartTiming();

        int StopTiming();
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class MainType : IMainType
    {
        private Stopwatch _stopWatch;

        public int GetInt()
        {
            return 42;
        }

        public void StartTiming()
        {
            _stopWatch = new Stopwatch();
            _stopWatch.Start();
        }

        public int StopTiming()
        {
            return (int)_stopWatch.ElapsedMilliseconds;
        }
    }
}

Using the exported COM object

I last touched C++ about 15 years ago. I’ve never programmed with COM in C++. With that valuation of my advice, here is how I went about calling my COM object from C++.

  • Remember to call CoInitialize(NULL) on each thread from which you call COM objects. Before the thread exits you should also call CoUninitialize()
  • Copy the tlb from the /bin directory of your .Net project into your C++ project. At the top of the file where you want to call your COM object put #import “<name of your tlb file>.tlb”.
  • Define a smart pointer to your type: this takes care of all the AddRef/ReleaseRef stuff which you should have read about in the introductory article I pointed you to: for example
    COMLibrary::IMainTypePtr myType;
  • Create an instance of your object:
    myType.CreateInstance(__uuidof(COMLibrary::MainType));
  • Use it:
    myType->GetInt();
  • During development, you might find that, having updated your tlb file, new or changed members don’t appear on the C++ side. There are two things to try here:
    1. In the Debug folder of your C++ project look for the two files <your typelib name>.tlh and <your typelib name>.tli and delete them. Then rebuild your project.
    2. If the project compiles but you get intellisense errors, try closing down the solution and deleting the intellisense cache files. These are located next to the solution file, and have extension sdf (for VS 2010) or ncb (for VS 2008 and earlier)

Here’s a fuller sample for the C++ side

#include "stdafx.h"
#include <iostream>
#import "ComLibrary.tlb"


int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);

    ComLibrary::IMainTypePtr myType;
    myType.CreateInstance(__uuidof(ComLibrary::MainType));

    
    myType->StartTiming();

    for (long long i=0; i < 1000000; i++)
    {
        myType->GetInt();
    }

    long timeInMilliseconds = myType->StopTiming();

    printf("%d", timeInMilliseconds);
    std::cin.get();

    CoUninitialize();
    return 0;
}

Other notes

  • If you try using your exported .Net type in VBA or VB6 and you get the error “Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic”, then check the parameter and return types on the interfaces you are exposing. I got this error when trying to return a .Net long – a 64-bit integer. VBA can’t count that far: it’s longs are only 32-bit.
  • Primitive types appear to translate fairly straight-forwardly between .Net and COM. Things start to get hairier when arrays become involved. On the COM side these become SAFEARRAYS, and look like being a right pain to deal with, somewhat mitigated by the CComSafeArray wrapper class.