Active Automation:- DELPHI不敌VB????(100分)

E

emoth

Unregistered / Unconfirmed
GUEST, unregistred user!
兄弟我向来都在用DELPHI(以前用VB)利用AutoCAD的ACTIVE AUTOMATION来进行CAD的
二次开发,现在遇到一个问题,不思不得其解:

我想通过程序在CAD中建立一个选择集,CAD帮助中的语法如下:

///////////////////////////////////
SelectOnScreen Method

Prompts the user to pick an object from the screen.
Signature
object.SelectOnScreen [FilterType][, FilterData]
Object SelectionSet,The object or objects this method applies to
FilterType Integer; input-only; optional A DXF group code specifying the type of filter to use.
FilterData Variant; input-only; optional The value to filter on.
Remarks
This method supports the filtering mechanism.
AutoCAD's default prompt for picking an object will be used automatically.
For more selection mode options, see the Select, SelectByPolygon, and SelectAtPoint methods.

////////////////////////////

应该说对于VB或者DELPHI程序语言来说,只要遵循此接口语法就可以正常使用SelectionSet的此“选择方法”。
但是我用DELPHI却总是不能成功,代码如下:

unit UnitMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Comobj;

type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses AutoCAD_TLB; //引用CAD接口单元

{$R *.dfm}

//建立选择集

procedure TForm1.Button1Click(Sender: TObject);

var
CadApp:IAcadApplication;//CAD应用程序对象
CadDOC:IAcadDocument;//CAD文档对象
CadSelect:IAcadSelectionSet;//CAD选择集对象

vType,vData:OleVariant;

begin
try
//获得CAD对象
cadapp:=GetActiveOleObject('AutoCAD.Application.15') as iAcadApplication;
caddoc:=CadApp.ActiveDocument;
try
//建立名为NEWSELECT的CAD选择集
CadSelect:=CadDOC.SelectionSets.Add('NewSelect');

//为SelectOnScreen方法的FilterType, FilterData参数做准备
vType:=VarArrayCreate([0,1],varInteger);
vData:=VarArrayCreate([0,1],varVariant);
vType[0]:=8;
vType[1]:=62;
vData[0]:='0';
vData[1]:=7;
//根据FilterType和FilterData建立选择集
★ CadSelect.SelectOnScreen(vType,VarArrayRef(vData));

//显示选择集的成员总数
ShowMessage(IntToStr(cadselect.count));
finally
//从CAD中删除选择集对象
CadDOC.SelectionSets.Item('NewSelect').Delete;
end;
except
on E:Exception do
ShowMessage(E.Message);
end;
end;

end.

程序运行到★句时,总说OLE错误,而且是未指定的错误。如果不指定FilterType, FilterData参数
(应该支持缺省),居然连编译都无法通过。

但是我用VB来编程,却又随便都能成功,代码我就不写了,具体过程如下。
因为VB无法象DELPHI一样用VarArrayCreat来建立一个数组,因此就只能先声明两个数组,
一个为Integer类型,一个为Variant类型,然后为它们赋值。
由于SelectOnScreen方法的FilterType, FilterData参数需要传递的是Variant类型,
因此就在VB中再次声明两个Variant类型的变量,将数组赋与此变量,
调用SelectOnScreen方法,成功!!

此外,也可以不设置FilterType, FilterData参数,同样可以成功。

难道真的是在这上面DELPHI不敌VB?

DELPHI对数据的类型要求是否太严格了??还是别的什么原因???
 
瞎猜不知道可不可以:
CadSelect:=CadDOC.SelectionSets.Add('NewSelect'); 这应该是个空集。
vType:=VarArrayCreate([0,1],varInteger);
vData:=VarArrayCreate([0,1],varVariant); 这 2 句应该是没有意义的。理由是 Delphi 怎么知道它们是和刚才 ADD 的是关联的呢。
可能应该是:
CadSelect.Items.Item.Type:= ... (或者应该是:SelectionSets.Items.Item.Type:= ...)
CadSelect.Items.Item.Data:= ... (或者应该是:SelectionSets.Items.Item.Data:= ...)
然后才可以使用 Add 方法: CadSelect:=CadDOC.SelectionSets.Add('NewSelect');
甚至如 DirextX 类似,还要对 Item 初始化为 0 :FillChar(Item,SizeOf(Item),0);不然在默认方法中针对 Variant 类型就有可能指针违规。
另外,CadSelect.SelectOnScreen 方法可能不是你解释的 “根据FilterType和FilterData建立选择集”,而是将 CadSelect 的内容显示到屏幕。
没接触过 ACAD 的编程,以上纯属瞎猜。帮你 UP 。
 
