c#调用c++出现异常

pixian0237 2017-04-12 10:44:56
c#代码

using System;
using System.Colle<img src="https://img-bbs.csdn.net/upload/201704/11/1491899383_919628.png" alt="">.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
[DllImport("smallT_BJ.dll", EntryPoint = "?smallTarget@@YA?AV?$Point_@H@cv@@PBD@Z",
ExactSpelling = true,CallingConvention = CallingConvention.Cdecl)]
// EntryPoint 这个比较复杂的部分是通过dependency查询到的函数名,与之前定义的函数名smallTarget多了很多字符
public static extern Point smallTarget(string path);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string path = " C:\\1.bmp";
Point point = new Point();
point = smallTarget(path);
}
}
}

c++dll下面是.h文件的内容
#include <windows.h>
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <opencv2\opencv.hpp>
#include <fstream>
#include <memory>
#include <stdlib.h>
#include <math.h>
#include <time.h>
using namespace std;
using namespace cv;
#ifdef SMALLT_BJ_EXPORTS
#define SMALLT_BJ_API __declspec(dllexport)
#else
#define SMALLT_BJ_API __declspec(dllimport)
#endif
SMALLT_BJ_API Mat filters(Mat source, int thresholdin);
SMALLT_BJ_API Mat filterspatial2(Mat source, int th);
SMALLT_BJ_API void cmedian(Mat src, Mat dst, int innerbox_size, int outerbox_size);
SMALLT_BJ_API Mat prefilter(Mat source);
SMALLT_BJ_API vector<Mat> getSuspects(Mat& frame, Mat src, int box_size, vector<Point>& LeftUpPoints, vector<vector<Point>>& cont);
SMALLT_BJ_API Point smallTarget(const char* file);

其他的都是乱码就不贴出来了,我在c#执行文件的目录下面(存放位置肯定没又问题,bin\Debug目录下)放了4个可能需要用到的文件,分别是.dll文件.h文件,.lib文件,.exp文件。在调用这个dll文件之前我用dependency测试了一下dll,发现缺少几个opencv方面的动态库,然后就加了相应的dll文件加在c#程序的目录下,后来又检测发现又缺少了更多的dll,然后又添加了相应的dll,最后检测不缺少dll,但是出现的问题是:“警告: 由于在延时加载依赖模块中丢失导入函数,至少有一个模块具有不能解析的导入。”。其中dll文件在别人给我的时候在我的电脑上进行了测试,并没问题,可以运行通过。
然后我在c#上进行调用dll出现的问题是

然后上网找了很多资料,实在是没用一个合适的解决方案、只能来请求下帮助,怎么解决?然后是dll制作出了问题还是c#d调用dll本来就存在一些问题呢?
...全文
256 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
白衣如花 2017-04-14
  • 打赏
  • 举报
回复
引用 13 楼 pixian0237 的回复:
好吧、、没用解决的办法。
而且建议你加一些输入信息 比如内部函数第一行给你给个打印,来判断是否调用成功 内部函数最后一行给个打印,来判断内部函数本身处理是否有bug 这样就能定位异常产生的具体位置:1,调用函数没找到;2,内部函数有bug;3,返回时类型转换问题
白衣如花 2017-04-14
  • 打赏
  • 举报
回复
引用 13 楼 pixian0237 的回复:
好吧、、没用解决的办法。
用用赵4老师的方法 dll内部声明函数时用extern "C" { 函数声明} 这个是告诉编译器,被包含的部分用C规则来编译 C++能重载比如 int max(int, int)和double max(double, double)能放到一个作用域内 如果不给修饰的话,你enterPoint=max,系统就不知道调用哪个函数了,所以会加一些特殊的符号,以及参数表的信息,也就是你函数入口十分奇怪的原因 而C不支持重载,所以入口就是你的函数名,一目了然
pixian0237 2017-04-13
  • 打赏
  • 举报
回复
好吧、、没用解决的办法。
赵4老师 2017-04-12
  • 打赏
  • 举报
