1、前奏
最近帮老师用opencv做一个东西,在VS2013中配置了opencv 的经典版本opencv2.4.9,做了一个视频解帧的工作,封装成了C++的dll,以供对方公司使用C#语言调用,所以我需要用C#测试我的dll
C#语言真是难缠,dll缺失或者dll中内部有错误都只给你一个信息那就是:“无法加载DLL"***.dll":找不到指定的模块(异常来自HRESULT:0x8007007E)”,真是令人头大
2、解决办法
我是尝试遍了所有的这个错误的解决办法,依然不能挽救错误,还是报同样的错误。
C#中调用C++的dll也有两种方式,一种是静态调用,一种是动态调用,我先使用的静态调用,网上的静态调用方式下出这个错误的方法已试完看到网上最好的那个帖子:
帖子如下地址如下:http://blog.csdn.net/peng825223208/article/details/38069779
这篇帖子我以为很吊,相信读我博客的你应该也会看到。由于不懂C#,就把它的代码敲进去进行尝试调用,什么改@“xxxx.dll”这样的绝对路径一点用都没有,还是找不到dll,它里面说的托管和非托管我百度了一下也没看明白,都是官方解释,到底什么样的dll是托管的什么样的dll是不托管的,对于一个C#初次接触的人来说不是很明白,代码敲进去也有错,所以就不用它的方法了,写的又不清楚。
接下来我贴一下我的dll的代码和我用C#调用这个dll的代码,供大家参考自己的改,然后我会给出可能出错的原因,90%错误都在其中。
2、利用C++写的dll代码(配置了opencv库):
/*
该函数主要功能为将输入的视频分解成帧,有三个输入参数
char*inputPath:输入视频
char*ouputPath:输出帧目录
int maxFrame:想要分解出的帧数,从第一帧算起
返回值为int类型,表示最终分解出的帧的个数,如果maxFrame大于视频实际帧数,返回实际帧数,如果小于则返回maxFrame帧
date:2017/10/26
author:tonglewang
address:ECNU
*/
#include "stdafx.h"
#include
#include
#include
using namespace std;
using namespace cv;
extern "C" __declspec(dllexport)int videoTransferToImage(char*inputPath, char*ouputPath,int maxFrame){
cout <<"already acesss the entrypoint!" <
Mat frame;
int i = 1;
VideoCapture inputVideo(inputPath);
if (!inputVideo.isOpened()){
return 0;
}
cout << "xunhuan entrypoint!" << endl;
while (1){
cout << "acess xuhuan!" << endl;
inputVideo >> frame;
if (frame.empty())
break;
sprintf_s(fileName, "%s%d.jpg", ouputPath, i);
imwrite(fileName, frame);
if (i==maxFrame)
break;
i++;
}
int frames = inputVideo.get(CV_CAP_PROP_FRAME_COUNT);
if (frames >= maxFrame)
return maxFrame;
else
return frames;
}
3.1 C#写的静态调用代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace VideoTranferToImageTest
{
class Program
{
static void Main(string[] args)
{
string videoPath = "D:\\workspace\\program\\VideoTranferToImage\\Test\\videoPath\\test.mp4";
string picturePath = "D:\\workspace\\program\\VideoTranferToImage\\Test\\picturePath\\";
int maxFrame = 100;
int frames = Test.videoTransferToImage(videoPath,picturePath,maxFrame);
Console.WriteLine("total frames=" + frames.ToString());
Console.ReadLine();
}
}
class Test {
//下面的"VideoTransferToImage.dll"为你的dll的存放路径,C#的话就存在项目的/bin/debug/目录下就好了,加什么@加不加都无所谓,路径中是不是用“//”双斜线都无所谓。
[DllImport("VideoTransferToImage.dll", EntryPoint = "videoTransferToImage",CallingConvention = CallingConvention.Cdecl)]
public static extern int videoTransferToImage(string inputPath, string ouputPath,int maxFrame);
}
}
3.2 C#写的动态调用代码
ausing System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string videoPath = "D:\\workspace\\program\\VideoTranferToImage\\Test\\videoPath\\test.mp4";
string picturePath = "D:\\workspace\\program\\VideoTranferToImage\\Test\\picturePath\\";
int maxFrame = 100;
int hModule = NativeMethod.LoadLibrary("VideoTransferToImage.dll");
if (hModule == 0) return;
//2. 读取函数指针
IntPtr intPtr = NativeMethod.GetProcAddress(hModule, "videoTransferToImage");
//3. 将函数指针封装成委托
videoTransferToImage addFunction = (videoTransferToImage)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(Add));
//4. 测试
Console.WriteLine(addFunction(videoPath, picturePath,maxFrame));
Console.Read();
}
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int videoTransferToImage(string inputPath, string ouputPath,int maxFrame);
}
public static class NativeMethod
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
public static extern int LoadLibrary(
[MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
public static extern IntPtr GetProcAddress(int hModule,
[MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
public static extern bool FreeLibrary(int hModule);
}
}
4、我的经历
我的最终错误是我前段时间还是做opencv开发dll的时候由于某种原因将系统中的msvcp120d.dll和msvcr120d.dll这两家伙给删了,因为C#它死活不报说缺两东西,死活就说你的"***.dll":找不到指定的模块(异常来自HRESULT:0x8007007E)找不到,所以这个问题一直没发现,使用depend walk工具查看结果也正常,没有显示缺这两个,我就怪了。最后我在偶然尝试一篇博客的动态调用的时候就跟着它的博客写了一个简单的a+b的dll,利用C#调用,因为这个根本不涉及dll,所以就暴露出来了,显示缺少上面两个dll,我最后才大工工程,整个问题持续一天才解决,因为我死活看我的dll就是在bin/debug目录下,就是在,为什么就是找不到呢?所以当你能够确定你的dll一定是在目录下的时候就不要去按照http://blog.csdn.net/peng825223208/article/details/38069779这篇博文去忙碌了,你该考虑另一个能99%造成这个错误的原因了,那便是你的dll调用了别的dll,或者你安装的操作系统里面根本就缺少一些dll,那怎么办呢?这两个办法各有妙招。办法如下:
4.1你的dll调用了别的dll
终极杀手锏,下载安装depend walk查看依赖关系,看你的dll到底还调用了那些dll,以opencv 为例,如果缺失的是以opencv开头的,索性把opencv安装目录下面的build/x86/opencv12/bin目录下的所有dll拷贝到你的工程的bin/debug/目录下就好了。至于depend显示的
IESHIMS.DLL找不到文件就不要信息,好多dll都有这毛病,这不是本质愿意,还有下面弹出的一些您在所有人博客里面都能看到的提示错误,就不要关心了,我的时这样的,下面的红色都是无关紧要的错误。
depend walk 官方下面地址:http://www.dependencywalker.com/
4.2 你的系统缺少一些dll
这个错误就得感谢我解决问题的那篇博客了,多谢这位博主的博客,给他点个赞博主文章地址:http://www.cnblogs.com/newstart/archive/2012/10/24/2736780.html
大家可以照着他的方法写一个add (a+b)的验证程序,因为这种简答的程序啥依赖的关键dll基本没有,依赖的都是系统相关的dll,所以很VS这是在运行就会告诉你,系统了缺少的一些dll,你就可以下载这些dll,放在您系统的windows目录下了,记住:64位系统放到这个C:\Windows\SysWOW64,32位系统放到这个C:\Windows\System32,不放心全都放吧。
5 总结
所以归结起来就是两个重大的原因你都要普查清楚,也是别人博客里面说的很多的两个原因:
1、一定要将dll放在项目\bin\Debug目录下,然后仔细检查 [DllImport("VideoTransferToImage.dll", EntryPoint = "videoTransferToImage",CallingConvention = CallingConvention.Cdecl)中的路径是否写的正确。
2、如果你确认你的dll就是在\bin\Debug目录下,上面语句也没问题,那么十有八九就是你的dll依赖别的dll,一种是系统的dll,一种是你写你的dll程序时,调用到了别的dll,例如opencv你写的时候包含了好多头文件,用到了好多dll。虽然没有明显在调用调用,但是你的每一个函数都是在调用opencv的dll。另外就是调用到了系统的dll。这两种错因我都给了解决方案,希望在解决dll错误的路上您一路好走!
最后就说一下,不要相信这篇博客里面的内容了
说的啥根本不知道,可能是我太肤浅,但是我觉得估计很多人都不是这个问题。