COM Interop with .NET Core 3.x and .NET 5.0

I previously wrote about COM Interop with .NET Core 1.0 and .NET Core 2.0. I haven’t had a chance to write it about it since, so this discusses both .NET Core 3.x and .NET 5.0.  If it wasn’t obvious from the previous articles, this is about calling COM components from .NET.  I believe work was done using COM interop in the other direction (calling .NET from COM components), but it is not discussed here. If you need to call .NET from Visual FoxPro, wwDotNetBridge is much simpler than standard COM Interop and highly recommended.

.NET Core 3.x

.NET Core 3.0 was a big release.  Unlike ASP.NET Core 2.0, ASP.NET Core 3.0 removed the option to run on .NET Full Framework 4.x.  It only runs on .NET Core, meaning you can no longer fall back to 4.x for COM Interop support.  WinForms and WPF desktop applications were also brought into the Core fold for the first time, and some of those apps might rely on COM Interop.

Some progress was made in 3.0, but the overall COM Interop story is the same as .NET Core 2.0.  That is, COM Interop works, but late binding using the C# dynamic keyword still doesn’t function.

dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true));
excel.Visible = true;
Console.WriteLine("Press Enter to close Excel.");
Console.ReadLine();
excel.Quit();

As with .NET Core 2.0, the above code will function in .NET Framework 4.x, but in .NET Core 3.x, an exception occurs:

‘System.__ComObject’ does not contain a definition for ‘Visible’

Microsoft had planned to bring full support for the dynamic keyword to .NET Core 3.0, but the job proved to be too large, so it was deferred. However, there are a couple of improvements.

In .NET Core 2.0, you had to jump through hoops to create an interop assembly for COM components.  In .NET 3.x projects with Visual Studio 2019, you can simply right-click on the project and select “Add COM Reference”.

In addition, some folks put together a workaround using a DynamicObject wrapper to access properties/methods on COM objects using the dynamic keyword, so you don’t have to use ugly reflection calls.  This may not work in every scenario, but you may find it meets your needs on .NET Core 3.x.

.NET 5.0

.NET 5.0 was just launched at .NET Conf on November 10, 2020. It represents the reunification of .NET Core and .NET Full Framework, hence the dropping of “Core” in the product name.  .NET 4.x remains a part of Windows and will continue to be supported indefinitely.  Some things are not coming over to .NET 5, such as Web Forms, WCF, and WWF… at least not by Microsoft. There are open-source projects to port WCF and WWF.  But what about COM Interop?  In my testing, it just works.  Indeed, Microsoft finished the work to support using the dynamic keyword with COM objects.  There’s not much more to say than that.  If incomplete COM Interop support has been keeping you on .NET Full Framework 4.x, now may be the time to take a look at .NET 5.0.

COM Interop with .NET Core 2.0

We previously discussed that COM Interop does not function in .NET Core 1.0.  What about .NET Core 2.0?  The answer is YES… with limitations.

The first and most obvious is that COM Interop only works with Windows, not other platforms.

The second limitation is that the .NET Core implementation does not include IDispatch, which means that late binding is not supported.  Almost 10 years ago, Microsoft introduced the dynamic keyword in C# 4.0, which made late binding with COM and other platforms much easier.

dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true));
excel.Visible = true;
Console.WriteLine("Press Enter to close Excel.");
Console.ReadLine();
excel.Quit();

The above code will function in .NET Framework, but in .NET Core 2.0, an exception occurs:

‘System.__ComObject’ does not contain a definition for ‘Visible’

If you look in Task Manager, you’ll see that Excel.exe has indeed started, but the object members cannot be access directly without IDispatch.  To work around this, you can use interop techniques that pre-date the dynamic keyword.

Interop assemblies are wrappers that enable .NET Core to interact with COM objects using early binding.  Microsoft provides interop assemblies for Office automation on NuGet and elsewhere.  Once the assemblies are installed, this code will work:

using Excel = Microsoft.Office.Interop.Excel;
...
var excel = new Excel.Application();
excel.Visible = true;
Console.WriteLine("Press Enter to close Excel.");
Console.ReadLine();
excel.Quit();

Aside: For web applications, Office automation is not recommended.  Check out the Open XML SDK.

What about your own COM objects? .NET Core projects do not provide a means to reference them directly.  However, you can create a .NET Framework 4.x project and add a reference to the COM object.  This will create an Interop.MyCOMObject.dll assembly in the obj/Debug folder, which you can then reference directly in the .NET Core project.

