前言

工作过程中难免遇到混合编程,现在我们要谈的是C#和c++语言的编程。C#要调用C++的库目前可选主要有两种方式:Com技术和平台调用(P/Invoke)。现在我们要谈的是P/Invoke技术。

一、平台调用

 使用平台调用的技术可以在托管代码中调用动态链接库(Dll)中实现的非托管函数,如Win32 Dll和C/C++ 创建的dll。看到这里,有些朋友们应该会有疑问——在怎样的场合我们可以使用

平台调用技术来调用动态链接库中的非托管函数呢?

.Net封装了一些系统的API供我们调用,一般我的操作我们是访问系统的,但是并不全面,我们也需要访问系统的本身的API。

有些程序,比如截图、底层处理等,使用C/C++有很大的优势,尤其是和其他公司的产品对接的时候。

托管代码的效率不如非托管代码,为了提高效率,此时也可以考虑托管代码中调用C库函数。

 平台调用步骤

(1).  获得非托管函数的信息,即dll的名称,需要调用的非托管函数名等信息

(2). 在托管代码中对非托管函数进行声明,并且附加平台调用所需要属性

(3). 在托管代码中直接调用第二步中声明的托管函数

平台调用的调用过程

(1)  查找包含该函数的DLL(本地函数声明的时候需要dllimport标识)

(2) 将找到的DLL加载到内存中。

(3) 查找函数在内存中的地址并把其参数推入堆栈,来封送所需的数据。CLR只会在第一次调用函数时,才会去查找和加载DLL,并查找函数在内存中的地址。当函数被调用过一次之后,CLR会将函数的地址缓存起来,CLR这种机制可以提高平台调用的效率。在应用程序域被卸载之前,找到的DLL都一直存在于内存中。

(4) 执行非托管函数。

二、C/C++库的构建

注意:C#是无法直接调用c++的类的,我们可以写一个c++的类,再通过C/C++封装成C函数的方式,对外暴露。为了简单,以下只使用C函数。

1、我们选择新建项目(C++,动态链接库)

2、不要选择空项目,因为可能有其他依赖项

3、删除自带的示例代码,我们添加自己的接口(附加一个回调方法)

#ifdef ADDDLL_EXPORTS
#define ADDDLL_API __declspec(dllexport)
#else
#define ADDDLL_API __declspec(dllimport)
#endif typedef void(__stdcall * RecvDataCallback) (unsigned char* pData, int iDataSize, int iMsgType, long lContext); long mCallBackFunction=NULL; EXTERN_C ADDDLL_API int add(int a,int b); EXTERN_C ADDDLL_API bool MLogin(long lInstance,ULONG pServerIP,int iConferenceNo,long pCallBackFunction,long lContext); EXTERN_C ADDDLL_API bool MLogout(long lInstance); EXTERN_C ADDDLL_API bool MSendData(long lInstance,unsigned char* pData,int iDataSize,int iMsgType); void logPrintf(const char* str);

cpp文件中实现

// AddDll.cpp : 定义 DLL 应用程序的导出函数。
// #include "stdafx.h"
#include "AddDll.h" int add(int a,int b)
{
return a + b;
} ADDDLL_API bool MLogin(long lInstance, ULONG pServerIP, int iConferenceNo, long pCallBackFunction, long lContext)
{
std::string strLog="log from login:\r\n";
strLog += "lInstance:" + std::to_string(lInstance);
strLog += " pServerIP:"+ std::to_string(pServerIP);
strLog += " iConferenceNo:" + std::to_string(pServerIP);
strLog += " pCallBackFunction:" + std::to_string(pCallBackFunction);
strLog += " lContext:" + std::to_string(lContext);
strLog += "\r\n" ;
logPrintf(strLog.c_str()); mCallBackFunction = pCallBackFunction; return true;
} ADDDLL_API bool MLogout(long lInstance)
{
std::string strLog = "log from logout:\r\n";
strLog += "lInstance:" + std::to_string(lInstance);
strLog += "\r\n";
logPrintf(strLog.c_str());
return true;
} ADDDLL_API bool MSendData(long lInstance, unsigned char * pData, int iDataSize, int iMsgType)
{
std::string strLog = "log from sendData:\r\n";
strLog += "lInstance:" + std::to_string(lInstance);
strLog += " pData:" + std::string((const char *)pData);
strLog += " iDataSize:" + std::to_string(iDataSize);
strLog += " iMsgType:" + std::to_string(iMsgType);
strLog += "\r\n";
logPrintf(strLog.c_str()); ( *(RecvDataCallback) mCallBackFunction)((unsigned char*) pData, strlen((const char *)pData), iMsgType, ); return true;
} void logPrintf(const char * str)
{
FILE* fp = fopen("D:\\log.txt", "a+");
if (fp)
{
fprintf(fp, str);
fclose(fp);
}
}

