Search code examples
c#comf#c#-to-f#

Difference in COM object creation in F# and C#


There are two identical COM object definitions.

F# version, WebUIPlugin project:

namespace WebUIPlugin

open System
open System.Runtime.InteropServices

[<Guid("BAEF0C5B-EFA5-4868-8342-7A1E6F8F7AF4")>]
type IPlugin =
    [<DispId(1)>]
    abstract OpenFormFromFile : path:string -> unit

[<Guid("8D71E2DB-D718-4595-B856-58D14EEAEBB2");
ClassInterface(ClassInterfaceType.None);
ComVisible(true)>]
type Plugin() = class
  interface IPlugin with
    member this.OpenFormFromFile(path) = ()
  end
end

C# version, WebUIPlugin2 project:

using System;
using System.Runtime.InteropServices;

namespace WebUIPlugin
{
    [Guid("BAEF0C5B-EFA5-4868-8342-7A1E6F8F7AF4")]
    public interface IPlugin
    {
        [DispId(1)]
        void OpenFormFromFile(string path);
    }

    [Guid("8D71E2DB-D718-4595-B856-58D14EEAEBB2")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class Plugin : IPlugin
    {
        public void OpenFormFromFile(string path)
        {
        }
    }
}

Project settings are the same.

While C# definition works quite well, the F# version fails with

An unhandled exception of type System.MissingMethodException occurred in mscorlib.dll
Additional information: Attempted to access a missing member.

when I try to invoke the member as follows (Example2 project):

class Program
{
    static void Main(string[] args)
    {
          var objectType = Type.GetTypeFromProgID("WebUIPlugin.Plugin");
          dynamic handler = Activator.CreateInstance(objectType);
          objectType.InvokeMember("OpenFormFromFile", BindingFlags.InvokeMethod, null, handler, new object[]{""});
    }
}

Compilation:

CALL "c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat"

MSBuild.exe WebUIPlugin.sln /nologo /target:Build /p:Configuration=Debug /p:Platform="x64"

Registration:

c:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /codebase /verbose WebUIPlugin\bin\x64\Debug\WebUIPlugin.dll 

Test:

C:\...\> Example2\bin\x64\Debug\Example2.exe

Unhandled Exception: System.MissingMethodException: Method 'WebUIPlugin.Plugin.OpenFormFromFile' not found.
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at CallSite.Target(Closure, CallSite, Type, String, BindingFlags, Object, Object, Object[])
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid6[T0,T1,T2,T3,T4,T5](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
   at Example2.Program.Main(String[] args) in ...\Example2\Program.cs:line 16

Register the C# version:

c:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /codebase /verbose WebUIPlugin2\bin\x64\Debug\WebUIPlugin2.dll 

Test:

C:\...\> Example2\bin\x64\Debug\Example2.exe

Works ok


Solution

  • Well, it's going to be that the issue in how F# implements interfaces. The interfaces are implemented in a way that they can be called only through the interface itself, not through the class.

    The following code works:

    var objectType = Type.GetTypeFromProgID("WebUIPlugin.Plugin");
    var handler = Activator.CreateInstance(objectType);
    var types = objectType.FindInterfaces((_1, _2) => true, null);
    var iPlugin = types.First(t => t.Name == "IPlugin");
    iPlugin.InvokeMember("OpenFormFromFile", BindingFlags.InvokeMethod, null, handler, new object[]{""});
    

    To make it work the F# interface should be ComVisible as well.