F# Reactive 从 COM 订阅非标准事件
F# Reactive subscribe to non-standard events from COM
我有一个本机 COM 库(我可以对其进行修改),并且我有一个 F# 应用程序试图使用项目中引用的 Interop 库中的 Rx 使用事件。
let events = obj :?> _IInteropEvents_Event
let disposable =
Observable.take 1 events.ComEventArrived
|> Observable.subscribe (fun sender data -> ()) // sender : ISender, data : IData
这里的错误信息,我没有完全理解,是:
The event 'ComEventArrived' has a non-standard type. If this event is
declared in another CLI language, you may need to access this event
using the explicit add_ComEventArrived and remove_ComEventArrived
methods for the event. If this event is declared in F#, make the type
of the event an instantiation of either 'IDelegateEvent<>' or
'IEvent<,_>'.
我不介意使用 add_ComEventArrived
,但我不知道如何使用 Observable
奇怪的是,ComEventArrived
有 2 个内部互操作类型的参数,如果我尝试订阅其他仅编组 IUnknown 的事件,它会起作用并且我没有收到 "non-standard type" 错误:
let events = obj :?> _ISnapshotEvents_Event
let disposable =
Observable.take 1 events.SnapshotEventArrived
|> Observable.subscribe (fun sender -> ()) // sender : IUnknown (unit)
我怎样才能解决问题?
- 修改 COM 库以修复非标准事件类型错误。
- 对观察者使用显式 add_/remove_ 函数。take/Observer.subscribe.
- 取消订阅前触发事件 n 次的其他方法,无需使用 mutable/lock.
我目前已阅读:
- SO: 20819860
- SO: 41439213
- Throttling F# events
我不太了解 COM 互操作,但我可以举一个将非标准事件转换为 IObservable
的示例,这应该涵盖您的第二点。
非标准事件的一个示例是 AppDomain.CurrentDomain.AssemblyResolve
,其中事件处理程序需要 return Assembly
而不是 returning unit
。要使用 add_
和 remove_
函数将其包装到 IObservable
中,您可以这样写:
let assemblyResolve =
{ new IObservable<_> with
member x.Subscribe(observer) =
let handler = ResolveEventHandler(fun o a ->
let mutable res = None
observer.OnNext((o, a, fun a -> res <- Some a))
res.Value )
AppDomain.CurrentDomain.add_AssemblyResolve(handler)
{ new IDisposable with
member x.Dispose() =
AppDomain.CurrentDomain.remove_AssemblyResolve(handler) } }
我们使用 F# 对象表达式创建 IObservable
的新实现。在 Subscribe
成员中,我们创建一个 ResolveEventHandler
并使用 add_AssemblyResolve
添加它。 Subscribe
的结果是 IDisposable
实现,然后使用 remove_AssemblyResolve
.
注销事件处理程序
这里的丑陋之处在于观察者的 OnNext
函数不能 return 任何东西,因此我们改为给它一个带有参数的三元素元组以及一个设置 return 值(这是 AssemblyResolve
特有的,所以我不怀疑你会需要这样的东西)。
我有一个本机 COM 库(我可以对其进行修改),并且我有一个 F# 应用程序试图使用项目中引用的 Interop 库中的 Rx 使用事件。
let events = obj :?> _IInteropEvents_Event
let disposable =
Observable.take 1 events.ComEventArrived
|> Observable.subscribe (fun sender data -> ()) // sender : ISender, data : IData
这里的错误信息,我没有完全理解,是:
The event 'ComEventArrived' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_ComEventArrived and remove_ComEventArrived methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<>' or 'IEvent<,_>'.
我不介意使用 add_ComEventArrived
,但我不知道如何使用 Observable
奇怪的是,ComEventArrived
有 2 个内部互操作类型的参数,如果我尝试订阅其他仅编组 IUnknown 的事件,它会起作用并且我没有收到 "non-standard type" 错误:
let events = obj :?> _ISnapshotEvents_Event
let disposable =
Observable.take 1 events.SnapshotEventArrived
|> Observable.subscribe (fun sender -> ()) // sender : IUnknown (unit)
我怎样才能解决问题?
- 修改 COM 库以修复非标准事件类型错误。
- 对观察者使用显式 add_/remove_ 函数。take/Observer.subscribe.
- 取消订阅前触发事件 n 次的其他方法,无需使用 mutable/lock.
我目前已阅读:
- SO: 20819860
- SO: 41439213
- Throttling F# events
我不太了解 COM 互操作,但我可以举一个将非标准事件转换为 IObservable
的示例,这应该涵盖您的第二点。
非标准事件的一个示例是 AppDomain.CurrentDomain.AssemblyResolve
,其中事件处理程序需要 return Assembly
而不是 returning unit
。要使用 add_
和 remove_
函数将其包装到 IObservable
中,您可以这样写:
let assemblyResolve =
{ new IObservable<_> with
member x.Subscribe(observer) =
let handler = ResolveEventHandler(fun o a ->
let mutable res = None
observer.OnNext((o, a, fun a -> res <- Some a))
res.Value )
AppDomain.CurrentDomain.add_AssemblyResolve(handler)
{ new IDisposable with
member x.Dispose() =
AppDomain.CurrentDomain.remove_AssemblyResolve(handler) } }
我们使用 F# 对象表达式创建 IObservable
的新实现。在 Subscribe
成员中,我们创建一个 ResolveEventHandler
并使用 add_AssemblyResolve
添加它。 Subscribe
的结果是 IDisposable
实现,然后使用 remove_AssemblyResolve
.
这里的丑陋之处在于观察者的 OnNext
函数不能 return 任何东西,因此我们改为给它一个带有参数的三元素元组以及一个设置 return 值(这是 AssemblyResolve
特有的,所以我不怀疑你会需要这样的东西)。