当前位置:首页 > 电源 > 数字电源
[导读] 基于COM组件的Matlab与C++混合编程方式因拥有独立的运行环境和两种语言的互补优势而被众多科研人员和编程人员所接受,同时也是MathWorks公司推荐使用的混编方式。但在程序设计过程中,通常会遇到两个难点问题:(1)

  基于COM组件的Matlab与C++混合编程方式因拥有独立的运行环境和两种语言的互补优势而被众多科研人员和编程人员所接受,同时也是MathWorks公司推荐使用的混编方式。但在程序设计过程中,通常会遇到两个难点问题:(1)二维数组参数的传递与输出; (2)Matlab生成图形嵌入VC工程界面中。对于第一个问题,Bruce McKinney[1]在MSDN上指出;“如果对一维数组进行操作,则SAFEARRAY函数变的简单且易操作。但是对于多维数组,同样的操作要复杂得多”,造成这一问题的根源在于Matlab与C++对多维数组元素的存储方式不同。而在本项目开发过程中回避了这个复杂的过程,转而利用Matlab强大的数组处理功能将其解决。Matlab完善的图形处理功能是其被科研人员所推崇的原因之一,但因其图形拥有独立的窗口,严重影响了整体界面美观和用户交互体验,所以Matlab图形嵌入是混合编程中不可回避的问题。对此将通过实例介绍两种图形嵌入方法并分析说明其优缺点和相关细节。
    本文示例均在Matlab R2008a和VC 6.0平台下完成,且通过调试能够正常运行。
