C#中调用C++的dll的参数为指针类型的导出函数(包括二级指针的情况)

一:首先什么是IntPtr

先来看看MSDN上说的:用于表示指针或句柄的平台特定类型。这个其实说出了这样两个事实,IntPtr 可以用来表示指针或句柄、它是一个平台特定类型。对于它的解释,这个哥们写的比较好:It's a class that wraps a pointer that is used when calling Windows API functions. The underlying pointer may be 32 bit or 64 bit, depending on the platform.

二:用在什么地方

(1)C#调用WIN32 API时

(2)C#调用C/C++写的DLL时(其实和1相同,只是这个一般是我们在和他人合作开发时经常用到)

三:怎样用

例如有一函数原型定义为:DLLDemo_API int __stdcall Inptr_Test (LONG param1, HWND hWnd);那么我们在C#中引用时就要这样写:

 [DllImport("DllPlayer.dll", EntryPoint = "IP_TPS_OpenStream")]
 public static extern int  Inptr_Test (int param1, IntPtr hWnd);

在调用的时候就可以向Inptr_Test 的第二个参数传入某一控件的Handle。这里涉及到C#类型与C++类型的对应关系,网上这种有很多,这里就不再赘述,只谈几个经常用到的和经常出错的。

      (1)一般对于char* ,void*这种可以直接对应IntPtr,比如在C#中,我们经常用string类型,其转换为IntPtr再传给char*,void*等,转换方法为

string txt="test"; Marshal.StringToCoTaskMemAuto(txt);

这里有时会用StringToCoTaskMemAnsi,不过StringToCoTaskMemAuto自动分配内存就可以了。这样就会将txt的内容复制到非托管的内存块中。

(2)对于结构体,比如有一结构体 StructText,将其转换为Intptr,尽量不要直接用Marshal.StructureToPtr,这样很容易出错。可以这样来用:

intsize = Marshal.SizeOf(StructText);//获取结构体占用空间大小

IntPtrintptr= Marshal.AllocHGlobal(size);//声明一个同样大小的空间

Marshal.StructureToPtr(StructText, intptr, true);//将结构体放到这个空间中



C++中使用指针是家常便饭了,也非常的好用,这也是我之所以喜欢C++的原因之一。但是在C#中就强调托管的概念了,指针就不用想了。本来如果就在C#的世界里面写代码,也还算舒服,但是万事万物总有联系,这不,现在公司的另外一个用C#作的项目就碰到问题了,要调用之前用C++写的一个DLL中的一些函数,很多函数的参数都是指针类型的,这下可麻烦咯,公司里做C#的都是刚起步,C++又只有我最熟悉,这项技术研究工作又光荣的落到我身上。

        我对C#也不甚熟悉,所以也许我的方法不一定是最直接的,但是测试的结果是满足了这个调用需要了的。下面我就详细介绍一下。

        使用unsafe、fix等关键字应该是能够实现的,但是他们项目组要求不用这个,所以我也没深入去试验。除了这个方法,应该来说是有两个思路的,第一个思路可能看起来比较直接,使用ref,ref这个关键字似乎有点特殊性,字面上理解似乎应该和C++中的引用类型相对应,不过似乎它还是有一定特殊性的,貌似以前看到过一篇文章说ref会自己去判断是引用类型还是指针,我尝试了一下,果然是可行的。但是对于有二级指针的情况ref也就不灵了~这就导出了我的另一个思路,使用Marshal。

下面我们还是代码说明问题:
以下是C++DLL中的代码片断,主要是使用到的两个结构的定义,以及导出函数TestFunction的定义。

 

 

复制代码
C++ DLL中的代码片断
#pragma pack(push)
#pragma pack(1)
typedef struct EmmStruct {
    int len;
} EMMSTRUCT, *LPEMMSTRUCT;

typedef struct MyStruct {
    int iParam;
    long size;
    LPEMMSTRUCT lpEmmStructArr;
} MYSTRUCT, *LPMYSTRUCT;
#pragma pack(pop)

