2010-07-21 7 views
9

Trong Delphi, IUnknown được khai báo là:Delphi: Làm thế nào để triển khai QueryInterface của IUnknown?

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 

Lưu ý: Các tham số đầu ra là untyped

Trong TInterfacedObject hậu duệ của tôi tôi cần phải xử lý QueryInterface, vì vậy tôi có thể trả về một đối tượng có hỗ trợ các yêu cầu giao diện:

function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if IsEqualGUID(IID, IFooBar) then 
    begin 
     Obj := (TFooBar.Create(Self) as IFooBar); 
     Result := S_OK; 
    end 
    else 
     Result := inherited QueryInterface(IID, {out}Obj); 
end; 

Sự cố xảy ra trên dòng:

Obj := (TFooBar.Create(Self) as IFooBar); 

Delphi phàn nàn:

điều hành không áp dụng đối với loại toán hạng này

Rõ ràng tôi không biết làm thế nào hoặc những gì để gán cho một tham số untyped out. tôi có thể thử một cách ngẫu nhiên mọi thứ, với hy vọng rằng trình biên dịch sẽ ngừng phàn nàn:

Obj := TFooBar.Create(Self); 

Obj := Pointer(TFooBar.Create(Self)); 

Obj := Pointer(TFooBar.Create(Self) as IFooBar); 

Bỏ qua tất cả các mã tôi đã viết (nếu cần): làm thế nào để tôi thực hiện QueryInterface trong một hậu duệ đối tượng từ TInterfacedObject?


Vấn đề thực sự tôi đã cố gắng để giải quyết có thể được luộc xuống tôi muốn:

tôi muốn ghi đè lên các phương pháp trong một giao diện

Trong cùng một cách :

TList = class(TObject) 
... 
    function GetItem(Index: Integer): Pointer; 
    procedure SetItem(Index: Integer; Value: Pointer); 
    property Items[Index: Integer]: Pointer read GetItem write SetItem; 
end; 

có thể được ghi đè trong lớp hậu duệ:

TStudentList = class(TList) 
... 
    function GetItem(Index: Integer): TStudent; 
    procedure SetItem(Index: Integer; Value: TStudent); 
    property Items[Index: Integer]: TStudent read GetItem write SetItem; 
end; 

tôi muốn nên giống với giao diện:

IFoo = interface(IUnknown) 
... 
    function GetItem(Index: Variant): Variant; 
    procedure SetItem(Index: Variant; Value: Variant); 
    property Items[Index: Variant]: Variant read GetItem write SetItem; 
end; 

IFooGuidString = interface(IFoo) 
... 
    function GetItem(Index: TGUID): string ; 
    procedure SetItem(Index: TGUID; Value: string); 
    property Items[Index: TGUID]: string read GetItem write SetItem; 
end; 

Vấn đề là làm thế nào tôi có để bắt đầu tải lên đối tượng thực hiện của tôi với:

TFoo = class(TInterfacedObject, IFoo, IFooGuidString) 
public 
    function IFoo.GetItem = FooGetItem; 
    procedure IFoo.SetItem = FooSetItem; 
    function FooGetItem(Index: Variant): Variant; 
    procedure FooSetItem(Index: Variant; Value: Variant); 

    function IFooGuidString.GetItem = FooGuidStringGetItem; 
    procedure IFooGuidString.SetItem = FooGuidStringSetItem; 
    function FooGuidStringGetItem(Index: TGUID): string ; 
    procedure FooGuidStringSetItem(Index: TGUID; Value: string); 
end; 

Và có không chỉ là hai phương pháp trong IFoo, có 6. Và sau đó nếu tôi muốn thêm một giao diện được hỗ trợ khác:

IFooInt64String = interface(IFoo) 
... 
    function GetItem(Index: Int64): string ; 
    procedure SetItem(Index: Int64; Value: string); 
    property Items[Index: Int64]: string read GetItem write SetItem; 
end; 


TFoo = class(TInterfacedObject, IFoo, IFooGuidString) 
public 
    function IFoo.GetItem = FooGetItem; 
    procedure IFoo.SetItem = FooSetItem; 
    function FooGetItem(Index: Variant): Variant; 
    procedure FooSetItem(Index: Variant; Value: Variant); 

    function IFooGuidString.GetItem = FooGuidStringGetItem; 
    procedure IFooGuidString.SetItem = FooGuidStringSetItem; 
    function FooGuidStringGetItem(Index: TGUID): string ; 
    procedure FooGuidStringSetItem(Index: TGUID; Value: string); 

    function IFooInt64String.GetItem = FooInt64StringGetItem; 
    procedure IFooInt64String.SetItem = FooInt64StringSetItem; 
    function FooInt64StringGetItem(Index: Int64): string ; 
    procedure FooInt64StringSetItem(Index: Int64; Value: string); 
end; 

