2009-06-29 38 views
7

Đi qua những gì nhìn vào cái nhìn đầu tiên như một vấn đề MT, nhưng tôi đang cố gắng để hiểu chi tiết mô hình STA được sử dụng bởi COM +.Legacy VB6 COM + DLL gọi vào bản địa Win32 DLL - vấn đề luồng với STA?

Có hiệu quả, tôi có một thành phần COM + kế thừa, được viết bằng VB6, mà các cuộc gọi vào một bản địa (ví dụ, không phải COM) Win32 DLL được viết bằng C++.

Có một số vấn đề liên tục (và không thể tái tạo trong thử nghiệm) với nó, tôi đã thêm một số mã gỡ lỗi để tìm hiểu những gì đang diễn ra và thấy rằng khi sự cố xảy ra, tôi có thông điệp tường trình xen kẽ trong tệp - ngụ ý rằng DLL đã được gọi bởi hai chủ đề cùng một lúc.

Bây giờ ghi nhật ký đi vào một tập tin trên mỗi luồng dựa trên _getpid() và GetCurrentThreadId(), do đó, dường như khi mã trong DLL C++ được gọi, nó được gọi hai lần trên cùng một luồng cùng một lúc . Sự hiểu biết của tôi về STA cho biết điều này có thể là trường hợp như COM marshalls trường hợp cá nhân của các đối tượng vào một chuỗi treo đơn và tiếp tục thực hiện theo ý muốn.

Unfortuantly Tôi không chắc chắn nơi để đi từ đây. Tôi đang đọc rằng tôi nên gọi CoInitialiseEx() trong DllMain() để nói với COM rằng đây là một DLL STA, nhưng những nơi khác nói rằng điều này chỉ hợp lệ cho COM DLL và sẽ không có bất kỳ tác dụng trong một DLL bản địa. Lựa chọn duy nhất khác là để bọc các phần của DLL lên như là phần quan trọng để serialize truy cập (lấy bất kỳ hiệu suất hit có trên cằm).

Tôi có thể thử và làm lại DLL, nhưng không có trạng thái chia sẻ hoặc toàn cục - tất cả mọi thứ trong biến cục bộ, vì vậy trong lý thuyết, mỗi cuộc gọi sẽ có ngăn xếp riêng, nhưng tôi tự hỏi liệu mô hình STA có cơ bản không lẻ có hiệu lực về điều này và chỉ cần nhập lại vào DLL đã được nạp tại cùng một điểm vào như một cuộc gọi khác. Thật không may, tôi không biết làm thế nào để chứng minh hoặc kiểm tra lý thuyết này.

Các câu hỏi cơ bản là:

  1. Khi một STA COM + thành phần gọi một DLL có nguồn gốc, không có gì trong mô hình STA để ngăn chặn các hoạt động "chủ đề" bị đình chỉ và điều khiển được truyền qua một "chủ đề "ở giữa cuộc gọi DLL?
  2. CoInitialiseEx() có đúng cách để giải quyết vấn đề này hay không?
  3. Nếu không (1) hoặc (2) là giả định "tốt", điều gì đang xảy ra?
+0

>> nó được gọi hai lần trên cùng một chuỗi cùng một lúc. << Bây giờ điều đó thật buồn cười vì nó không thể là "cùng một lúc". Có lẽ mã này là đệ quy gọi chính nó là tốt nhất. – wqw

Trả lời

1

Trong máy chủ COM được tạo luồng căn hộ, mỗi trường hợp của lớp COM được đảm bảo được truy cập bằng một chuỗi duy nhất. Điều này có nghĩa là trường hợp là chủ đề an toàn. Tuy nhiên, nhiều phiên bản có thể được tạo đồng thời, sử dụng các luồng khác nhau. Bây giờ, như xa như máy chủ COM có liên quan, DLL gốc của bạn không phải làm bất cứ điều gì đặc biệt. Chỉ cần suy nghĩ về kernel32.dll, được sử dụng bởi mỗi thực thi - nó khởi tạo COM khi được sử dụng bởi một máy chủ COM?

Từ quan điểm của DLL, bạn phải đảm bảo rằng bạn là chủ đề an toàn, vì các trường hợp khác nhau có thể gọi cho bạn cùng một lúc. STA sẽ không bảo vệ bạn trong trường hợp này. Vì bạn nói rằng bạn không sử dụng bất kỳ biến toàn cục nào, tôi chỉ có thể giả định vấn đề là ở nơi khác và chỉ xảy ra để hiển thị trên các trường hợp dường như trỏ đến nội dung COM. Bạn có chắc bạn không có một số vấn đề bộ nhớ C++ cũ đơn giản không?

