F# 和 C# 中 COM 对象创建的区别

Difference in COM object creation in F# and C#

有两个相同的 COM 对象定义。

F# 版本,WebUIPlugin 项目:

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#版本,WebUIPlugin2项目:

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)
        {
        }
    }
}

项目设置相同。

虽然 C# 定义工作得很好,但 F# 版本失败

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

当我尝试如下调用成员时(Example2 项目):

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[]{""});
    }
}

编译:

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"

报名人数:

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

测试:

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

注册C#版本:

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

测试:

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

工作正常

好吧,这将是 F# 如何实现接口的问题。这些接口的实现方式是只能通过接口本身调用它们,而不是通过 class.

以下代码有效:

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[]{""});

要使其正常工作,F# 界面也应该是 ComVisible。