在VS2010上使用C#调用非托管C++生成的DLL文件(图文讲解)

背景 

     在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用。本篇文章将引导你快速理解这个调用的过程。

 

步骤

1. 创建一个CSharpInvokeCPP的解决方案:

image

 

2. 创建一个C++的动态库项目:

image

 

3. 在应用程序设置中,选择“DLL”,其他按照默认选项:

image

最后点击完成,得到如图所示项目:

image

      我们可以看到这里有一些文件,其中dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。详细内容可以参考(http://blog.csdn.net/benkaoya/archive/2008/06/02/2504781.aspx)。

 

4. 现在我们打开CSharpInvokeCPP.CPPDemo.cpp文件:

现在我们加入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// CSharpInvokeCPP.CPPDemo.cpp : 定义 DLL 应用程序的导出函数。
//
 
#include "stdafx.h"
 
extern "C" __declspec(dllexport) int Add(int x, int y)
{
    return x + y;
}
extern "C" __declspec(dllexport) int Sub(int x, int y)
{
    return x - y;
}
extern "C" __declspec(dllexport) int Multiply(int x, int y)
{
    return x * y;
}
extern "C" __declspec(dllexport) int Divide(int x, int y)
{
    return x / y;
}

      extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。而被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。

      __declspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。

      extern "C" __declspec(dllexport)加起来的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做成的DLL。

 

5. 编译项目程序,最后在Debug目录生成CSharpInvokeCPP.CPPDemo.dll和CSharpInvokeCPP.CPPDemo.lib

image

我们用反编译工具PE Explorer查看下该DLL里面的方法:

image

可以发现对外的公共函数上包含这四种“加减乘除”方法。

 

6. 现在来演示下如何利用C#项目来调用非托管C++的DLL,首先创建C#控制台应用程序:

image

 

7. 在CSharpInvokeCSharp.CSharpDemo项目上新建一个CPPDLL类,编写以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CPPDLL
{
    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]
    public static extern int Add(int x, int y);
 
    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]
    public static extern int Sub(int x, int y);
 
    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]
    public static extern int Multiply(int x, int y);
 
    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]
    public static extern int Divide(int x, int y);
}

DllImport作为C#中对C++的DLL类的导入入口特征,并通过static extern对extern “C”进行对应。

 

8. 另外,记得把CPPDemo中生成的DLL文件拷贝到CSharpDemo的bin目录下,你也可以通过设置【项目属性】->【配置属性】->【常规】中的输出目录:

image

这样编译项目后,生成的文件就自动输出到CSharpDemo中了。

 

9. 然后在Main入口编写测试代码:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void Main(string[] args)
{
    int result = CPPDLL.Add(10, 20);
    Console.WriteLine("10 + 20 = {0}", result);
 
    result = CPPDLL.Sub(30, 12);
    Console.WriteLine("30 - 12 = {0}", result);
 
    result = CPPDLL.Multiply(5, 4);
    Console.WriteLine("5 * 4 = {0}", result);
 
    result = CPPDLL.Divide(30, 5);
    Console.WriteLine("30 / 5 = {0}", result);
 
    Console.ReadLine();
}

 

运行结果:

image

方法得到调用。

10. 以上的方法只能通过静态方法对于C++中的函数进行调用。那么怎样通过静态方法去调用C++中一个类对象中的方法呢?现在我在CPPDemo项目中添加一个头文件userinfo.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class UserInfo {
private:
    char* m_Name;
    int m_Age;
public:
    UserInfo(char* name, int age)
    {
        m_Name = name;
        m_Age = age;
    }
    virtual ~UserInfo(){ }
    int GetAge() { return m_Age; }
    char* GetName() { return m_Name; }
};

 

在CSharpInvokeCPP.CPPDemo.cpp中,添加一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "malloc.h"
#include "userinfo.h"
 
typedef struct {
    char name[32];
    int age;
} User; 
 
UserInfo* userInfo;
 
