自定义实体的COM封装

如果自定义实体在AutoCAD中处于选中状态时,属性面板能够显示其属性并供用户修改,那么用户与自定义实体的交互性将得到极大提升,这在实际工程中是非常实用的!天正系列软件内置大量自定义实体,并且供用户修改属性驱动,其技术核心就是COM封装。

本文逐步讲解如何对自定义实体进行COM封装。

对自定义实体进行COM包装

本文在已有的自定义实体项目中添加自定义实体的COM包装类,所以在创建自定义实体项目时需要选择COM相关的选项。

创建COM包装类

  • 通过项模板添加COM包装类

  • 设置若干COM支持,这里要输入自定义实体类名

点击“Finish”按钮后,在项目文件夹中添加了CustomEntity1COMWrapper.hCustomEntity1COMWrapper.cpp两个文件,并且IDE自动更新了MyCustomEntity.idl文件中的内容。

重写自定义实体的一个虚函数

为自定义实体重写subGetClassID()虚函数,给*pClsid赋值,这关系到COM包装的成败!

需要添加#include "MyCustomEntity_i.c"代码,不能是MyCustomEntity_i.h

1
2
3
4
5
6
Acad::ErrorStatus CustomEntity1::subGetClassID(CLSID* pClsid) const
{
assertReadEnabled();
*pClsid = CLSID_CustomEntity1COMWrapper;
return Acad::ErrorStatus();
}

包装自定义实体属性

下面逐步讲解如何实现属性面板的制作,本文暂时仅考虑R1、R2、N三个属性的封装。

  • MyCustomEntity.idl中添加接口声明
1
2
3
4
5
6
7
8
interface IMyCE1OPM : IAcadEntity	{
[propget, id(1), helpstring("property R1")] HRESULT R1([out, retval] double* pVal);
[propput, id(1), helpstring("property R1")] HRESULT R1([in] double newVal);
[propget, id(2), helpstring("property R2")] HRESULT R2([out, retval] double* pVal);
[propput, id(2), helpstring("property R3")] HRESULT R2([in] double newVal);
[propget, id(3), helpstring("property N")] HRESULT N([out, retval] long* pVal);
[propput, id(3), helpstring("property N")] HRESULT N([in] long newVal);
};
  • 在COM头文件中添加方法声明
1
2
3
4
5
6
7
8
9
10
public:
//IMyCE1OPM
STDMETHOD(get_R1)(double* pVal);
STDMETHOD(put_R1)(double newVal);

STDMETHOD(get_R2)(double* pVal);
STDMETHOD(put_R2)(double newVal);

STDMETHOD(get_N)(long* pVal);
STDMETHOD(put_N)(long newVal);
  • 在COM头文件中声明属性映射
1
2
3
4
5
BEGIN_OPMPROP_MAP()
OPMPROP_ENTRY(0, 0x00000001, PROPCAT_Data, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")
OPMPROP_ENTRY(0, 0x00000002, PROPCAT_Data, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")
OPMPROP_ENTRY(0, 0x00000003, PROPCAT_Data, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")
END_OPMPROP_MAP()
  • 在COM头文件中定义属性号
1
2
3
#define DISPID_R1   0x00000001
#define DISPID_R2 0x00000002
#define DISPID_N 0x00000003
  • 在COM源文件中定义几个宏
1
2
3
4
5
6
7
8
9
10
11
12
#define AXEntityDocLockNoDbOk(objId)                        \
AcAxDocLock docLock(objId, AcAxDocLock::kNormal); \
if (docLock.lockStatus() != Acad::eNoDatabase && \
docLock.lockStatus() != Acad::eOk) \
throw docLock.lockStatus();

#define AXEntityDocLock(objId) \
AcAxDocLock docLock(objId, AcAxDocLock::kNormal); \
if(docLock.lockStatus() != Acad::eOk) \
throw docLock.lockStatus();

#define CHECKOUTPARAM(x) if (x==NULL) return E_POINTER;
  • 在COM源文件中添加实现方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::get_R1(double* pVal)
{
CHECKOUTPARAM(pVal);
try
{
Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForRead, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

*pVal = pPoly->getR1();
}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed to open object");
}
return S_OK;
}

STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::put_R1(double newVal)
{
try
{
AXEntityDocLockNoDbOk(m_objRef.objectId());

Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForWrite, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

if ((es = pPoly->setR1(newVal)) != Acad::eOk)
return Error(L"Failed!");
else
Fire_Notification(DISPID_R1);

}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed!");
}
return S_OK;
}

STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::get_R2(double* pVal)
{
CHECKOUTPARAM(pVal);
try
{
Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForRead, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

*pVal = pPoly->getR2();
}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed to open object");
}
return S_OK;
}

STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::put_R2(double newVal)
{
try
{
AXEntityDocLockNoDbOk(m_objRef.objectId());

Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForWrite, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

if ((es = pPoly->setR2(newVal)) != Acad::eOk)
return Error(L"Failed!");
else
Fire_Notification(DISPID_R2);
}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed!");
}
return S_OK;
}

STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::get_N(long* pVal)
{
CHECKOUTPARAM(pVal);
try
{
Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForRead, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

*pVal = pPoly->getN();
}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed to open object");
}
return S_OK;
}

STDMETHODIMP_(HRESULT __stdcall) CCustomEntity1COMWrapper::put_N(long newVal)
{
try
{
AXEntityDocLockNoDbOk(m_objRef.objectId());

Acad::ErrorStatus es;
AcAxObjectRefPtr<CustomEntity1> pPoly(&m_objRef, AcDb::kForWrite, Adesk::kTrue);
if ((es = pPoly.openStatus()) != Acad::eOk)
throw es;

if ((es = pPoly->setN(newVal)) != Acad::eOk)
return Error(L"Failed!");
else
Fire_Notification(DISPID_N);
}
catch (const Acad::ErrorStatus)
{
return Error(L"Failed!");
}
return S_OK;
}

测试属性面板

重新编译自定义实体,加载dbx和arx文件,在模型空间添加自定义实体后,选中自定义实体后在属性面板上显示R1、R2、N三个属性,调整这些属性可以驱动图形变化,很完美!

评论