一、发生的背景 
    在开发新项目中使用了新的语言开发 C# 和新的技术方案 WEB Service,但是在新项目中,一些旧的模块需要继续使用,一般是采用 C 或 C++ 或 Delphi 编写的,如何利用旧模块对于开发人员来说,有三种可用方法供选择:第一、将 C 或 C++ 函数用 C# 彻底改写一遍,这样整个项目代码比较统一,维护也方便一些。但是尽管微软以及某些书籍说,C# 和 C++ 如何接近,但是改写起来还是很痛苦的事情,特别是 C++ 里的指针和内存操作;第二、将 C 或 C++ 函数封装成 COM,在 C# 中调用COM 比较方便,只是在封装时需要处理 C 或 C++ 类型和 COM 类型之间的转换,也有一些麻烦,另外COM 还需要注册,注册次数多了又可能导致混乱;第三、将 C 或 C++ 函数封装成动态链接库,封装的过程简单,工作量不大。因此我决定采用加载动态链接库的方法实现,于是产生了在 C# 中如何调用自定义的动态链接库问题,我在网上搜索相关主题,发现一篇调用系统 API 的文章,但是没有说明如何解决此问题,在 MSDN 上也没有相关详细说明。基于此,我决定自己从简单出发,逐步试验,看看能否达到自己的目标。 
    (说明一点:我这里改写为什么很怕麻烦,我改写的代码是变长加密算法函数,代码有600多行,对算法本身不熟悉,算法中指针和内存操作太多,要想保证算法正确,最可行的方法就是少动代码,否则只要有一点点差错,就不能肯定算法与以前兼容) 
   
  二、技术实现 
    下面看看如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,这个不需要多说,大家参考下面宏定义即可: 
   
  #define LIBEXPORT_API extern "C" __declspec(dllexport) 
  第一步,我先从简单的调用出发,定义了一个简单的函数,该函数仅仅实现一个整数加法求和: 
   
  LIBEXPORT_API int mySum(int a,int b){ return a+b;} 
  C# 导入定义: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
      EntryPoint=" mySum ", 
      CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)] 
      public static extern int mySum (int a,int b); 
  } 
  在C#中调用测试: 
   
  int iSum = RefComm.mySum(2,3); 
  运行查看结果iSum为5,调用正确。第一步试验完成,说明在C#中能够调用自定义的动态链接库函数。 
   
  第二步,我定义了字符串操作的函数(简单起见,还是采用前面的函数名),返回结果为字符串: 
   
  LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,"%s",a); return a;} 
  C# 导入定义: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Auto, 
       CallingConvention=CallingConvention.StdCall)] 
       public static extern string mySum (string a, string b); 
  } 
  在C#中调用测试: 
   
  string strDest=""; 
  string strTmp= RefComm.mySum("12345", strDest); 
  运行查看结果 strTmp 为"12345",但是strDest为空。我修改动态链接库实现,返回结果为串b: 
   
  LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,"%s",a) return b;} 
  修改 C# 导入定义,将串b修改为ref方式: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)] 
       public static extern string mySum (string a, ref string b); 
  } 
  在C#中再调用测试: 
   
  string strDest=""; 
  string strTmp= RefComm.mySum("12345", ref strDest); 
    运行查看结果 strTmp 和 strDest 均不对,含不可见字符。再修改 C# 导入定义,将CharSet从Auto修改为Ansi: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)] 
       public static extern string mySum (string a, string b); 
  } 
  在C#中再调用测试: 
   
  string strDest=""; 
  string strTmp= RefComm. mySum("12345", ref strDest); 
    运行查看结果 strTmp 为"12345",但是串 strDest 没有赋值。第二步实现函数返回串,但是在函数出口参数中没能进行输出。再次修改 C# 导入定义,将串b修改为引用(ref): 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)] 
       public static extern string mySum (string a, ref string b); 
  } 
  运行时调用失败,不能继续执行。 
   
  第三步,修改动态链接库实现,将b修改为双重指针: 
   
  LIBEXPORT_API char *mySum(char *a,char **b){sprintf((*b),"%s",a); return *b;} 
  C#导入定义: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)] 
       public static extern string mySum (string a, ref string b); 
  } 
  在C#中调用测试: 
   
  string strDest=""; 
  string strTmp= RefComm. mySum("12345", ref strDest); 
    运行查看结果 strTmp 和 strDest 均为"12345",调用正确。第三步实现了函数出口参数正确输出结果。 
   
  第四步,修改动态链接库实现,实现整数参数的输出: 
   
  LIBEXPORT_API int mySum(int a,int b,int *c){ *c=a+b; return *c;} 
  C#导入的定义: 
   
  public class RefComm 
  { 
  [DllImport("LibEncrypt.dll", 
       EntryPoint=" mySum ", 
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)] 
       public static extern int mySum (int a, int b,ref int c); 
  } 
  在C#中调用测试: 
   
  int c=0; 
  int iSum= RefComm. mySum(2,3, ref c); 
  运行查看结果iSum 和c均为5,调用正确。 
    经过以上几个步骤的试验,基本掌握了如何定义动态库函数以及如何在 C# 定义导入,有此基础,很快我实现了变长加密函数在 C# 中的调用,至此目标实现。 
   
  三、结论 
    在 C# 中调用 C++ 编写的动态链接库函数,如果需要出口参数输出,则需要使用指针,对于字符串,则需要使用双重指针,对于 C# 的导入定义,则需要使用引用(ref)定义。 
    对于函数返回值,C# 导入定义和 C++ 动态库函数声明定义需要保持一致,否则会出现函数调用失败。定义导入时,一定注意 CharSet 和 CallingConvention 参数,否则导致调用失败或结果异常。运行时,动态链接库放在 C# 程序的目录下即可,我这里是一个 C# 的动态链接库,两个动态链接库就在同一个目录下运行。