extern "C" __declspec(dllexport) User* Create(char* name, int age)   
{  
    User* user = (User*)malloc(sizeof(User));
 
    userInfo = new UserInfo(name, age);
    strcpy(user->name, userInfo->GetName()); 
    user->age = userInfo->GetAge();
 
    return user;
}

这里声明一个结构,包括name和age,这个结构是用于和C#方面的结构作个映射。

注意:代码中的User*是个指针,返回也是一个对象指针,这样做为了防止方法作用域结束后的局部变量的释放。

strcpy是个复制char数组的函数。

 

11. 在CSharpDemo项目中CPPDLL类中补充代码:

 

1
2
3
4
5
6
7
8
9
10
11
[DllImport("CSharpInvokeCPP.CPPDemo.dll")]
public static extern IntPtr Create(string name, int age);
 
[StructLayout(LayoutKind.Sequential)]
public struct User
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Name;
 
    public int Age;
}

 

其中这里的结构User就和C++中的User对应。

 

12. 在Program.cs中补充代码:

IntPtr ptr = CPPDLL.Create("李平", 27);
CPPDLL.User user = (CPPDLL.User)Marshal.PtrToStructure(ptr, typeof(CPPDLL.User));
Console.WriteLine("Name: {0}, Age: {1}", user.Name, user.Age);

 

注意:红色字体部分,这里结构指针首先转换成IntPtr句柄,然后通过Marshal.PtrToStructrue转换成你所需要的结构。

 

运行结果:

image

 

最后附上我的源代码:CSharpInvokeCPP.rar,希望对大家有所帮助:)

 

出处:http://www.cnblogs.com/liping13599168/archive/2011/03/31/2000320.html

=======================================================================================

自己整理一些其他类型的参数的调用:

C++的DLL中传递字符串指针 

查询第三方提供的帮助说明文档,传递的函数格式如下

MOSS_EXPORT ULONG_32 STDCALL MOSS_GetPlayedTime  ( IN const USER_LOGIN_ID_INFO_S *  pstUserLoginIDInfo,  
  IN const CHAR *  pcChannelCode,  OUT CHAR *  pszTime )   
获得当前播放时间(字符串型)。
参数
[IN] pstUserLoginIDInfo 用户登录ID信息标识。  
[IN] pcChannelCode 播放通道编码。  
[OUT] pszTime 存放当前播放时间值,缓冲区长度应不小于::MOSS_TIME_LEN(32)。  
返回错误码

 