生成即可

4、需要注意的是,不要勾选空项目,因为可能有依赖项,如果是给C++调用,那么此处可以选择空项目

一定不要忘记导出标识

extern "C" __declspec(dllexport)
参见:点我三、C#端调用
  /// <summary>
/// 注意:需要类中声明,方法中new,避免被回收
/// </summary>
/// <param name="pData"></param>
/// <param name="iDataSize"></param>
/// <param name="iMsgType"></param>
/// <param name="lContext"></param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RecvDataCallback(IntPtr pData,int iDataSize, int iSendMsgType, int iCallBackMsgType, long lContext); public static class CRelay
{
public static Int32 lInstance = ; [DllImport("RelayDllR.DLL", EntryPoint = "CreateInstance", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 CreateInstance(); [DllImport("RelayDllR.DLL", EntryPoint = "DestroyInstance", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyInstance(Int32 lInstance); [DllImport("RelayDllR.DLL", EntryPoint = "Login", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern bool Login(Int32 lInstance, UInt32 pServerIP, Int32 iConferenceNo,/*RecvDataCallback*/Int32 pCallBackFunction, Int32 lContext); [DllImport("RelayDllR.DLL", EntryPoint = "Logout",SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern bool Logout(Int32 lInstance); [DllImport("RelayDllR.DLL", EntryPoint = "SendData", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern bool SendData(Int32 lInstance, [MarshalAs(UnmanagedType.LPArray)] byte[] pData, Int32 iDataSize, Int32 iMsgType);

注意:回调的声明不能少好    [UnmanagedFunctionPointer(CallingConvention.Cdecl)],否则可能只能调用有限次数后被平台释放,调用处应该在类中声明,方法中初始化(避免被GC回收)。

 

最新文章

  1. 第 21 章 CSS3 文本效果
  2. VS类自定义版权注释
  3. iphone 6 设置自定义铃声(未越狱)
  4. CQRS学习——最小单元的Cqrs(CommandEvent)[其一]
  5. ADS1.2 集成开发环境的使用
  6. jquery 银行卡号验证
  7. C++设计模式---职责链模式
  8. opencv + cuda编译
  9. Go开发之路(目录)
  10. c++ cout、cin、endl
  11. spark单机搭建
  12. 反射生成 INSERT 多个对象的 SQL 语句(批量插入)
  13. Executor介绍
  14. MYSQL 文件类型
  15. mybatis四(动态sql)
  16. idea 导入项目后不能执行main方法
  17. Linux Timer定时器【转】
  18. 通过URI返回File文件
  19. stenciljs 学习八 组件测试
  20. Intellij IDEA Debug

热门文章

  1. python基础【第五篇】
  2. flex属性设置
  3. USACO Milk Routing /// 优先队列广搜
  4. MVC中的自定义标签分页控件,仅供大家学习!!
  5. vps被封逃逸_v2+cloudflare+websocket+tls+nginx
  6. Sql批量修改语句
  7. Linux主机通过windows虚拟机上网
  8. Android开发笔记之ArrayAdapter
  9. Redis中存储对象区别
  10. python判断文件的编码格式是否为UTF8 无BOM格式