Và mọi thứ trở nên thực sự khó sử dụng rất nhanh.

+0

Tôi khuyến khích bạn đăng câu hỏi về "vấn đề thực sự" của bạn. Nghe có vẻ thú vị, và tôi có một ý tưởng mơ hồ về cách làm điều đó (một cái gì đó về * aggregates * và * containers *). Bạn có thể thấy rằng bạn đang viết nhiều mã hơn bạn cần. –

+0

@Rob Kennedy Nếu bạn có thể nghĩ về một tiêu đề hữu ích cho nó, điều đó sẽ tạo ra sự quan tâm, tôi chắc chắn sẽ làm điều đó. tôi thấy rằng nếu không có một móc * tốt, câu hỏi sẽ không được trả lời. Câu hỏi này là tốt, bởi vì câu hỏi, như được diễn đạt, nghe có vẻ dễ dàng - vì vậy tôi đã hút những người nghĩ rằng họ có thể cho tôi một việc học dễ dàng. Mặt khác, bạn và Uwe dường như tuần tra cho các câu hỏi Delphi :) –

Trả lời

6

Bạn cần Type-cast trái bên của câu lệnh gán.Bằng cách đó, các tham số không kiểu có một loại, và trình biên dịch biết làm thế nào để gán cho nó một giá trị:

IFooBar(Obj) := TFooBar.Create(Self) as IFooBar; 

Xin lưu ý rằng bạn đang vi phạm một trong những requirements of COM. Nếu bạn truy vấn cho một giao diện, bạn sẽ có thể truy vấn kết quả cho IUnknown và luôn nhận được giá trị như nhau:

Foo.QueryInterface(IUnknown, I1); 
I1.QueryInterface(IFooBar, B); 
B.QueryInterface(IUnknown, I2); 
Assert(I1 = I2); 

Nếu bạn chỉ muốn tạo ra đối tượng mới của loại TFooBar, sau đó cung cấp giao diện của bạn một phương pháp mà tạo ra những:

function TFoo.NewFooBar: IFooBar; 
begin 
    Result := TFooBar.Create(Self) as IFooBar; 
end; 
+0

tôi có thể giải quyết một vấn đề (luôn luôn trả lại cùng một 'IUnknown') bằng cách sử dụng từ khóa' implements', và ủy quyền giao diện mới cho một lớp bộ điều hợp được tạo trong bộ nạp tài sản. Nhưng có, điều đó vẫn gây ra vấn đề khác của các đối tượng mới mỗi lần. tôi sẽ phải cache 'n'-objects, một cho mỗi giao diện' n' được hỗ trợ. * thở dài * Cập nhật câu hỏi ban đầu với vấn đề thực tế tôi đang cố gắng giải quyết. –

2

Dựa trên việc thực hiện TObject.GetInterface trong System.pas tôi sẽ đề nghị này:

Pointer(Obj) := TFooBar.Create(Self); 
+0

Điều đó không gọi đếm tham chiếu giao diện chính xác. –

+0

Bạn sẽ cần phải gọi '_AddRef' sau đó. Để gọi đó, bạn sẽ cần phải nhập 'Obj' vào một loại giao diện. Tốt hơn là chỉ cần truyền đến một loại giao diện ở vị trí đầu tiên, như trong câu trả lời của tôi. –

+0

Bạn đang đúng _AddRef là cần thiết, tôi nên biên dịch và thử nó trước khi gửi bài. –

2

Bên cạnh những nhận xét của phá vỡ các quy tắc ở đây Rob, bạn thậm chí có thể thành công với cấu trúc này:

function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if IsEqualGUID(IID, IFooBar) then 
     Result := TFooBar.Create(Self).QueryInterface(IID, obj) 
    else 
     Result := inherited QueryInterface(IID, {out}Obj); 
end; 

tôi đã không điều tra này, nhưng bạn có thể gặp một số vấn đề với tính tham chiếu ...

+0

Đó chắc chắn là một ý tưởng khéo léo. Nhưng tôi nghĩ rằng bạn đang phải, tôi sẽ phải thêm một cuộc gọi đến '_AddRef' một nơi nào đó trong đó. –

+0

Tôi không nghĩ rằng bạn sẽ có vấn đề về tính tham chiếu tại đây. Số lượng tham chiếu sẽ là 1 trong khi hàm khởi tạo chạy. Khi hàm khởi tạo trả về, số đếm về 0, nhưng đối tượng không tự xóa (xem 'TInterfacedObject.AfterConstruction'). Sau đó, phương thức 'QueryInterface' của đối tượng mới được gọi, và nó đặt số tham chiếu cho bạn. –

+0

Lệnh QueryInterface() trên đối tượng TFooBar xử lý _AddRef() cần thiết cho bạn. QueryInterface() luôn tăng số lượng tham chiếu của giao diện mà nó trả về. –