下面概述了一种对只读系统变量进行读/写访问的方法,特别是系统变量TDCREATE、TDUCREATE、TDUPATE、TDUUPDATE、TDUSRTIMER和TDINDWG将是可修改的,此外,目标平台是AutoCAD 2004,其他版本可能会工作,但计划对代码进行一点调整,因为不同版本的函数名称可能不同。让我也强调一下,您可能不希望下面的代码用于您控制范围之外的程序,除非您计划测试和支持AutoCAD的每一种风格和垂直。 为了避免被细节埋没,我必须假设目标受众了解C++调用约定、C++名称混淆以及如何消除它们(至少了解使用Dependency Walker的水平)。如果您计划进一步开发这个项目,理解VTables也会有所帮助,但对于本文来说不是必需的。很好,我觉得这篇文章缩短了3页。 对于那些饥肠辘辘的人来说,这是最后一段代码。
- // Define callbacks for functions
- typedef AcDbDate (CALLBACK* TDDINDWG)(void);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDD_INDWG) (AcDbDate const &);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDD_USR_TIMER) (AcDbDate const &);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDL_CREATE) (AcDbDate const &);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDL_UPDATE) (AcDbDate const &);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDU_CREATE) (AcDbDate const &);
- typedef Acad::ErrorStatus (CALLBACK* SET_TDU_UPDATE) (AcDbDate const &);
- typedef void * (CALLBACK* SLOW_DB_HEADER) (void);
- void SetDwgDate(void)
- {
- HMODULE hMod = GetModuleHandle("acdb16.dll");
- SET_TDD_INDWG setTddInDwg = (SET_TDD_INDWG) GetProcAddress(hMod, "?setTddInDwg@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
- SET_TDD_USR_TIMER setTddUsrTimer = (SET_TDD_USR_TIMER) GetProcAddress(hMod, "?setTddUsrTimer@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
- SET_TDL_CREATE setTdlCreate = (SET_TDL_CREATE) GetProcAddress(hMod, "?setTdlCreate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
- SET_TDL_UPDATE setTdlUpdate = (SET_TDL_UPDATE) GetProcAddress(hMod, "?setTdlUpdate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
- SET_TDU_CREATE setTduCreate = (SET_TDU_CREATE) GetProcAddress(hMod, "?setTduCreate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
- SET_TDU_UPDATE setTduUpdate = (SET_TDU_UPDATE) GetProcAddress(hMod, "?setTduUpdate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
-
- SLOW_DB_HEADER slowDbHeader = (SLOW_DB_HEADER) GetProcAddress(hMod, "?slowDbHeader@AcDbStub@@ABEPAVAcDbHeader@@XZ");
- SLOW_DB_HEADER * pSlowDbHeader = NULL;
- Acad::ErrorStatus es;
- AcDbDate dbDate;
- // set _thiscall to 0
- _asm mov ecx, 0
- _asm call slowDbHeader
- // store the returned instance of AcDbHeader
- _asm mov pSlowDbHeader, eax
-
- // set _thiscall to the instance of AcDbHeaer and call
- // setTdlCreate like any other function.
- _asm mov ecx, pSlowDbHeader
- es = setTdlCreate(dbDate);
- _asm mov ecx, pSlowDbHeader
- es = setTduCreate(dbDate);
- _asm mov ecx, pSlowDbHeader
- es = setTdlUpdate(dbDate);
- _asm mov ecx, pSlowDbHeader
- es = setTduUpdate(dbDate);
- _asm mov ecx, pSlowDbHeader
- es = setTddInDwg(dbDate);
- _asm mov ecx, pSlowDbHeader
- es = setTddUsrTimer(dbDate);
- }
特别感兴趣的是我们通过C++乱码名称检索的函数指针,下面是解开的函数签名。函数指针本身是无用的,因为它们是AcDbHeader的成员,AcDbHeader也没有文档记录,我们需要acdb header的一个实例。函数 - class AcDbHeader * AcDbStub::slowDbHeader(void)
用于获取指向AcDbHeader的实例指针,剩下的代码只是设置一些寄存器并调用AcDbHeader成员函数。AcDbHeader、AcDbStub和_thiscall要了解如何将所有这些整合在一起,我们需要了解一些细节。一、AcDbHeader是如何实现的?我们可以做几个猜测,要么AcDbDatabase派生自AcDbHeader,要么AcDbHeader是AcDbDatabase的成员。下面的陈述很容易回答这个问题:1,AcDbDatabase是AcDbHeader(不太可能)= FALSE。
2.AcDbDatabase有一个AcDbHeader -(这更有意义,但事实证明不是这样)尽管第2项有意义,但我们在AcDbDatabase中找不到AcDbHeader的任何getter/setter。相反,我们找到的唯一getter是slowDbHeader(void),它是AcDbStub的成员函数。ARX文档在AcDbObjectId描述中这样描述AcDbStub:这实际上是说AcDbStub是AcDbObjectId的组合,更具体地说,AcDbObjectId强烈拥有AcDbStub。查看AcDbObjectId的类定义的ARX头文件dbid.h,您会看到其中的关系。有了这些信息,我们似乎可以通过解引用AcDbObjectId并调用slowDbHeader()来获得指向AcDbHeader的指针。其代码为: - void * pStub = (void *&) * acdbHostApplicationServices()->workingDatabase()->textStyleTableId();
- _asm mov ecx, pStub
- _asm call slowDbHeader
- _asm mov pAcDbHeader, eax
但是它导致了一个异常,我没有调查原因,因为对slowDbHeader的快速反汇编显示它将0作为thiscall指针(寄存器ecx)异常。设置slowDbHeader调用就这样完成了, - _asm mov ecx, 0
- _asm call slowDbHeader
- _asm mov pAcDbHeader, eax
并且pAcDbHeader现在指向AcDbHeader的有效实例。现在很容易进行AcDbHeader::setTd*调用,只需确保ecx寄存器包含pAcDbHeader的实例指针。摘要现在,您有了一种更改以前只读的绘制时间属性的方法。在32位系统上,调用约定是_thiscall,它就像_stdcall一样,只是ecx寄存器保存类的实例指针。在64位Windows上,调用约定是_fastcall,需要修改代码来处理它。只要堆栈和寄存器设置正确,任何AcDbHeader函数都可以被调用。即使这里只使用了很少的函数,您也可以看到代码很快就变成了维护的噩梦,并且容易出错。在下一篇文章中,我将以一种更加可移植和类型安全的方式包装我们使用的AcDbHeader函数。保罗。
本帖以下内容被隐藏保护;需要你回复后,才能看到! 游客,如果您要查看本帖隐藏内容请 回复 |