随笔 - 181  文章 - 15  trackbacks - 0
<2006年8月>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿(1)

随笔分类

随笔档案

My Tech blog

搜索

  •  

最新评论

阅读排行榜

评论排行榜

         目录
        com方式调用matlab(一)
        com方式调用matlab(二) 
        com方式调用matlab(三)
        com方式调用matlab(四) 
        com方式调用matlab(五)
        com方式调用matlab(六)
        com方式调用matlab(附:运行结果及代码)
        前面已经介绍了几个辅助类的实现,比如读取xml、实现用户UI等等。那么如何使用这些辅助类呢,请看下面的类图:
        matlabpic1.JPG

        负责与Matlab组件进行交互的类CMatlabGraph以及负责视图显示的CParamView都与配置类CGraphConfiguration相关联。而CGraphConfiguration又通过CMyXml与配置文件打交道。另外CparamView与CEditCtrlFactory相关联,从而把配置信息显示出来。
        以下是CGraphConfiguration的类定义:
       

class  CGraphConfiguration  
{
public :
    list
< CString >  m_sGraphNames; // 图像名称列表
    CString m_sError; // 错误信息
    CGraphConfiguration(CString sFileName); // 配置初始化
     virtual   ~ CGraphConfiguration();
    
bool  FindGraphInfoByName(CMatlabGraph *  pGraph,CString sName); // 根据图形名称得到图形信息
     bool  PutConfigurationToView(CParamView *  pView,CString sName); // 根据图形名称对控件列表初始
     bool  SetGraphParaValue(CParamView *  pView,CString sName); // 设置绘图值
private :
    CString m_sFileName;
// 文件名称
};

        其中FindGraphInfoByName是用来被CMatlabGraph类的实例调用的。通过此方法,CMatlabGraph的实例取得了名如sNamr的图形的配置信息。
        PutConfigurationToView用来将名字为sName的绘图配置信息提供给CParamView的实例,后者在界面上绘制图形名称、参数等信息。
        SetGraphParaValue接受CParamView类对于配置信息的修改,并保存到配置文件中。
        以下为方法FindGraphInfoByName的实现:
bool CGraphConfiguration::FindGraphInfoByName(CMatlabGraph* pGraph,CString sName)
{
    
try
    {
        
//读取配置文件
        CMyXml xml;
        xml.LoadXmlFromFile(m_sFileName.GetBuffer(m_sFileName.GetLength()));
        xml.GetMatchedRootElementList(
"Components");
        xml.MoveCurrentTo(
0);
        xml.GetChildNodes();
        
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
        {
            
if(!xml.MoveCurrentTo(iNode))
                
return false;
            CString sNodeName(_com_util::ConvertBSTRToString(xml.GetAttrbuteValue(
"Name")));
            
//如果找到与当前名称相同的文件则读取所有配置内容
            if(sNodeName==sName)
            {
                
//取组件文件的名称
                CString sComFileName(_com_util::ConvertBSTRToString(xml.GetAttrbuteValue("physicalName")));
                
//取GUID
                xml.GetMatchedSubElementList("GUID");
                xml.MoveCurrentTo(
0);
                CString sGUID(_com_util::ConvertBSTRToString(xml.GetCurrentNodeValue()));
                
//取RIID
                xml.GetMatchedParentElementList("RIID");
                xml.MoveCurrentTo(
0);
                CString sRIID(_com_util::ConvertBSTRToString(xml.GetCurrentNodeValue()));
                
//取方法名称
                xml.GetMatchedParentElementList("Method");
                xml.MoveCurrentTo(
0);
                xml.GetMatchedSubElementList(
"MethodName");
                xml.MoveCurrentTo(
0);
                CString sMethod(_com_util::ConvertBSTRToString(xml.GetCurrentNodeValue()));
                
//将值传递给CMatlabGraph对象
                USES_CONVERSION;
                
//赋组件文件名称
                pGraph->m_sComFileName=sComFileName;
                
//赋GUID
                if(!SUCCEEDED(CLSIDFromString(T2OLE((LPCTSTR)sGUID.GetBuffer(sGUID.GetLength())),&(pGraph->m_clsid))))
                {
                    
this->m_sError="取GUID失败!请检查配置文件";
                    
return false;
                }
                
//赋RIID
                if(!SUCCEEDED(CLSIDFromString(T2OLE((LPCTSTR)sRIID.GetBuffer(sRIID.GetLength())),&(pGraph->m_riid))))
                {
                    
this->m_sError="取RIID失败!请检查配置文件";
                    
return false;
                }
                
//赋方法名称
                pGraph->m_sMethodName=sMethod;
                
//取方法参数值并赋给图形对象
                
//return true;
                if(!xml.GetMatchedParentElementList("Param"))
                    
return true;
                xml.MoveCurrentTo(
0);
                
long lParamCount=xml.GetCurrentListLength();
                
//对图形对象的参数列表进行初始化
                
//因为有可能存在没有参数的方法,所以这里在没有取到任何参数的时候返回true
                if(0==lParamCount)
                    
return true;
                
//设置接口方法参数个数
                pGraph->m_nparacount=lParamCount;
                pGraph
->m_pvars=new CComVariant[lParamCount];
                
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
                {
                    
if(!xml.MoveCurrentTo(iNode))
                        
return false;
                    
//取得参数值的字符串形式
                    CString sValue(_com_util::ConvertBSTRToString(xml.GetCurrentNodeValue()));
                    
double fValue;
                    
//转换为浮点数
                    sscanf(sValue,"%lf",&fValue);
                    
//转换为VARIANT形式
                    
//pGraph->m_pvars[iNode].vt=
                    pGraph->m_pvars[iNode]=CComVariant(fValue);

                }
                
return true;
            }
        }
        
this->m_sError="配置信息未找到";
        
return false;
    }
    
catch(CException *e)
    {
        
throw e;
    }
}

        以下为方法PutConfigurationToView的实现
