对于我的测试,我创建了两个拥有两个自动化对象类型的COM库.容器对象保存内容对象的列表,而内容对象持有对其容器的弱引用.
以下两种情况进行了测试和正常工作(弱引用设置为null并释放内存):
>具有接口和CoClasses的单个COM库.
>两个COM库,一个具有接口,另一个与CoClasses
但是,当我将coclasses放在两个独立的库中时,代码会产生“无效的类型转换”,当删除[weak]属性时,该错误消失.请原谅这个奇怪的样本,其目的只是为了使问题变得最小,不能作为标准编码习惯
这是第一个定义两个接口的库.ridl文件,同时定义了CoClass
容器:
[ uuid(E1EE3651-A400-49BF-B5C5-006D9943B9C0),version(1.0) ] library DelphiIntfComLib { importlib("stdole2.tlb"); interface IMyContainer; interface IMyContent; coclass MyContainer; [ uuid(A7EF86F7-40CD-41EE-9DA1-4D9B7B24F06B),helpstring("Dispatch interface for MyContainer Object"),dual,oleautomation ] interface IMyContainer: IDispatch { [id(0x000000C9)] HRESULT _stdcall Add([in] IMyContent* AMyContent); }; [ uuid(BFD6D976-8CEF-4264-B95A-B5DA7817F6B3),helpstring("Dispatch interface for MyContent Object"),oleautomation ] interface IMyContent: IDispatch { [id(0x000000C9)] HRESULT _stdcall SetWeakReferenceToContainer([in] IMyContainer* AContainer); }; [ uuid(1F56198B-B1BE-4E11-BC78-0E6FF8E55214) ] coclass MyContainer { [default] interface IMyContainer; }; };
这是我的容器实现
unit Unit1; {$WARN SYMBOL_PLATFORM OFF} interface uses ComObj,ActiveX,DelphiIntfComLib_TLB,StdVcl,Generics.Collections; type TMyContainer = class(TAutoObject,IMyContainer) private FList: TList<IMyContent>; protected procedure Add(const AMyContent: IMyContent); safecall; public Destructor Destroy; override; procedure Initialize; override; end; implementation uses ComServ; procedure TMyContainer.Add(const AMyContent: IMyContent); begin FList.Add(AMyContent); AMyContent.SetWeakReferenceToContainer(self); end; destructor TMyContainer.Destroy; begin FList.Free; inherited; end; procedure TMyContainer.Initialize; begin inherited; FList := TList<IMyContent>.create; end; initialization TAutoObjectFactory.Create(ComServer,TMyContainer,Class_MyContainer,ciMultiInstance,tmApartment); end.
我的第二个库引用第一个,只包含我的内容界面的CoClass
[ uuid(65659EE4-1949-4112-88CA-F2D5B5D8DA2C),version(1.0) ] library DelphiImplComLib { importlib("stdole2.tlb"); importlib("DelphiIntfComLib.dll"); coclass MyContent; [ uuid(79D1669A-8EB6-4AE6-8F4B-91137E6E6DC1) ] coclass MyContent { [default] interface IMyContent; };
其实施与弱参考
unit Unit2; {$WARN SYMBOL_PLATFORM OFF} interface uses ComObj,DelphiImplComLib_TLB,DelphiIntfComLib_TLB; type TMyContent = class(TAutoObject,IMyContent) private [Weak] //If included will cause "invalid class typecast" error FContainer : IMyContainer; protected procedure SetWeakReferenceToContainer(const AContainer: IMyContainer); safecall; end; implementation uses ComServ; procedure TMyContent.SetWeakReferenceToContainer(const AContainer: IMyContainer); begin FContainer := AContainer; end; initialization TAutoObjectFactory.Create(ComServer,TMyContent,Class_MyContent,tmApartment); end.
我测试如下
program Project13; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils,DelphiImplComLib_TLB in 'impl\DelphiImplComLib_TLB.pas',DelphiIntfComLib_TLB in 'Intf\DelphiIntfComLib_TLB.pas'; var GMyContainer : IMyContainer; GMyContent : IMyContent; begin GMyContainer := CoMyContainer.Create; GMyContent := CoMyContent.Create; GMyContainer.Add(GMyContent); end.
为什么我在分割实现时遇到错误?如何缓解这个问题?
解决方法
但是,只要不需要使用[weak]的自动修改功能,可以使用无类型的指针:
type TMyContent = class(TAutoObject,IMyContent) private FContainer : Pointer{IMyContainer}; ... end;
只要您需要使用其方法/属性,就必须将FContainer类型转换为IMyContainer,例如:
IMyContainer(FContainer).Add(...);
type TMyContent = class(TAutoObject,IMyContent) private [Unsafe] FContainer : IMyContainer; ... end;
正如马可博客中提到的:
Weak and Unsafe Interface References in Delphi 10.1 Berlin
What if the object has a standard reference count implementation and you want to create an interface reference that is kept out of the total count of references? You can now achieve this by adding the [unsafe] attribute to the interface variable declaration,changing the code above to:
06003
Not that this is a good idea,as the code above would cause a memory leak. By disabling the reference counting,when the variable goes out of scope nothing happens. There are some scenarios in which this is beneficial,as you can still use interfaces and not trigger the extra reference. In other words,an unsafe reference is treated just like… a pointer,with no extra compiler support.
Now before you consider using the unsafe attribute for having a reference without increasing the count,consider that in most cases there is another better option,that is the use of weak references. Weak references also avoid increasing the reference count,but they are managed. This means that the system keeps track of weak references,and in case the actual object gets deleted,it will set the weak reference to nil. With an unsafe reference,instead,you have no way to know the status of the target object (a scenario called dangling reference).