定义接口函数

        [DllImport("xp_frame.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern UInt32 MOSS_GetPlayedTime(ref USER_LOGIN_ID_INFO_S stUserLoginInfo, byte[] szChannelCode, IntPtr ptrTime);

 

调用方法:

                IntPtr ptrCurrTime = Marshal.AllocHGlobal(sizeof(char) * MOSSSDK.MOSS_TIME_LEN);
                ulRet = MOSSSDK.MOSS_GetPlayedTime(ref stUserLoginIDInfo, bytChannelCode, ptrCurrTime);
                String strTime = Marshal.PtrToStringAnsi(ptrCurrTime);
                Marshal.FreeHGlobal(ptrCurrTime);
                if (!String.IsNullOrEmpty(strTime.TrimEnd('\0')))
                {
                    dt = Convert.ToDateTime(strTime, dtFormat);
                }

 

C++的DLL中传递结构体和结构体指针

查询第三方提供的帮助说明文档,传递的函数格式如下

MOSS_EXPORT ULONG_32 STDCALL MOSS_SlaveRecordRetrieval  ( IN USER_LOGIN_ID_INFO_S *  pstUserLogIDInfo,  
  IN BOOL_T  bExtDomainDev,  
  IN REC_QUERY_INFO_S *  pstSDKRecQueryInfo,  
  IN QUERY_PAGE_INFO_S *  pstQueryPageInfo,  
  OUT RSP_PAGE_INFO_S *  pstRspPageInfo,  
  OUT RECORD_FILE_INFO_S *  pstSDKRecordFileInfo  
 ) 
摄像机的备用存储录像检索 
参数
[IN] pstUserLogIDInfo 用户登录ID信息标识  
[IN] bExtDomainDev 是否外域共享推送的摄像机  
[IN] pstSDKRecQueryInfo 回放检索消息数据结构,对于外域摄像机,szCamCode为摄像机共享编码  
[IN] pstQueryPageInfo 请求分页信息  
[OUT] pstRspPageInfo 返回分页信息  
[OUT] pstSDKRecordFileInfo 录像文件信息数据结构体  
返回错误码

根据查看上面的参数我们知道,只有bExtDomainDev是一个bool类型的,其他的都是结构体,

我们看看USER_LOGIN_ID_INFO_S结构体的定义

成员变量:
CHAR  szUserCode [MOSS_USER_CODE_LEN] 
CHAR  szUserLoginCode [MOSS_RES_CODE_LEN] 
CHAR  szUserIpAddress [MOSS_IPADDR_LEN] 

详细描述:
用户登录ID信息结构

查看REC_QUERY_INFO_S的结构体定义:

成员变量
CHAR  szCamCode [MOSS_RES_CODE_LEN] 
TIME_SLICE_S  stQueryTimeSlice 
ULONG_32  ulDomainLevel 
ULONG_32  ulIndistinctQuery 
ULONG_32  ulType 
BOOL_T  bIsAddToInnerTable 
CHAR  szReserve [16] 

详细描述
回放相关检索消息数据结构 

我们看到上面的结构体其实还是一个嵌套,包含一个TIME_SLICE_S结构体,如下:

成员变量 
CHAR  szBeginTime [MOSS_TIME_LEN] 
CHAR  szEndTime [MOSS_TIME_LEN] 

详细描述
基本时间片信息

在查看QUERY_PAGE_INFO_S结构体的定义:

成员变量
ULONG_32  ulPageRowNum 
ULONG_32  ulPageFirstRowNumber 
BOOL_T  bQueryCount 

详细描述
分页请求信息,待查询数据的每条数据项对应一个序号。序号从1开始,连续增加。

 

查看RSP_PAGE_INFO_S的结构体定义如下:

成员变量 
ULONG_32  ulRowNum 
ULONG_32  ulTotalRowNum 

详细描述
分页响应信息

 最后还要一个RECORD_FILE_INFO_S结构体的定义

成员变量
CHAR  szFileName [MOSS_FILE_NAME_LEN] 
CHAR  szStartTime [MOSS_TIME_LEN] 
CHAR  szEndTime [MOSS_TIME_LEN] 
ULONG_32  ulSize 
CHAR  szSpec [MOSS_DESC_LEN] 

详细描述
录像文件信息(查询录像文件列表时返回) 

我们先定义接口

        [DllImport("MOSS_sdk.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern UInt32 MOSS_SlaveRecordRetrieval(ref USER_LOGIN_ID_INFO_S stUserLogIDInfo, int bExtDomainDev,
            ref REC_QUERY_INFO_S stSDKRecQueryInfo, ref QUERY_PAGE_INFO_S stQueryPageInfo,
            ref RSP_PAGE_INFO_S pstRspPageInfo, IntPtr pstSDKRecordFileInfo);

 

我们在定义需要使用到的结构体

    [StructLayout(LayoutKind.Sequential)]
    public struct USER_LOGIN_ID_INFO_S
    {
        /** 用户编码 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_USER_CODE_LEN)]
        public byte[] szUserCode;

        /** 用户登录ID,是用户登录后服务器分配的,它是标记一次用户登录的唯一标识 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_RES_CODE_LEN)]
        public byte[] szUserLoginCode;

        /** 用户登录的客户端IP地址 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_IPADDR_LEN)]
        public byte[] szUserIpAddress;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct REC_QUERY_INFO_S
    {
        /** 摄像头编码*/
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_RES_CODE_LEN)]
        public byte[] szCamCode;

        /** 检索的起始/结束时间 */
        public TIME_SLICE_S stQueryTimeSlice;

        /* 录像的域级别计数: 用于国标协议跨域回放 */
        public UInt32 uiDomainLevel;

        /* Begin add by zhengyibing(01306), 2014-04-19 for 新国标修订*/
        /* 新增模糊查询类型  #INDISTINCT_QUERY_TYPE_E */
        public UInt32 uiIndistinctQuery;

        /* 新增录像检索类型  #RECORD_QUERY_TYPE_E */
        public UInt32 uiType;

        /** 保留字段 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] szReserve;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TIME_SLICE_S
    {
        /** 开始时间 格式为"hh:mm:ss"或"YYYY-MM-DD hh:mm:ss", 视使用情况而定 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_TIME_LEN)]
        public byte[] szBeginTime;

        /** 结束时间 格式为"hh:mm:ss"或"YYYY-MM-DD hh:mm:ss", 视使用情况而定 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_TIME_LEN)]
        public byte[] szEndTime;

        public static TIME_SLICE_S GetInstance()
        {
            TIME_SLICE_S stTimeSlice = new TIME_SLICE_S();
            stTimeSlice.szBeginTime = new byte[MOSSSDK.MOSS_TIME_LEN];
            stTimeSlice.szEndTime = new byte[MOSSSDK.MOSS_TIME_LEN];

            return stTimeSlice;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct REC_QUERY_INFO_S
    {
        /** 摄像头编码*/
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_RES_CODE_LEN)]
        public byte[] szCamCode;

        /** 检索的起始/结束时间 */
        public TIME_SLICE_S stQueryTimeSlice;

        /* 录像的域级别计数: 用于国标协议跨域回放 */
        public UInt32 uiDomainLevel;

        /* Begin add by zhengyibing(01306), 2014-04-19 for 新国标修订*/
        /* 新增模糊查询类型  #INDISTINCT_QUERY_TYPE_E */
        public UInt32 uiIndistinctQuery;

        /* 新增录像检索类型  #RECORD_QUERY_TYPE_E */
        public UInt32 uiType;

        /** 保留字段 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] szReserve;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct QUERY_PAGE_INFO_S
    {
        /** 分页查询中每页的最大条目数, 不能为0, 也不能大于#MOSS_PAGE_QUERY_ROW_MAX_NUM */
        public UInt32 ulPageRowNum;

        /** 分页查询中第一条数据的序号(即查询从第ulPageFirstRowNumber条数据开始的符合条件的数据), 取值符合ULONG类型的范围即可 */
        public UInt32 ulPageFirstRowNumber;

        /** 是否查询条目总数, BOOL_TRUE时查询; BOOL_FALSE时不查询 */
        public UInt32 bQueryCount;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECORD_FILE_INFO_S
    {
        /** 文件名 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_FILE_NAME_LEN)]
        public byte[] szFileName;

        /** 文件起始时间, 满足"%Y-%m-%d %H:%M:%S"格式, 长度限定为24字符 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_TIME_LEN)]
        public byte[] szStartTime;

        /** 文件结束时间, 满足"%Y-%m-%d %H:%M:%S"格式, 长度限定为24字符 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_TIME_LEN)]
        public byte[] szEndTime;

        /** 文件大小, 目前暂不使用 */
        public UInt32 ulSize;

        /** 描述信息, 可不填 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_DESC_LEN)]
        public byte[] szSpec;
    }

最后我们看看如何调用的

            QUERY_PAGE_INFO_S stPageInfo = new QUERY_PAGE_INFO_S();
            RSP_PAGE_INFO_S stRspPageInfo = new RSP_PAGE_INFO_S();

            REC_QUERY_INFO_S stRecQueryInfo = new REC_QUERY_INFO_S();
            stRecQueryInfo.szReserve = new byte[20];

            stRecQueryInfo.szCamCode = new byte[MOSSSDK.MOSS_RES_CODE_LEN];
            Encoding.UTF8.GetBytes(m_cameraCode, 0, Encoding.UTF8.GetByteCount(m_cameraCode), stRecQueryInfo.szCamCode, 0);

            stRecQueryInfo.stQueryTimeSlice.szBeginTime = new byte[MOSSSDK.MOSS_TIME_LEN];
            Encoding.Default.GetBytes(strBegin, 0, Encoding.Default.GetByteCount(strBegin), stRecQueryInfo.stQueryTimeSlice.szBeginTime, 0);
            stRecQueryInfo.stQueryTimeSlice.szEndTime = new byte[MOSSSDK.MOSS_TIME_LEN];
            Encoding.Default.GetBytes(strEnd, 0, Encoding.Default.GetByteCount(strEnd), stRecQueryInfo.stQueryTimeSlice.szEndTime, 0);
            stRecQueryInfo.uiIndistinctQuery = 3;

            IntPtr ptrRecFileList = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(RECORD_FILE_INFO_S)) * 30);

            stPageInfo.ulPageFirstRowNumber = ulBeginNum;
            stPageInfo.ulPageRowNum = 30;
            ulRet = MOSSSDK.MOSS_SlaveRecordRetrieval(ref stUserLoginIDInfo, 1, ref stRecQueryInfo, ref stPageInfo, ref stRspPageInfo, ptrRecFileList);

            RECORD_FILE_INFO_S stRecFileItem;
            for (int i = 0; i < stRspPageInfo.ulRowNum; ++i)
            {
                IntPtr ptrTemp = IntPtr.Zero;
                if (SdkManager.Is64Bit)
                {
                    ptrTemp = new IntPtr(ptrRecFileList.ToInt64() + Marshal.SizeOf(typeof(RECORD_FILE_INFO_S)) * i);
                }
                else
                {
                    ptrTemp = new IntPtr(ptrRecFileList.ToInt32() + Marshal.SizeOf(typeof(RECORD_FILE_INFO_S)) * i);
                }
                stRecFileItem = (RECORD_FILE_INFO_S)Marshal.PtrToStructure(ptrTemp, typeof(RECORD_FILE_INFO_S));

                string strFile = Encoding.UTF8.GetString(stRecFileItem.szFileName);

                RecFileList.Add(stRecFileItem);
            }

 

再看一个使用示例

MOSS_EXPORT ULONG_32 STDCALL MOSS_StartPlayer  ( IN USER_LOGIN_ID_INFO_S *  pstUserLoginIDInfo,  
  IN ULONG_32  ulPlayWndNum,  
  INOUT PLAY_WND_INFO_S *  pstPlayWndInfo  
 )   
启动播放器组件。
参数
[IN] pstUserLoginIDInfo 用户登录ID信息标识  
[IN] ulPlayWndNum 播放窗格数量,具体取值视实际情况而定  
[IN][OUT] pstPlayWndInfo 播放窗格编码结构体指针。  
返回错误码

在看看USER_LOGIN_ID_INFO_S结构体的定义

成员变量:
CHAR  szUserCode [MOSS_USER_CODE_LEN] 
CHAR  szUserLoginCode [MOSS_RES_CODE_LEN] 
CHAR  szUserIpAddress [MOSS_IPADDR_LEN] 

详细描述:
用户登录ID信息结构

查看PLAY_WND_INFO_S结构体的定义

结构体成员变量说明:
CHAR szPlayWndCode[MOSS_RES_CODE_LEN] 
播放窗格资源编码 

 

那么我们先定义结构体和接口:

    [StructLayout(LayoutKind.Sequential)]
    public struct USER_LOGIN_ID_INFO_S
    {
        /** 用户编码 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_USER_CODE_LEN)]
        public byte[] szUserCode;

        /** 用户登录ID,是用户登录后服务器分配的,它是标记一次用户登录的唯一标识 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_RES_CODE_LEN)]
        public byte[] szUserLoginCode;

        /** 用户登录的客户端IP地址 */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_IPADDR_LEN)]
        public byte[] szUserIpAddress;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PLAY_WND_INFO_S
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = MOSSSDK.MOSS_RES_CODE_LEN)]
        public byte[] szPlayWndCode;
    }

