来自引用库的函数,该库具有函数指针作为参数

时间:2014-06-20 17:36:29

标签: vb.net delegates visual-studio-2013 function-pointers

更新:更改了问题以匹配更新的代码,并更具体地解决了我的问题。

我没有使用VB.NetVisual Studio的经验,也没有C的有限经验。我一直在尝试了解Function PointersDelegates,但仍然没有完全理解它们。

我正在VB.Net中编写一个项目,它调用dll文件中的方法。

来自dll文件的C代码:

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;

typedef void (__stdcall *fp_setbaud)(WORD);
typedef short (__stdcall *fp_get)(WORD);
typedef void (__stdcall *fp_put)(BYTE);
typedef void (__stdcall *fp_flush)(void);
typedef void (__stdcall *fp_delay)(WORD);


BYTE __stdcall InitRelay(fp_setbaud _setbaud, fp_get _get, fp_put _put,
            fp_flush _flush, fp_delay _delay) { ... }

BYTE __stdcall ReadRelay(void){ ... }

VB.net模块代码:

Declare Function InitRelay Lib "Z:\Devel\RelayAPI\Debug\RelayAPI.dll" (ByVal setbaud As Action(Of Short), ByVal getit As Func(Of Short, Short), ByVal putit As Action(Of Short), ByVal flushit As Action, ByVal delay As Action(Of Short)) As Byte

Declare Function ReadRelay Lib "Z:\Devel\RelayAPI\Debug\RelayAPI.dll" () As Byte

Public Sub setbaud(ByVal baud As Short)
    ...
End Sub

Public Function getit(ByVal timeout As Short) As Short
    ...
End Function

Public Sub putit(ByVal dat As Short)
    ...
End Sub

Public Sub flushit()
    ...
End Sub

Public Function delaymS(ByVal mS As Short) As Short
    ...
End Function

VB.Net表格代码:

    Dim a As Byte

    Call InitRelay(AddressOf moduleCode.setbaud, AddressOf moduleCode.getit,      
                   AddressOf moduleCode.putit, AddressOf moduleCode.flushit, 
                   AddressOf moduleCode.delaymS)

    a = ReadRelay()

我得到的错误是当我从VB.Net代码中调用a = ReadRelay()时。错误出现在每个参数上,并说明如下:

An unhandled exception of type 'System.AccessViolationException' occurred 

ReadRelay函数使用传递给InitRelay函数的所有函数。在InitRelay之前调用ReadRelay并且没有错误。我假设错误仍然与我如何将函数指针传递给InitRelay函数有关。

我一直在使用this website来尝试找出类型转换,但我仍然不知道该怎么做。

有没有人知道我应该做些什么才能正确调用这些功能?

修改

新代表声明如下所示:

Private setBaudDelegate As New Action(Of Short)(AddressOf modCommStuff.setbaud)
Private getItDelegate As New Func(Of Short, Short)(AddressOf modCommStuff.getit)
Private putItDelegate As New Action(Of Short)(AddressOf modCommStuff.putit)
Private flushItDelegate As New Action(AddressOf modCommStuff.flushit)
Private delayItDelegate As New Action(Of Short)(AddressOf modCommStuff.delaymS)

....

 Call InitRelay(setBaudDelegate, getItDelegate, putItDelegate, flushItDelegate, delayItDelegate)

1 个答案:

答案 0 :(得分:1)

我认为你需要固定代表,以免他们被垃圾收集。

dim handle as GCHandle = GCHandle.Alloc(ObjectToPin, GCHandleType.Pinned)

使用.Free

完成后,不要忘记释放对象
handle.Free()

本网站提供更多信息。 http://manski.net/2012/06/pinvoke-tutorial-pinning-part-4/

更新1

在阅读更多内容之后,看起来固定代理并不是特别允许的,但您仍然需要将代理保留在内存中。我会尝试为您的代理创建实例变量,并将它们保存在表单代码中定义的字段中。这应该足以让代表们不要收集垃圾并保持不受管理的"存根"从清理起来。

  

同样,托管代表可以被封送到非托管状态   代码,它们作为非托管函数指针公开。打电话   这些指针将执行非托管到托管转换;一个   改变呼叫惯例;进入正确的AppDomain;和   任何必要的论证编组。显然是非托管功能   指针必须引用固定地址。如果这是一场灾难   GC正在重新安置!这导致许多应用程序创建了一个   固定代表的句柄。这完全没必要。该   非托管函数指针实际上是指本机代码存根   我们动态生成以执行转换&封送处理。这个   存根存在于GC堆外部的固定内存中。

     

但是,应用程序负责以某种方式扩展   代理的生命周期,直到不再发生非托管调用   码。本机代码存根的生命周期与之直接相关   代表的一生。一旦代表被收集,随后   通过非托管函数指针调用将崩溃或以其他方式   腐败了这个过程。

更新2 以下是实际代码的示例。我只对那些需要一个参数的东西做了,如果你需要根据其他参数进行调整,请告诉我。

Private Sub InitRelay(d1 As Action(Of Short))
    'This sub represents the InitRelay function exported by your library.  You wouldn't      actually have this directly in your code.
End Sub


Private Sub setBaud(baud As Short)
    'This is the SetBaud sub in your module
End Sub

'This would be in your form code, at the class level (outside all subs)
Private setBaudDelegate As New Action(Of Short)(AddressOf setBaud)

'This sub is whatever sub in your code calls InitRelay
Private Sub Test()
    InitRelay(setBaudDelegate)
End Sub