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

时间:2011-10-25 16:08:45

标签: 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
%]

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

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

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

我的问题

我想使用一些foocancel回调来调查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
%]

但是我当然希望这些回调在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,我不知道?

  3. 进一步调查

    日期+ 1

    即使仍然不稳定,我也成功地使用matlab回调我的C#代码,似乎mclCreateSimpleFunctionHandle是方向。

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

    1. 期待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);
      

      // 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*句柄,然后我就像这样封送我的代表:

      // 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委托:

      // 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代理。

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

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

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

1 个答案:

答案 0 :(得分:1)

明白了

mclCreateSimpleFunctionHandle实际上是正确的API函数调用,以便创建一个数组变量(在matlab的一侧)持有一个函数指针(在外部)。我现在能够编译m代码以回调我的C#代码以进行取消和升级。

mclCreateSimpleFunctionHandle

描述了{{1}}的正确编组