A COM object may return other objects that are not part of the type library, and early binding is not an option. This happens often in my Visual FoxPro interop code. You can use reflection to access the object members.

object excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true));
excel.GetType().InvokeMember("Visible",BindingFlags.SetProperty, Type.DefaultBinder, excel, new object[] { true });
Console.WriteLine("Press Enter to close Excel.");
Console.ReadLine();
excel.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, excel, null);

The dynamic keyword makes for more natural code, but you can make reflection less cumbersome than above with your own wrapper methods.

For the adventurous, the Powershell team has created their own implementation of IDispatch. I don’t know how reusable this implementation is, but it may be worth a look.

If you’re using in-process (DLL) COM servers, be aware of 32-bit vs 64-bit issues. For web  applications, take a look a Rick Strahl’s post on STA components. I don’t know if these techniques are available in .NET Core.  In my experience, these issues do not apply with out-of-process (EXE) COM servers, but your mileage may vary.

Lastly, keep in mind that ASP.NET Core 2.0 continues to run on .NET Framework 4.x, in addition to .NET Core.  If you’re already restricted to Windows, there aren’t many reasons to prefer .NET Core over the full framework (yet), so it remains the best option for COM Interop.  That said, it’s good to know these possibilities exist with .NET Core.

Microsoft has been choosy about what they bring over to .NET Core.  Over time, they have been finding their way towards more parity with .NET Framework. It was recently announced that WinForms/WPF will be coming to .NET Core 3.0. They may find that a lot of existing code relies on late binding.  I would not be surprised if IDispatch makes a comeback.

COM Interop with .NET Core

Logo_DotNet

UPDATE: COM Interop with .NET Core 2.0

The short story is that COM interop does not function in .NET Core 1.0. .NET Core is Microsoft’s open-source, cross-platform implementation of the core libraries in the .NET Framework.  With its cross-platform focus, the exclusion of COM interop is intentional.

ASP.NET Core is Microsoft’s open-source web framework that sits on top of the base .NET framework.  If you require COM interop and want to take advantage of new features in ASP.NET Core 1.0, you are in luck. Scott Hanselman reminds us that ASP.NET Core runs not only on .NET Core, but also on the full .NET Framework 4.6.

Here is some typical code for instantiating a COM object in C#:

dynamic myObject = Activator.CreateInstance(Type.GetTypeFromProgID("My.COMObject", true));

If you try to compile this code with .NET Core 1.0, it simply won’t work because Type.GetTypeFromProgID() is not available in the API.

That describes the current situation, but what about the future?  A while back, there was actually talk about bringing pieces of COM to Mac and Linux.  I think those plans have been scrapped or would only have limited use.

.NET Standard is an effort to bring a minimum common API set to all .NET platforms, present (Full Framework 4.6, .NET Core, Xamarin, Mono) and future.  .NET Standard 2.0 will be implemented in .NET Core 1.1, and it brings in a lot of missing APIs from the full framework. (UPDATE: .NET Core 1.1 has been released since this was written. It includes many new APIs, but not full support for .NET Standard 2.0. That will show up in a future release of .NET Core.)   It should make porting existing code to .NET Core much easier.  One of the APIs slated for 2.0 (as of this writing) is Type.GetTypeFromProgID().  That means that COM interop will work on .NET Core 1.1, right? Wrong. Calling this method will throw a “Not Implemented” or “Platform Not Supported” error.  As I was told by a .NET Foundation member:

There is often incorrect assumption made that “included in .NET Standard” == “works in .NET Core”. There are going to be some APIs that will throw PlaformNotSupportedException on .NET Core, also this set be different between Windows and Unix.

That’s a bit counter-intuitive to me. First of all, it seems like .NET Core should be the “reference implementation” of .NET Standard.  Beyond that, the availability of APIs on unsupported platforms may lead a developer to believe an API is fully functional when it is not. To solve that issue, tooling is coming that will identify APIs that are not supported on certain platforms/runtimes (hopefully before a developer has gone through a porting effort). Also, keep in mind that a least-common-denominator approach has already been tried with Portable Class Libraries, and the .NET team is going for something better. The .NET Standard team is currently accepting feedback on GitHub, so feel free to post your thoughts and questions.

Looking beyond .NET Standard 2.0 and .NET Core 1.1, several COM/native interop pieces have already been segregated for a possible future extension. Also, the source code for the underlying functionality is readily available.  I think it is only a matter of time before COM interop will be available for use with .NET Core.

References:

Debugging in ParallelFox

