我有两个问题;我相信第一个,更容易的问题必须在第二个之前解决,所以我在这里只会坚持这个问题。
首先,概述: 我有一个使用USB端口的硬件设备,并有一个自定义DLL与之通信。 我正在使用VB.net从C ++升级。 自定义DLL有很多功能,我已经能够为除了一个之外的所有函数编程,使用IntPtr和编组函数 更简单的DLL调用;最后一个,这将是我的第二个问题/帖子,给我带来了问题。它是一个回调类型操作,并使用TYPEDEF定义。
所以,第一个问题: 我该如何转换
typedef void (WINAPI *MyFunctPtr)(unsigned char*,int, LPVOID)
进入VB.net?我理解(我认为)这是定义了一个名为MyFunctPtr的指针,该指针包含三个参数,并且是VOID的别名,这意味着它不会返回任何内容。这是正确的,我如何在VB.net中使用它?
typedef的用法如下:
AddHandler(MyPtrType func, LPVOID pParam);
其中AddHandler是DLL调用(这将是我的第二个问题/帖子的主题,以及所需的DECLARE语句。)
为了追求这个主题,我观看了很多论坛和Q / A类型的讨论,但似乎没有一个专门解决这个问题(至少,不是我在我的无知中,可以说)。我确实在这个论坛中发现了一个似乎非常接近同样问题的线程(" Using a C-callback function with .NET"),但我不知道该怎么说;我甚至不理解答案,更不用说这个问题了!
正如我所说,这个问题还有第二部分:
1.此代码旨在通过USB与外部硬件设备进行通信。我成功地使用了许多其他函数,使用DLL调用和使用INTPTR进行Marshaling。
2.此代码所需的功能有些不同。基本上,涉及四项工作:
a)回应" GoButton"单击执行DLL调用,向外部设备注册CallBack函数(这是一个DLL调用,当然,它传递一个对CallBack函数的引用。这告诉外部硬件在何时发送它的数据适当的事件发生)并产生第二个线程。
b)作为新生成的第二个线程,通过执行DLL调用来响应,该调用实际上告诉外部硬件&#34; OK,开始响应事件,并将数据发送到CallBack&#34; < / p>
c)在第一个/原始主题中回复&#34; StopBUtton&#34;单击执行DLL调用,实际上,它告诉外部硬件,&#34;确定,停止响应事件,并且不向CallBack发送任何数据&#34;
d)CallBack函数本身。
&#34; d&#34;我认为,这只是一个数据处理程序,应该与我已经为其他非CallBack函数编写的数据处理程序没有什么不同。 &#34; B&#34;实际上产生了第二个线程来处理CallBack响应,因为第一个线程必须可用于响应&#34; C&#34;的点击事件。
好的,所以这里是传统的DLL,按顺序:
A)
BYTE WINAPI AddHandler(MyPtrType func, LPVOID pParam); //BYTE is Int32 in VB.net
注意使用&#34; MyPtrType&#34; typedef(这里重复定义),它与CallBack函数
具有相同的三个指针 typedef void (WINAPI *MyPtrType)(unsigned char*, int, LPVOID);
b)中
BYTE WINAPI Enable(); //BYTE is Int32 in VB.net
c)中
BYTE WINAPI Disable(); //BYTE is Int32 in VB.net
以下是调用上述代码的函数:
A)
GoButton_Click()
{
AddHandler(MyCallbackFunction, this);
BeginThread(SecondThread, this);
//First thread has spawned second thread, and is now free to continue = exit this function
}
b)在SecondThread中:
SecondThread(LPVOID pParam)
{
Dialog* pthis = (Dialog*)pParam;
int ResponseFlag = 0; //int is Int32 in VB.net
ResponseFlag = Enable();
//This call will not return until the external device gets the "Stop" command, thus it exists in the second thread
return 0;
}
c)在&#34;停止&#34;按钮事件:
StopButton_Click()
{
int ResponseFlag = 0; //int is Int32 in VB.net
ResponseFlag = Disable();
}
d)在回调函数中:
MyCallbackFunction((unsigned char *buf, int rev, LPVOID pParam))
{
Dialog* pthis = (Dialog*)pParam;
CString str;
for(int i = 0; i < rev; i++)
{
str.Format("%02X ",buf[i]);
pthis->Data += str;
}
}
我知道我的系统中的BYTE = Int32,因为我在其他功能中成功使用它。
这就是我现在的位置:
Private Delegate Sub ParsedDataDelegate()
Private Declare Function EnableData Lib "Foo.dll" () As Int32 'EnableData()
Private Declare Function DisableData Lib "Foo.dll" () As Int32 'DisableData()
Private Declare Function AddDataHandle Lib "Foo.dll" (By??? ??? As IntPtr, By??? Parameter As IntPtr) As Int32 'AddDataHandle(MyFunctPtr func,LPVOID pParam)
'Note: the first parameter to "AddDataHandle" is some kind of reference to "ParseDataHandler" as the callback
'==>ALSO, SOMETHING GOES HERE TO EQUATE TO "typedef void (WINAPI *MyFunctPtr)(unsigned char*,int, LPVOID)"
Sub StartButton_Click
'This is main thread
Dim Result As Int32
Dim EnableReadData As New Thread(AddressOf ParseDataHandler)
'Register callback with external hardware device
'Result = AddDataHandle(????, ????) <==Don't yet know what to put here,
'until I figure out "typedef void (WINAPI *MyFunctPtr)(unsigned char*,int, LPVOID)"
'Spawn second thread
EnableReadData.Start()
End Sub
Sub EnableReadData
'This is spawned thread
Dim Result As Int32
'Invoke the callback
Me.Invoke(New ParseDataDelegate(AddressOf ParseDataHandler))
'Start the hardware device to get data
Result = EnableData() 'This DLL call blocks(here in the 2nd thread) until
'the StopButton_Click event (in the 1st thread) occurs to call the DisableData DLL
End Sub
Private Sub ParseDataHandler()
'Grab and display data here
End Sub
Sub StopButton_Click
Dim Result As Int32
'Stop the hardware device
Result = DisableData()
End Sub
我不仅不确定要为TypeDef使用什么,我也不确定我是否正确使用了CallBack的概念,这意味着我可能没有使用声明本身正确!
感谢您忍受这篇长篇文章。我已经把头撞在墙上差不多一个星期了,试图对抗那三个未知数,其中每一个都可能是另一个。我完全迷失了,在这一点上,不是在寻求帮助。请帮帮我。
谢谢查理
=============================================== ======================= 编辑10月28日,更新:
这是我最近的尝试。请注意,尽管列出了错误,但我觉得我们正在取得进展。感谢这个论坛的支持,我能够(我相信)继续前进,因为以前的错误似乎得到了显着的解决,让我接下来的尝试。请意识到,对我来说,这都是实验......我可能完全朝着错误的方向前进:
Private Declare Function EnableData Lib "foo.dll" () As Int32 'EnableData()
Private Declare Function DisableData Lib "foo.dll" () As Int32 'DisableData()
Private Declare Function AddDataHandle Lib "foo.dll" (ByVal Handler As MyFunctPtr, ByVal ThisClass As IntPtr) As Int32 'AddDataHandle(MyFunctPtr func,LPVOID pParam)
Private Delegate Sub ParseDataDelegate(DataBuffer As Byte(), DataLength As Integer, ParamPointer As IntPtr)
Private Delegate Sub MyFunctPtr(DataBuffer As Byte(), DataLength As Integer, ParamPointer As IntPtr) 'typedef void (WINAPI *MyFunctPtr)(unsigned char*,int, LPVOID)
Sub StartButton_Click
'This is main thread
Dim Result As Int32
Dim Callback As ParseDataDelegate
Note: Different attempts at same call...
'Attempt #1 (no parameters) produces this error, repeated 3 times, one for each parameter:
'Argument not specified for parameter 'DataBuffer' of 'Private Sub ParseCardDataHandler(DataBuffer() As Byte, DataLength As Integer, ParamPointer As System.IntPtr)'.
Dim EnableReadData As New System.Threading.Thread(System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(ParseDataHandler()))
'Attempt #2 (adding the parameters) produces this error, repeated 3 times, one for each parameter:
'1)'DataBuffer' is not declared. It may be inaccessible due to its protection level.
Dim EnableData As New System.Threading.Thread(System.Runtime.InteropServices.Marshal.GetFunctionPtrForDelegate(ParseDataHandler(DataBuffer(), DataLength, ParamPointer)))
Callback = AddressOf ParseDataHandler 'Don't let this get collected! keep it in a class variable for as long as the DLL is using it
'I get this error here:
'Value of type 'System.IntPtr' cannot be converted to 'xxxx.xxxx.MyFunctPtr'.
Result = AddDataHandle(System.Runtime.InteropServices.Marshal.GetFunctionPtrForDelegate(Callback), IntPtr.Zero)
EnableReadData.Start()
End Sub
Private Sub EnableReadData()
Dim Result As Int32
'This produces an error of "Expression Expected", 3 times, one for each parameter. What is wanted after the ":="? Or is this call wrong altogether?
Me.Invoke(New ParseDataDelegate(AddressOf ParseDataHandler(DataBuffer:=,DataLength:=, ParamPointer:=)))
Result = EnableData()
End Sub
Private Sub ParseDataHandler(DataBuffer As Byte(), DataLength As Integer, ParamPointer As IntPtr) '(a As Byte(), b As Integer, c As IntPtr)'(ByVal DataBuffer As String, ByVal Length As Integer, SomeParameter As IntPtr)
Stop
End Sub
然而,我必须感谢所有人的帮助。
查理
=============================================== ================================== 10月29日 更新:
取得了一些进展。回调是有效的,但还有一些其他问题。这是迄今为止的代码:
'Class level...
Private Declare Function EnableData Lib "foo.dll" () As Int32 'EnableData()
Private Declare Function DisableData Lib "foo.dll" () As Int32 'DisableData()
Private Declare Function AddDataHandle Lib "foo.dll" (ByVal Handler As MyFunctPtr, ByVal ThisClass As IntPtr) As Int32
Private Delegate Sub MyFunctPtr(DataBuffer As Byte(), DataLength As Integer, ParamPointer As Object) 'typedef void (WINAPI *MyFunctPtr)(unsigned char*,int, LPVOID)
Dim Callback As System.Threading.Thread
'Code level...
Sub StartButton_Click
'This is main thread
Dim Result As Int32
'Define the callback, point to desired second thread
Callback = New System.Threading.Thread(AddressOf EnableReadData) 'System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate
'Register the callback with the external hardware
Result = AddDataHandle(AddressOf ParseDataHandler, IntPtr.Zero)
'Start the second thread
Callback.Start()
End Sub
Sub StopButton_Click
Dim Result As Int32
'Stop the hardware device
Result = DisableData()
End Sub
Sub EnableReadData()
'This is the secondary thread
Dim Result As Int32
'Start the hardware device
Result = EnableData()
End Sub
Sub ParseDataHandler(DataBuffer As Byte(), DataLength As Integer, ParamPointer As Object) '(a As Byte(), b As Integer, c As IntPtr)'(ByVal DataBuffer As String, ByVal Length As Integer, SomeParameter As IntPtr)
Debug.Print(DataBuffer(0))
End Sub
此时,我有两个问题和一个问题:
1)ParseDataHandler例程中的DataLength值显示大约200多个字节的数据,但DataBuffer显示长度为1.显然,1是错误的,但是200+是否正确?需要进一步研究。另外,如果DataLength是正确的,我不确定如何从Byte数组转换为字符串。
2)我得到一个&#34; SEHException未得到处理&#34;信息。描述是&#34;外部组件抛出异常。&#34;我假设,由于硬件一直在使用原始代码,它现在仍在使用。此外,术语&#34;外部组件&#34;可能实际上并不意味着系统的外部,而是第二个线程作为主线程的外部。这似乎是一个可行的理论吗?
3)我用于AddDataHandle的定义包括&#34; ... ThisClass As IntPtr&#34;。 IntPtr应该是一个Object,但是要这样调用它,我必须传入一个Object。现在我使用IntPtr.Zero,因为我认为对象是正确的(&#34; Me&#34;)会出错。我应该使用什么对象? MyBase?我的课?或完全不同的东西?
继续我的追求,并感谢所有帮助过的人。现在,如果有人能就这最后三个问题向我提出建议......?:)
再次感谢, 查理
=============================================== ===
成功! 10月30日
这是最终的代码段:
Private Declare Function EnableData Lib "Foo.dll" () As Int32
Private Declare Function DisableData Lib "Foo.dll" () As Int32
Private Declare Function AddDataHandle Lib "Foo.dll" (ByVal Handler As MyFunctPtr, ByVal ThisClass As IntPtr) As Int32
Private Delegate Sub MyFunctPtr(ByVal DataBuffer As IntPtr, ByVal DataLength As Integer, ByVal ParamPointer As IntPtr)
Dim Callback As System.Threading.Thread
Delegate Sub SetTextCallback([text] As String)
Private Sub GoButton_Click(sender As Object, e As EventArgs)
Dim Result As Int32
'Define the callback, point to desired second thread
Callback = New System.Threading.Thread(AddressOf Me.EnableReadData)
'Register the callback with the external hardware
'NOTE: THE DLL EXPECTS THE LAST PARAMETER HERE TO BE AN OBJECT, SO IT CAN TURN AROUND AND
'PASS THAT SAME OBJECT BACK TO US AS THE "PARAMPOINTER" IN THE "MYFUNCTPTR" DELEGATE.
'HOWEVER, WE CAN SET IT TO ZERO SIMPLY BECAUSE WE DON'T CARE ABOUT IT. WE ALREADY KNOW WE
'WANT THE DATA TO END UP IN A SPECIFIC CONTROL, SO WE'LL INVOKE THAT CONTROL OURSELVES WHEN
'NEEDED. SEE "ParseDataHandler"
Result = AddDataHandle(AddressOf ParseDataHandler, IntPtr.Zero)
'Start the second thread "EnableReadData"
Callback.Start()
End Sub
Private Sub EnableReadData()
Dim Result As Int32
Dim ErrorData As String
'Start the hardware device
Result = EnableData()
End Sub
Private Sub ParseDataHandler(ByVal DataBuffer As IntPtr, ByVal DataLength As Integer, ByVal ParamPointer As IntPtr)
'HERE IS WHERE WE CAN IGNORE THE LAST PARAMETER, AS IT WAS PASSED IN VIA THE DLL AND IS
'SUPPOSED TO REPRESENT THE OBJECT THAT DISPLAYS THE DATA, IN THIS CASE OUR "lblData" LABEL.
'SINCE WE ARE CROSS_THREADING TO SHOW THE DATA ANYWAY, WE ALREADY KNOW WHERE WE ARE GOING TO
'SEND IT, SO WE JUST DO THAT; DON'T NEED THE LAST PARAMETER DATA.
'SEE "GoButton_Click"
Dim Data1 As String
Dim Data2 As New System.Text.StringBuilder(DataLength * 2)
Dim TempChar As String
Dim TempData(DataLength - 1) As Byte
Dim TempByte As Byte
'Copy DataBuffer stream into TempData byte array
System.Runtime.InteropServices.Marshal.Copy(DataBuffer, TempData, 0, DataLength)
'Convert each byte in the byte array into a two nibble hex stream
For Each TempByte In TempData
TempChar = Conversion.Hex(TempByte)
If TempChar.Length = 1 Then TempChar = "0" & TempChar
Data2.Append(TempChar)
Data2.Append(" ")
Next
'Convert hex stream to string
Data1 = Data2.ToString()
'Call the cross-thread delegate operation
Me.ShowData([Data1])
Application.DoEvents()
End Sub
Private Sub ShowData(ByVal [Data] As String)
'Is thread that originally created lblData the same thread that wants to use it now?
If Me.lblData.InvokeRequired Then
'No, so need to invoke the delegate for it...
'Define the delegate
Dim DataDelegate As New SetTextCallback(AddressOf ShowData)
'Invoke the delegate, passing the text
Me.Invoke(DataDelegate, New Object() {[Data]})
Else
'Yes, so can write directly. NOTE: THIS SHOULD NEVER HAPPEN, WE ARE NOT CALLING DIRECT FROM ANYPLACE
Me.lblData.Text = [Data]
End If
Application.DoEvents()
End Sub
Private Sub Stop_Click(sender As Object, e As EventArgs)
Dim Result As Int32
Dim ErrorData As String
Result = DisableData()
End Sub
我要感谢所有花时间指出我正确方向的人。我希望这个代码示例能够帮助其他人。
查理
答案 0 :(得分:3)
你的电话最终应该
callback = AddressOf MyHandler ' don't let this get collected! keep it in a class variable for as long as the DLL is using it
Result = AddDataHandle(Marshal.GetFunctionPtrForDelegate(callback), IntPtr.Zero)
在C ++中,第二个参数用于传递对象指针。在.NET中,这不起作用,因为垃圾收集器在内存中移动对象。但它不需要,因为GetFunctionPtrForDelegate()
函数在生成机器代码时对对象指针进行编码。
答案 1 :(得分:1)
基于@David Heffernan的提示,我试试看:
Delegate Sub MyFunctPtr(a As Byte(), b As Integer, c As Byte())
void
表示不返回任何内容。 Sub
是VB.NET中的等价物。以下是a link to explain unsigned char*
Byte()
成为Short
的方式。对于您的C ++代码,第二个参数也可以是WINAPI
,depending on which compiler was used。
我删除__stdcall
,因为它与c As Any
和VB can only use that相同。
最后一个也可以写成{{1}},depending on the function being used:
如何编组LPVoid参数的正确答案是该函数特有的。