Cit*_*ane 5 matlab matlab-deployment matlab-compiler
我的问题非常特定于matlab编译器和运行时的神秘面纱.由于只有熟悉matlab运行时API的人才能回答,所以我缩短了很多细节.如果我应该更加冗长,请告诉我.
使用matlab编译器和运行时,我可以调用一个用C#程序的m代码编写的函数.我们说要打电话:
function [result] = foo(n)
%[
result = 0;
for k = 1:n,
pause(1.0); % simulate long processing
result = result + 42;
end
%]
Run Code Online (Sandbox Code Playgroud)
with(在C#代码中的一些dllimports后面的某个地方):
mclFeval(IntPtr inst, string name, IntPtr[] plhs, IntPtr[] prhs)
Run Code Online (Sandbox Code Playgroud)
到目前为止,非常好,我对此没有任何问题(即初始化运行时,加载'.cft'文件,使用.Net类型来回编组MxArray等等)
我想foo
用一些cancel
和progress
回调来调查我的函数的进展:
function [result] = foo(n, cancelCB, progressCB)
%[
if (nargin < 3), progressCB = @(ratio, msg) disp(sprintf('Ratio = %f, Msg = %s', ratio, msg)); end
if (nargin < 2), cancelCB = @() disp('Checking cancel...'); end
result = 0;
for k = 1:n,
if (~isempty(cancelCB)),
cancelCB(); % Up to the callback to raise some error('cancel');
end;
if (~isempty(progressCB)),
progressCB(k/n, sprintf('Processing (%i/%i)', k, n));
end
pause(1.0); % simulate long processing
result = result + 42;
end
%]
Run Code Online (Sandbox Code Playgroud)
但是我当然希望这些回调在C#代码中,而不是在m-one中.
查看'mclmcr.h'头文件,看起来这些函数可能有所帮助:
extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn);
extern bool mclRegisterExternalFunction(HMCRINSTANCE inst, const char* varname, mxFunctionPtr fcn);
Run Code Online (Sandbox Code Playgroud)
不幸的是,这些都是完全无证的,我发现没有用于模仿它们如何工作的用例.
我还考虑过在C#中创建一个COM可见对象,并将其作为参数传递给matlab代码:
// Somewhere within C# code:
var survey = new ComSurvey();
survey.SetCancelCallback = () => { if (/**/) throw new OperationCancelException(); };
survey.SetProgressCallback = (ratio, msg) => { /* do something */ };
Run Code Online (Sandbox Code Playgroud)
function [result] = foo(n, survey)
%[
if (nargin < 2), survey = []; end
result = 0;
for k = 1:n,
if (~isempty(survey)),
survey.CheckCancel(); % up to the COM object to raise exception
survey.SetProgress(k/n, sprintf('Processing... %i/%i', k, n));
end
pause(1.0); % simulate long processing
result = result + 42;
end
%]
Run Code Online (Sandbox Code Playgroud)
我非常熟悉创建数值和结构数组的函数,并知道如何使用它们:
extern mxArray *mxCreateNumericArray(...)
extern mxArray *mxCreateStructArray(...)
Run Code Online (Sandbox Code Playgroud)
无论如何,COM对象如何打包到MxArrays,我不知道?
天+ 1
即使仍然不稳定,我成功地让matlab回调到我的C#代码中,这似乎mclCreateSimpleFunctionHandle
是方向.
注意:以下代码仅供参考.它可能不适合您自己的上下文.我稍后会提供更简单的代码(即一旦我得到稳定的解决方案).
期待签名mxFunctionPtr
,我创建了两个这样的代表:
// Mimic low level signature for a Matlab function pointer
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
delegate void MCRInteropDelegate(int nlhs, IntPtr[] plhs, int nrhs, IntPtr[] prhs);
Run Code Online (Sandbox Code Playgroud)
和
// Same signature (but far more elegant from .NET perspective)
delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins);
Run Code Online (Sandbox Code Playgroud)我也像这样链接到运行时:
[DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn);
Run Code Online (Sandbox Code Playgroud)假设MxArray
我的.NET类只是封装了mxArray*
句柄,那么我就像这样封送我的代理:
// Create MxArray from corresponding .NET delegate
static MxArray CreateFromDelegate(MCRDelegate del)
{
// Package high level delegate signature to a 'dllimport' signature
MCRInteropDelegate interopDel = (nlhs, plhs, nrhs, prhs) =>
{
int k = 0;
var varargouts = new MxArray[nlhs];
var varargins = new MxArray[nrhs];
// (nrhs, prhs) => MxArray[] varargins
Array.ForEach(varargins, x => new MxArray(prhs[k++], false)); // false = is to indicate that MxArray must not be disposed on .NET side
// Call delegate
del(varargouts, varargins); // Todo: varargouts created by the delegate must be destroyed by matlab, not by .NET !!
// MxArray[] varargouts => (nlhs, plhs)
k = 0;
Array.ForEach(plhs, x => varargouts[k++].getPointer());
};
// Create the 1x1 array of 'function pointer' type
return new MxArray(MCRInterop.mclCreateSimpleFunctionHandle(interopDel));
}
Run Code Online (Sandbox Code Playgroud)最后,假设module
是一个实例MCRModule
(同样,我的一类封装hInst*
在低级mclFeval
API中),我能够调用foo
函数并让它进入我的.NET cancel
委托,如下所示:
// Create cancel callback in .NET
MCRDelegate cancel = (varargouts, varargins) =>
{
if ((varargouts != null) && (varargouts.Length != 0) { throw new ArgumentException("'cancel' callback called with too many output arguments"); }
if ((varargins != null) && (varargins.Length != 0) { throw new ArgumentException("'cancel' callback called with too many input arguments"); }
if (...mustCancel...) { throw new OperationCanceledException(); }
}
// Enter the m-code
// NB: Below function automatically converts its parameters to MxArray
// and then call low level mclFeval with correct 'mxArray*' handles
module.Evaluate("foo", (double)10, cancel);
Run Code Online (Sandbox Code Playgroud)
这个.NET代码工作正常,并且foo
确实cancel
正确地回调了委托.
唯一的问题是它很不稳定.我的猜测是我使用了太多匿名函数,可能其中一些函数处理得太早......
将在接下来的几天内尝试提供稳定的解决方案(希望使用更简单的代码在您自己的上下文中进行读取和复制粘贴以便立即进行测试).
如果您认为我的方向错误,请告诉我mclCreateSimpleFunctionHandle
.