目录
com方式调用matlab(一)
com方式调用matlab(二)
com方式调用matlab(三)
com方式调用matlab(四)
com方式调用matlab(五)
com方式调用matlab(六)
com方式调用matlab(附:运行结果及代码)
前面已经介绍了几个辅助类的实现,比如读取xml、实现用户UI等等。那么如何使用这些辅助类呢,请看下面的类图:
负责与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 阅读(882)
评论(0) 编辑 收藏 引用 所属分类:
我的代码玩具