//调用接口定义
[DllImport("MOSS_sdk.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern UInt32 MOSS_StartPlayer(ref USER_LOGIN_ID_INFO_S stUserLoginInfo, UInt32 ulPlayWndNum, IntPtr ptrPlayWndInfo);

调用方式:

                IntPtr ptrPlayWndInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PLAY_WND_INFO_S)));
                ulRet = CommonFunc.StartPlayer(ref stUserLoginInfo, 1, ptrPlayWndInfo);

 自己比较一下上面两种结构体和结构体指针的传递的用法。

 

 

C++的DLL中传递回调函数指针

如下,设置回调函数指针,触发某个事件时,会把消息通知到应用程序。查看第三方提供的帮助文档说明:

MOSS_EXPORT ULONG_32 STDCALL MOSS_SetRunMsgCB ( IN RUN_INFO_CALLBACK_EX_PF pfnRunInfoFunc )
设置接收消息或者异常消息的回调函数。
参数
[IN] pfRunInfoFunc 消息或者异常消息回调函数的指针。
返回错误码

再次查看RUN_INFO_CALLBACK_EX_PF参数类型的定义,如下

typedef VOID(STDCALL* RUN_INFO_CALLBACK_EX_PF) (IN const USER_LOGIN_ID_INFO_S *pstUserLoginIDInfo, IN ULONG_32 ulRunInfoType, IN VOID *pParam)
消息或者异常消息回调函数的指针类型
参数
[IN] pstUserLoginIDInfo 用户登录ID信息标识。
[IN] ulRunInfoType 消息或者异常消息类型,对应::RUN_INFO_TYPE_E枚举中的值。
[IN] pParam 存放消息或者异常消息数据的缓冲区指针(所存放的数据与消息或异常消息类型有关)。
返回无。

 

