Matlab:如何从外部 API 调查已编译的 m 代码进程?

标签 matlab matlab-deployment matlab-compiler

我的问题非常具体到 matlab 编译器和运行时的奥秘。因为只有熟悉 matlab runtime API 的人才会回答,所以我缩短了很多细节。如果我应该更详细一些,请告诉我。

简介

使用 matlab 编译器和运行时,我可以从 C# 程序中调用用 m 代码编写的函数。假设调用:

function [result] = foo(n)
%[
    result = 0;
    for k = 1:n,
        pause(1.0); % simulate long processing
        result = result + 42;
    end
%]

with(在 C# 代码中某些 dllimports 后面的某处):

mclFeval(IntPtr inst, string name, IntPtr[] plhs, IntPtr[] prhs)

到目前为止,非常好,我对此没有任何问题(即初始化运行时、加载“.cft”文件、使用 .Net 类型来回编码 MxArray,等等)

我的问题

我想使用一些 cancelprogress 回调来调查我的 foo 函数的进度:

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
%]

但我当然希望这些回调在 C# 代码中,而不是在 m-one 中。

调查

  1. 查看“mclmcr.h”头文件,看起来这些函数可能会有帮助:

    extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn);
    extern bool mclRegisterExternalFunction(HMCRINSTANCE inst, const char* varname, mxFunctionPtr fcn);
    

    不幸的是,这些完全没有文档记录,我找不到可以模仿的用例来理解它们的工作原理。

  2. 我还考虑过在 C# 中创建一个 COM 可见对象并将其作为参数传递给 matlab 代码:

    // Somewhere within C# code:
    var survey = new ComSurvey();
    survey.SetCancelCallback =  () => { if (/**/) throw new OperationCancelException(); };
    survey.SetProgressCallback = (ratio, msg) => { /* do something */ };
    

    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
    %]
    

    我非常熟悉创建数字和结构数组的函数并知道如何使用它们:

    extern mxArray *mxCreateNumericArray(...)
    extern mxArray *mxCreateStructArray(...)
    

    总之,COM对象是如何打包到MxArrays的,我不知道?

进一步调查

第一天

即使仍然不稳定,我也成功地让 matlab 回调到我的 C# 代码中,看来 mclCreateSimpleFunctionHandle 是前进的方向。

注意:以下代码仅供引用。它可能不适合您自己的上下文。稍后我将提供更简单的代码(即,一旦我将获得稳定的解决方案)。

  1. 查看 mxFunctionPtr 的签名,我创建了两个这样的委托(delegate):

    // 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);
    

    // Same signature (but far more elegant from .NET perspective)
    delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins);  
    
  2. 我也像这样链接到运行时:

    [DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
    static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn);
    
  3. 假设 MxArray 是我的一个 .NET 类,它简单地封装了 mxArray* 句柄,然后我像这样编码我的委托(delegate):

    // 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));
    }
    
  4. 最后,假设 moduleMCRModule 的实例(同样,我的一个类将 hInst* 封装在低级别mclFeval API),我能够调用 foo 函数并让它进入我的 .NET cancel 委托(delegate),如下所示:

    // 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);
    

    此 .NET 代码运行良好,foo 确实正确地回调了 cancel 委托(delegate)。

    唯一的问题是它非常不稳定。我的猜测是我使用了太多的匿名函数,并且可能其中一些被过早处置了......

    将在接下来的几天内尝试提供稳定的解决方案(希望使用更简单的代码在您自己的上下文中阅读和复制粘贴以便立即测试)。

    如果您认为我使用 mclCreateSimpleFunctionHandle 的方向错误,请告诉我。

最佳答案

知道了

mclCreateSimpleFunctionHandle 实际上是调用的正确 API 函数,以便创建一个数组变量(在 matlab 端)保存函数指针(在外部端)。我现在能够编译 m 代码以回调到我的 C# 代码中以用于取消和进度目的。

描述了 mclCreateSimpleFunctionHandle 的正确编码 here

关于Matlab:如何从外部 API 调查已编译的 m 代码进程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7892254/

相关文章:

matlab - 在Matlab中选择图像上的像素时,索引指的是什么?

c++ - 如何初始化和加载MCR

asp.net - MATLAB Builder NE 在 IIS 7.5 上导致应用程序池崩溃

c - Matlab 生成的 C 代码无法正常工作

windows - 带有编译应用程序的 Matlab onCleanup (windows)

oop - 单独使用方法名称解析 MATLAB 类方法句柄

matlab - 如何在 Sublime Text 3 中定义自定义关键字自动缩进行为?

matlab - MATLAB 编译器的安全性

c# - 作为 C# 安装程序向导的一部分安装 Matlab MCR

c - Matlab 在 C 中生成的 Dll