1.
静态构造函数用于初始化静态数据,或用于执行仅需执行一次的特殊操作。

最近在开发服务后台的时候,使用c#调用了多个c++编写的dll,期间遇到了一系列的问题,经过一番努力最后都一一解决了,在此做个总结,方便以后参考,毕竟这些问题也都是很常见的,主要有以下问题:

计算机容量单位:

2.
静态构造函数既没有访问修饰符,也没有参数。

  • 类型对照问题
  • 内存释放问题
  • 版本问题(x86与x64)
  • 编译问题(静态与动态)
  • 资源加载问题
  • 异常捕获与问题定位
  • vs实时调试问题

1位 = 1bit;

3.
在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。

类型对照问题

c#调用c++方法时,首先要在类中定义一个与c++方法对应的外部方法,因为该方法是用C#语言定义的,那么肯定要弄清楚C#类型与c++类型如何对应,否则会导致调用失败,关于这个问题其实不算什么问题,网上有很多类型对照的文章,都有很详细的对应列表,用的时候参考一下就可以了。还可以使用工具,自动根据c++方法签名生成对应的C#
import方法签名,参考P/Invoke Interop
Assistant。不过有一个问题还是要注意的,在x86模式下c#中的int对应c++中的int,而在x64模式下C#中的int是对应c++中的long,就这么一个小小的变量类型,在不经意间可能就会导致c++代码出错。

还有一个问题是:托管的 PInvoke 签名与非托管的目标签名不匹配,可以在C#代码的方法特性上加上CallingConvention.Cdecl。如下所示:

[DllImport("dllname.dll", CharSet = CharSet.Ansi, EntryPoint = "methodname", CallingConvention = CallingConvention.Cdecl)]

8bit = 1byte = 1字节 ;

4.
无法直接调用静态构造函数。

内存释放问题

由于这个问题经常遇到,并且如果不能解决的话肯定不会再考虑使用该dll了,这是一个可用性的问题。所以我在调用c++方法的时候,通常都会先批量跑一边,通过日志记录下每调用一次方法后,当前进程所占用的内存大小,这样在运行一段时间以后,就能很清楚的看到内存是否持续增长,如果是的话就需要和编写该dll的同事进行沟通,给他们提供测试数据,确认产生问题的原因。有时即使C++中的方法进行了内存释放,并且在c++测试代码中已经没有内存增长问题了,但是在C#中调用的时候内存还是会持续增长,该问题可能跟使用的场景有关,我这里是因为调用了一个返回char
*类型的c++方法,我直接用C#中的字符串类型的一个变量接收了,结果发现内存总是释放不了,后来让同事把c++的方法更改了一下参数,然后在C#中用StringBuilder类型的变量作为参数传入c++方法中来接收该方法的结果,这样该内存问题就解决了。

C#
// 在C#中声明与C++方法对应的dllimport方法
[DllImport("dllname.dll", CharSet = CharSet.Ansi, EntryPoint = "Handle", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CPPMethod(string content,StringBuilder result);

// 该变量用来接收c++方法的处理结果,作为传出参数传入c++方法,在构造的时候必须明确指定大小
// 如果不指定或者指定的大小不足,会导致c++方法出现空间分配不够的异常
StringBuilder resultSB = new StringBuilder(length);
string cppParam = "some content";
bool isSuccess = CPPMethod(cppParam,resultSB);  // CPPMethod是与C++方法对应的dllimport方法

C++ 
// C++中的DLL函数原型,即:C#中要调用的方法,此处不再返回char *类型的结果,而是将结果放到传出参数result中
extern "C" __declspec(dllexport) bool Handle(char* content, char* result);  // result为传出参数

有的时候内存问题是纯粹由于c++代码导致的,一般遇到内存问题,我会用c++的测试工程再跑一遍,看看是否仍有该问题,如果是说明真是c++的bug了,可以通知同事去修改bug了。

内存问题有时候并不会体现的十分明显,这需要我们更加细心的观察日志并发现导致问题的真正原因。我之前遇到该方面的一个问题,刚开始内存涨幅非常明显,经过多次与开发该dll的同事沟通后,问题已经解决的差不多了,但是大量测试后发现内存还是会有一点上涨,虽然幅度很小,但第六感告诉我此中必有蹊跷,这要是上线跑个几天岂不是还得爆,后来我把每一次调用c++方法后当前进程占用的内存输出到文件中,经过仔细观察,发现绝大部分文件(文件内容要传入c++方法中进行处理)都没问题,内存都很平稳,但是有极小一部分文件在传入c++方法后,会导致内存相比其他文件有一个明显的增长,看来问题是出现在这些文件中,随后把这些文件单独放在一起进行循环调用,内存一下子就大幅增长了,后面就不用说了,问题当然解决了。因此,要保持记日志的良好习惯,哪怕是在测试工程中

1024bytes = 1kbytes =1KB;

5.
如果静态构造函数引发异常,运行时将不会再次调用该构造函数,并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。

版本问题(x86与x64)

版本不匹配的话,在调试时会提示正在加载格式不正确的dll,如果使用的是32位的c++版dll,需要把C#项目的编译平台设置为x86,如果使用的是64位的c++版dll,则设置为any
cpu和x64都可以,这个需要自己根据实际情况对应好就可以了。如果程序对内存的使用比较高,最好将程序编译为64位,因为32位程序对单进程的内存大小有限制,经测试最大不超过2G。因为我的程序刚开始使用的是32位的c++版dll,并且在运行时需要调用这些dll加载很多资源,加载完这些资源进程占用的内存就差不多快2G了,所以总会莫名其妙的崩掉,甚至在加载的过程中就直接崩掉了,当时预感到是32位的问题,后来让同事将dll重新编译为64位后就没有这个问题了。可以通过dumpbin命令判断一个dll是32位还是64位,打开vs开发人员命令提示,输入:dumpbin /headers 你的dll路径,例如:dumpbin /headers d:\test.dll,如下图所示:

图片 1

如果是32位dll,红框那里会显示
图片 2

这里有一个地方需要注意,默认asp.net项目在调试时会运行在32位下的iisexpress进程中,如果你的项目是64位的,那么需要在VS中将iisexpress配置为64位模式,如下图所示:

图片 3

1024KB = 1Million Bytes = 1MB = 1兆 ;

发表评论

电子邮件地址不会被公开。 必填项已用*标注