前言

工作过程中难免遇到混合编程,现在我们要谈的是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. ZanUI-WeApp -- 一个颜值高、好用、易扩展的微信小程序 UI 库

    ZanUI-WeApp -- 一个颜值高.好用.易扩展的微信小程序 UI 库:https://cnodejs.org/topic/589d625a5c8036f7019e7a4a 微信小程序之官方UI ...

  2. 【目录】ASP.NET Core 基础教程

    ASP.NET Core 基础教程 ASP.NET Core 基础教程 ASP.NET Core 简介 ASP.NET Core Windows 环境配置 ASP.NET Core macOS 环境配 ...

  3. MVC中的自定义标签分页控件,仅供大家学习!!

    public static HtmlString ShowPageNavigate(this HtmlHelper htmlHelper, int currentPage, int pageSize, ...

  4. centos6系列更换阿里yum源

    1.首先备份原来的cent os官方yum源 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak 2. ...

  5. 微信小程序のmina架构

  6. linux 命令 - man, help, info(查看命令帮助手册)

    man, help, info - 查看命令帮助手册   help xxx  # 显示内置命令帮助信息: xxx --help  # 显示外置命令帮助信息: man xxx  # 没有内建与外部命令的 ...

  7. pair queue____多源图广搜

    .简介 class pair ,中文译为对组,可以将两个值视为一个单元.对于map和multimap,就是用pairs来管理value/key的成对元素.任何函数需要回传两个值,也需要pair. 该函 ...

  8. Eclipse中发布Maven管理的Web项目时找不到类的问题根源和解决办法(转)

    转自:http://blog.csdn.net/lvguanming/article/details/37812579?locationNum=12 写在前面的话 现在是越来越太原讨厌Eclipse这 ...

  9. C# 利用发射动态创建泛型类型的对象,泛型类型支持带惨的构造函数

    static class Program { static void Main(string[] args) { var aaa = Create<AAA>("aaa" ...

  10. AngularJS 项目开发实战

    目录 啰嗦一下 你问我答 目录结构 压缩 其他 啰嗦一下 最近在开发一个项目时,调研了一下AngularJS,发现这个框架功能很丰富,而且用起来也很方便,所以深入了解了一下,在此分享一下我的感悟. A ...