1 Excel文件读取方法
    VC平台下的Excel文件读取方式复杂难懂,但如果使用Matlab与VC的混合编程方式编写将更加简便,而且拥有较强的可扩展性。下面代码详细讲解了该过程。
    (1)在Matlab平台下建立CExcelRead.m文件,代码如下:
    function [output,row,col] = CExcelRead(filePath)
    //filePath包含了指定文件的路径和文件名,由VC平台函数GetPathName()获取
    [typ, desc] = xlsfinfo(filePath);
    //xlsfinfo()可获取指定Excel文件中各个sheet工作区的名称,返回值存入名为desc的cell数组中
    sheet1 = cell2mat(desc(1));
    //默认打开”sheet1”(名称任意),并将其转换为字符串
    output = xlsread(filePath,sheet1);
    //返回该Excel文件中的数据,返回值存入output数组中
    [row,col] = size(output);
    //获取录入数据的行列值,返回到VC平台以供其拷贝指定内存大小的数据
    output = reshape(output,row*col,1);
    //将该二维数组转换为一维。这是第二部分二维数组处理的第一步
    保存文件后,使用deploytool工具将其制作成名为FileOpen的COM(封装MCR),注册该控件并将相应的文件拷贝到MFC的工程中,对于基础操作此处不再赘述。
    (2)在VC平台下创建名为PCA的基于对话框的MFC工程,添加一打开文件的按钮控件,ID为ID_FILE_OPEN。篇幅所限只给出部分核心代码,首先在Dlg类中添加3个私有成员变量用以保存Matlab读取的数据及行列值,即:
    double *m_originData; long   m_row, m_col;
    在响应函数中添加如下代码
    CFileDialog fileDlg(TRUE); 
    fileDlg.m_ofn.lpstrTitle="请选择你的excel数据";
    fileDlg.m_ofn.lpstrFilter="Text Files(*.xls)*.xlsAll Files(*.*)*.*";
  if(IDOK==fileDlg.DoModal() {
     CString fileName; IFileOpenclass *pfile;
    VARIANT filePath,output,rowOutput,colOutput;
                                //m文件的输入参数
    fileName = fileDlg.GetPathName();
                          //获取文件路径并存于fileName中
    CoInitialize(NULL);                                //COM初始化
    HRESULT hr = CoCreateInstance(CLSID_FileOpenclass,NULL,CLSCTX_ALL,IID_IFileOpenclass,(void**)&pfile);
                                    //新建COM实例
    VariantInit(&filePath);              //VARIANT数据初始化
    
    filePath.vt = VT_BSTR;           //指定filePath变量类型
    //将fileName中保存的指定文件路径名保存至filePath参数中
    filePath.bstrVal = fileName.AllocSysString();
    pfile->CExcelRead(3,&output,&rowOutput, &colOutput,filePath);
    m_row=(long) rowOutput.dblVal;       
                                 //取出行列值及录入数据
    m_col = (long) colOutput.dblVal;
     m_originData = (double *)malloc(sizeof(double)*m_row*m_
col); memcpy(m_originData,output.parray->pvData,m_row*m_
col*sizeof(double));
        //将matlab读入数据保存到m_originData供后面程序使用
    } 上述C++代码中省略了异常处理和相关的内存、COM释放代码,由于代码比较简单所以不做进一步解析。请注意,下面将省略COM初始化及实例化等相同代码。
    (3) 需要重点说明的是该方法的扩展性。通常Excel文件中保存多个工作区,有时用户可能需要打开同一Excel文件中不同工作区中的数据,常规方法实现过于复杂,但对于本文介绍的方法可以通过修改添加几条语句即可实现。首先,新建一个m文件用来处理工作区的选择,代码如下:
    function [sheet,col] = CSheetSelected(filePath)
    [typ, sheet] = xlsfinfo(filePath);
    [row,col] = size(sheet);
    由于返回值是一个cell数组,所以VC平台要使用CStringArray数据结构保存返回值,并显示各工作区名称供用户选择。然后,通过人机交互将用户选择的工作区参数保存并传递至CExcelRead.m,通过在CExcelRead.m增加一个工作区选择参数,并对代码稍作修改即可。
2 二维数组参数的传递与输出[2]
    下面以主成分分析为例介绍基于COM的Matlab与VC混合编程中二维数组参数处理。
    (1) 主成分分析pcamat.m代码如下:
    function [eigenvector,eigenvalue] = pcamat(oriData,row,column)
    //在Excel读入时已经完成了二维数组输出的关键步骤,即输出时将二维数组转换为一维数组。但在VC平台接收还原为二维时要注意,Matlab数组存储方式是按列存储,而VC平台下数组是按行存储,所以转换时0~row-1为第一列,row~2*row-1为第二列,以此类推。本文输入参数oriData是一维数组,所以要将其还原为二维数组使Matlab程序能够正常运行,即下一行代码所示。
    oriData = reshape(oriData,row,column);
    dataSTD=std(oriData,0,1); dataMean = mean(oriData);
    dataSR = (oriData-dataMean(ones(row,1),:))./dataSTD(ones(row,1),:);
    [eigenvector,newdata,eigenvalue,Exa]=princomp(dataSR);
    //第三行至此处均为主成分分析内容
    eigenvector = reshape(eigenvector,column*column,1);
    //与Excel文件读取时类似,将二维输出转换为一维数组进行输出
    (2) 保存后,封装打包为COM组件,并完成注册等相关操作。在PCA工程对话框上添加一个名为PCATest的按钮控件,核心代码如下:
    VARIANT oriData,row,column,eigVector,eigValue;
    VariantInit(&oriData);                             //参数初始化
    
    oriData.vt = VT_R8|VT_ARRAY;
                           //定义SAFEARRAY类型的一维数组
    SAFEARRAYBOUND rgsadound[1];
    rgsadound[0].lLbound = 0;
    rgsadound[0].cElements = m_row*m_col;
    oriData.parray = SafeArrayCreate(VT_R8,1,rgsadound);    
    oriData.parray->pvData = m_originData;
    //完成相关设置后,将第一步读入的数据录入到oriData中,即赋给pcamat的oriData。到此完成了二维数组的传递
    row.vt = VT_I4; col.vt = VT_I4;
    row.lVal = m_row; col.lVal = m_col;
    pca->pcamat(2,&eigVector,&eigValue,oriData,row,col);
    memcpy(result, eigVector.parray->pvData,m_col*m_col*
sizeof(double));
    综上,二维数组参数处理就是使用reshape()函数对输入输出数据维数进行变换来完成操作。
3 Matlab图像嵌入VC界面[3]
3.1 基于CWnd类的图像嵌入

    在Windows操作系统下,所有应用程序的窗口都是基于MFC中的CWnd类。所以可以通过调用该类或其派生类中的方法实现图像嵌入。基本思想:在Matlab平台下用COM封装产生图形窗口的程序;在VC平台获取Figure窗口的句柄,将Figure窗口设为VC程序的子窗口;(3)将Figure窗口移动到指定显示位置。
    程序实现如下:
    (1)将原pcamat.m进行修改,添加生成图像的相关代码
    function [eigenvector,eigenvalue] = pcamat(oriData,row,column,picName)
    //增加picName参数,VC平台下hFig将通过该名称获取生成图像的句柄
    figure('NumberTitle','Off','MenuBar','None','ToolBar','Figure','Name',picName,'Units','Points');
    //图像参数预设,保留工具栏。使用Matlab提供的工具栏的所有功能是使用该方法的最大优点
    percent = 100*eigenvalue /sum(eigenvalue);
                                         //计算贡献率
    pareto(percent);                                              //画图
    xlabel('主成分');
    ylabel('方差占的比重(%)');
    (2)封装打包成名为figure的COM组件,并完成注册等相关操作。因为修改后的m文件运行结果包含输出结果和图像两部分,所以下面有关图像处理的代码依然在PCATest控件的响应函数中。
    CString WNDName = "Demo";         //自定义窗口名称
    Ifigureclass *pic;
    VARIANT oriData, row, col, picName,eigVector, eigValue;    VariantInit(&picName);             
    ……    
    picName.vt = VT_BSTR;         //将自定义窗口名称赋予
                                                  Matlab生成图像
    picName.bstrVal = WNDName.AllocSysString();
    HWND hFig;  int timer = 50;
    //用死循环确保可以获取到图像句柄,注意此处必须使用sleep(),给予系统足够的响应时间
    while(1){
        pic->pcamat(2,&eigVector,&eigValue,oriData,row,col,
            picName);   
            Sleep(timer);
        hFig = ::FindWindow(NULL,FigName);
          if(hFig != NULL){
            break;
        }
        timer += 10;
        pic->Release();
        }               
    long lStyle = ::GetWindowLong(hFig,GWL_STYLE);  //设置Figure窗口样式。
    //注意SetWindowLong()和SetWindowPos()先后顺序,详见MSDN
    ::SetWindowLong(hFig,GWL_STYLE,lStyle&(~WS_CAPTION)&(~WS_THICKFRAME))
    ::SetWindowPos(hFig,NULL,0,0,0,0,SWP_NOMOVE|SWP_
        NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_
        FRAMECHANGED);CRect PlotRec;
    CWnd *PlotArea = GetDlgItem(IDC_STATIC_FIGURE);                                       //设置图像显示区域
    PlotArea->GetWindowRect(&PlotRec);
    long Width = PlotRec.right - PlotRec.left;
    long Height = PlotRec.bottom - PlotRec.top;
    ::SetParent(hFig,PlotArea->GetSafeHwnd());
                                    //设置图像的父窗口
    ::SetWindowPos(hFig,NULL,0,0,Width,Height,SWP_NOZORDER|SWP_NOACTIVATE);
    运行结果如图1所示。该方法的缺点是,在图像生成时会有闪烁现象。而优点是前面提到的可以继续使用Matlab提供的工具栏。鉴于该缺点影响整体美观,所以引入下面第二种方法。

3.2基于Bitmap类的图像嵌入
    通过Bitmap类将Matlab生成的.bmp文件加载到VC工程中,使用Bitmap类中的成员函数对图像进行处理。由于相关函数可以通过帮助手册获取,所以此处不再给出相应代码,运行结果如图2所示。该方法避免了方法一生成图像时的闪烁现象,但是BMP图像显示效果较差而且无法使用Matlab提供的工具菜单,这是其不足之处。

    使用COM组件进行混合编程时,往往习惯于在VC平台下思考所遇到的问题,但是这样不仅使得问题可能变得复杂化,或得不到妥善解决,而且也违背了“混合”的初衷。二维数组参数处理就是一个很好的例证。其次,充分利用Matlab特性可以使得程序具备良好的扩展性和稳定性,对Excel文件读取方式进行的扩展,明显使软件更加人性化。对于图像嵌入问题,虽然文中提出的两种嵌入方式可以满足基本需求,但是仍然存在一些瑕疵,还需要进一步研究。
参考文献
[1] MCKINNEY B. Article 5. The Safe OLE Way of Handling  Arrays.MSDN, 2001.
[2] 刘维.精通Matlab与C++混合程序设计(第2版)[M].北京:北京航空航天大学出版社,2008.
[3] 董维国.深入浅出Matlab 7.X混合编程[M].北京:机械工业出版社,2006.

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

北京2025年9月10日 /美通社/ -- 以"智领工业 全球互联"为主题的2025全球工业互联网融合发展交流大会于9月6日在中国东北辽宁省沈阳市举行。 图为2025全球工业互联网融合发展交流大会9月6日于辽宁沈阳举办...

关键字: 工业互联网 数字化 COM HTML

柏林2025年9月9日 /美通社/ -- 2025年9月5日,纳斯达克上市公司优克联集团(NASDAQ: UCL)旗下全球互联品牌GlocalMe,正式亮相柏林国际消费电子展(IFA 2025),重磅推出融合企...

关键字: LOCAL LM BSP 移动网络

深圳2025年9月9日 /美通社/ -- PART 01活动背景 当技术的锋芒刺穿行业壁垒,万物互联的生态正重塑产业疆域。2025年,物联网产业迈入 "破界创造"与"共生进化" 的裂变时代——AI大模型消融感知边界,...

关键字: BSP 模型 微信 AIOT

"出海无界 商机无限"助力企业构建全球竞争力 深圳2025年9月9日 /美通社/ -- 2025年8月28日, 由领先商业管理媒体世界经理人携手环球资源联合主办、深圳•前海出海e站通协办的...

关键字: 解码 供应链 AI BSP

柏林2025年9月9日 /美通社/ -- 柏林当地时间9月6日,在2025德国柏林国际电子消费品展览会(International Funkausstellung...

关键字: 扫地机器人 耳机 PEN BSP

柏林2025年9月9日 /美通社/ -- 总部位于迪拜的生活方式科技品牌ASTRAUX强势亮相2025年柏林国际电子消费品展览会(IFA),首次推出的三款原创产品引发广泛关注,成功将品牌推向绿色出行与智能生活领域的舆论焦...

关键字: ST COM AI GLOBAL

武汉2025年9月9日 /美通社/ -- 7月24日,2025慧聪跨业品牌巡展——湖北•武汉站在武汉中南花园酒店隆重举办!本次巡展由慧聪安防网、慧聪物联网、慧聪音响灯光网、慧聪LED屏网、慧聪教育网联合主办,吸引了安防、...

关键字: AI 希捷 BSP 平板

上海2025年9月9日 /美通社/ -- 9月8日,移远通信宣布,其自研蓝牙协议栈DynaBlue率先通过蓝牙技术联盟(SIG)BQB 6.1标准认证。作为移远深耕短距离通信...

关键字: 蓝牙协议栈 移远通信 COM BSP

上海2025年9月9日 /美通社/ -- 为全面落实党中央、国务院和上海市委、市政府关于加快发展人力资源服务业的决策部署,更好发挥人力资源服务业赋能百业作用,8月29日,以"AI智领 HR智链 静候你来&quo...

关键字: 智能体 AI BSP 人工智能

北京2025年9月8日 /美通社/ -- 近日,易生支付与一汽出行达成合作,为其自主研发的"旗驭车管"车辆运营管理平台提供全流程支付通道及技术支持。此次合作不仅提升了平台对百余家企业客户的运营管理效率...

关键字: 一汽 智能化 BSP SAAS
关闭