如何使用 F# 访问 COM 变体属性
How to access COM variant properties with F#
我可以通过 ActiveX 控件访问遗留系统。我可以使用 F# 中的 ProgID 创建此控件的实例,然后成功调用各种方法。但是,我在尝试使用它的属性时遇到问题。这些已被声明为 Variant 类型。
我可以像这样在 C# 中成功地做到这一点:
using System.Runtime.InteropServices;
// asuming I have already created an instance of the Legacy ActiveX control - axLegacy
object v = new VariantWrapper(0.0);
axLegacy.Get("Color", ref v);
在 F# 中,除了 属性:
open System
open System.Reflection
open System.Runtime.InteropServices
let getParamVal (legacyType: Type, instance: obj, propertyName: string, x: byref<obj>) =
let res = legacyType.InvokeMember(name = "Get", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = instance, args = [|propertyName, x|])
res
[<EntryPoint>]
let main argv =
let axLegacyType = Type.GetTypeFromProgID("Legacy.ProgID.1")
let axLegacy = Activator.CreateInstance(axLegacyType) // create an instance of the type
// calling a method with no parameters - works
let res = axLegacyType.InvokeMember(name = "MethodNoParams", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = axLegacy, args = null)
// calling a method with parameters - works
let res2 = axLegacyType.InvokeMember(name = "MethodWithParam", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = axLegacy, args = [|"method param value goes here"|])
// accessing a property - does not work
let mutable propertyVal = new VariantWrapper(0.0) :> obj // downcast to an Object
// this throws the COM exception
let res = getParamVal(axLegacyType, instance, "PropertyName", &magVal)
我快到了,如果有人能帮助我,我将不胜感激。我知道一个明显的答案是只使用 C#,但实际上 F# 更符合我的要求。另外,F# 是一种很酷的语言:)
=编辑=
我设法整理出 byref 位并添加了 getParamVal 函数。根据评论,我还为控件生成了 IDL,并包含以下重要部分:
[
uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX),
helpstring("Dispatch interface for AxLegacy Control"),
hidden
]
dispinterface AxLegacy {
methods:
[id(0x00000002)]
long Get(
BSTR lpszParam,
VARIANT* vValue);
};
我收到的实际错误是:
System.Reflection.TargetInvocationException
HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib
StackTrace:
at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
at Program.getParamVal(Type legacyType, Object instance, String cmd, Object& x) in C:\Source\F#\AxLegacyConsole\AxLegacyConsole\Program.fs:line 12
at Program.main(String[] argv) in C:\Source\F#\AxLegacyConsole\AxLegacyConsole\Program.fs:line 25
Inner Exception 1:
COMException: Type mismatch. (0x80020005 (DISP_E_TYPEMISMATCH))
我真的解决了这个问题,这花了很多功夫!以下是简要概述:
- 我必须先在 Visual Studio 2017 年创建东西(它完全支持 F# .NET Framework 项目)
- 我创建了一个 F# 控制台应用程序 (.NET Framework)
- 创建了一个虚拟 C# 库 (.NET Framework)
- 已将 ActiveX 控件添加到 C# 项目
- 已将构建 Interop 程序集的部分从 CSPROJ 复制到 FSPROJ 文件
- 已将生成的互操作 DLL 复制到 F# 控制台应用程序的生成输出文件夹
我在此处更详细地说明了我是如何做到的:
Using an ActiveX Control in F#
我可以通过 ActiveX 控件访问遗留系统。我可以使用 F# 中的 ProgID 创建此控件的实例,然后成功调用各种方法。但是,我在尝试使用它的属性时遇到问题。这些已被声明为 Variant 类型。
我可以像这样在 C# 中成功地做到这一点:
using System.Runtime.InteropServices;
// asuming I have already created an instance of the Legacy ActiveX control - axLegacy
object v = new VariantWrapper(0.0);
axLegacy.Get("Color", ref v);
在 F# 中,除了 属性:
open System
open System.Reflection
open System.Runtime.InteropServices
let getParamVal (legacyType: Type, instance: obj, propertyName: string, x: byref<obj>) =
let res = legacyType.InvokeMember(name = "Get", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = instance, args = [|propertyName, x|])
res
[<EntryPoint>]
let main argv =
let axLegacyType = Type.GetTypeFromProgID("Legacy.ProgID.1")
let axLegacy = Activator.CreateInstance(axLegacyType) // create an instance of the type
// calling a method with no parameters - works
let res = axLegacyType.InvokeMember(name = "MethodNoParams", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = axLegacy, args = null)
// calling a method with parameters - works
let res2 = axLegacyType.InvokeMember(name = "MethodWithParam", invokeAttr = (BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public), binder = null, target = axLegacy, args = [|"method param value goes here"|])
// accessing a property - does not work
let mutable propertyVal = new VariantWrapper(0.0) :> obj // downcast to an Object
// this throws the COM exception
let res = getParamVal(axLegacyType, instance, "PropertyName", &magVal)
我快到了,如果有人能帮助我,我将不胜感激。我知道一个明显的答案是只使用 C#,但实际上 F# 更符合我的要求。另外,F# 是一种很酷的语言:)
=编辑= 我设法整理出 byref 位并添加了 getParamVal 函数。根据评论,我还为控件生成了 IDL,并包含以下重要部分:
[
uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX),
helpstring("Dispatch interface for AxLegacy Control"),
hidden
]
dispinterface AxLegacy {
methods:
[id(0x00000002)]
long Get(
BSTR lpszParam,
VARIANT* vValue);
};
我收到的实际错误是:
System.Reflection.TargetInvocationException
HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib
StackTrace:
at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
at Program.getParamVal(Type legacyType, Object instance, String cmd, Object& x) in C:\Source\F#\AxLegacyConsole\AxLegacyConsole\Program.fs:line 12
at Program.main(String[] argv) in C:\Source\F#\AxLegacyConsole\AxLegacyConsole\Program.fs:line 25
Inner Exception 1:
COMException: Type mismatch. (0x80020005 (DISP_E_TYPEMISMATCH))
我真的解决了这个问题,这花了很多功夫!以下是简要概述:
- 我必须先在 Visual Studio 2017 年创建东西(它完全支持 F# .NET Framework 项目)
- 我创建了一个 F# 控制台应用程序 (.NET Framework)
- 创建了一个虚拟 C# 库 (.NET Framework)
- 已将 ActiveX 控件添加到 C# 项目
- 已将构建 Interop 程序集的部分从 CSPROJ 复制到 FSPROJ 文件
- 已将生成的互操作 DLL 复制到 F# 控制台应用程序的生成输出文件夹
我在此处更详细地说明了我是如何做到的: Using an ActiveX Control in F#