最近老师在做项目,有一个需求,就是他现在用matlab写了一些操作图像的代码,但是需要在C#环境下调用。基本上就是这样的需求吧。现在老师给我的文件是,如下图:
下面我分别解释一下这些文件,希望以后有这个需求的同学能少走一些弯路。
前八个一看就懂,.h和.c是源文件,.lib和.dll是生成的动态链接库,最后一个文件是:如果你的电脑上没有安装matlab,那么你需要下载并安装这个文件,如果你的电脑上安装了matlab,好像在matlab的安装目录下有这个文件,你找到,安装就行了,我是直接按的,并没有找,虽然我的电脑上也有matlab文件。
环境:
操作系统:win7 x64
环境: vs2012
那个matlab我不知道,应该没有关系吧,我直接拿到手的就是matlab生成好的dll。
下面就按我的说了:
刚开始,用的网上的办法,总是出问题,因为网上说要在引用里面添加上这些dll,然后在using,但是这样的话总是出问题,弄了半天也没有弄好,只好想别的办法了。出现的问题如下:
未能添加对XXXX的引用,请确保此文件可访问,并且是一个有效的程序集或从com组件。
错误原因:应该是在生成matlab的dll的时候应该直接生成com组件才能用。
后来,我使用了和调用C++生成的动态链接库一样的办法,dllimport函数:
[DllImport("libFDS.dll", CharSet = CharSet.Ansi)]
public static extern MWNumericArray mlxFDS(int imglen, MWCharArray []imgFilename, int pathlen, MWCharArray []path);
但是这样的话又会出现这样的问题,在编译的时候:
未经处理的异常: System.NotSupportedException: 无法从非托管句柄创建 SafeHandle
字段。
在 System.StubHelpers.StubHelpers.FmtClassUpdateCLRInternal(Object obj, Byte*
pNative)
在 matlabconsoletest.Program.mlxFDS(Int32 imglen, MWCharArray[] imgFilename,
Int32 pathlen, MWCharArray[] path)
在 matlabconsoletest.Program.Main(String[] args) 位置 d:\13liushuanpeng\matla
bconsoletest\matlabconsoletest\Program.cs:行号 34
请按任意键继续. . .
实在没有办法了后来,由于我原来做过用C#调用C++的动态链接库,而且成功了(参见我前面的一篇文章),所以想,能不能我先用C++调用matlab的dll,然后我在用C# 调用C++的动态链接库(事实证明可行)。
首先:我的原函数如下:就是.c文件中的函数
bool MW_CALL_CONV mlxFDS(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[])
{
return mclFeval(_mcr_inst, "FDS", nlhs, plhs, nrhs, prhs);
}
LIB_libFDS_C_API
bool MW_CALL_CONV mlfFDS(int nargout, mxArray** result, mxArray* imgFilename, mxArray*
temPath)
{
return mclMlfFeval(_mcr_inst, "FDS", nargout, 1, 2, result, imgFilename, temPath);
}
经过查资料,主要参考的是这篇文章:https://www.mathworks.com/matlabcentral/newsreader/view_thread/266426
http://blog.csdn.net/xiaowei_cqu/article/details/7346534这篇看着也对。
写出的如下代码:
int _tmain(int argc, char* argv[])
{<span style="white-space:pre"> </span>char fname[] = ".\\210103196303274887\\sy_20140728112957_210103196303274887_-859566652.jpg";
char path[] = ".\\210103196303274887\\";
libFDSInitialize();
libFRSInitialize();
mxArray *path_C[2];
path_C[0] = mxCreateString(fname);
path_C[1] = mxCreateString(path);
mxArray *output[1];
mlxFDS(1,output,2,path_C);
mxArray *y = NULL; //定义输出结果指针
y = output[0];
double res = mxGetScalar(y);
return 0;
}
下面我们就用上面的代码来生成C++的dll了。生成办法,那个前面一篇文章有,可以参考。直接代码如下:
#include <tchar.h>
#include "libFRS.h"
#include "libFDS.h"
#include "mclmcr.h"
#include "matrix.h"
#include "mclcppclass.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <direct.h>
#include <windows.h>
using namespace std;
//#pragma comment(lib,"libFRS.dll")
//#pragma comment(lib,"libFDS.dll")
extern "C" __declspec(dllexport) double callMatlab(char fname[],char path[])
{
mxArray *path_C[2];
path_C[0] = mxCreateString(fname);
path_C[1] = mxCreateString(path);
mxArray *output[1];
mlxFDS(1,output,2,path_C);
mxArray *y = NULL; //定义输出结果指针
y = output[0];
double res = mxGetScalar(y);
return res;
}
当然,别忘记了把相应的dll和lib库包含尽量,需要在项目属性中,添加一些matlab的一些库,比如上面代码include的
#include "mclmcr.h"
#include "matrix.h"
#include "mclcppclass.h"
这三个,就是matlab库中的,在项目--属性--VC++目录--包含目录和库目录中分别包含
C:\Program Files\MATLAB\MATLAB Compiler Runtime\v81\extern\include
和
C:\Program Files\MATLAB\MATLAB Compiler Runtime\v81\extern\lib\win64\microsoft此为上面第八个文件MCR的安装路径下
之后把相应的lib库包含进来就行了
libFRS.lib
libFDS.lib
mclmcrrt.lib
libmx.lib
libmat.lib
mclmcr.lib
winmm.lib
运行,就可以生成两个文件,分别为.lib和.dll。。。
然后我们在在C#中调用这两个动态链接库。别忘记把相应的文件拷贝到对应的debug和release目录下。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using MathWorks.MATLAB.NET.Arrays;
using MathWorks.MATLAB.NET.Utility;
namespace matlabCons
{
class Program
{
[DllImport("matlabDll.dll", CharSet = CharSet.Ansi)]
public static extern double callMatlab(string fname,string path);
[DllImport("libFRS.dll", CharSet = CharSet.Ansi)]
public static extern bool libFRSInitialize();
[DllImport("libFDS.dll", CharSet = CharSet.Ansi)]
public static extern bool libFDSInitialize();
[DllImport("libFRS.dll", CharSet = CharSet.Ansi)]
public static extern bool libFRSTerminate();
[DllImport("libFDS.dll", CharSet = CharSet.Ansi)]
public static extern bool libFDSTerminate();
static void Main(string[] args)
{
libFDSInitialize();
libFRSInitialize();
string fname = ".\\210103196303274887\\sy_20140728112957_210103196303274887_-859566652.jpg";
string path = ".\\210103196303274887\\";
int aa = System.Environment.TickCount;
double ret = callMatlab(fname,path);
Console.WriteLine(System.Environment.TickCount - aa); //单位毫秒
Console.WriteLine(ret);
libFRSTerminate();
libFDSTerminate();
}
}
}
debug下的文件如下图:
最后两个文件,为C++生成的。
BTW:extern “C”的意义:
这就是连接规范的概念,由于C++的编译器在编译的时候会用到一种Name-Mangling技术,将函数进行重命名
机制。然而C++标准并没有规定一种统一的重命名方案,因此不同的编译器对重载函数可能产生不同风格的内部
标识符,这就是不同厂商的C++编译器和连接器不能兼容的一个主要原因。
所以我们这里用到了extern “C”,因为C的连接规范是通用连接规范。所以可以用来解决这个问题。