肯定不行,CAD的方法是首先建立一个空的选择集,然后使用选择方法来添加元素
(如果需要,可以指定过滤参数和过滤条件,由两个数组内容指定,具体见CAD帮助)。

以下是SelectionSet对象的CAD帮助内容:

A group of one or more AutoCAD objects specified for processing as a single unit.

VBA object name: AcadSelectionSet
Create using: SelectionSets.Add
Access via: SelectionSets.Item
Document.ActiveSelectionSet

To delete a selection set, or delete items from a selection set, use one of the following methods:
Clear: The Clear method empties the selection set. The selection set will still exist, but won't contain any items. The items that previously resided in the selection still exist, but they no longer reside in the selection set.
RemoveItems: The RemoveItems method removes one or more items from a selection set. The removed items still exist, but they no longer reside in the selection set.

Erase: The Erase method deletes all items in a selection set. The selection set still exists, but won't contain any items. The items that previously resided in the selection set no longer exist.
Delete: The Delete method deletes a selection set object, but not the objects in the selection set. While the selection set itself will not exist after the call to the Delete method, the items previously in the selection set will still exist.
To create a selection set, use the Add method. To edit or query a selection set, use the following methods and properties:

Methods

AddItems
Clear
Delete
Erase
Highlight
ItemRemove
ItemsSelect
SelectAtPoint
SelectByPolygon
SelectOnScreen
Update


Properties

Application
Count
Name
///////以下为SelectOnScreen方法//////////
SelectOnScreen Method

See Also Example

Prompts the user to pick an object from the screen.

Signature
object.SelectOnScreen [FilterType][, FilterData]

Object SelectionSet
The object or objects this method applies to.
FilterType Integer; input-only; optional A DXF group code specifying the type of filter to use.
FilterData Variant; input-only; optional The value to filter on.

Remarks

This method supports the filtering mechanism.
AutoCAD's default prompt for picking an object will be used automatically.
For more selection mode options, see the Select, SelectByPolygon, and SelectAtPoint methods.

//////以下为VB例程(使用SelectByPolygon方法,提供了关于FilterType, FilterData参数的使用方法)/////////

Sub Example_SelectByPolygon()
' This example adds entities to a selection set by defining a polygon.

Dim ssetObj As AcadSelectionSet
Set ssetObj = ThisDrawing.SelectionSets.Add("TEST_SSET2")

' Add all the entities that lie within a fence to the selection set
Dim mode As Integer
Dim pointsArray(0 To 11) As Double
mode = acSelectionSetFence

pointsArray(0) = 28.2: pointsArray(1) = 17.2: pointsArray(2) = 0
pointsArray(3) = -5: pointsArray(4) = 13: pointsArray(5) = 0
pointsArray(6) = -3.3: pointsArray(7) = -3.6: pointsArray(8) = 0
pointsArray(9) = 28: pointsArray(10) = -3: pointsArray(11) = 0

ssetObj.SelectByPolygon mode, pointsArray

' Add all the Circles that lie within fence to the selection set

ReDim gpCode(0 To 1) As Integer
gpCode(0) = 0
gpCode(1) = 10

Dim pnt(0 To 2) As Double
pnt(0) = 3: pnt(1) = 6: pnt(2) = 0

ReDim dataValue(0 To 1) As Variant
dataValue(0) = "Circle"
dataValue(1) = pnt

Dim groupCode As Variant, dataCode As Variant

groupCode = gpCode
dataCode = dataValue

ssetObj.SelectByPolygon mode, pointsArray, groupCode, dataCode

End Sub
 
这里不知道是否会有帮助:
http://community.borland.com/article/0,1410,10194,00.html
 