知道了这些,我们先回调方法的接口调用

        [DllImport("xp_frame.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern UInt32 MOSS_SetRunMsgCB(IntPtr ptrRunInfoFunc);

 

再定义一个回调函数的delegate,你也可以使用Action或者Func的用法来代替这个delegate

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void RUN_INFO_CALLBACK_EX_PF(ref USER_LOGIN_ID_INFO_S pstUserLoginIDInfo, UInt32 ulRunInfoType, IntPtr pParam);

 

 定义自己的回调方法

        public void callBackMsg(ref USER_LOGIN_ID_INFO_S stUserLoginIDInfo, uint ulRunInfoType, IntPtr ptrParam)
        {
            switch ((RUN_INFO_TYPE_E)ulRunInfoType)
            {
                case RUN_INFO_TYPE_E.RUN_INFO_Wan:
                    break;
                case RUN_INFO_TYPE_E.RUN_INFO_Error:
                    {
                        RUN_INFO_EX_S stDownLoadInfo = (RUN_INFO_EX_S)Marshal.PtrToStructure(ptrParam, typeof(RUN_INFO_EX_S));
                        MessageBox.Show("运行的错误信息");
                    }
                    break;
                case RUN_INFO_TYPE_E.RUN_INFO_DOWN_END:
                    {
                        //下载消息
                        DownLoadComplete(ptrParam);
                    }
                    break;
                default:
                    break;
            }
        }

其中RUN_INFO_TYPE_E枚举对应的我就不一一列举出来,只写了个别的事件处理。

设置回调函数,如下:

IntPtr ptrCallbakc = Marshal.GetFunctionPointerForDelegate(callBackMsg);
uint ulResult = MOSSSDK.MOSS_SetRunMsgCB(ptrCallbakc);

 这样就可以收到消息了。

 

posted on 2016-11-03 18:58  jack_Meng  阅读(6746)  评论(0编辑  收藏  举报

导航