bool CGraphConfiguration::PutConfigurationToView(CParamView *pView,CString sName)//把xml的内容从界面显示出来
{
    
if(!pView)
    {
        
this->m_sError="视图对象为空!";
        
return false;
    }
    pView
->m_sGraphName=sName;
    
try
    {
        
//读取配置文件
        CMyXml xml;
        xml.LoadXmlFromFile(m_sFileName.GetBuffer(m_sFileName.GetLength()));
        xml.GetMatchedRootElementList(
"Components");
        xml.MoveCurrentTo(
0);
        xml.GetChildNodes();
        
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
        {
            
if(!xml.MoveCurrentTo(iNode))
                
return false;
            CString sNodeName(_com_util::ConvertBSTRToString(xml.GetAttrbuteValue(
"Name")));
            
//如果找到与当前名称相同的文件则读取所有配置内容
            if(sNodeName==sName)
            {
                xml.GetMatchedSubElementList(
"Method");
                xml.MoveCurrentTo(
0);
                xml.GetMatchedSubElementList(
"Param");
                
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
                {
                    
if(!xml.MoveCurrentTo(iNode))
                        
return false;
                    
//取得参数值的字符串形式
                    CString sStatic(_com_util::ConvertBSTRToString(xml.GetAttrbuteValue("Name")));
                    
//sStatic.Format("参数%d",iNode+1);
                    CString sValue(_com_util::ConvertBSTRToString(xml.GetCurrentNodeValue()));
                    CString sStaticText;
                    sStaticText.Format(
"StaticParam%d",iNode);
                    CString sEditText;
                    sEditText.Format(
"EditText%d",iNode);
                    pView
->m_editFactory->createCtrl(pView->m_pCWnd,"CStatic",sStaticText,CRect(30+120*iNode,50,70+120*iNode,70));//动态显示参数名称
                    pView->m_editFactory->createCtrl(pView->m_pCWnd,"CEdit",sEditText,CRect(80+120*iNode,50,140+120*iNode,70));//动态显示参数
                    CWnd *pWnd=(CWnd*)pView->m_editFactory->getCtrl(sStaticText);
                    pWnd
->SetWindowText(sStatic);//把参数名称显示出来
                    pWnd=(CWnd*)pView->m_editFactory->getCtrl(sEditText);
                    pWnd
->SetWindowText(sValue);//把参数值显示出来
                }        
                
return true;
            }
        }
        
return true;
    }
    
catch(CException *e)
    {
        
//throw e;
        return false;
    }

    

}

 

        以下为SetGraphParaValue的实现

