visual c#中调用visual c++ 编译的cxform.dll问题总结
来源:互联网 发布:信息技术教学的软件 编辑:程序博客网 时间:2024/06/17 03:43
1.调用bat自动生成的cxform-c.dll库的结果
最近有一个项目,收到“J2000”的坐标要转换为“GEO”格式显示,不同坐标系之间的转换,网上已经有现成的源代码了,源代码叫做cxform,百度搜索不到的话,google一下,就可以找到其网站介绍,这里给出网站链接,这个网站中Download下下载选项,可以直接现在源代码进行编译,也可以下载在MSVC和GCC、linux、Mac以及SunOS下编译好的dll文件,如下图所示。
这里推荐下载源代码,因为我们只在c#中调用cxform-c.dll,而编译好的dll文件可以同时用于idl和C的调用。
刚开始我抱着多一事不如少一事的态度,想着直接用编译好的cxform.dll,但是在c#中调用过程中发现提示,无法在cxform.dll中无法找到date2es的入口点,又去百度了这个问题的原因,最后发现问题在于,这个dll库根本没有吧date2es这个函数作为导出函数以作为其他程序调用的API,既然没有把这个函数作为API让其它程序调用,其它程序调用当然就找不到date2es这个入口点了。这是经过提示,我下载一个depends_x86的程序,把cxform.dll直接在depends_x86中打开,得到如下所示的情况。
选中CXFORM.dll,看到Function中只有三个函数,没有我们需要的date2es函数,所以在c#中调用date2es函数就会出现这个问题,其原因就是编译好的dll库中没有date2es函数,depends_x86很好的说明了这个问题。既然编译源代码不行,那我们用现成的还不行么?那么问题来了,怎么用呢?没事,我们接着往下说。
我们就用它自己的源代码文件下的make_CXFORM_MSVC.bat来手动编译cxform-c.dll。这个cxform-c.dll和cxform.dll的区别在于,cxform-c.dll没办法支持IDL的调用,但是cxform可以支持IDL的调用,不过本文中我们不会用IDL调用,我们只需要C调用即可。打开Visual studio 命令提示(2010)命令行窗口(以管理员的方式运行,打开VS2010命令行窗口的方法直接在开始菜单输入“cmd”就能显示VS2010的命令提示符,之后右键单击选择以管理员身份运行,这是为了避免文件写入的Permission Denied错误。),命令行中切换到源代码文件所在目录,输入命令:cd /d 源代码中cxform.h的所在的目录,例如,我的visual studio中输入的命令如下:
cd /d C:\Users\tiger\Downloads\cxform-0.71_source.tar\cxform-0.71_source\cxform-0.71
如下图所示,则切换到了该目录下。
然后输入make_CXFORM_MSVC.bat,开始运行,然后提示只生成c调用的dll么?我们输入‘y’表示确认,随后在该目录下就生成了cxform-c.dll,生成的该dll在depends中打开截图如下:
可以看到在Function一个描述中一个函数都没有,这说明该源代码编译错误,我们并没有将接口导出来。那我们是不是应该尝试一下其他编译结果呢?比如说GCC下编译得到的dll?会不会完美的解决这个问题呢?我告诉你:
我已经试过GCC编译下的结果,同时也试过在64位和32位VS命令提示符下进行过计算,结果在生成的dll库仍然没有可以调用的函数,那么怎么办呢?最后我想到了一个办法,通过微软教程学习如何制作dll库,然后比对这个源代码和教程的区别,找出差异,再进行修改。
2.自己手动编译dll库
这里贴出一个教程,来自MSDN的官方教程,连接如下:
https://msdn.microsoft.com/zh-cn/library/ms235636.aspx
我不再对教程重复,而是针对教程讲解一下其机理。在这个教程里面,最重要的一部分就是MathFuncsDll.h文件中的如下代码:
// MathFuncsDll.h#ifdef MATHFUNCSDLL_EXPORTS#define MATHFUNCSDLL_API __declspec(dllexport) #else#define MATHFUNCSDLL_API __declspec(dllimport) #endif
暂且对于这个代码的意义我们不去深究,但是确定的是,定义任何dll都不能少了上述声明代码,定义自己的导出dll时,只需要将上述代码中“MATHFUNCSDLL”变成自己头文件的名字,头文件全部大写,相信你也可以看出来,MATHFUNCSDLL就是教程中头文件的名字。如果你想对这个声明的内涵和外延有进一步的了解,我只能帮你到这儿了:
dllexport和dllimport
接下来第二重要的部分,我们且看MathFuncsDll.h文件中的函数的声明方式:
namespace MathFuncs{ // This class is exported from the MathFuncsDll.dll class MyMathFuncs { public: // Returns a + b static MATHFUNCSDLL_API double Add(double a, double b); // Returns a - b static MATHFUNCSDLL_API double Subtract(double a, double b); // Returns a * b static MATHFUNCSDLL_API double Multiply(double a, double b); // Returns a / b // Throws const std::invalid_argument& if b is 0 static MATHFUNCSDLL_API double Divide(double a, double b); };}
我们的重点应该关注于这些函数的声明中有一个修饰符“MATHFUNCSDLL_API”,这个符号极其重要,可以看出这个符号的构成是形式是”头文件名称_API”的形式,这个符号表明了这个函数作为导出函数,可以被外部程序调用,如果没有这个修饰符,编译成功的dll文件中无法找到接口函数。
既然知道了问题dll编译接口函数的必备条件,那么就好办了,我们只需要稍加修改我们的cxform.h、文件中的内容就行了。对于cxform.h文件,我们修改如下:
在头文件中照猫画虎,加入如下代码:
#ifdef CXFORMCDLL_EXPORTS#define CXFORMCDLL_API __declspec(dllexport)#else#define CXFORMCDLL_API __declspec(dllimport)#endif
同时,将cxform的声明和date2es形式修改为如下形式:
CXFORMCDLL_API int cxform(const char *from,const char *to,const double et,Vec v_in,Vec v_out);CXFORMCDLL_API long date2es(int yyyy, int mm, int dd, int hh, int mm2, int ss);
好了,最后点击生成(&B)中的生成解决方案,打开我们dll如下图所示:
可以看出,date2es和cxform两个函数已经导出,因此,说明编译dll已经成功了。当然了,如果你觉得很繁琐的话,我也已经将该dll打包上传,连接如下:
http://download.csdn.net/detail/t46414704152abc/9855135
3.在c#中调用该dll
接下来,就是在c#中调用这个dll库的方法了,需要将该DLL文件放入Debug文件目录下,或者系统System32文件目录下面。在c#要调用该dll的类中加入如下代码,下列代码中CallingConvention = CallingConvention.Cdel不可以发生变化,如果发生变化,则我们编译上述dll的代码也要相应变化。
//int cxform(const char *from,const char *to,const double et,Vec v_in,Vec v_out);//上面注释是c++中的cxform的参数形式,但是在c#中调用需要进行格式转换,其中Vec是装有三个double元素的数组[DllImport("cxform-c.dll", EntryPoint = "cxform", CallingConvention = CallingConvention.Cdecl)]public static extern int cxform(string from, string to, double et, double[] v_in, double[] v_out);//long date2es(int yyyy, int mm, int dd, int hh, int mm2, int ss);//上面注释是c++中date2es的参数形式,但是在c#中调用需要进行格式转换[DllImport("cxform-c.dll", EntryPoint = "date2es", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]public static extern int date2es(int yyyy, int mm, int dd, int hh, int mm2, int ss);
本例子中的变量转换规则如下
于是,贴出下文代码中一个c#的例子
static void Main(string[] args) { DateTime dt = new DateTime(2017, 5, 27, 21, 40, 51); dt = dt.ToUniversalTime(); //将GSE坐标转换GEO坐标 string from = "GSE"; string to = "GEO"; long es = date2es(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); double[] coord = new double[3] { 10.0, 10.0 , 10.0 };//设置坐标 double[] rsltCoord = new double[3];//存放转换结果 cxform(from, to, es, coord, rsltCoord);//进行坐标转换 foreach (double x in rsltCoord) { Console.Write(x); } }
4.内容总结
- 如果你的代码在运行中出现了类似于如下的错误:
检测到 FatalExecutionEngineError Message: 运行时遇到了错误。此错误的地址为
0x6d968090,在线程 0xa28 上。错误代码为 0xc0000005。此错误可能是 CLR 中的
bug,或者是用户代码的不安全部分或不可验证部分中的 bug。此 bug 的常见来源包括用户对 COM-interop 或 PInvoke
的封送处理错误,这些错误可能会损坏堆栈。
那么可能是你的c++和c#之间参数转换的问题了,需要再次检查你的参数转换是否出现了问题
2.如果你的代码在运行中出现了如下错误:
对 PInvoke 函数“xxFunction()”的调用导致堆栈不对称。原因可能是托管的 PInvoke
签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。
解决方法如下:
方法1.在c#中函数声明处改一个参数,[DllImport(“xx.dll”, EntryPoint=“xxFunction”, CallingConvention = CallingConvention.Cdecl)]调用时不变
方法2.在c++代码中改对应的c++函数参数从extern“C” _declspec(dllexport) void xxFunction()改成
extern“C” _declspec(dllexport) void __stdcall xxFunction(),即再添加一个修饰关键字“__stdcall”
具体原因如下:
在c++WIN32程序中有三种calling convention(呼叫约定):__cdecl, __stdcall, __fastcall默认为__cdecl,而c#中默认为CallingConvention =CallingConvention.Winapi,两个平台呼叫约定不一致,所以会出现提示的不匹配错误。
__cdecl为调用函数即C#中清理堆栈中保存的参数。参数的大小不确定时用这个,比如string
__stdcall对应c#中CallingConvention =CallingConvention.Winapi,它由c++中函数自动清理。
Win32 calling convention(呼叫约定)的三种约定具体分析见下面链接:
http://www.cnblogs.com/super119/archive/2011/04/10/2011304.html
http://www.cnblogs.com/dust/articles/1190641.html。
3.官方既然提供了dll库,但是这个库又不能使用,妈卖批不知道想干什么?害得我搞这事情搞了一个星期,既然提供了dll,那么为什么没有接口函数?为什么没有接口函数?为什么没有接口函数?不过通过这坏事,也让我学习到了通过VS如何编译生成dll库,同时在c#中调用。不过正是因为官方源代码存在的问题,使得我学到了更多的知识。正是应验了那句话“在失败中学到的,往往比在成功中学到的更多!”
4.最后通过这件事情,学到的一件事情,遇到不要轻易放弃,你已经坚持了这么久,离成功很近了,再坚持一次说不定就有转机了,每次坚持不下去了,告诉自己,再坚持一次,再努力一次,你的付出不会亏待你的,真的会有奇迹发生!
- visual c#中调用visual c++ 编译的cxform.dll问题总结
- 如何在 Visual Studio 中混合编译C# 和 C++代码,生成单一的DLL?
- Visual Studio中调用matlab生成的dll
- C#中调用C++dll错误问题
- 【C#】Visual C# 生成DLL文件 --转
- C#调用C++DLL的小总结8---C++Dll中函数返回字符串指针
- 关于Visual 2005中MFC调用Berkeley DB的编译错误问题
- 解决Visual Studio 2005中找不到MFC80UD.dll的问题
- 解决Visual Studio 2005中找不到MFC80UD.dll的问题
- 解决Visual Studio 2005中找不到MFC80UD.dll的问题
- 决Visual Studio 2005中找不到MFC80UD.dll的问题
- 解决Visual Studio 2005中找不到MFC80UD.dll的问题
- 解决Visual Studio 2005中找不到MFC80UD.dll的问题
- Visual Studio调用自己编写的DLL
- C/C++/C#问题记录(一)VS2012编译调用dll项目找不到lib的workaround
- C#调用C++DLL总结
- Visual C# 引用 PDFBox的dll
- 在Visual C#中调用API的基本过程:
- 时序图(Sequence Diagram)
- 暴力博弈的一些的题(不定时更新)
- 什么是设计模式
- linux学习笔记-----任务栏消失解决方案
- 进程池和进程之间的通信
- visual c#中调用visual c++ 编译的cxform.dll问题总结
- 网站设计-html实现点击链接跳转到当前页面的某个位置
- 关于unity中使用solidwork模型材质及动画丢失问题
- BZOJ 1626: [Usaco2007 Dec]Building Roads 修建道路 kruskal
- 【并发】并发工具类
- 使用VRTK在场景中实现传送 (三)
- 在Linux上面搭建PHP开发环境
- android studio中的instant run造成的一些奇葩bug
- 2017 计蒜之道 初赛 第四场