2013-04-25 30 views
5

Tôi sử dụng COM với ứng dụng VB6 cũ.DispID phải là duy nhất trên các giao diện?

Tôi đã thay đổi mã của mình để sử dụng DispID trong giao diện vì nó có vẻ hoạt động tốt hơn sử dụng [ClassInterface(ClassInterfaceType.AutoDual)].

Nhưng nó được phép bắt đầu trong mỗi giao diện tính từ DispID (1), ngay cả khi một lớp sử dụng hai giao diện?

Ứng dụng có hoạt động theo cách này ổn định không? Hay tôi có hiểu nhầm điều gì đó?

[ComVisible(true)] 
[Guid("9E1125A6-...")] 
public interface IMyInterface1 
{ 
    [DispId(1)] 
    string Name1 { get; } 
} 

[ComVisible(true)] 
[Guid("123425A6-...")] 
public interface IMyInterface2 
{ 
    [DispId(1)] 
    string Name2 { get; } 
} 

[ComVisible(true)] 
[ClassInterface(ClassInterfaceType.None)] 
class MyClass : IMyInterface1, IMyInterface2 
{ 
    public string Name1 { get { return "Name1"; } } 
    public string Name2 { get { return "Name2"; } } 
} 
+2

Giao diện đầu tiên bạn liệt kê là giao diện duy nhất mà VB6 có thể thấy khi liên kết muộn. Nó sẽ là giao diện được đánh dấu là giao diện [Mặc định]. Vì vậy, không phải là một vấn đề thực sự vì giao diện khác là không thể sử dụng anyway. Mặc dù đó có thể là một vấn đề thực sự;) –

Trả lời

5

Có được phép bắt đầu trong mỗi giao diện tính từ DispID (1), ngay cả khi một lớp sử dụng hai giao diện?

Các tranh chấp phải là duy nhất trong giao diện. Bạn nên sử dụng hai giao diện, mỗi giao diện có thuộc tính DISPID 1 của riêng mình, ngay cả khi cả hai giao diện đều được thực hiện bởi cùng một đối tượng COM.

Vì VB6 được đề cập, tuy nhiên, bạn cần lưu ý rằng VB6 sẽ không thích hơn 2 giao diện công văn được triển khai trên cùng một đối tượng COM và có thể "chỉ nhìn thấy" đầu tiên/chính. Đó là, vấn đề không phải là DISPID va chạm (mà không phải là một vấn đề ở tất cả), nhưng thực tế là VB6 không thể làm việc một cách chính xác với các đối tượng phơi bày 2 + hai giao diện. Lý do tại sao điều này xảy ra trong mô tả trên MSDN trong Multiple Dual Interfaces:

Bởi vì chỉ có một giao diện IDispatch được tiếp xúc, khách hàng chỉ có thể truy cập vào đối tượng của bạn thông qua giao diện IDispatch sẽ không thể truy cập vào các phương pháp hoặc tài sản ở bất kỳ giao diện khác.

Và thật đáng buồn, đó là trường hợp của VB6. Không giống như các môi trường nâng cao hơn, nó truy vấn các giao diện theo cách mà "các phương thức hoặc các thuộc tính trong bất kỳ giao diện nào khác" không thể truy cập được. Chỉ định DISPID khác nhau sẽ không giúp đỡ ở đó.

2

Chỉ có một IDispatch thực hiện mỗi đối tượng COM, vì vậy nếu bạn muốn có một cuộc gọi như IDispatch::Invoke để thành công, bạn cần phải có DISPIDs độc đáo cho mỗi đối tượng COM.

EDIT: Trong thực tế, sau khi suy nghĩ thêm về nó, câu hỏi là khá không liên quan, như Hans chỉ ra trong bình luận của mình. Bởi vì bạn định nghĩa ClassInterfaceType là None, nó có nghĩa là .NET sẽ chỉ làm cho giao diện đầu tiên của IMyInterface1 có thể sử dụng được (theo mặc định, nhưng bạn có thể cấu hình giao diện mặc định bằng cách sử dụng thuộc tính Class ComDefaultInterfaceAttribute).

Và nếu bạn sử dụng ClassInterfaceType làm AutoDual hoặc AutoDispatch, DISPID sẽ được tạo tự động và xác định thủ công sẽ không được sử dụng. .NET không kết hợp hoặc hợp nhất các giao diện, và thực tế là các phân phối khác nhau là không quan trọng trong trường hợp ".NET tiếp xúc như COM" này, vì chỉ có một bộ DISPID được sử dụng (cho giao diện mặc định).). Lưu ý nếu bạn xác định hai lần cùng một tập hợp các DISPID trên cùng một lớp, nó sẽ biên dịch tốt, nhưng regasm sẽ khiếu nại và bỏ qua những người trùng lặp.

Dưới đây là một chương trình nhỏ C++ xác nhận tất cả điều này:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    CoInitialize(NULL); 
    IDispatch *pDispatch; 
    CoCreateInstance(__uuidof(MyClass), NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pDispatch); 
    DISPID dispid; 
    LPOLESTR name1 = L"Name1"; 
    LPOLESTR name2 = L"Name2"; 
    HRESULT hr; 
    hr = pDispatch->GetIDsOfNames(IID_NULL, &name1, 1, 0, &dispid); 
    printf("Name1:%i hr=0x%08X\n", dispid, hr); 
    hr = pDispatch->GetIDsOfNames(IID_NULL, &name2, 1, 0, &dispid); 
    printf("Name2:%i hr=0x%08X\n", dispid, hr); 
    pDispatch->Release(); 
    CoUninitialize(); 
    return 0; 
} 

Nó sẽ ra này:

Name1:1 hr=0x00000000 (S_OK) 
Name2:-1 hr=0x80020006 (DISP_E_UNKNOWNNAME) 

Nó bạn thay đổi để AutoDispatch hoặc AutoDual, nó sẽ sản lượng này (id được tính sử dụng một số thuật toán):

Name1:1610743812 hr=0x00000000 
Name2:1610743813 hr=0x00000000 
+0

Không có giới hạn của một IDispatch cho mỗi đối tượng COM. Vâng, IDispatch có thể là một, nhưng các đối tượng thực hiện giao diện ** bắt nguồn ** từ IDispatch và số của chúng không bị hạn chế (và chắc chắn DISPID được gán cho các phương thức của các giao diện có nguồn gốc đó chứ không phải IDispacth). –

+0

Tôi không nghĩ rằng tôi đã nói bất cứ điều gì trái với những gì bạn nói. Nhưng kể từ khi cùng một đối tượng COM sẽ luôn luôn trở về cùng một con trỏ IDispatch, tôi hầu như không thấy làm thế nào bạn có thể sử dụng hai phương pháp với cùng một DISPID khi bạn đang sử dụng giao diện IDispatch. Không có gì thể chất thực thi quy tắc đó. Nếu nó hoạt động (và tôi không bao giờ nói mã OP sẽ không hoạt động như vậy, với cùng một Dispid được khai báo hai lần), đó là vì mã không sử dụng giao diện IDispatch ở cuối. –

+0

Vấn đề là vài ứng dụng truy vấn IDispatch. Thay vào đó, họ truy vấn trực tiếp các giao diện có nguồn gốc IDispatch (ví dụ: chúng được phát hiện qua thư viện kiểu), vì vậy không có vấn đề gì khi có nhiều triển khai IDispatch và tất cả chúng hoàn toàn có thể sử dụng được. Không chỉ bởi VB6, nhưng nó giống như một ngoại lệ (giới hạn bất thường). –

Các vấn đề liên quan