bool CGraphConfiguration::SetGraphParaValue(CParamView* pView,CString sName)
{
    
if(!pView)
    {
        
this->m_sError="视图对象为空!";
        
return false;
    }
    pView
->m_sGraphName=sName;
    
try
    {
        
//读取配置文件
        CMyXml xml;
        xml.LoadXmlFromFile(m_sFileName.GetBuffer(m_sFileName.GetLength()));
        xml.GetMatchedRootElementList(
"Components");
        xml.MoveCurrentTo(
0);
        xml.GetChildNodes();
        
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
        {
            
if(!xml.MoveCurrentTo(iNode))
                
return false;
            CString sNodeName(_com_util::ConvertBSTRToString(xml.GetAttrbuteValue(
"Name")));
            
//如果找到与当前名称相同的文件则读取所有配置内容
            if(sNodeName==sName)
            {
                xml.GetMatchedSubElementList(
"Method");
                xml.MoveCurrentTo(
0);
                xml.GetMatchedSubElementList(
"Param");
                
for(int iNode=0;iNode<xml.GetCurrentListLength();iNode++)
                {
                    
if(!xml.MoveCurrentTo(iNode))
                        
return false;
                    CString sValue;
                    CString sEditText;
                    sEditText.Format(
"EditText%d",iNode);
                    CWnd 
*pWnd;
                    pWnd
=(CWnd*)pView->m_editFactory->getCtrl(sEditText);
                    pWnd
->GetWindowText(sValue);
                    
/*if(sValue==""||sValue==NULL)
                    {
                        sValue="0.0";
                    }
*/
                    xml.SetCurrentNodeValue(sValue.GetBuffer(sValue.GetLength()));
                }
                
return xml.Save();
            }
        }
        
return true;
    }
    
catch(CException *e)
    {
        
return false;
    }

}


        因为我们需要让CGraphConfiguration能够访问到CMatlabGraph的成员,而这些成员又不希望被其他的类访问到,所以这里把CGraphConfiguration定义为CMatlabGraph的友元类。

        下面是CMatlabGraph的类定义:

class CMatlabGraph  
{
public:
    CMatlabGraph(CGraphConfiguration 
*cfg);
    
virtual ~CMatlabGraph();
    
bool DrawGraph(CString sGraphName);//绘图
    CString m_sError;//错误信息
private:
    CLSID m_clsid;
//组件对象ID
    IID m_riid;//接口ID
    CString m_sMethodName;//接口方法名称
    CString m_sComFileName;//组件文件名称
    CComVariant *m_pvars;//接口方法参数
    CGraphConfiguration *m_pcfg;//配置
    int m_nparacount;//接口方法参数个数
    friend class CGraphConfiguration;
    HINSTANCE m_hinstLib;
};

        其中,图形绘制的工作要通过DrawGraph来进行。
        具体实现为:
bool CMatlabGraph::DrawGraph(CString sGraphName)//绘图
{
    
if(!this->m_pcfg)
    {
        
this->m_sError="获取配置文件失败!";
        
return false;
    }
    
if(!m_pcfg->FindGraphInfoByName(this,sGraphName))
    {
        
this->m_sError=m_pcfg->m_sError;
        
return false;
    }
    ::CoInitialize(NULL);
    
//分发接口,用于执行不知道名字的接口方法
    IDispatch *pIDisp=NULL;
    
//指向读入的组件文件的句柄
    HINSTANCE hinstLib;
    
//指向类厂的实例化方法
    MYPROC ProcAdd;
    BOOL fRunTimeLinkSuccess 
= FALSE;
    
//int rtn=0;
    
//读取组件文件载入内存
    hinstLib = LoadLibrary(this->m_sComFileName.GetBuffer(m_sComFileName.GetLength()));
    
//如果执行成功,则hinstLib非空
    if (hinstLib != NULL) 
    {
        
//读取指向获取类厂的函数的指针
        ProcAdd =(MYPROC)GetProcAddress(hinstLib, "DllGetClassObject");
        
// 如果获取成功,则创建类厂
        if (fRunTimeLinkSuccess = (ProcAdd != NULL))
        {
            
//类厂接口
            IClassFactory *pIf;
            
//初始类厂接口为空
            pIf=NULL;
            
if(SUCCEEDED(ProcAdd(m_clsid,IID_IClassFactory,(void **)&pIf))&&(pIf!=NULL))
            {
                
if(!SUCCEEDED(pIf->CreateInstance(NULL,m_riid,(void **)&pIDisp))||(pIDisp==NULL))
                {
                        pIf
->Release();
                        pIf
=NULL;
                        FreeLibrary(hinstLib);
                        ::CoUninitialize();
                        
return false;
                }
                m_hinstLib
=hinstLib;
                
//因为已经取得分发接口,故释放类厂接口
                pIf->Release();
                pIf
=NULL;
                
//根据名称查找接口方法
                USES_CONVERSION;
                OLECHAR FAR
* szMember = T2OLE((LPCTSTR)(this->m_sMethodName.GetBuffer(this->m_sMethodName.GetLength())));
                
//获取方法ID
                DISPID MethodID;
                
if(!SUCCEEDED(pIDisp->GetIDsOfNames(IID_NULL,&szMember,1,LOCALE_SYSTEM_DEFAULT,&MethodID)))
                {
                    
this->m_sError="取接口方法ID失败";
                    
return false;
                }
                
//填入参数
                DISPPARAMS dispparams = { this->m_pvars, NULL, this->m_nparacount, 0};
                
//调用方法
                HRESULT hr=pIDisp->Invoke(MethodID,IID_NULL,GetUserDefaultLCID(),DISPATCH_METHOD,&dispparams,NULL,NULL,NULL);
                
if(!SUCCEEDED(hr))
                {
                    
this->m_sError.Format("调用失败!错误码:%x",hr); //="调用失败!";
                    FreeLibrary(hinstLib);
                    ::CoUninitialize();
                    
return false;
                }
                
//释放文件
                
//FreeLibrary(hinstLib);
                
//::CoUninitialize();
                return true;    
            }
        }
    }
    ::CoUninitialize();
    
return false;
}
        这里的MYPROC是一个函数指针。当我们想要从一个dll库里面调用一个函数的时候,首先需要定义一个与之匹配的函数指针。这里我们想要调用DLLGetClassObject这个函数以获取类厂。所以预先定义一个与Dll中定义相同的函数指针:
       