楼上大侠,这个网站我去访问过,但是就是没有关于FilterType和FilterData参数的调用举例。
换句话说,对于形如AddPolyline类型的方法,因为它的传入参数是一个DOUBLE型的数组,因此VB和DELPHI都可以,但是对于
FilterData参数,由于其为一个Variant类型的数组,因此就出现了VB和DELPHI调用的差异(当然,这只是我的参想,但是好象又确实没有别的什么理由)

//////以下为CAD的添加多义线方法,需要传入一个数组//////////////

RetVal = object.AddPolyline(VerticesList)

Object ModelSpace Collection, PaperSpace Collection, Block

The object or objects this method applies to.
VerticesList Variant (array of doubles); input-only An array of OCS coordinates used to create the polyline vertices. Each vertex is represented with three elements, with the first two being the X and Y coodinates in OCS; the third element is ignored. At least two points (six elements) are required for constructing a polyline object. The array size must be a multiple of three.
RetVal Polyline object The newly created Polyline object.

Remarks

To create a polyline containing arcs, first create the straight polyline, then set the bulge at specific vertices using the SetBulge method.
This method exists for backward compatibility only. Use the new AddLightweightPolyline method to create polylines with an optimized format that saves memory and disk space.

Coordinates can be converted to and from the OCS using the TranslateCoordinates method.


 
把VB的代码直接改成Pascal
别管
CadApp:IAcadApplication;//CAD应用程序对象
CadDOC:IAcadDocument;//CAD文档对象
CadSelect:IAcadSelectionSet;//CAD选择集对象
这些接口,全部用自动化类型试试。
 
我再插一句。
看你原始代码:
var
....
CadSelect:IAcadSelectionSet; // CAD选择集对象
begin
try
.....
try
// 建立名为NEWSELECT的CAD选择集
CadSelect:=CadDOC.SelectionSets.Add('NewSelect');
....
end;
我的观点:
CadSelect <--- 它是 CAD 选择集对象。
CadSelect:=CadDOC.SelectionSets.Add('NewSelect'); <---- 可以这样吗 ? 这是一个对象操作,可以赋值 ?
CadSelect:IAcadSelectionSet <---- 这是一个接口,并没有在你的代码里获得这个接口啊。
如果改为如下这样,倒还可以看看:
CadSelect:=CadDOC.SelectionSets; // 将活动文档的选择集赋给 CadSelect
CadSelect.add('NewSelect'); // 对 CadSelect 操作
.... // 对 CadSelect 的其他操作。

更直接的,既然 IAcadSelectionSet 接口已经可以在 CadApp.ActiveDocument.SelectionSets
中获得,就表示你可以直接使用了,它是活动文档的一个属性,就应该在获得活动文档后直接
对它操作才正确啊。如果不是这样,它只是一个独立的对象,你应该通过接口规则来获得这个
对象,然后将这个对象和活动文档对象关联才正确。所以我认为你的原始代码应该修改。
看 VB 代码:
Dim ssetObj As AcadSelectionSet
Set ssetObj = ThisDrawing.SelectionSets.Add("TEST_SSET2")
这里的 Set 应该是类似 pascal 中的 Create 。意思是先创建一个 ssetObj 对象,只有这个
对象是被创建了,分配到了可以支配的内存,才可以将右边的值送进去。而 Delphi 你需要手
工创建一个对象,才可以有可支配的内存。至于 VB 为什么可以将一个对象操作赋给一个对象,
我就不知道了,也许是 VB 特有的做法,可能是指将一个名字叫“TEST_SSET2”的 SelectionSets
对象克隆给 ssetObj 吧。
 
小雨哥你好,可能还在线上吧。

CadSelect:IAcadSelectionSet;
是指CADselect 是一个选择集对象,是自CAD文档对象IAcadDocument以下的IAcadSelectionSets
选择集集合的一个子对象,因此对于你的

CadSelect:=CadDOC.SelectionSets; // 将活动文档的选择集赋给 CadSelect
CadSelect.add('NewSelect'); // 对 CadSelect 操作

