zoukankan      html  css  js  c++  java
  • 关于c#互操作调用C Dll多级指针一个想法。

    在c#调用c dll互操作中,c dll中的函数可能会有些多级指针作为参数或者返回值的情况,

    本文的目的就是提供一种安全情况下,.net对于C DLL的调用想法,之所以叫想法,是因为这个方法

    还是有些不完美的地方,哪里不完美,将在文章末尾为大家说明。

    首先,关于.net(C#)如何调用c dll的基本方法,详见此文。

    http://my.oschina.net/bubifengyun/blog/96252

    我的环境为vs2015 community版本。

    首先建立在你的解决方案添加两个控制台项目,它们分别为一个C#,C++控制台:

    其中c++控制台项目创建时请选择控制台的Dll项目.而非默认的控制台项目。

    在你的c++控制台项目中添加两个文件:MyDll.cpp,MyDll.h,内容如下:

    MyDll.cpp

    #include "stdafx.h"
    #include "MyDLL.h"//貌似这两个头文件的顺序不能颠倒。我试了很多次,但是不能确定。
    #include "stdlib.h"
    int StateNum = 0;//全局状态量。

    //MyStruct定义在MyDll.h中

    /*以下为五种级别的结构体指针函数定义*/
    MyStruct GetStruct(MyStruct st) {
     st.MyVal = StateNum++;
     return st;
    }
    MyStruct* GetStruct1(MyStruct *st) {
     st->MyVal = StateNum++;
     
     return st;
    }
    MyStruct** GetStruct2(MyStruct **st) {
     (*st)->MyVal = StateNum++;
     
     return st;
    }
    MyStruct*** GetStruct3(MyStruct ***st) {
     (**st)->MyVal = StateNum++;
     
     return st;
    }
    MyStruct**** GetStruct4(MyStruct ****st) {
     (***st)->MyVal = StateNum++;
     
     return st;
    }

    MyDll.h如下:

    #ifndef LIB_H
    #define LIB_H
    typedef struct MyStruct {
     int MyVal;
     MyStruct *Next;
    }MyStruct;

    extern "C" _declspec(dllexport) MyStruct  GetStruct(MyStruct st);
    extern "C" _declspec(dllexport) MyStruct*  GetStruct1(MyStruct *st);
    extern "C" _declspec(dllexport) MyStruct**  GetStruct2(MyStruct **st);
    extern "C" _declspec(dllexport) MyStruct***  GetStruct3(MyStruct ***st);
    extern "C" _declspec(dllexport) MyStruct****  GetStruct4(MyStruct ****st);
    #endif

     c#项目Program.cs编辑如下;

    using System;
    using System.Runtime.InteropServices;

    namespace CDllInvoker {
        [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi) ]
        public  struct MyStruct {
            public int MyVal;
            public IntPtr Next;//此处对应C中的结构体指针,本节不会用到,详情请见我的博客:
        }
      
        class Program {       

            //DLLInvoked.dll已在解决方案目录下,且其生成到目录属性被置为始终复制;
            [DllImport("DLLInvoked.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
            public extern static MyStruct GetStruct(MyStruct st);//结构实体的传递及其接收
            [DllImport("DLLInvoked.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
            public extern static IntPtr GetStruct1(IntPtr st); //一级指针的传递及其接收,之后以此类推
            [DllImport("DLLInvoked.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
            public extern static IntPtr GetStruct2(IntPtr st);
            [DllImport("DLLInvoked.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
            public extern static IntPtr GetStruct3(IntPtr st);
            [DllImport("DLLInvoked.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
            public extern static IntPtr GetStruct4(IntPtr st);

            //其中level为想要取的指针级别,若你对泛型还不了解,建议读者去看看C#的泛型知识;
            static IntPtr GetPtrFromStructure<T>(T structure, short level = 1) where T:struct {
                int sizeOfStructure = Marshal.SizeOf(typeof(T));
                int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr));

                IntPtr ptr = Marshal.AllocHGlobal(sizeOfStructure);
                Marshal.StructureToPtr(structure, ptr, true);

                try {
                        for (int index = 1; index < level; index++) {
                            var innerPtr = Marshal.AllocHGlobal(sizeOfIntPtr);
                            Marshal.StructureToPtr(ptr, innerPtr, true);
                            Marshal.FreeHGlobal(innerPtr);
                            ptr = innerPtr;
                        }
                        return ptr;
                    }
                    catch (Exception ex) {
                        throw ex;
                    }
            }
            //其中level为传入指针的级别;
            static T  GetStructureFromPtr<T>(IntPtr ptr,short level = 1) where T:struct {
                T entity;
                IntPtr resPtr = ptr;
                for(int index =1;index < level; index++) {
                    var innerPtr = Marshal.PtrToStructure<IntPtr>(resPtr);
                    resPtr = innerPtr;
                }
                entity = (T)Marshal.PtrToStructure(resPtr, typeof(T));
                return entity;
            }

            static void Main(string[] args) {
                int sizeOfmyStruct = Marshal.SizeOf(typeof(MyStruct));
                MyStruct st = new MyStruct();
                IntPtr ptr;
                try {
                    Console.WriteLine("Invoking entity parameter with returning val...");
                    st = GetStruct(st);
                    Console.WriteLine(st.MyVal);

                    Console.WriteLine("Invoking level1 ptr with returning val...");
                    ptr = GetPtrFromStructure<MyStruct>(st, 1);
                    GetStruct1(ptr);
                    st = GetStructureFromPtr<MyStruct>(ptr, 1);
                    Console.WriteLine(st.MyVal);

                    Console.WriteLine("Invoking level2 ptr with returning val...");
                    ptr = GetPtrFromStructure<MyStruct>(st, 2);
                    GetStruct2(ptr);
                    st = GetStructureFromPtr<MyStruct>(ptr, 2);
                    Console.WriteLine(st.MyVal);
                   
                    Console.WriteLine("Invoking level3 ptr with returning val...");
                    ptr = GetPtrFromStructure<MyStruct>(st, 3);
                    GetStruct3(ptr);
                    st = GetStructureFromPtr<MyStruct>(ptr, 3);
                    Console.WriteLine(st.MyVal);
                   
                    Console.WriteLine("Invoking level4 ptr with returning val...");
                    ptr = GetPtrFromStructure<MyStruct>(st, 4);
                    GetStruct4(ptr);
                    st = GetStructureFromPtr<MyStruct>(ptr, 4);
                    Console.WriteLine(st.MyVal);
                }
                catch(AccessViolationException ex) {
                    Console.WriteLine(ex.Message);
                    //当level>2时,会有概率抛出此错误,至今还不清楚是什么原因
                }
                Console.Read();
            }
        }
    }

     

    其中,值得注意的是,当调用指针级别大于2时,有可能会出项错误,具体原因尚不清楚:

     这里我推荐的方法是,若当你所需要的指针级别大于3时,请使用ref+低一级的指针,

    以减少出现上述错误的可能。

    比如若你想使用如上的MyStruct*** GetStruct(MyStruct ***st);

    你可以使用如下的方法; 
    var  ptr = GetPtrFromStructure<MyStruct>(st, 2);
    GetStruct3(ref ptr);
    st = GetStructureFromPtr<MyStruct>(ptr, 2);
    Console.WriteLine(st.MyVal);

    当然,在Program.cs中定义的GetStruct3方法也需要改变参数为ref IntPtr;

    当然,如果有大神知道其中的原因,欢迎在下方留言。

  • 相关阅读:
    java实现遍历树形菜单方法——设计思路【含源代码】
    java实现动态验证码源代码——接受ajax的jsp
    java实现动态验证码源代码——接受ajax的jsp
    java实现动态验证码源代码——绘制验证码的jsp
    java实现动态验证码源代码——绘制验证码的jsp
    java实现动态验证码源代码——jsp页面
    java实现动态验证码源代码——jsp页面
    struts+hibernate+oracle+easyui实现lazyout组件的简单案例——struts.xml配置详情
    struts+hibernate+oracle+easyui实现lazyout组件的简单案例——struts.xml配置详情
    struts+hibernate+oracle+easyui实现lazyout组件的简单案例——Action的实现类
  • 原文地址:https://www.cnblogs.com/ponus/p/5711757.html
Copyright © 2011-2022 走看看