“可写”;只读系统变量”;(打包)
好吧,前面关于可写的“只读系统变量”的主题的代码有点原始,并且是维护的噩梦。 在这里,我打包了代码,使其更易于移植,并且不
易出现错误。 概述的方法不使用类,因为这只是导致我符合
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 ***** 保罗,这是很有趣的事。 可以在不使用任何内联程序集的情况下有效地执行此操作,并且仅依赖C++指向成员的指针。有关此方法的示例,请参阅例如http://otb.manusoft.com/2009/03/objectarx-2010-dealing-with-missing.htm。 很好的密码,欧文。我本来打算走一条类似的路线,但因为我不知道AcDbHeader是否派生或派生自哪个类,所以拒绝了它,以便与vtbl保持同步
由于vtables是在编译时构建的,AcDbHeader需要知道,正如页面上的
示例所知道的AcGiFaceData<这是一个很好的解决方案,如果类结构在编译时已知,则效果良好。否则,必须采用不太优雅的方法 实际上,您可以在不知道原始vtable结构的情况下欺骗和使用功能。只需以您想要的任何方式声明您自己的 AcDbHeader 类即可。您添加的任何虚拟成员都可以声明为__declspec(dllimport),瞧!唯一的限制是,您无法将自己的 AcDbheader 对象传递给未使用 AcDbheader 声明的任何人(在实践中,这意味着您需要跳过一些箍,以确保在调用函数时将 AutoCAD 的 AcDbHeader 对象之一作为 *this* 指针传递)。 哦,只是为了澄清一下:我帖子中的vtable技巧只有在函数*未*导出时才是必要的。只要函数被导出,你就不需要知道vtable顺序。 哎呀,忘了去掉第一段。
谢谢,总是有不止一种方法...(在这里插入隐喻)...
页:
[1]