pkohut 发表于 2009-8-25 03:03:16

“可写”;只读系统变量”;(打包)

好吧,前面关于可写的“只读系统变量”的主题的代码有点原始,并且是
维护的噩梦。 在这里,我打包了代码,使其更易于移植,并且不
易出现错误。 概述的方法不使用类,因为这只是导致我符合
THISCALL调用约定,将“MyAcDbHeader”的“this”指针自动填充到
ecx寄存器上。
该代码的目标是以类型安全和
半可移植的方式使AcDbHeader的调用成员函数。 大多数代码是针对编译器的,允许它在
编译时捕获语法错误和类型不匹配。 模板函数完成了大部分实际
工作,编译器为发布版本很好地优化了这些工作。
我再次首先介绍完整的代码:
// MyAcDbHeader.h
#pragma once
#if defined(_M_IX86)
// code is stricly for 32bit x86
namespace MyAcDbHeader {
        // AutoCAD AcDbHeader typedefs.Get these from the undecorated names
        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);
        //// defined to test different template definitions
        //typedef double (CALLBACK* DIM_SCALE) (void);
        //typedef Acad::ErrorStatus (CALLBACK* GET_DIMSTYLE_CHILE_DATA) (AcRxClass const *, AcDbDimStyleTableRecord * &, AcDbObjectId &);
        // pointers to AutoCAD AcDbHeader function pointers.
        // Before using these function they first need to be initialized by calling
        // InitAcDbHeader, otherwise they are set to NULL and will GPF.
        extern SET_TDD_INDWG setTddInDwg;
        extern SET_TDD_USR_TIMER setTddUsrTimer;
        extern SET_TDL_CREATE setTdlCreate;
        extern SET_TDL_UPDATE setTdlUpdate;
        extern SET_TDU_CREATE setTduCreate;
        extern SET_TDU_UPDATE setTduUpdate;
        extern SLOW_DB_HEADER slowDbHeader;
        extern SLOW_DB_HEADER * pAcDbHeader;
        //// declared to test different template definitions
        //extern DIM_SCALE dimScale;
        //extern GET_DIMSTYLE_CHILE_DATA getDimStyleChildData;
        // InitAcDbHeader, must be called prior to using any other function pointers.
        // Makes call to SetAcDbHeaderInstance before returning.
        void InitAcDbHeader(AcDbObjectId stubId = AcDbObjectId::kNull);
       
        // SetAcDbHeaderInstanceFrom retieves the AcDbHeader from AutoCAD by
        // dereferencing stubId to _thiscall (ecx) prior to calling slowDbHeader.
        // If stubId == AcDbObjectId::kNull then _thiscall (ecx) is set to 0 prior
        // to calling slowDbHeader.
        void SetAcDbHeaderInstanceFrom(AcDbObjectId stubId);
// set 1 byte alignment
#pragma pack(push, 1)
        // define typesafe template for AcDbHeader functions that have
        // no parameters.
        // T is the return type
        // T1 is the type of the callee
        template
        T func(T1 callee)
        {
                _asm mov ecx, pAcDbHeader
                        T rVal = callee();
                return rVal;
        }
        // define typesafe template for AcDbHeader functions that have
        // 1 parameter.
        // T is the return type
        // T1 is the type of the callee
        // T2 is the type for parameter 1
        template
        T func(T1 callee, T2 const & inObj)
        {
                _asm mov ecx, pAcDbHeader
                        T rVal = callee(inObj);
                return rVal;
        }
        // define typesafe template for AcDbHeader functions that have
        // 3 parameters.
        // T is the return type
        // T1 is the type of the callee
        // T2 is the type for parameter 1, T3 for parameter 2, T4 for parameter 3
        template
        T func(T1 callee, T2 const * inPtr, T3 *& refPtr, T4 & outObj)
        {
                _asm mov ecx, pAcDbHeader
                        T rVal = callee(inPtr, refPtr, outObj);
                return rVal;
        }
#pragma pack(pop)
}
#endif

