2012-04-11 26 views
7

Tôi có một lớp trong ứng dụng .NET 3.5 C# WinForms có năm phương thức. Mỗi phương pháp sử dụng các bộ giao diện C++ COM khác nhau. Đang sử dụng Marshal.FinalReleaseCOMObject để xóa các đối tượng COM này. Mã này hoạt động tốt trên nền tảng .NET này mà không có bất kỳ vấn đề nào. Nhưng khi tôi di chuyển ứng dụng này để .NET 4.0, tôi bắt đầu nhận được lỗi này bằng một trong những phương pháp này tại một đường nơi tôi đúc một biến ICOMInterface1-ICOMInterface2, ví dụ:"Đối tượng COM đã được tách khỏi RCW cơ bản của nó không thể được sử dụng" với .NET 4.0

ICOMInterface1 myVar= obj as ICOMInterface2; 

đối tượng COM có được tách khỏi RCW cơ bản của nó không thể được sử dụng .

Và nếu tôi xóa dòng đang sử dụng Marshal.FinalReleaseCOMObject, tôi không gặp lỗi này.

Tôi thiếu gì ở đây? Và làm thế nào để tôi làm sạch các đối tượng COM không được quản lý từ bộ nhớ trên nền tảng .NET 4.0?

Trả lời

13

Câu trả lời đơn giản là không bao giờ sử dụng Marshal.FinalReleaseComObject trừ khi bạn hoàn toàn phải. Và nếu bạn làm thế, có một số quy tắc bổ sung mà bạn phải tuân theo.

Khi đối tượng COM được sử dụng trong .NET, thời gian chạy sẽ tạo ra cái được gọi là "RCW" hoặc "trình bao bọc có thể gọi thời gian chạy" cho đối tượng đó. RCW này chỉ là một đối tượng bình thường chứa một tham chiếu COM trên đối tượng. Khi đối tượng này được thu thập rác, nó sẽ gọi IUnknown :: Release() trên đối tượng COM, như bạn mong đợi. Điều này có nghĩa là trừ khi đối tượng COM của bạn yêu cầu rằng Bản phát hành cuối cùng() được thực hiện tại một thời điểm nhất định trong thời gian, chỉ cần để người thu gom rác chăm sóc nó. Nhiều đối tượng COM rơi vào trường hợp này, vì vậy, hãy hoàn toàn xác minh rằng bạn phải quản lý cuộc gọi đến Release() một cách cẩn thận.

Vì vậy, khi bạn đang gọi FinalReleaseComObject, về cơ bản là giảm tham chiếu RCW có trên đối tượng COM cho đến khi nó chạm 0 và RCW sau đó phát hành đối tượng COM. Tại thời điểm này, RCW này bây giờ đã được xây dựng, và bất kỳ việc sử dụng nó sẽ đưa ra ngoại lệ mà bạn đã thấy. CLR (theo mặc định) chỉ tạo một RCW duy nhất cho bất kỳ đối tượng COM cơ bản nào, vì vậy điều này có nghĩa là nếu API COM bạn đang sử dụng trả về cùng một đối tượng hai lần, nó sẽ chỉ có một RCW. Gọi FinalReleaseComObject có nghĩa là đột nhiên tất cả sử dụng của RCW đó là bánh mì nướng.

Cách duy nhất để đảm bảo bạn có một Marshal.GetUniqueObjectForIUnknown duy nhất, ngăn chặn mọi chia sẻ RCW. Nhưng như tôi đã nói trước đó, trong hầu hết các API COM, điều này không cần thiết phải làm ngay từ đầu, do đó, đừng làm điều đó.

Paul Harrington đã viết một số tốt blog post về [Final] ReleaseComObject và nó là tệ nạn. Đó là một vũ khí nguy hiểm mà trừ khi cần thiết sẽ chỉ làm tổn thương bạn. Vì bạn đang đặt câu hỏi này, tôi nghi ngờ bạn không thực sự cần phải gọi nó cả. :-)

+0

Cảm ơn Jason cho đầu vào của bạn.Điều này giải thích đúng cho phương pháp ReleaseCOMObject là tốt? Bởi vì, khi tôi sử dụng nó thay vì FinalReleaseCOMObject, tôi vẫn thấy hành vi tương tự ... ie.it hoạt động trên 3,5 nhưng không Trên 4.0.Also, đã tự hỏi những gì đã có .net framework phiên bản đã làm với hành vi này? Có phải vì cách GC công trình được thay đổi bây giờ trong 4,0? – user74042

+1

Vì vậy, RCWs mình có một refcount được giảm đi khi bạn gọi Marshal.ReleaseComObject. FinalReleaseComObject chỉ gọi cho đến khi số lần truy cập được tính bằng không. –

+1

Theo như những gì thay đổi trong CLR gây ra điều này ...thật khó để nói mà không có trình gỡ lỗi. –

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