ParallelFox 0.6 Beta was released today.  Earlier this week, I posted a couple of new training videos about Worker Events on VFPX.  When I started, I figured the training video would be 30-45 minutes to cover all the features in ParallelFox.  I’m now up to 4 videos clocking in at almost two hours.  Part of that is my slow presentation (sorry for that, I was doing most of it off the cuff), but if you’ve ever put together videos like this, then you know how time consuming they are.  I feel like it was important to actually demonstrate those concepts in action, but for the rest of the topics, that is not a requirement.  It will be quicker for you and me if I present the remaining topics in written form, and this is the first entry in that series.  I will link to all entries from the main ParallelFox page on VFPX.  Occasionally, a video may be called for, in which case I will create a short video and link to it from the associated blog entry.  These entries may someday make their way into a Help file.  Ok, let’s get started with the first topic…

Debugging
FoxPro does not allow you to debug into COM servers.  To spell that out, suppose you created a COM server in VFP, and then instantiated that object in VFP as well.  The code would look something like this:

Local loMyCOMObject as MyCOMServer.MyObject
loMyCOMObject = CreateObject(“MyCOMServer.MyObject”)

Set Step On

loMyCOMObject.DoSomething()

In the code above, Set Step On would open the debugger, but if you tried to step into the DoSomething() method, you would not be able to see the code inside the COM object.  VFP would execute the method then return to the calling program.  Calling Set Step On (or setting a breakpoint) inside the COM object doesn’t work either.  To work around this, you have to instantiate your objects as regular FoxPro objects and fully debug them before building them into a separate COM server.

Aside: Years ago, Robert Green and Calvin Hsia demonstrated debugging FoxPro COM objects from Visual Studio. This was around the time Microsoft introduced .NET, and even though they had already decided there would be no VFP.NET, VFP still needed a good integration “story” if it were to remain part of Visual Studio.  You know the rest of the story.  The Fox was taken “out of the box” before the release of VFP 7.0, so it no longer needed that story, and the feature was dropped.

This limitation could pose some problems for ParallelFox, because code is run in parallel by means of COM servers.  It is not convenient to simply instantiate an object in the main process, and the code you are running in parallel may not be inside a class anyway.  Even if it were convenient, the code would be not be running in parallel, only in the main process, so it’s not exactly an ideal situation.

Fortunately, ParallelFox makes it easy to debug your code by providing a debug mode.  To turn on debug mode, simply pass .T. as the third parameter to the StartWorkers() method:

Local Parallel as Parallel of ParallelFox.vcx
Parallel = NewObject(“Parallel”, “ParallelFox.vcx”)

Parallel.StartWorkers(“MyProc.prg”,,.t.)

This tells ParallelFox to start the workers in full instances of VFP, rather than as standard COM objects.  Simply SET STEP ON in your worker code (breakpoints may not transfer to worker instances), and the debugger will open in the worker instance.

To use debug mode, the main process needs to know where ParallelFox.vcx and WorkerMgr.vcx are, so make sure they are in your path.  If you’re going to use the Worker object, the worker processes need to know where ParallelFox.vcx is as well, so make sure your workers can find it before you instantiate the Worker object.

VARIANTs on Fox

As I mentioned previously, I have been experimenting with integrating our application with Maximizer CRM.  Working with their SDK, it doesn’t take long to figure out that the com interfaces were designed with VB and C++ programmers in mind.  Most of it works with VFP, but there are a few exceptions.  I ran into a problem with an interface that accepts a variant variable by reference.  I was surprised to find out that Fox can’t create a variant.

Shirley I can’t be serious?  Everyone knows that all variables in Fox are variants.  While that may be true, Fox can’t create a variable of type variant that can be passed to com components.  Fox always assigns a type to a variable.  Even a simple local myvariable statement creates a logical variable.  Attempting to pass the variable by reference to a com component may result in a “type mismatch” error.

This isn’t entirely Fox’s fault.  After all, should a COM component assume that the calling environment can create a variant?  Microsoft concedes this is a problem in C++ with MFC components and recommends using ATL instead (see link below).

So, what can you do if you run into this situation?  Ideally, the COM interface would be changed not to use variants, but I doubt Maximizer will change their interfaces, in place for years, just so I can use VFP.  VB does allow you to create variables of type variant, so I created a VB wrapper component.  VFP passes a string to the VB component, the VB component converts that to a variant and passes it to the Maximizer component.  It’s not an ideal solution, but it works.

Prb: ActiveX controls passing variant* back to VFP cause error