前言

工作过程中难免遇到混合编程,现在我们要谈的是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回收)。

 

.Net平台调用の初识的更多相关文章

  1. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  2. 初识Hadoop

    第一部分:              初识Hadoop 一.             谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...

  3. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  4. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  5. UI篇(初识君面)

    我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...

  6. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

  7. 初识SpringMvc

    初识SpringMvc springMvc简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 s ...

  8. 初识redis数据类型

    初识redis数据类型 1.String(字符串) string是redis最基本的类型,一个key对应一个value. string类型是二进制安全的.意思是redis的string可以包含任何数据 ...

  9. Redis初识、设计思想与一些学习资源推荐

    一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...

随机推荐

  1. css页面网址

    前端必看的文章 1.CSS设置居中的方案总结  https://juejin.im/post/5a7a9a545188257a892998ef 2.阮一峰老师的网站 http://www.ruanyi ...

  2. 浅析php-fpm和fastcgi的关系

    先讲讲CGI吧 浏览器向web server发起请求的时候,要有url,header,params等等吧,为什么有这些数据呢,这就是CGI的事了,CGI就规定了,传哪些数据,用什么样的格式传输 web ...

  3. 解决.Net MVC 中出现 非介入式客户端验证规则中的验证类型名称必须唯一。下列验证类型出现重复: required 的bug

    最近在开动科技创新作品的开发,出现了一个让人很烦恼的错误,每次从浏览页跳转到编辑页时就会出现一下错误 非介入式客户端验证规则中的验证类型名称必须唯一.下列验证类型出现重复: required 上一下出 ...

  4. Spring boot ----RestTemplate学习笔记

    ****spring boot-----restTemplate 封装了HttpURLConnection,HttpClient,Netty等接口访问实现库 restTemplet包含以下部分 Htt ...

  5. js基本算法

    一.阶乘(递归思想) // 计算阶乘 function factorial(n) { if (n === 1) { return 1 } return n * factorial(n - 1) } 二 ...

  6. Android Studio奇淫巧技

    目录 3.1 Android Studio使用初探 Project面板 Stucture面板 Android Monitor Keymap Tip of the Day 快速查找 Search Act ...

  7. 安卓Unity3d游戏的逆向破解

    使用到的工具 ApkToolBox .NET Reflector Reflexil(.NET程序编辑器) 怎么判定它是Unity游戏呢 一般的路径格式是这样的assets\bin\Data\Manag ...

  8. Java/sql找出oracle数据库有空格的列

    1.java方式 String table_sql = "select table_name from user_tables";//所有用户表 List<String> ...

  9. PHP ftp_pasv() 函数

    定义和用法 ftp_pasv() 函数把被动模式设置为打开或关闭. 在被动模式中,数据连接是由客户机来初始化的,而不是服务器.这在客户机位于防火墙之后时比较有用. 语法 ftp_pasv(ftp_co ...

  10. 学习android文档 -- Adding the Action Bar

    1. Setting Up the Action Bar:users-sdk version 11以上可以使用holo主题:如果不使用holo主题,或者sdk版本较低,则需要在manifest文件的& ...