是不正确的,你的第一句实际上是将一个选择集和一个选择集的集合相对等了,换句话说
CADSELECT实际上就是SelectionSets的一个ITEM



AcadSelectionSets
 
CAD文档的集合集为SelectionSets Collection,它只有Methods:Add、Item两种方法,Properties:
Application、Count两个属性,而只有它的ITEM->SelectionSet Object才有Select、SelectAtPoint、SelectByPolygon、
SelectOnScreen等根据条件建立选择集的方法。刚才我刚刚才用VB作试验,完全成功,能成功
访问VB程序通过Automation自动建立的选择集中的各个图元(譬如ID),道理肯定还是在于向COM服务器
传递一个Variant变量这个地方!!!
 
这样吧,我先下线去试一试。
 
其实我对你在问的 CAD 编程一窍不通,我是按 COM 和 Delphi 的规则在套你的程序流程,
我所以坚持我的说法,就是你执行到了具体对一个选择集进行操作时就会发生错误。因为在
我看来,这个选择集只有前向声明而并没有真正在内存诞生。之所以说“倒还可以看看”,
是说如果它不用创建,要直接用也要这样表述才行的意思,不是说这是正确的。
 
请看我刚刚试验的代码:

CadSelect:=CadDOC.ActiveSelectionSet;
//设定为活动选择集对象
ShowMessage(IntToStr(CadSelect.Count));
//显示选择集对象总数
ShowMessage(cadselect.Name);
//显示选择集名称

均可调试通过,说明对象是真正建立而且是可以访问的,但是一执行SelectOnScreen就会出
现未知错误。

我也知道只要按COM的参数传递规,不管是DELPHI还是VB,都可以做到。但是DELPHI对数据类型的检验实在是
太严格了,尤其是对于变体和数组之间。根据老外写的DELPHI5开发人员指南中所说的,我还试着用了一下专为变体数组
设计的函数VarArrayRef,故障依旧!
 
Delphi5开发人员指南

Va r A r r a y R e f ( )主要用来解决向O L E 服务器传送Va r i a n t 数组时要出现的问题。
当向自动化服务器的方法传送一个包含Va r i a n t 数组的Va r i a n t 变量时,问题就产生了,例如:
Server.PassVariantArray(VA)
这里传送的不是一个v a r i a n t 数组,而是一个包含了v a r i a n t 数组的v a r i a n t 变量,
这是有明显区别的。
如果自动化服务器希望接收一个v a r i a n t 数组而不是指向它的一个引用,
如果用上面的语法来调用自动化的方法,就会产生一个错误。在这种情况下就要用Va r A r r a y R e f ( ),
示例如下:
Server.PassVariantArray(varArrayRef(VA))
 
一个接口如 CadSelect:= CadDOC.ActiveSelectionSet 这样处理好象也不行。
再查看一下 AutoCAD_TLB.pas 类型库再说,万一如 VB 那样,这个 ActiveSelectionSet
的次级项目是一个 TSelectionSet 的话呢。
 
还有一个思路,假如这样:
CadSelect.Name:='NewSelect';
CadDOC.SelectionSets.Item:=CadSelect;
.....
 
还是行不通,始终不是初始化选择集的问题,还是传递参数的问题。要不然是CAD的服务器不支持。也不知道对不对
 
我也被这个问题困了几天了,不加过滤可以,加了就不行,我也是一直怀疑是参数传递的问题,
但一直没办法,我加100分
 
看了题目,想插两句,以前用的是VC,现在换成Delphi,当然VB也用过。老实说写COM程序
特别是自动化的COM对象,还是VC与Delphi好些,VB太。。。不过,不是说VB不好,如果能
够暴露更多的控制细节,VB是相当相当不错的东东。Delphi的类型库引入是个BUG,如果能
将此问题解决,那么也是完美。自动化的对象作为COM的特列,还是类似Delphi之类的程序
编起来顺手
 
我现在也遇到了同样的问题,不知如何解决

to xialin2:
你说不加过滤可以,我现在就想调用select方法,并且是acSelectionSetAll模式,因此想略去
后面四个可选参数,但编译通不过。如果对可选参数随便负值,就出现ole错误了
 
顶部