+0

Sự cố chỉ xuất hiện trên hệ thống sản xuất tại thời điểm tải. Mã cho DLL không có biến toàn cầu, chỉ sử dụng người dân địa phương. Tệp nhật ký đầu ra, được đặt tên duy nhất trên cơ sở thread và process id chứa đầu ra xen kẽ, cho thấy rằng nó được gọi hai lần bởi cùng một luồng ở giữa trạng thái. Trong tất cả các môi trường khác, mã thực thi hoàn toàn chính xác. – THEMike

+0

1. Bạn đang gặp phải vấn đề gì? sự cố, treo máy, hành vi sai trái? 2. Bạn có tạo chủ đề hoặc gửi tin nhắn trực tiếp hay gián tiếp không? 3. Hãy thử tạo một minidump (hoặc dump đầy đủ) ngay sau khi bạn xác định một vấn đề, theo dõi stack có thể giúp xác định các vấn đề trong sự thoải mái của env dev của bạn. – eran

+0

Re - "đồng bằng cũ C + + bộ nhớ vấn đề": Có nó đã được điều này cuối cùng, nhưng không phải trong malloc() loại trường, nhưng có một mảng char được định nghĩa là tĩnh. Raymond Chen viết blog về nó ở đây http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx và tôi đã tìm thấy xác nhận về một vài địa điểm khác cũng như các trang web. Tái cấu trúc mã để loại bỏ tĩnh dường như được giải quyết nó. –

0

Tôi nghi ngờ vấn đề của bạn ở đâu đó sâu bên trong DLL được gọi, nó thực hiện cuộc gọi COM đến một căn hộ khác (một chủ đề khác trong cùng một quá trình, hoặc một đối tượng trong MTA hoặc một quy trình khác).COM cho phép một chuỗi STA chờ kết quả của một cuộc gọi đi để nhận cuộc gọi đến khác, xử lý nó một cách đệ quy. Ví dụ: Một cuộc gọi B, B gọi A back, A gọi lại B - nhưng có thể nhận cuộc gọi từ các đối tượng khác nếu bạn đã đưa ra một con trỏ giao diện cho nhiều máy khách hoặc máy khách đã chia sẻ con trỏ giao diện cho một ứng dụng khách khác. Nói chung đó là một ý tưởng tồi để đưa ra các con trỏ giao diện đến một đối tượng đơn luồng cho nhiều luồng máy khách, vì chúng sẽ chỉ phải đợi cho nhau. Tạo một đối tượng công nhân cho mỗi chủ đề.

COM không thể tạm dừng và tiếp tục thực hiện theo ý muốn trên bất kỳ luồng nào - một cuộc gọi đến mới trên chuỗi STA chỉ có thể đến thông qua bơm thông báo. Khi 'bị chặn' chờ phản hồi, chuỗi STA thực sự đang bơm thông báo, kiểm tra bằng Bộ lọc Thư (xem IMessageFilter) xem có nên xử lý thông báo hay không. Tuy nhiên, trình xử lý tin nhắn không được thực hiện cuộc gọi đi mới - nếu họ thực hiện COM sẽ trả về lỗi RPC_E_CANTCALLOUT_INEXTERNALCALL ("Đó là bất hợp pháp khi gọi ra trong khi bên trong bộ lọc thư.")

Sự cố tương tự có thể xảy ra nếu bạn có thông báo bơm (GetMessage/DispatchMessage) bất cứ nơi nào trong DLL gốc. Tôi đã có vấn đề với DoEvents của VB trong các thủ tục giao diện.

CoInitializeEx chỉ nên được gọi bởi tác giả của một chuỗi, bởi vì chỉ có họ biết hành vi bơm thông điệp của họ sẽ là gì. Có khả năng là nếu bạn cố gắng gọi nó trong DllMain nó sẽ đơn giản thất bại, như DLL gốc của bạn đang được gọi để đáp ứng với một cuộc gọi COM, vì vậy người gọi phải có cuối cùng đã được gọi là CoInitializeEx trên thread để thực hiện cuộc gọi. Làm điều đó trong thông báo DLL_THREAD_ATTACH, đối với các luồng mới được tạo ra, có thể hoạt động bề ngoài nhưng làm cho chương trình bị trục trặc nếu COM chặn khi nó cần bơm và ngược lại.

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