extern "C" void __declspec(dllexport) __stdcall  TestFunction(LPMYSTRUCT lpMyStruct)
{
    lpMyStruct->iParam = 100;
    lpMyStruct->size = 10;
    lpMyStruct->lpEmmStructArr = new EMMSTRUCT[lpMyStruct->size];
    for(int i=0;i<lpMyStruct->size;i++) {
        lpMyStruct->lpEmmStructArr[i].len = i;
    }
}

那么再来看看C#中调用的代码:
C#中调用的代码片断using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;      //使用C#导入dll必须的

namespace csharptest
{
    //StructLayout和FieldOffset这些设置不是必须的,只是为了防止对齐的问题最好加上,这样自己心里有数对齐到哪一位
    [StructLayout(LayoutKind.Explicit)]
    public struct EmmStruct
    {
        [FieldOffset(0)]
        public int len;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct MyStruct
    {
        [FieldOffset(0)]
        public int iParam;
        [FieldOffset(4)]
        public int size;
        [FieldOffset(8)]
        public IntPtr ptrEmmStruct;
    }

    class Program
    {
         // dll中导出函数的声明
        [DllImport("dllforcsharp.dll", CallingConvention=CallingConvention.Winapi)]
        public extern static void TestFunction(IntPtr ptr);

        static void Main(string[] args)
        {
            try
            {
                MyStruct s = new MyStruct();
                IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(s));
                Marshal.StructureToPtr(s, ptr, false);

                TestFunction(ptr);

                s = (MyStruct)Marshal.PtrToStructure(ptr, typeof(MyStruct));

                EmmStruct ret;
                for (int i = 0; i < s.size; i++)
                {
                    IntPtr ptr2 = new IntPtr(s.ptrEmmStruct.ToInt32() + 4 * i);
                    ret = (EmmStruct)Marshal.PtrToStructure(ptr2, typeof(EmmStruct));
                }

                Marshal.FreeHGlobal(ptr);
            }
            catch (Exception e)
            {
                string str = e.Message;
            }
            finally
            {
            }
        }
    }
}

代码也不多,而且从字面的意思就能知道是干什么的了,所以我就没写注释。用这种方法就实现了参数中含有二级指针的情况。要注意的就是C#中的long和C++中不同,它占8字节。所以一般情况下C++中long的,C#里面用int或者int32就ok了。我自己对C#不是特别熟悉,所以可能也未能完全讲解清楚,甚至可能存在漏洞,有高人见到的话,可以指点指点。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果C++ DLL参数指针类型,那么在C#调用DLL时,需要使用平台调用(P/Invoke)技术,并且需要特别注意C++C#之间的内存管理问题。具体步骤如下: 1. 在C++,使用`__declspec(dllexport)`关键字导出函数,例如: ``` __declspec(dllexport) void MyFunction(int* intPtr); ``` 2. 在C#,使用`DllImport`特性声明导出函数及其参数,例如: ``` using System.Runtime.InteropServices; [DllImport("MyCppDll.dll")] public static extern void MyFunction(IntPtr intPtr); ``` 3. 在C#,使用`Marshal.AllocHGlobal`方法分配一段内存,并使用`Marshal.WriteInt32`方法将C#的整数值写入该内存,例如: ``` int myInt = 123; IntPtr intPtr = Marshal.AllocHGlobal(sizeof(int)); Marshal.WriteInt32(intPtr, myInt); ``` 4. 在C#调用C++ DLL函数,并传入指针参数,例如: ``` MyFunction(intPtr); ``` 5. 在C++,使用指针访问该内存,并读取或修改其的值,例如: ``` void MyFunction(int* intPtr) { int myInt = *intPtr; *intPtr = myInt * 2; } ``` 6. 在C#,使用`Marshal.ReadInt32`方法读取C++函数修改后的整数值,例如: ``` int resultInt = Marshal.ReadInt32(intPtr); ``` 7. 在C#,释放用于存储整数值的内存,例如: ``` Marshal.FreeHGlobal(intPtr); ``` 需要注意的是,由于C++C#使用不同的内存管理方式,需要特别小心对象的创建和销毁,以避免内存泄漏和崩溃等问题。另外,如果C++函数参数是指向对象的指针,需要特别注意指针所指向的内存是否已经正确释放,否则也可能会导致内存泄漏和崩溃等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值