程序名:MFCOpenCADWithARX.exe
版本:1.0
时间:15.11.2
*****************************************************
首先感谢 @七彩云南,在网上查找资料的时候发现了他的资料,很是详细,但是没有找到文章的出处。
我的程序没有他的复杂。
*****************************************************
用途:用外部程序启动Autocad程序,同时自动加载和启动ARX程序。
环境:Win7 64位 VS2010 AutoCAD2010 自己的Arx程序
背景:传统的插件加载模式是,打开CAD——工具——加载程序——选择你的ARX程序——成功之后输入命令行启动你的ARX程序或者通过别的方式打开。
优点:1、只是安装好外部程序EXE文件,使用点击打开就能自动加载ARX并打开插件。
2、这个程序仅仅是加载了自己的Arx文件,不会改变CAD的外观以及你之前的环境配置。不会永久的更改你的AutoCAD的界面。
启动程序应该做如下工作:
1、启动AutoCAD
2、加载ARX程序
3、发送命令到AutoCAD,启动所做的ARX
一、启动AutoCAD的方法:
七彩云南在他的文章中列出了3种方法分别是
1、用COM的方式启动 使用AutoCAD COM接口
2、采用ShellExecute 我也不知道是个什么方法,不做解释
3、CreatProcess方法 比较复杂 下文主要讲这种方法
*************************************************************
关于CreatProcess方法
关于这个函数,如果你看不懂MSDN,这里有个大招:
http://baike.baidu.com/link?url=G4XIZs8nbrteQ3spqXgUFo0bNwHRinatQ4mqfi-mV3VEompeNnRaJ8FmksCDCwokPaT4AKgyWanV7Hq1kZYRCa
百度百科上的,去看看什么用途
STARTUPINFO si;
memset(&si,0,sizeof(STARTUPINFO));
si.cb=sizeof(STARTUPINFO);
si.dwFlags=STARTF_USESHOWWINDOW;
si.wShowWindow=SW_SHOWMAXIMIZED;
PROCESS_INFORMATION pi;//必备参数设置结束
UINT uTimeoutms=60*1000;
DWORD dwStart = GetTickCount();//计算时间开始
//AfxMessageBox(lpszAcadExeFile);
//AfxMessageBox(strExePath);
BOOL bRet = CreateProcess(lpszAcadExeFile,
_T("/nologo"),
NULL,
NULL,
FALSE,
0,
NULL,
strExePath,
&si,
&pi);
if(!bRet)
{
MessageBoxW(_T("启动cad失败"));
return false;
}//end if
引用:七彩云南的话:
采用CreateProcess 方法虽然抓住了对AutoCAD 程序(对“你的启动程序.exe”
来讲此时的AutoCAD 为子进程)启动之后的控制,可还有很多的问题需要解决,难题依然不
断:
1、如何判断AutoCAD 已经初始化完成,进入等待客户输入消息循环;
2、如何向AutoCAD 发送命令,以方便加载“你的arx 程序”
3、何时才应该进行关闭启动闪屏和退出“你的启动程序.exe”
1、如何判断AutoCAD 已初始化完成,进入等待用户输入阶段?
解答: 按照CreateProcess 函数的解释, 启动成功子进程(acad.exe) 后, 返回
PROCESS_INFORMATION 结构信息,即前面所定义的那个“pi”参数,其结构信息描述如下:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; //进程的主句柄
HANDLE hThread; //进程的主线程句柄
DWORD dwProcessId; //进程ID
DWORD dwThreadId; //主线程ID
} PROCESS_INFORMATION,;
以上参数中HANDLE hProcess 就是我们想要的acad.exe 程序启动后的句柄,可是如何
将此句柄(HANDLE)转换为彼句柄(HWND)呢?那为什么一定要转换呢?这是因为:
彼句柄(HWND)才是VC++的MFC 所鼓吹的“视窗(Window)”的控制类句柄,没有彼句
柄(HWND),想控制窗口操作,根本就不可能!原因是没有几个用进程HANDLE 的API 函数可
操控于具有窗口界面(Window)的函数(当然笔者应该说很多,可MFC 封装的太深了,所以
有=没有)。想操作AutoCAD 的Window 界面,必须先获取彼句柄(HWND)!那么如何进行了?
好累啊,一个个问题接踵而来,搜了网上的资源有两天两夜之久,最后的方法还是我最
不擅长的方法(也是arx 程序员不熟悉的领域!),该方法是:遍历桌面上的所有已启动窗口
(Window)进程(“窗口”,为什么要在这里注释一下呢?因为我要找的是“窗口”,而不是
所有已启动的其他非窗口进程,因为我要的目的是找到那个彼句柄(HWND),而非窗口进程
是没有HWND 嘛,还有一点就是有了HWND 就等于有了CWnd,而CWnd 又是专门操纵窗口(Window)
对象的指针类型。CWnd 在MFC 中属于“总理”级别的,想想吧,为什么我要不厌其烦的找到
它的原因,就是为了办大事!),从中找出和刚才那个pi 变量返回的进程ID( DWORD
dwProcessId)相同进程,那就是我所想要的,也是我刚刚启动了的“那一个”AutoCAD,而
不是其他的客户已启动的AutoCAD 实例。
关于这段话 的意思是这样的:
我们获取的句柄类型为:HANDLE,但是我们想要控制的视窗是这样的句柄HWND,由于MFC封装太深,所以我们必须要找到一个合适的方法找到这个HWND。方法就是我们有pi这个值。其pi.ProcessID是我们启动窗口的唯一值,那么我们可以遍历桌面上的所有已启动窗口(Window)进程,对比找到。
代码:
HWND CMFCopencadDlg::GetHandlebyProcessID(DWORD dwProcessID) { if(dwProcessID==NULL) return NULL; HWND hWndtmp = ::GetWindow(::GetDesktopWindow(), GW_CHILD); while(hWndtmp) { if (::IsWindow(hWndtmp) && ::IsWindowVisible(hWndtmp)) { DWORD dwProcessIDnew = 0; DWORD dwThreadIDnew = ::GetWindowThreadProcessId(hWndtmp, &dwProcessIDnew); if(dwThreadIDnew!=0) { if (dwProcessIDnew == dwProcessID) return hWndtmp; } } hWndtmp = ::GetWindow(hWndtmp, GW_HWNDNEXT); }//end while return NULL; }
2、加载ARX程序
3、发送命令到AutoCAD,启动所做的ARX
由于cad可以在工作区查找acad.rx文件,因此我们只要编写一个rx文件放在工作区,这样就可以自己启动我们的arx程序
rx程序编写过程是 打开记事本,输入arx程序的完整路径即可
如果要编写程序自己去写rx文件,一定要注意格式。把要自动加载的东西一一列上即可。
发送命令到AutoCAD:
只有第五种方法才可行, 但是该方法中的
adsw_acadMainWnd()显然又是arx 函数,经七彩云南改写如下:
void CMFCopencadDlg::SendAcadCommand(HWND hWnd, LPCTSTR lpszCmd, BOOL IsSendEsc2)
{
if(hWnd == NULL) return;
if(lpszCmd==NULL) return;
LPCTSTR strCmd = lpszCmd;
CString strCmd1 = strCmd;
CString strRight1 = strCmd1.Right(1);
LPCTSTR space = _T(" ");
if(strRight1!=_T(" "))
{
CString str=(CString) strCmd + _T(" ");
strCmd = str;
}//末尾添加至少一个空格
////1. 发命令前加按了两个ESCAPE
if(IsSendEsc2)
{
TCHAR szEsc[3];
COPYDATASTRUCT cmddata;
szEsc[0] = VK_ESCAPE;
szEsc[1] = VK_ESCAPE;
szEsc[2] = NULL;
cmddata.dwData = (DWORD)1;
cmddata.cbData = (DWORD)(_tcslen(szEsc)+1)*sizeof(TCHAR);
cmddata.lpData = (TCHAR*)szEsc;
::SendMessage(hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cmddata);
}
//
TCHAR *pszCmd = (TCHAR*)(LPCTSTR)strCmd;
COPYDATASTRUCT cmdMsg;
cmdMsg.dwData = (DWORD)1;
cmdMsg.cbData = (DWORD)(_tcslen(pszCmd)+1) * sizeof(TCHAR);
cmdMsg.lpData =(TCHAR*) pszCmd;
::SetForegroundWindow(hWnd);
::BringWindowToTop(hWnd);
::SendMessage(hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cmdMsg);
//AfxMessageBox(pszCmd);
return;
}
在此感谢原博主。
PS:不同版本CAD使用的编码不一样 AutoCAD2010使用的是Unicode编码,07-08好像是多节字符集,在开发的时候,要注意版本的使用字符集,并且保证一致!!!!!!!!!!!!!!!