各位大侠,小弟由于某些原因需要使用 VB.NET 调用一个标准C 的 DLL,以下是该函数的描述级调用,我该如何翻译成VB的呢?
定义:
int32 s7_get_multiple_read_cnf(
void *od_ptr, /* In call */
ord16 *result_array, /* Returned */
ord16 *var_length_array, /* Call and Returned */
void *value_array /* Returned */
)
一个可运行的 C 程序例子:
static void my_get_multiple_read_cnf(ord32 cp_descr)
{ int32 ret;
ord16 result[2],var_length[2];
ord8 data_buffer[4]; /* four Byte are necessary for reading two MWORD */
ord8 *value_ptr[2]; /* initialize arrays */
result[0] = S7_RESULT_OBJ_NOT_EXISTS;
result[1] = S7_RESULT_OBJ_NOT_EXISTS;
var_length[0] = 2;
var_length[1] = 2;
value_ptr[0] = &data_buffer[0]; /* set first pointer to begin of buffer */ /问题点
value_ptr[1] = &data_buffer[2]; /* set sec. pointer to offset 2 */ /问题点 if((ret=s7_get_multiple_read_cnf(NULL,result,var_length,value_ptr))!=S7_OK)
{
my_exit(cp_descr,"Error s7_get_multiple_read_cnf",ret);
}
}
问题来了,实际上 value_ptr的第0和第1个元素分别存的是 data_buffer[0] 和 data_buffer[2]的地址,然后将value_ptr作为参数传送给 DLL 中,但是我试了不是报错就是返回值 Value_ptr 中只有一个值。VB.net中该如何处理呢??重分相赠,各位大侠快来呀~~
定义:
int32 s7_get_multiple_read_cnf(
void *od_ptr, /* In call */
ord16 *result_array, /* Returned */
ord16 *var_length_array, /* Call and Returned */
void *value_array /* Returned */
)
一个可运行的 C 程序例子:
static void my_get_multiple_read_cnf(ord32 cp_descr)
{ int32 ret;
ord16 result[2],var_length[2];
ord8 data_buffer[4]; /* four Byte are necessary for reading two MWORD */
ord8 *value_ptr[2]; /* initialize arrays */
result[0] = S7_RESULT_OBJ_NOT_EXISTS;
result[1] = S7_RESULT_OBJ_NOT_EXISTS;
var_length[0] = 2;
var_length[1] = 2;
value_ptr[0] = &data_buffer[0]; /* set first pointer to begin of buffer */ /问题点
value_ptr[1] = &data_buffer[2]; /* set sec. pointer to offset 2 */ /问题点 if((ret=s7_get_multiple_read_cnf(NULL,result,var_length,value_ptr))!=S7_OK)
{
my_exit(cp_descr,"Error s7_get_multiple_read_cnf",ret);
}
}
问题来了,实际上 value_ptr的第0和第1个元素分别存的是 data_buffer[0] 和 data_buffer[2]的地址,然后将value_ptr作为参数传送给 DLL 中,但是我试了不是报错就是返回值 Value_ptr 中只有一个值。VB.net中该如何处理呢??重分相赠,各位大侠快来呀~~
里面的e对应data_buffer的成员(有两个数据就需要两个GCHandle ,里面的GC2就是放到value_ptr中的成员。
记得要调用API之后,再调用Free()。
Public Function VarPtr(ByVal e As Object) As Integer
Dim GC As GCHandle = GCHandle.Alloc(e, GCHandleType.Pinned)
Dim GC2 As Integer = GC.AddrOfPinnedObject.ToInt32
GC.Free()
Return GC2
End Function
把 value_ptr 数组也用 GCHandle.Alloc 的方式钉起来试试。应该是 ByVal 吧。
你 C 代码能返回正确结果?
s7_get_multiple_read_cnf(NULL,result,var_length,value_ptr) 怎么知道数组成员是2个而不是其它数量?
C 里面的指针数组没有个数信息,你又没有在数组成员中添加结束标志。
会报运算错误,因为左边是byte类型,而右边是int32 怎么处理呢?
甚至比近指针(16位)还短,根本不能在指向外部内存啊。
我认为你的 C 代码调用根本没有在 data_buffer 中得到正确的数据。
请仔细阅读接口文档。
typedef SN_Uint8 ord8 /*unsigned char */
我问的是语句 value_ptr[0] = &data_buffer[0] 的意义和在?
既然不是完整的指针,data_buffer 怎么会有值呢?你加个调试输出就可以检测的啊。
value_array: Pointer to buffers provided by the user program. The
variable values that were read are entered in the
buffers. Once again the order is the same as
specified in the request. When evaluating the buffer
contents, the data type must be taken into account.
It is also important to take into account that the
variable values are saved byte aligned, in other
words without padding bytes between two
components.
这个内存怎么分就靠 var_length_array 了。
ord8 *value_ptr[2];
对应的VB6代码:
dim value_ptr(1) as longvalue_ptr(0) = varptr(data_buffer(0))
value_ptr(1) = varptr(data_buffer(2))
虽然ord8 是unsigned char 类型,是8位的;但 ord * 就是指向 ord8类型数据的指针,它却是32位的…………
数据是 {Int32,Int32} 就划分为 {4字节,4字节}
数据是 {Int16,Int16} 就划分为 {2字节,2字节} 多出4字节没用到
数据是 {Int8,Int16,Int32} 就划分为 {1字节,2字节,4字节} 多出1字节没用到
void* 是不检测类型的,你把读写写内存块当成二进制文件一样理解,数据按照各自的长度逐个接起来。总之你先调试好 C 代码,要能正确取得了数据,而不是只看返回值。
常见的错误 C 代码都是认为自己写对的,其实指针乱指。
#15的说明哪里能看出来 value_array 是指针的指针了?甚至还是个指针的数组?
搂主的逻辑回路有异常人,难道还能传染的
ord8 value_ptr[2];
定义一个数组,2个元素,数据类型是 ord8。
ord8 类型 == SN_Uint8类型; SN_Uint8类型 == unsigned char类型ord8 *value_ptr[2];
定义一个指针数组,2个元素,指针指向的数据类型是 ord8类型的数据。居然说我是捣乱!!!!!
那ObjPtr(value_buffer(0)) 和 ObjPtr(value_buffer(1)) 得到的值相等才是正确的!
因为它们是属于同一个数组的元素,两个表达式都是要取value_buffer的描述符地址,必然是相等。
还有,我在18楼的代码中,
value_ptr(0)和value_ptr(1)的值是指向的数据,而不是指向另一个指针。楼主的C++代码应该是没有错的。
但关键还是要看楼主的VB代码是否正确使用了这个API !
还有,我在18楼的代码中,
value_ptr(0)和value_ptr(1)的值是指向的数据,而不是指向另一个指针。楼主的C++代码应该是没有错的。
但关键还是要看楼主的VB代码是否正确使用了这个API !
确实比较奇怪 同样的代码在 vb6 里调用 varptr 得到的值是正常的,在 vb.net里调用 varptr访问不同的变量返回值就是一样的。
必须在 API 调用之前 Alloc()、调用之后 Free()!
你还去直接调用!我前面的的文字说明都白讲了?
确实比较奇怪 同样的代码在 vb6 里调用 varptr 得到的值是正常的,在 vb.net里调用 varptr访问不同的变量返回值就是一样的。
怎么会有这种怪事?
按理说,VB.NET中也不应该把VarPtr() 的作用和含义改变啊!
我这儿没有.NET的环境来测试,如果.NET中真的不能通过VarPtr() 正确获取数组元素的地址了,
那么你用VarPtr和CopyMemory这个API验证一下,它是不是都返回的第1个元素的地址。
如果是第一个元素地址,那么其它元素的地址可以通过计算得到。
没有 VarPtr() 函数!!
没有 VarPtr() 函数!!!
就算没环境,随便搜一下关键字 VB.Net VarPtr 一大堆都在讨论怎么在 VB.Net 中实现 VarPtr。
var_length_array(i) = 1
GC = GCHandle.Alloc(value_buffer(i), GCHandleType.Pinned)
value_array(i) = GC.AddrOfPinnedObject.ToInt32
Next i
Tiger_Zhao你的办法可行~~~~
最后我将 value_array()定义成Integer类型后,DLL 中的函数执行正常,但是没有返回值,有个问题,我发现使用以上代码将value_buffer() 中对应的内存地址赋到 value_array() 中,并执行了DLL中的函数后, value_buffer() 的内存地址会变化(我通过VS中带的监视器监视我传给 value_array()的内存地址可以看到正确的返回值),所以导致在value_buffer() 中还是看不到返回值,请问有什么办法在这个脚本执行过程中使 value_buffer() 的内存地址不发生变化呢??
那value_buffer() 和 value_array() 用到的内存区,全部用API来申请和释放。
相当于在 C++中用 New和Delete 来处理……
等于只有最后一个是钉住的。而且.NET中中Byte也是Object,Byte数组的两个成员并不是紧邻的两字节。
象你C代码中一个把 data_buffer[0]、data_buffer[1] 作为一个 WORD 进行内存复制是不行的。
Imports System.Runtime.InteropServicesModule Module1 Sub Main()
Dim value_buffer(1) As Byte
Dim value_array(1) As Integer
Dim GC(1) As GCHandle
Dim bytes(24 - 1) As Byte For i = 0 To 1
GC(i) = GCHandle.Alloc(value_buffer(i), GCHandleType.Pinned)
value_array(i) = GC(i).AddrOfPinnedObject.ToInt32
Console.WriteLine("{0:X8}", value_array(i))
Next i '调用API' For i = 0 To 1
GC(i).Free()
Next Console.ReadLine()
End SubEnd Module
地址差了好远
015BFB88
015CA18C还有……
Imports System.Runtime.InteropServicesModule Module1 <StructLayout(LayoutKind.Sequential)> _
Public Class ByteArray2
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public item() As Byte Public Sub New()
ReDim Me.item(1)
End Sub
End Class <StructLayout(LayoutKind.Sequential)> _
Public Class ByteArray8
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> Public item() As Byte Public Sub New()
ReDim Me.item(7)
End Sub
End Class <StructLayout(LayoutKind.Sequential)> _
Public Class IntArray2
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public item() As Integer Public Sub New()
ReDim Me.item(1)
End Sub
End Class Sub Main()
Dim value_buffer As New ByteArray2
Dim value_array As New IntArray2
Dim bytes As New ByteArray8 Console.WriteLine("value_buffer : Size = {0} : Count = {1}", Marshal.SizeOf(value_buffer), value_buffer.item.Count)
Console.WriteLine("value_array : Size = {0} : Count = {1}", Marshal.SizeOf(value_array), value_array.item.Count)
Console.WriteLine("bytes : Size = {0} : Count = {1}", Marshal.SizeOf(bytes), bytes.item.Count) '申请一块内存pBuffer'
Dim pBuffer As IntPtr = Marshal.AllocHGlobal(2)
Debug.Assert(pBuffer.ToInt32 > 0, "地址不过2G.")
Console.WriteLine("pBuffer : {0:X8}", pBuffer.ToInt32) '把value_array的指针执行前面的内存块'
For i = 0 To 1
value_array.item(i) = pBuffer.ToInt32 + i * 1
Next i '把value_array复制到另外一块内存pArray'
Dim pArray As IntPtr = Marshal.AllocHGlobal(8)
Console.WriteLine("pArray : {0:X8}", pArray.ToInt32)
Marshal.StructureToPtr(value_array, pArray, True) '查看pArray的内容'
Marshal.PtrToStructure(pArray, bytes)
Console.WriteLine(BitConverter.ToString(bytes.item)) '用pArray调用API' '把返回的数据复制回数组'
Marshal.PtrToStructure(pBuffer, value_buffer)
Marshal.PtrToStructure(pArray, value_array) Marshal.FreeHGlobal(pBuffer)
Marshal.FreeHGlobal(pArray) Console.ReadLine()
End SubEnd Module
value_buffer : Size = 2 : Count = 2
value_array : Size = 8 : Count = 2
bytes : Size = 8 : Count = 8
pBuffer : 01364110
pArray : 01339F90
10-41-36-01-11-41-36-01