2016-12-02 53 views
6

Cân nhắc viết chức năng tùy chỉnh có thể tái sử dụng bên trong cơ thể của nó tạo đối tượng COM và gọi phương thức đến một số giao diện COM. Để làm việc này đúng, CoInitializeEx và các API phù hợp CoUninitialize phải được gọi.Khởi tạo và dọn dẹp COM thích hợp ở mức chi tiết cấp chức năng?

Gọi các API khởi tạo và dọn dẹp COM bên trong nội dung của hàm sẽ ẩn chi tiết triển khai COM cho người gọi và cũng sẽ xóa gánh nặng khỏi người gọi.

Nhưng đang gọi CoInitializeEx và nội dung phù hợp CoUninitialize bên trong chức năng được coi là thực hành mã hóa tốt?

Gọi những hàm COM/cleanup COM ở mức độ chi tiết chức năng hàm ý quá nhiều chi phí cho mỗi cuộc gọi hàm?

Có những hạn chế nào khác trong thiết kế này không?

+0

"* drawbacks *" cuộc gọi lồng nhau hoặc đệ quy của các chức năng như vậy vẫn có thể thực hiện được? – alk

+0

@alk - vâng, chức năng này có thể được gọi là đệ quy – RbMm

+0

Tôi cảm thấy đây không chỉ là câu hỏi cụ thể về C/C++. – alk

Trả lời

6

Đây là một thực tế khủng khiếp và về cơ bản là sai. Điều quan trọng là một giá trị lớn cho đối số thứ 2 (dwCoInit). Nó phải là COINIT_APARTMENTTHREADED, thường được viết tắt là STA hoặc COINIT_MULTITHREADED (MTA). Đây là lời hứa mà bạn thực hiện, kiểu cách hy vọng-chết-trái tim của bạn. Nếu bạn phá vỡ lời hứa thì chương trình sẽ chết. Thông thường do bế tắc, không nhận được sự kiện dự kiến ​​hoặc có sự hoàn hảo không được chấp nhận chậm.

Khi bạn chọn STA thì bạn hứa rằng luồng được xử lý tốt và có thể hỗ trợ các thành phần COM không an toàn chỉ. Thực hiện lời hứa đó yêu cầu rằng luồng bơm vòng lặp thông điệp và không bao giờ chặn. Hành vi chung của một luồng hỗ trợ GUI chẳng hạn. Phần lớn các thành phần COM không phải là chủ đề an toàn.

Khi bạn chọn MTA, bạn không hứa hẹn bất kỳ hỗ trợ nào. Thành phần bây giờ phải tự bảo vệ để giữ cho chính nó an toàn. Thường được thực hiện tự động bằng cách có cơ sở hạ tầng COM tạo ra một chủ đề của chính nó để cung cấp cho các thành phần một ngôi nhà an toàn. Một chi tiết khác mà bạn cần quan tâm là marshaling con trỏ giao diện, yêu cầu hàm trợ giúp CoMarshalInterThreadInterfaceInStream() hoặc giao diện IGlobalInterfaceTable thuận tiện hơn. Điều này đảm bảo rằng một proxy được tạo ra để quản lý công tắc ngữ cảnh chủ đề yêu cầu.

MTA nghe có vẻ thuận tiện, nhưng không phải không có hậu quả, cuộc gọi getter thuộc tính đơn giản có thể mất nhiều gấp x10000 lần. Overhead áp đặt bởi các thiết bị chuyển mạch bối cảnh thread và cần phải sao chép bất kỳ đối số và giá trị trả về trên khung stack. Và marshaling con trỏ giao diện có thể dễ dàng thất bại, tác giả của các thành phần COM thường không cung cấp proxy/stub cần thiết hoặc họ cố tình bỏ qua nó bởi vì nó chỉ đơn giản là quá khó khăn hoặc tốn kém để sao chép dữ liệu.

Điểm mấu chốt là sự lựa chọn giữa STA và MTA không bao giờ có thể được thực hiện bởi một thư viện. Nó không biết đậu về chủ đề, nó không tạo ra chủ đề đó. Và không thể biết nếu luồng bơm vòng lặp tin nhắn hoặc khối. Đó là tất cả các mã hoàn toàn nằm ngoài tầm với của thư viện. Nếu không, lý do chính xác mà cơ sở hạ tầng COM cần phải biết điều này là tốt, nó tương tự như vậy không thể làm cho các giả định về chủ đề.

Lựa chọn phải được tạo bởi mã đã tạo và khởi tạo chuỗi, luôn luôn là chính ứng dụng. Trừ khi thư viện tạo một chuỗi cho mục đích thực hiện cuộc gọi an toàn. Nhưng sau đó với hậu quả của mã luôn luôn được làm chậm. Bạn nhắc nhở người gọi thư viện của mình rằng anh ấy không hiểu đúng bằng cách trả về mã lỗi CO_E_NOTINITIALIZED không thể tránh khỏi.

Fwiw, đây là thứ bạn thấy trong .NET Framework. CLR luôn gọi CoInitializeEx() trước khi một luồng có thể thực thi bất kỳ mã được quản lý nào. Vẫn là một lựa chọn phải được thực hiện bởi các lập trình viên của ứng dụng, hoặc thường là mẫu dự án, được thực hiện với thuộc tính [STAThread] trên Main() hoặc Thread.SetApartmentState() gọi cho một chuỗi công nhân.

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