// MyAcDbHeader.cpp
#include
#include "MyAcDbHeader.h"
namespace MyAcDbHeader {
        #if defined(_M_IX86)
        // code is stricly for 32bit x86
        // Define acad family from 2004 to 2006.They should all have
        // the same mangled AcDbHeader function names, but needs to be
        // varified and coded accordingly.
        // Calling convention is THISCALL, so register ECX needs to be
        // set properly prior to making function calls.
        #if defined(ARX2004)
                #define ACAD2004_FAMILY 1
        #elif defined(ARX2005)
                #define ACAD2004_FAMILY 1
        #elif defined(ARX2006)
                #define ACAD2004_FAMILY 1
        #endif
        // 2007 through 2009 should also have the same mangled AcDbHeader function names
        // Place family check code here, and Place mangled names in InitAcDbHeader.
        // 2010 32 bit, same as 2007 family.
        // 2010 64 bit, calling convention is FASTCALL, Guessing the _thiscall will need to be placed
        // in RCX, but haven't really looked into it
        // Declare instances of AcDbHeader functions.
        SET_TDD_INDWG setTddInDwg = NULL;
        SET_TDD_USR_TIMER setTddUsrTimer = NULL;
        SET_TDL_CREATE setTdlCreate = NULL;
        SET_TDL_UPDATE setTdlUpdate = NULL;
        SET_TDU_CREATE setTduCreate = NULL;
        SET_TDU_UPDATE setTduUpdate = NULL;
        SLOW_DB_HEADER slowDbHeader = NULL;
        SLOW_DB_HEADER * pAcDbHeader = NULL;
        //// declared to test different template definitions
        //DIM_SCALE dimScale = NULL;
        //GET_DIMSTYLE_CHILE_DATA getDimStyleChildData = NULL;
        void InitAcDbHeader(AcDbObjectId stubId) {
                HMODULE hMod = GetModuleHandle("acdb16.dll");
#ifdef ACAD2004_FAMILY
                // Get addresses of AcDbHeader functions, using mangled string names.
                setTddInDwg = (SET_TDD_INDWG) GetProcAddress(hMod, "?setTddInDwg@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                setTddUsrTimer = (SET_TDD_USR_TIMER) GetProcAddress(hMod, "?setTddUsrTimer@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                setTdlCreate = (SET_TDL_CREATE) GetProcAddress(hMod, "?setTdlCreate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                setTdlUpdate = (SET_TDL_UPDATE) GetProcAddress(hMod, "?setTdlUpdate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                setTduCreate = (SET_TDU_CREATE) GetProcAddress(hMod, "?setTduCreate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                setTduUpdate = (SET_TDU_UPDATE) GetProcAddress(hMod, "?setTduUpdate@AcDbHeader@@QAE?AW4ErrorStatus@Acad@@ABVAcDbDate@@@Z");
                slowDbHeader = (SLOW_DB_HEADER) GetProcAddress(hMod, "?slowDbHeader@AcDbStub@@ABEPAVAcDbHeader@@XZ");
                //// a couple extra's to test different template definitions
                //dimScale = (DIM_SCALE) GetProcAddress(hMod, "?dimscale@AcDbHeader@@QBENXZ");
                //getDimStyleChildData = (GET_DIMSTYLE_CHILE_DATA) GetProcAddress(hMod, "?getDimstyleChildData@AcDbHeader@@QBE?AW4ErrorStatus@Acad@@PBVAcRxClass@@AAPAVAcDbDimStyleTableRecord@@AAVAcDbObjectId@@@Z");
#endif
                SetAcDbHeaderInstanceFrom(stubId);
        }
        void SetAcDbHeaderInstanceFrom(AcDbObjectId stubId)
        {
                void * pStub = stubId.isValid() ? (void *&) *stubId : NULL;
                _asm mov ecx, pStub
                _asm call slowDbHeader
                _asm mov pAcDbHeader, eax
        }
        #endif
}

// acrxEntryPoint.cpp
...
...
#include "MyAcDbHeader.h"
...
...
// - navSetDwgDate.setDwgDate command (do not rename)
        static void navSetDwgDatesetDwgDate(void)
        {
                using namespace MyAcDbHeader;
                InitAcDbHeader();
                AcDbDate dt;
                Acad::ErrorStatus es;
                es = func(*setTddInDwg, dt);
                es = func(*setTddUsrTimer, dt);
                es = func(*setTdlCreate, dt);
                es = func(*setTdlUpdate, dt);
                es = func(*setTduCreate, dt);
                es = func(*setTduUpdate, dt);
                // double d = func(*dimScale);
                // AcRxClass * rxClass = NULL; // needs to point to an instance of dimension class descriptor
                // AcDbDimStyleTableRecord * pRcd = NULL; // needs to point to instance of AcDbDimStyleTableRecord
                // AcDbObjectId styleId; // if es == eOk then will == dimension style ID
                // es = func(*getDimStyleChildData, rxClass, pRcd, styleId);
        }

模板“func”(忘记将其重命名为更好的名称)被3个不同的签名覆盖。 其中2个只是测试用例,
以确保覆盖工作(确实如此)。 希望代码有足够的文档记录来理解。 如果不是我自己或这里的其他人
,将很乐意回答您的问题。
哦,没有错误捕获。 如果有异常,我希望程序GPF可以修复代码。
可以添加错误处理,但是如果发生异常,则应用程序和AutoCAD的状态将是未知的,因此我认为最好只是出现故障。
挑战
我没有对代码做任何事情,这里有它。 因此,挑战在于添加对AutoCAD其他风格的支持,甚至可能弄清楚
如何将其包装在自己的漂亮类中。
**** Hidden Message *****

owenwengerd 发表于 2009-8-25 08:43:34

保罗,这是很有趣的事。   

pkohut 发表于 2009-9-1 12:47:10

可以在不使用任何内联程序集的情况下有效地执行此操作,并且仅依赖C++指向成员的指针。有关此方法的示例,请参阅例如http://otb.manusoft.com/2009/03/objectarx-2010-dealing-with-missing.htm。

owenwengerd 发表于 2009-9-1 15:30:56

很好的密码,欧文。我本来打算走一条类似的路线,但因为我不知道AcDbHeader是否派生或派生自哪个类,所以拒绝了它,以便与vtbl保持同步
由于vtables是在编译时构建的,AcDbHeader需要知道,正如页面上的
示例所知道的AcGiFaceData<这是一个很好的解决方案,如果类结构在编译时已知,则效果良好。否则,必须采用不太优雅的方法

owenwengerd 发表于 2009-9-1 15:48:04

实际上,您可以在不知道原始vtable结构的情况下欺骗和使用功能。只需以您想要的任何方式声明您自己的 AcDbHeader 类即可。您添加的任何虚拟成员都可以声明为__declspec(dllimport),瞧!唯一的限制是,您无法将自己的 AcDbheader 对象传递给未使用 AcDbheader 声明的任何人(在实践中,这意味着您需要跳过一些箍,以确保在调用函数时将 AutoCAD 的 AcDbHeader 对象之一作为 *this* 指针传递)。

pkohut 发表于 2009-9-1 15:55:01

哦,只是为了澄清一下:我帖子中的vtable技巧只有在函数*未*导出时才是必要的。只要函数被导出,你就不需要知道vtable顺序。

pkohut 发表于 2009-9-1 15:57:06

哎呀,忘了去掉第一段。
谢谢,总是有不止一种方法...(在这里插入隐喻)...
页: [1]
查看完整版本: “可写”;只读系统变量”;(打包)