本文讨论了如何将MFC与ARX应用相结合进行AutoCAD的二次开发,并对何时选用动态链接方式,何时选用静态链接方式以及与ARX应用升级有关的问题作了详尽的论述。
关键词: MFC ARX 动态链接 静态链接
一.引言
AutoCAD是应用广泛的通用计算机辅助设计软件包。自AutoCAD 13版本开始,AutoDesk公司推出了一种新的面向对象的软件接口ARX(AutoCAD Runtime Extenstion)。该接口以VC++为开发语言,ARX库为开发工具,采用面向对象的编程方法且支持MFC编程。至此开发者们便获得了一片广阔的天空既可利用MFC,又可与ARX工具库结合来实现AutoCAD的二次开发。
Microsoft基础类库(MFC)是C++类库的扩展,它提供了大量预写类和支持码,可以处理许多标准Windows编程任务如生成窗口和处理消息。它也可以在程序中加入复杂的特性如工具条、分窗视图和OLE2支持。因此使用MFC可以简化程序编写,使开发者节省不少编码工作。ARX是自 AutoCAD R13版开始特有的面向对象C++开发环境、功能强大、运算效率高。通过在CAD应用系统的应用表明,它的许多新特性能实现许多原来开发环境难以实现的功能,提高开发效率。ARX代表着AutoCAD的发展方向,将成为新一代应用软件开发的首选工具。
自Windows推出后,AutoCAD开发者们一直都在努力寻求一种方法使其应用程序既具有Windows的友好界面又不失其功能,但总是不尽人意,ARX的推出使开发者多年的愿望成为可能。针对以上的问题,本文将对如何在ARX应用程序中使用MFC的各个方面作一详尽讨论,并给出实现MFC与 ARX应用结合开发出完美的AutoCAD应用程序的方法(本文讨论基于AutoCAD R14,ObjectARX 2.0进行)。
二.MFC在ARX应用中的常见问题
由于ARX应用既可以与动态链接的MFC库一起建立,也可与静态链接的MFC库一起建立,所以在ARX应用中我们可以充分利用MFC的优点。以下我们将分两部分对一些常见问题进行讨论。
采用动态链接还是静态链接
在一ARX应用中采用动态链接的MFC库还是静态链接的MFC库很大程度上是由应用程序性能与额外的AutoCAD开发中不同版本的协调间的比决定的。开发者应决定是动态链接所带来的性能优越更重要还是协调某一版本编译器与当前版本AutoCAD所采用的编译器的一致性更重要。以动态链接的MFC库形式建立的ARX应用与AutoCAD共享同一MFC动态链接库,因此该应用比采用静态链接的MFC库形式建立的应用更小、运行效率更高。然而它却具有:①.所用的开发编译器必须与用来构建AutoCAD的编译器版本相同 ②.最新版本的开发编译器可能无法使用 ③.在新发行的AutoCAD版本中以早期版本建立的ARX应用可能无法工作等缺点。
由于静态链接的MFC库形式建立的ARX应用把MFC库也加载到了每一静态链接的ARX应用中,故上述问题对此类ARX应用不存在但此类应用却有:①.更大的内存要求②.运行速度慢(因所需的交换空间更大)等缺点。因此开发者应权衡利弊再作取舍(本文仅讨论动态链接的情况,静态链接与动态链接极为相似,读者可参考相应的开发文档)。
MFC和无模式对话框
因AutoCAD总是试图不使系统聚焦于其子窗口,故无模式对话框应当发出一请求。在一常规间歇内,无模式对话框将收到 WM_ACAD_KEEPFOCUS窗口消息(在adscodes.h中定义为1001)。当对话框接收到此消息时,如其想保持聚焦则应返回TRUE,否则返回FALSE(缺省值),此时鼠标一离开对话框系统就不再聚焦于对话框。在对话框的消息映射中ON_MESSAGE()函数如下:
BEGIN_MESSAGE_MAP( HelloDlg, CDialog )
ON_COMMAND( IDCLOSE, OnClose )
ON_COMMAND( IDC_DRAW_CIRCLE, OnDrawCircle )
ON_MESSAGE( WM_ACAD_KEEPFOCUS, onAcadKeepFocus )
END_MESSAGE_MAP()
在这里应用对话框类为HelloDlg,它由Cdialog类派生。当将这一入口加入到消息映射中时必须为该消息写一句柄函数。假定我们已写了一函数keepFocus(),它在对话框想保持聚焦时返回TRUE,反之返回FALSE则消息句柄函数应为:ON_COMMAND( IDCLOSE, OnClose )
ON_COMMAND( IDC_DRAW_CIRCLE, OnDrawCircle )
ON_MESSAGE( WM_ACAD_KEEPFOCUS, onAcadKeepFocus )
END_MESSAGE_MAP()
afx_msg LONG HelloDlg::onAcadKeepFocus( UINT, LONG )
{
return keepTheFocus() ? TRUE : FALSE;
}
{
return keepTheFocus() ? TRUE : FALSE;
}
三.ARX应用于动态链接的MFC库
以下我们将分五部分对在ARX中使用动态链接的MFC库进行讨论
1.动态链接的MFC的项目设置
首先在项目选项中选“MFC AppWizard(DLL)”,然后选Regular DLL using shared MFC DLL,然后输入项目名
在“Project Settings”对话框中“General”标签项中“Microsoft Foundation Classes ”域选“Use MFC in a Shared DLL for the field.”
删除由AppWizard所建立的.cpp、.h项目文件,把arxmfcdll.cpp和resourcehelper.h加入到项目中(在Object ARX SDK中objectArx"utils"mfcextras目录下)
在Link标签项中,“General”栏“Output file name”项输入编译后的应用程序名,后缀为.arx
在Link标签项中,“Output”栏,“Base address”项输入“0x1c000000”,“Entry-point symbol”项输入“DllEntryPoint@12”
在C/C++标签项中,“Code Generation”栏“Use Run-time library”项选“Multithread DLL”
在C/C++标签项中,“Preprocessor”栏中定义_WINDLL,_AFXDLL
2.MFC的初始化
为了执行DLL初始化,在acrxEntryPoint()函数中应当调用DllMain()函数,方法如下所示:extern "C" AcRx::AppRetCode acrxEntryPoint( AcRx::AppMsgCode msg, void* )
{
switch( msg )
{
case AcRx::kInitAppMsg:
DllMain(_hdllInstance, DLL_PROCESS_ATTACH, NULL);
file://加入特定的应用初始化代码
break;
case AcRx::UnloadAppMsg:
DllMain(_hdllInstance, DLL_PROCESS_DETACH, NULL);
break;
default:
break;
}
return AcRx::kRetOK;
}
开发者只需按前面所述的方法进行设置则以上工作均无需开发者介入,开发者只需加入相应的用户函数即可。{
switch( msg )
{
case AcRx::kInitAppMsg:
DllMain(_hdllInstance, DLL_PROCESS_ATTACH, NULL);
file://加入特定的应用初始化代码
break;
case AcRx::UnloadAppMsg:
DllMain(_hdllInstance, DLL_PROCESS_DETACH, NULL);
break;
default:
break;
}
return AcRx::kRetOK;
}
3.资源管理
在ARX应用与AutoCAD和其它应用程序使用共享的MFC库时,资源管理变得极为重要。你必须清晰得管理好你的应用程序资源,防止它与AutoCAD或其它ARX应用程序的资源发生冲突。清晰地资源设置一般包括以下三步:
在将系统资源设为你的应用资源以前,调用AfxGetResourceHandle()函数获得系统当前资源。
在执行完任何要求使用自定义资源的函数之后,立刻将系统资源还原为以前保存的资源句柄。
在应用程序对话框命令句柄需调用要求获得AutoCAD资源的AutoCAD API函数(或调用AutoCAD命令)时,我们应当首先在调用函数前将资源设为AutoCAD,然后函数执行完后再恢复应用程序资源(acedGetAcadResourceInstance()函数可获得AutoCAD的资源句柄)。为使资源管理更简单我们使用了一个C++类 CtemporaryResourceOverride(objectarx"utils"mfcextras目录下可找到),在该类的一个对象建立时,对象将系统资源设为你的应用资源,对象删除时,析构函数自动将资源设为系统资源。CtemporaryResourceOverride声明请查阅 objectarx"utils"mfcextras目录下的afxmfcdll.cpp文件。
4.实例
假设现在要在一对话框中按下“OK”按钮后绘制一圆,则利用MFC的ARX应用实现为先利用ClassWizard加入对象ID为ON_OK的消息映射,然后编辑代码
void CDiaTest::OnOK()
{
HINSTANCE hin_new;
hin_new=acedGetAcadResourceInstance(); file://获得ACAD的资源
CTemporaryResourceOverride *jxs; file://定义新对象
jxs=new CTemporaryResourceOverride(hin_new) ;//为jxs分配内存,并初始化
createCircle();//画圆
delete jxs;//删除对象
CDialog::OnOK();
}
AcDbObjectId createCircle()
{
AcGePoint3d center(9.0, 3.0, 0.0);
AcGeVector3d normal(0.0, 0.0, 1.0);
AcDbCircle *pCirc = new AcDbCircle(center, normal, 2.0);
AcDbBlockTable *pBlockTable;
acdbCurDwg()->getBlockTable( pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt( ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId circleId;
pBlockTableRecord->appendAcDbEntity(circleId, pCirc);
pBlockTableRecord->close();
pCirc->close();
return circleId;
}
{
HINSTANCE hin_new;
hin_new=acedGetAcadResourceInstance(); file://获得ACAD的资源
CTemporaryResourceOverride *jxs; file://定义新对象
jxs=new CTemporaryResourceOverride(hin_new) ;//为jxs分配内存,并初始化
createCircle();//画圆
delete jxs;//删除对象
CDialog::OnOK();
}
AcDbObjectId createCircle()
{
AcGePoint3d center(9.0, 3.0, 0.0);
AcGeVector3d normal(0.0, 0.0, 1.0);
AcDbCircle *pCirc = new AcDbCircle(center, normal, 2.0);
AcDbBlockTable *pBlockTable;
acdbCurDwg()->getBlockTable( pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt( ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId circleId;
pBlockTableRecord->appendAcDbEntity(circleId, pCirc);
pBlockTableRecord->close();
pCirc->close();
return circleId;
}
5.ARX应用程序的升级
使用MFC的ARX应用要想在新版AutoCAD中使用必须进行升级,具体步骤如下:
定义_WINDLL和_AFXDLL
使用“multithreaded DLL runtime library”(发布版)
移走现有的ARX-MFC摸板
移走现有的DLL MFC初始化代码
移走任何CwinApp类的派生对象
把arxmfcdll.cpp和resourcehelper.h加入项目,并作相应调整 (这两个文件都在ObjectARX SDK目录 "objectarx"utils"mfcextras内)
四.结束语
作者在正进行的模具CAD系统的开发中使用了ARX与MFC结合编程的方法,实践证明一方面利用MFC进行Windows风格的编程,另一方面利用 ARX提供的类和函数深入到AutoCAD系统内部,实现系统功能要求,大大的提高了开发效率,无疑这是AutoCAD开发者们的最佳选择。