回复
不要做A语言代码修改为B语言代码的无用功。 也不要做用A语言代码直接调用B语言代码库这样复杂、这样容易出错的傻事。 只需让A、B语言代码的输入输出重定向到文本文件,或修改A、B语言代码让其通过文本文件输入输出。 即可很方便地让A、B两种语言之间协调工作。 比如: A将请求数据写到文件a.txt,写完后改名为aa.txt B发现aa.txt存在时,读取其内容,调用相应功能,将结果写到文件b.txt,写完后删除aa.txt,改名为bb.txt A发现bb.txt存在时,读取其内容,读完后删除bb.txt 以上A可以替换为任何一种开发语言或开发环境,B可以替换为任何一种与A不同的开发语言或开发环境。 除非A或B不支持判断文件是否存在、文件读写和文件更名。 但是谁又能举出不支持判断文件是否存在、文件读写和文件更名的开发语言或开发环境呢? 可以将临时文件放在RamDisk上提高效率减少磨损磁盘。 数据的结构很复杂的话,文本文件的格式问题可参考json或xml 共享临时文本文件这种进程之间的通讯方法相比其它方法的优点有很多,下面仅列出我现在能想到的: ·进程之间松耦合 ·进程可在同一台机器上,也可跨机,跨操作系统,跨硬件平台,甚至跨国。 ·方便调试和监视,只需让第三方或人工查看该临时文本文件即可。 ·方便在线开关服务,只需删除或创建该临时文本文件即可。 ·方便实现分布式和负载均衡。 ·方便队列化提供服务,而且几乎不可能发生队列满的情况(除非硬盘空间满) ·…… “跨语言、跨机,跨操作系统,跨硬件平台,跨国,跨*.*的”苦海无边, 回头是“使用共享纯文本文件进行信息交流”的岸!
赵4老师 2017-04-12
  • 打赏
  • 举报
回复
dll 导出函数名的那些事 关键字: VC++  DLL 导出函数  经常使用VC6的Dependency查看DLL导出函数的名字,会发现有DLL导出函数的名字有时大不相同,导致不同的原因大多是和编译DLL时候指定DLL导出函数的界定符有关系。 VC++支持两种语言:即C/C++,这也是造成DLL导出函数差异的根源 我们用VS2008新建个DLL工程,工程名为"TestDLL" 把默认的源文件后缀 .CPP改为.C(C文件) 输入测试代码如下: 01 int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 为了导出上面这个函数,我们有以下几个方法: 1. 使用传统的模块定义文件 (.def) 新建一个 后缀为.def的文本文件(这里建一个TestDll.Def),文件内容为: LIBRARY TestDll EXPORTS MyFunction 在 Link 时指定输入依赖文件:/DEF:"TestDll.Def" 2. Visual C++ 提供的方便方法 在01行的int 前加入 __declspec(dllexport) 关键字 通过以上两种方法,我们就可以导出MyFunction函数。 我们用Dependency查看导出的函数: 第一种方法导出的函数为: MyFunction 第二种方法导出的函数为: _MyFunction@4 __stdcall会使导出函数名字前面加一个下划线,后面加一个@再加上参数的字节数,比如_MyFunction@4的参数(int iVariant)就是4个字节 __fastcall与 __stdcall类似,不过前面没有下划线,而是一个@,比如@MyFunction@4 __cdecl则是始函数名。 小结:如果要导出C文件中的函数,并且不让编译器改动函数名,用def文件导出函数。 下面我们来看一下C++文件 我们用VS2008新建个DLL工程,工程名为"TestDLL" 默认的源文件后缀为 .CPP (即C++文件)。 输入测试代码如下: 01 int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 为了导出上面这个函数,我们有以下几个方法: 3. 使用传统的模块定义文件 (.def) 新建一个 后缀为.def的文本文件(这里建一个TestDll.Def),文件内容为: LIBRARY TestDll EXPORTS MyFunction 在 Link 时指定输入依赖文件:/DEF:"TestDll.Def" 4. Visual C++ 提供的方便方法 在01行的int 前加入 __declspec(dllexport) 关键字 通过以上两种方法,我们就可以导出MyFunction函数。 我们用Dependency查看导出的函数: 第一种方法导出的函数为: MyFunction 第二种方法导出的函数为: ?MyFunction@@YGHH@Z 可以看到 第二种方法得到的 导出函数名 并不是我们想要的,如果在exe中用显示方法(LoadLibrary、GetProcAddress)调用 MyFunction 肯定会失败。 但是用引入库(*.LIB)的方式调用,则编译器自动处理转换函数名,所以总是没有问题。 解决这个问题的方法是: 用VC 提供的预处理指示符 "#pragma" 来指定链接选项。 如下: #pragma comment(linker, "/EXPORT:MyFunction=?MyFunction@@YGHH@Z") 这时,就会发现导出的函数名字表中已经有了我们想要的MyFunction。但我们发现原来的那个 ?MyFunction@@YGHH@Z 函数还在,这时就可以把 __declspec() 修饰去掉,只需要 pragma 指令即可。 而且还可以使如下形式: #pragma comment(linker, "/EXPORT:MyFunction=_MyFunction@4,PRIVATE") PRIVATE 的作用与其在 def 文件中的作用一样。更多的#pragram请查看MSDN。 小结:如果要导出C++文件中的函数,并且不让编译器改动函数名,用def文件导出函数。 同时可以用#pragma指令(C 中也可以用)。 总结: C++编译器在生成DLL时,会对导出的函数进行名字改编,并且不同的编译器使用的改编规则不一样,因此改编后的名字也是不同的(一般涉及到C++ 中的重载等)。 如果利用不同编译器分别生成DLL和访问DLL的exe程序,后者在访问该DLL的导出函数时就会出现问题。如上例中函数MyFunction在C++编译器改编后的名字是?MyFunction@@YGHH@Z。我们希望编译后的名字不发生改变,这里有几种方法。 第一种方法是通过一个称为模块定义文件DEF来解决。 LIBRARY TestDll EXPORTS MyFunction LIBRARY 用来指定动态链接库内部名称。该名称与生成的动态链接库名一定要匹配,这句代码不是必须的。 EXPORTS说明了DLL将要导出的函数,以及为这些导出函数指定的符号名。 第二种是定义导出函数时加上限定符:extern "C" 如:#define DLLEXPORT_API extern "C" _declspec(dllexport) 但extern "C"只解决了C和C++语方之间调用的问题(extern "C" 是告诉编译器,让它按C的方式编译),它只能用于导出全局函数这种情况 而不能导出一个类的成员函数。 同时如果导出函数的调用约定发生改变,即使使用extern "C",编译后的函数名还是会发生改变。例如上面我们加入_stdcall关键字说明调用约定(标准调用约定,也就是WINAPI调用约定)。 #define DLLEXPORT_API extern "C" _declspec(dllexport) 01 DLLEXPORT_API int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 编译后函数名MyFunction改编成了_MyFunction@4 通过第一种方法模块定义文件的方式DLL编译后导出函数名不会发生改变。 DLL(动态库)导出函数名乱码含义 C++编译时函数名修饰约定规则: __stdcall调用约定: 1、以"?"标识函数名的开始,后跟函数名; 2、函数名后面以"@@YG"标识参数表的开始,后跟参数表; 3、参数表以代号表示: X--void D--char E--unsigned char F--short H--int I--unsigned int J--long K--unsigned long M--float N--double _N--bool .... PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复; 4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。 其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如 int Test1(char *var1, unsigned long)-----"?Test1@@YGHPADK@Z" void Test2()-----"?Test2@@YGXXZ" __cdecl调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。 __fastcall调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。 如果要用DEF文件输出一个"C++"类,则把要输出的数据和成员的修饰名都写入.def模块定义文件 所以... 通过def文件来导出C++类是很麻烦的,并且这个修饰名是不可避免的
pixian0237 2017-04-12
  • 打赏
  • 举报