在 C# 中加载自己编写的动态链接库的更多相关文章

  1. Dll的编写 在unity中加载

    1. 在VS中新建Dll项目 2.在头文件中对函数进行声明 extern "C" int _declspec(dllexport) testunity(); 3.在源文件中写函数体 ...

  2. DirectUI界面编程(三)从XML文件中加载界面

    Duilib支持xml界面布局,使得界面设计与逻辑处理相分离,本节介绍如何从xml文件中加载界面元素. 我们需要以下几个步骤: 创建并初始化CPaintManagerUI对象. 创建CDialogBu ...

  3. 在C 中加载TorchScript模型

    本教程已更新为可与PyTorch 1.2一起使用 顾名思义,PyTorch的主要接口是Python编程语言.尽管Python是合适于许多需要动态性和易于迭代的场景,并且是首选的语言,但同样的,在 许多 ...

  4. PE解析器与加载器编写指南

    PE解析器与加载器编写指南 最近准备去实习,看公司要求应该开发PE相关的查杀引擎,因此再回头复习一下PE格式,重新写一个PE解析器和PE加载器,再此记录下有关坑. PE解析器部分: 1)如何确定节区表 ...

  5. C#开发BIMFACE系列49 Web网页中加载模型与图纸的技术方案

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在BIMFACE二次系列博客中详细介绍了服务器端API的调用方式,如下列表 C#开发BIMFACE系列1   BIMFAC ...

  6. iOS Interface Builder:在.xib文件中加载另一个.xib文件

    在开发中,经常会用到一个需要重复使用的模块,比如好友列表中每个用户的展示或每条动态,这些都是相同的模版,这样我们就可以把这个部分提取出来放到一个单独的.xib中.那么提取出的.xib如何在其他.xib ...

  7. Flexigrid从对象中加载数据

    (有问题,在找…………) Flexigrid是用来动态加载数据的一种比较好(老)的Jquery表插件,然后有些时候,我们需要其从本地或者jQuery对象中加载数据,比如有这么个需求,页面显示中有两个表 ...

  8. C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类

    在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类Expression,利用.net的动态编译技术实现,代码如下: public clas ...

  9. 如何在HTML中加载Flash(2种实现方法)_HTML/Xhtml_网页制作

    点评:如何在HTML中加载Flash,为网页添加更多的色彩,普通的网页以无法满足用户的需求,接下来为大家介绍下2种在HTML中加载Flash的方法,感兴趣的各位可以适当参考下,希望对你有所帮助 第一种 ...

随机推荐

  1. 我的web前端之路 分享些前端的好书(转)

    WEB前端研发工程师,在国内算是一个朝阳职业,这个领域没有学校的正规教育,大多数人都是靠自己自学成才.本文主要介绍自己从事web开发以来 (从大二至今)看过的书籍和自己的成长过程,目的是给想了解Jav ...

  2. POJ 1064 Cable master

    Cable master Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37865   Accepted: 8051 Des ...

  3. Android入门:用HttpClient模拟HTTP的GET和POST请求

    一.HttpClient介绍   HttpClient是用来模拟HTTP请求的,其实实质就是把HTTP请求模拟后发给Web服务器:   Android已经集成了HttpClient,因此可以直接使用: ...

  4. codeforces 687D Dividing Kingdom II 带权并查集(dsu)

    题意:给你m条边,每条边有一个权值,每次询问只保留编号l到r的边,让你把这个图分成两部分 一个方案的耗费是当前符合条件的边的最大权值(符合条件的边指两段点都在一个部分),问你如何分,可以让耗费最小 分 ...

  5. C++实现离散余弦变换(参数为二维指针)

    C++实现离散余弦变换(参数为二维指针) 写在前面 到目前为止已经阅读了相当一部分的网格水印等方面的论文了,但是论文的实现进度还没有更上,这个月准备挑选一些较为经典的论文,将其中的算法实现.在实现论文 ...

  6. (转载)OC学习篇之---概述

    前言 终于开启了OC的学习篇了,之前由于工作上的事,学习就一直搁浅了,不过最近由于各种原因,感觉必须要开启iOS的开发旅程了,不然就老了.因为之前一直是做Android的,所以学习iOS来就没那么费劲 ...

  7. Java-note-字符串连接

    String a="100"; int b=2; String c=a+b; then the answer is c=1002; + make the two sides bec ...

  8. SQL中以count及sum为条件的查询

    在开发时,我们经常会遇到以“累计(count)”或是“累加(sum)”为条件的查询.比如user_num表: id user num 1 a 3 2 a 4 3 b 5 4 b 7   例1:查询出现 ...

  9. syscolumns、sysconstraints、sysobjects

    1.根据表名查询对象ID SELECT OBJECT_ID('Production.Product') 结果:1429580131 不能作为输入参数:列名.约束名 能作为输入参数:表名 2.根据对象I ...

  10. 使用3D物体做GUI界面

    通常来说,Unity自带的OnGUI不太好用,靠代码完成,在场景中无法直接编辑.所以,一般项目使用NGUI插件来做界面,但我这次要修改一个游戏,它没用NGUI,也没用OnGUI,而是使用类似NGUI的 ...