typedef int (CALLBACK *MYPROC)(REFCLSID,REFIID,LPVOID *);
        在调用任何Dll库中函数的时候,都需要首先通过LoadLibrary这个函数将Dll读入内存并获取句柄。该函数的参数很简单,就是你要调用的那个Dll的文件名称。
       
hinstLib = LoadLibrary(this->m_sComFileName.GetBuffer(m_sComFileName.GetLength()));

        之后,用函数GetProcAddress来取得Dll中的函数,并用预先定义好的函数指针指向它。GetProcAddress的参数有两个,一个是Dll在内存中的句柄,另外一个是你要调用的函数名称。
ProcAdd =(MYPROC)GetProcAddress(hinstLib, "DllGetClassObject");
        用DllGetClassObject来获取类厂。我们的最终目的是获取IDispatch,以此来通过组件所实现的接口方法名称来呼叫方法,所以首先需要创建类厂,并用类厂的CreateInstance来获取IDispatch。
if(SUCCEEDED(ProcAdd(m_clsid,IID_IClassFactory,(void **)&pIf))&&(pIf!=NULL))
            {
                
if(!SUCCEEDED(pIf->CreateInstance(NULL,m_riid,(void **)&pIDisp))||(pIDisp==NULL))
                {
}
}
        因为Matlab组件的接口对IDispatch进行了继承,所以这里直接使用m_riid来获取IDispatch接口。
        然后使用IDispatch的GetIDsOfNames函数来获得我们想要调用的那个方法。该方法以一个长整型MethodID来表示。
        最后,把MethodID传递给IDispatch的Invoke方法,这样就调用了Matlab组件中的绘图方法。
       
HRESULT hr=pIDisp->Invoke(MethodID,IID_NULL,GetUserDefaultLCID(),DISPATCH_METHOD,&dispparams,NULL,NULL,NULL);

        另外,该方法的参数被放到DISPPARAMS数组中:
       

DISPPARAMS dispparams = { this->m_pvars, NULL, this->m_nparacount, 0};

         以上是绘图操作的具体实现,接下来看一下CParamView的代码:
        下面是CParamView的类定义:

class CParamView
{
public:
    
//控件工厂
    CEditCtrlFactory *m_editFactory;
    
//绘图名称
    CString m_sGraphName;
    
//窗口指针
    CWnd *m_pCWnd;
    CParamView(CGraphConfiguration 
*cfg,CWnd *pCWnd);
    
virtual ~CParamView();
    
//视图初始化
    bool InitView(CString sName);
    
bool SetParamValue(CString sName);
private:
    CGraphConfiguration 
*m_pcfg;//配置
};

        其中InitView的作用是:当用户变换所选图像的时候,对图像参数区进行刷新。以下为实现:
       
bool CParamView::InitView(CString sName)
{
    delete 
this->m_editFactory;
    
this->m_editFactory=new CEditCtrlFactory();
    
this->m_editFactory->createCtrl(this->m_pCWnd,"CStatic","",CRect(0,50,1024,70));
    
return  this->m_pcfg->PutConfigurationToView(this,sName);
    
}
        当用户更改当前图像的参数时,调用SetParamValue:
bool CParamView::SetParamValue(CString sName)
{
    
return this->m_pcfg->SetGraphParaValue(this,sName);
}

        那么整个以Com方式调用Matlab的实现也就介绍完毕了。
        从上面的代码可以看出,CParamView以及CMatlabGraph与CGraphConfiguration的耦合是比较紧密的。另外在CGraphConfiguration中还存在不少duplication(副本)代码。整体设计并不好,各个层次存在一定的依赖性。当然出现这些问题,也和当时时间紧迫有一定关系。这里的初衷还是整理Matlab组件的调用方式,给没用过此方式的人提供若干思路。实在看不下去的同僚,也期待着您的批评指正。谢谢!
posted on 2006-08-28 21:41 littlegai 阅读(772) 评论(0)  编辑 收藏 引用 所属分类: 我的代码玩具

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理