回复
引用 7 楼 yunqing1201 的回复:
我做过类似的OCR项目,也是调用opencv,后来在实际中发现在c#中调用opencv确实比较麻烦,后来我是自己用c++封装成一个dll,然后再在c#中调用,可以查看我上传的源码!!!
刚刚下载了你的源码,不过没用找到C#如何调用的dll代码,就是运行exe文件时出现一个demo,其他的关于c#代码没有看到。现在问题又2个。1,就是c++里面const char*是对应c++中的string还是 IntPtr?2,因为我调用的这个dll也调用了Opencv的dll,那我需要把opencv相关的dll放到c#执行目录下面吗?谢谢啊
pixian0237 2017-04-12
  • 打赏
  • 举报
回复
引用 9 楼 u012948520 的回复:
EntryPoint = "?smallTarget@@YA?AV?$Point_@H@cv@@PBD@Z" 这个入口好蛋疼。。。封装dll的时候选择C编译链接模式嘛。。。C++支持函数重载,所以入口怪怪的 看异常不像是内存出错,怀疑就是入口没搞好
这个入口是通过depedency查到的,而且另外一个查询dll函数的软件也是这个入口。我现在感觉应该就是数据类型不匹配的原因了,输入的是图片的地址,dll里面的函数没用做任何处理直接返回图片地址,在c++测试的时候正确,可是c#调用返回的就是一串数字。而且每次返回的数字还不同。稀里糊涂的。。也是醉了
白衣如花 2017-04-12
  • 打赏
  • 举报
