各位大侠,小弟由于某些原因需要使用 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中该如何处理呢??重分相赠,各位大侠快来呀~~

解决方案 »

  1.   

    这样古怪的参数定义作为API定义真没见过。太恶心了!参考下面的代码,但是不要直接用该函数(因为里面已经调用了Free()了)。
    里面的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
      

  2.   

    这个方法我试了,由于 value_ptr是 byte类型的,调用时会报错,我现在的做法是直接调用 MSVBVM60中的 VarPtr 函数,之后用copymemory 拷贝到value_ptr中,但是还是只返回一个值, value_ptr在声明时是用byref还是byval呢? byref的话就返回一个值,byval则报不可访问受保护内存。。
      

  3.   

    你要 WORD 数据,所以 data_buffer 定义为 Short 类型。
    把 value_ptr 数组也用 GCHandle.Alloc 的方式钉起来试试。应该是 ByVal 吧。
      

  4.   

    我是需要 byte类型的 ,我去试试
      

  5.   


    你 C 代码能返回正确结果?
    s7_get_multiple_read_cnf(NULL,result,var_length,value_ptr) 怎么知道数组成员是2个而不是其它数量?
    C 里面的指针数组没有个数信息,你又没有在数组成员中添加结束标志。
      

  6.   

    如果执行成功 result_array 会返回 255  var_length_array会返回长度,现在每次都返回 1我试了一下 如果用 value_array(0=)GC.AddOfPinnedObject.Toint32 
    会报运算错误,因为左边是byte类型,而右边是int32 怎么处理呢?
      

  7.   

    ord8 是 8 位?
    甚至比近指针(16位)还短,根本不能在指向外部内存啊。
    我认为你的 C 代码调用根本没有在 data_buffer 中得到正确的数据。
    请仔细阅读接口文档。
      

  8.   

    是8位的,存的 它的定义是这样的
    typedef SN_Uint8 ord8  /*unsigned char */
      

  9.   

    typedef unsigned char SN_Uint8;
      

  10.   

    这个不需要,你确认是8位就行。
    我问的是语句 value_ptr[0] = &data_buffer[0] 的意义和在?
    既然不是完整的指针,data_buffer 怎么会有值呢?你加个调试输出就可以检测的啊。
      

  11.   

    我没有搭c的环境。。只是用了vb ,C的话前面还要加很多内容, 我理解就是在 value_array 中存储 data_buffer[0]和data_buffer[2]的 地址。难道不是吗?
      

  12.   

    这个是 value_array 的参数描述信息,没看到什么特别的内容啊~~~~
    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. 
      

  13.   

    它就是一个指针,指向一块内存。
    这个内存怎么分就靠 var_length_array 了。
      

  14.   

    看来楼主也不算太外行的人,我就简单的说一下,也许你就明白了:
    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位的…………
      

  15.   

    比如 value_array 的内存长度是8字节。
    数据是 {Int32,Int32} 就划分为 {4字节,4字节}
    数据是 {Int16,Int16} 就划分为 {2字节,2字节} 多出4字节没用到
    数据是 {Int8,Int16,Int32} 就划分为 {1字节,2字节,4字节} 多出1字节没用到
    void* 是不检测类型的,你把读写写内存块当成二进制文件一样理解,数据按照各自的长度逐个接起来。总之你先调试好 C 代码,要能正确取得了数据,而不是只看返回值。
    常见的错误 C 代码都是认为自己写对的,其实指针乱指。
      

  16.   

    #18别捣蛋!
    #15的说明哪里能看出来 value_array 是指针的指针了?甚至还是个指针的数组?
    搂主的逻辑回路有异常人,难道还能传染的
      

  17.   

    赵老虎,你没注意到 ord8  *value_ptr[2]; 这句代码中的星号吗?
    ord8  value_ptr[2];
    定义一个数组,2个元素,数据类型是 ord8
    ord8 类型  == SN_Uint8类型; SN_Uint8类型 == unsigned char类型ord8  *value_ptr[2];
    定义一个指针数组,2个元素,指针指向的数据类型是 ord8类型的数据。居然说我是捣乱!!!!!
      

  18.   

    不过挺奇怪的是 我用 ObjPtr(value_buffer(0)) 和 ObjPtr(value_buffer(1)) 得到的值是相等的,这是为啥?
      

  19.   

    要用 VarPtr , 不是用ObjPtr !
      

  20.   

    ObjPtr是取对象指针的,它的参数能传入数组?我还真没想到能用这个。不过,如果真能传入数组:
    那ObjPtr(value_buffer(0)) 和 ObjPtr(value_buffer(1)) 得到的值相等才是正确的!
    因为它们是属于同一个数组的元素,两个表达式都是要取value_buffer的描述符地址,必然是相等。
      

  21.   

    我觉得是你没看懂那段C++代码吧!
    还有,我在18楼的代码中,
    value_ptr(0)和value_ptr(1)的值是指向的数据,而不是指向另一个指针。楼主的C++代码应该是没有错的。
    但关键还是要看楼主的VB代码是否正确使用了这个API !
      

  22.   

    我觉得是你没看懂那段C++代码吧!
    还有,我在18楼的代码中,
    value_ptr(0)和value_ptr(1)的值是指向的数据,而不是指向另一个指针。楼主的C++代码应该是没有错的。
    但关键还是要看楼主的VB代码是否正确使用了这个API !
    确实比较奇怪 同样的代码在 vb6 里调用 varptr 得到的值是正常的,在 vb.net里调用 varptr访问不同的变量返回值就是一样的。
      

  23.   

    #4的那个VarPtr是错误的调用例子,必须在 GC2 使用结束之后才能调用 Free(),所以是不能简单地封装成函数的。
    必须在 API 调用之前 Alloc()、调用之后 Free()!
    你还去直接调用!我前面的的文字说明都白讲了?
      

  24.   

    我觉得是你没看懂那段C++代码吧!....................

    确实比较奇怪 同样的代码在 vb6 里调用 varptr 得到的值是正常的,在 vb.net里调用 varptr访问不同的变量返回值就是一样的。
    怎么会有这种怪事? 
    按理说,VB.NET中也不应该把VarPtr() 的作用和含义改变啊!
    我这儿没有.NET的环境来测试,如果.NET中真的不能通过VarPtr() 正确获取数组元素的地址了,
    那么你用VarPtr和CopyMemory这个API验证一下,它是不是都返回的第1个元素的地址。
    如果是第一个元素地址,那么其它元素的地址可以通过计算得到。
      

  25.   

    VB.Net 根本没有 VarPtr() 函数!
    没有 VarPtr() 函数!!
    没有 VarPtr() 函数!!!

    就算没环境,随便搜一下关键字 VB.Net VarPtr 一大堆都在讨论怎么在 VB.Net 中实现 VarPtr。
      

  26.   

    确实没有这个 函数 但可以通过调用 API的方式 调用vb6中的动态库达到调用这个函数的效果
      

  27.   

    API函数被调用了和被正确调用了还差得远呢!
      

  28.   

      For i = 0 To read_para_index
                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() 的内存地址不发生变化呢??
      

  29.   

    又有新的发现,并不是执行了 DLL 中的函数后 数组 value_buffer() 的内存地址会变化,而是执行了  GCHandle.Alloc(value_buffer(i), GCHandleType.Pinned)  之后 value_buffer() 数组的内存地址就会变化。。该如何处理呢???
      

  30.   

    既然 .NET 中的这些变量地址“无法自己管控”,
    那value_buffer() 和 value_array() 用到的内存区,全部用API来申请和释放。
    相当于在 C++中用 New和Delete 来处理……
      

  31.   

    而且这个API函数是不会自己分配空间的,我试过了,必须给它一个地址它才不会报错。
      

  32.   

    C是正常的,返回值写到了,data_buffer数组中,但是在VB里 data_buffer的内存地址会变。另外我试了,即便没有这个API函数直接定义一个空数组连续使用GC读两次,每次读到的值是不一样的
      

  33.   

    应该是你只用了一个GC变量,那么后一个赋值的时候前一个可能就自动释放了。
    等于只有最后一个是钉住的。而且.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还有……
      

  34.   

    从《NET中使用CopyMemory》学来的
    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