回复
EntryPoint = "?smallTarget@@YA?AV?$Point_@H@cv@@PBD@Z" 这个入口好蛋疼。。。封装dll的时候选择C编译链接模式嘛。。。C++支持函数重载,所以入口怪怪的 看异常不像是内存出错,怀疑就是入口没搞好
白衣如花 2017-04-12
  • 打赏
  • 举报
回复
引用 6 楼 zhao4zhong1 的回复:
感觉你思想僵化了。。。都是解决方案而已 C#调用C++一般还是楼主那种,注意好对应关系,编码格式什么的就好了
yunqing1201 2017-04-12
  • 打赏
  • 举报
回复
我做过类似的OCR项目,也是调用opencv,后来在实际中发现在c#中调用opencv确实比较麻烦,后来我是自己用c++封装成一个dll,然后再在c#中调用,可以查看我上传的源码!!!
赵4老师 2017-04-12
  • 打赏
  • 举报
回复
赵四老师,你是一直这么说啊。但是问题我怎么才可以同时让c#和c++程序在 C#中启动C++进程或C++中启动C#进程或外部脚本比如bat中同时启动C#和C++进程都行。
pixian0237 2017-04-12
  • 打赏
  • 举报
回复
引用 4 楼 qq_34266409 的回复:
那就应该是这个问题了,你用IntPtr试试看
还是一样的结果,每次输入相同的数据返回的始终都不同
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
       [DllImport("smallT_BJ.dll", EntryPoint = "test", CallingConvention = CallingConvention.Cdecl)]
        public static extern Point test(IntPtr path);
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
           string path = " C:\\2.bmp";
           IntPtr init = Marshal.StringToHGlobalAnsi(path);
            Point point = new Point();
            point = test( init);
            MessageBox.Show(point.ToString());
    }
}
这个是C#代码,其中test函数的作用就是返回图片像素的大小,内部就一行程序
Mooyi_水 2017-04-12
  • 打赏
  • 举报
回复
那就应该是这个问题了,你用IntPtr试试看
pixian0237 2017-04-12
  • 打赏
  • 举报
回复
引用 1 楼 zhao4zhong1 的回复:
不要做A语言代码修改为B语言代码的无用功。 也不要做用A语言代码直接调用B语言代码库这样复杂、这样容易出错的傻事。 只需让A、B语言代码的输入输出重定向到文本文件,或修改A、B语言代码让其通过文本文件输入输出。 即可很方便地让A、B两种语言之间协调工作。 比如: A将请求数据写到文件a.txt,写完后改名为aa.txt B发现aa.txt存在时,读取其内容,调用相应功能,将结果写到文件b.txt,写完后删除aa.txt,改名为bb.txt A发现bb.txt存在时,读取其内容,读完后删除bb.txt 以上A可以替换为任何一种开发语言或开发环境,B可以替换为任何一种与A不同的开发语言或开发环境。 除非A或B不支持判断文件是否存在、文件读写和文件更名。 但是谁又能举出不支持判断文件是否存在、文件读写和文件更名的开发语言或开发环境呢? 可以将临时文件放在RamDisk上提高效率减少磨损磁盘。 数据的结构很复杂的话,文本文件的格式问题可参考json或xml 共享临时文本文件这种进程之间的通讯方法相比其它方法的优点有很多,下面仅列出我现在能想到的: ·进程之间松耦合 ·进程可在同一台机器上,也可跨机,跨操作系统,跨硬件平台,甚至跨国。 ·方便调试和监视,只需让第三方或人工查看该临时文本文件即可。 ·方便在线开关服务,只需删除或创建该临时文本文件即可。 ·方便实现分布式和负载均衡。 ·方便队列化提供服务,而且几乎不可能发生队列满的情况(除非硬盘空间满) ·…… “跨语言、跨机,跨操作系统,跨硬件平台,跨国,跨*.*的”苦海无边, 回头是“使用共享纯文本文件进行信息交流”的岸!
赵四老师,你是一直这么说啊。但是问题我怎么才可以同时让c#和c++程序在
引用 2 楼 qq_34266409 的回复:
看到过一个调用C++ dll的最常见的问题就是C++ 用指针而C#不用指针的问题,你这个会不会是这个问题?
c++那边用的是const char *类型,对应的是c#string类型。我也不知道是不是指针的问题,现在正在找问题。。
Mooyi_水 2017-04-12
  • 打赏
  • 举报
回复
看到过一个调用C++ dll的最常见的问题就是C++ 用指针而C#不用指针的问题,你这个会不会是这个问题?

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