2011-01-04 28 views
18

Tôi có một ứng dụng sử dụng hội đồng Interop Office. Tôi biết về "Runtime Callable Wrapper (RCW)" được quản lý bởi thời gian chạy. Nhưng tôi không chắc chắn làm thế nào số lượng tham chiếu được tăng lên. MSDN nói,RCW & đếm tham chiếu khi sử dụng COM interop trong C#

RCW chỉ giữ một tham chiếu đến đối tượng COM được bao bọc bất kể số lượng khách hàng được quản lý gọi là bao nhiêu.

Nếu tôi hiểu nó một cách chính xác, trên ví dụ sau,

using Microsoft.Office.Interop.Word; 

static void Foo(Application wrd) 
{ 
    /* .... */ 
} 

static void Main(string[] args) 
{ 
    var wrd = new Application(); 
    Foo(wrd); 
    /* .... */ 
} 

Tôi đi qua các ví dụ wrd đến phương pháp khác. Nhưng điều này không làm tăng số lượng tham chiếu nội bộ. Vì vậy, tôi tự hỏi về những kịch bản số tham chiếu được tăng lên? Bất cứ ai có thể chỉ ra một kịch bản mà số tham chiếu được tăng lên?

Ngoài ra tôi đọc một số blog nói rằng tránh sử dụng dấu chấm kép khi lập trình với các đối tượng COM. Một cái gì đó như, wrd.ActiveDocument.ActiveWindow. Tác giả tuyên bố rằng trình biên dịch tạo các biến riêng biệt để giữ các giá trị sẽ tăng bộ đếm tham chiếu. IMHO, điều này là sai và ví dụ đầu tiên chứng minh điều này. Đúng không?

Mọi trợ giúp sẽ tuyệt vời!

+0

Marshall.AddRef & Marshall.Release trở đếm tham chiếu mới cho các đối tượng COM. Bạn không chắc chắn nó chính xác như thế nào nhưng ít nhất bạn có thể kiểm tra yêu cầu của tác giả. – Arseny

Trả lời

40

Tôi cũng đã nghiên cứu câu hỏi này, làm việc trên ứng dụng trung tâm COM/.Net-Interop, chống rò rỉ, treo máy và treo máy.

Câu trả lời ngắn: Mỗi khi đối tượng COM được truyền từ môi trường COM sang .NET.

Long trả lời:

  1. Đối với mỗi đối tượng COM có một RCW đối tượng [Test 1] [Ref 4]
  2. đếm tham chiếu được tăng lên mỗi lần đối tượng được yêu cầu từ bên trong đối tượng COM (tài sản gọi hoặc phương thức trên đối tượng COM trả về đối tượng COM, số tham chiếu đối tượng COM trả về sẽ được tăng thêm một) [Test 1]
  3. Số tham chiếu không được tăng lên bằng cách truyền tới giao diện COM khác của đối tượng hoặc di chuyển tham chiếu RCW xung quanh [ Kiểm tra 2]
  4. Referenc e đếm được tăng lên mỗi lần một đối tượng được thông qua như là một tham số trong trường hợp nêu ra bởi COM [Ref 1]

Trên một mặt lưu ý: Bạn nên LUÔN phát hành COM đối tượng ngay sau khi bạn đã kết thúc việc sử dụng chúng. Rời khỏi công việc này với GC có thể dẫn đến rò rỉ, hành vi bất ngờ và sự kiện deadlocks. Điều này quan trọng hơn gấp mười lần nếu bạn truy cập đối tượng không phải trên chuỗi STA mà nó đã được tạo trên đó. [Ref 2] [Ref 3] [Kinh nghiệm cá nhân đau đớn]

Tôi hy vọng tôi đã bao trả tất cả các trường hợp, nhưng COM là một cookie khó khăn. Chúc mừng.

Test 1 - số tài liệu tham khảo

private void Test1(_Application outlookApp) 
{ 
    var explorer1 = outlookApp.ActiveExplorer(); 
    var count1 = Marshal.ReleaseComObject(explorer1); 
    MessageBox.Show("Count 1:" + count1); 

    var explorer2 = outlookApp.ActiveExplorer(); 
    var explorer3 = outlookApp.ActiveExplorer(); 
    var explorer4 = outlookApp.ActiveExplorer(); 

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4); 
    var count2 = Marshal.ReleaseComObject(explorer4); 
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals); 
} 
Output: 
Count 1: 4 
Count 2: 6, Equals: True 

Test 2 - tài liệu tham khảo đếm tiếp.

private static void Test2(_Application outlookApp) 
{ 
    var explorer1 = outlookApp.ActiveExplorer(); 
    var count1 = Marshal.ReleaseComObject(explorer1); 
    MessageBox.Show("Count 1:" + count1); 

    var explorer2 = outlookApp.ActiveExplorer(); 

    var explorer3 = explorer2 as _Explorer; 
    var explorer4 = (ExplorerEvents_10_Event)explorer2; 
    var explorerObject = (object)explorer2; 
    var explorer5 = (Explorer)explorerObject; 

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5); 
    var count2 = Marshal.ReleaseComObject(explorer4); 
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals); 
} 
Output: 
Count 1: 4 
Count 2: 4, Equals: True 

Nguồn tôi tiếp trên ngoài việc trải nghiệm và thử nghiệm của tôi:

1. Johannes Passing's - RCW Reference Counting Rules != COM Reference Counting Rules

2. Eran Sandler - Runtime Callable Wrapper Internals and common pitfalls

3. Eran Sandler - Marshal.ReleaseComObject and CPU Spinning

4. MSDN - Runtime Callable Wrapper

+0

cảm ơn rất nhiều. Tôi sẽ xác minh các bài kiểm tra của bạn và quay lại. –

+0

Những thử nghiệm này không tính đến thực tế là có thực sự HAI bộ đếm tham chiếu, thực tế là một đối tượng COM (chỉ được tăng lên một lần bởi CLR) và bộ điều khiển tham chiếu được quản lý trong RCW có số lượng như số số lần đối tượng được truyền từ thế giới bản địa (tức là số lần bạn muốn gọi 'ReleaseComObject'). Bài kiểm tra của bạn chỉ đo thứ hai, mà là một câu trả lời một phần cho câu hỏi. – hypersw

-2

Bạn cần gọi số Marshal.ReleaseComObject trên biến số wrd để giải phóng tham chiếu đến ứng dụng từ.

Bằng cách đó, nếu Word không hiển thị và bạn đóng ứng dụng của mình, thì exe cũng sẽ tải xuống trừ khi bạn đã hiển thị nó cho người dùng.

+0

cảm ơn, nhưng điều này không trả lời câu hỏi. Tôi biết về các phương thức 'Marshal.ReleaseComObject' và' Marshal.FinalReleaseComObject'. Câu hỏi của tôi là nhiều hơn về cách tính toán tham chiếu nội bộ hoạt động và tại điểm nào nó được tăng lên. –

1

Bạn không cần phải điều trị đặc biệt. Thời gian chạy chỉ giữ một tham chiếu đến đối tượng COM. Lý do cho điều này là GC theo dõi tất cả các tài liệu tham khảo được quản lý, vì vậy khi RCW đi ra khỏi phạm vi và được thu thập, các tài liệu tham khảo COM được phát hành. Khi bạn chuyển qua một tham chiếu được quản lý, GC sẽ theo dõi nó cho bạn - đây là một trong những lợi thế lớn nhất của thời gian chạy dựa trên GC trên lược đồ AddRef/Release cũ.

Bạn không cần phải gọi thủ công Marshal.ReleaseComObject trừ khi bạn muốn phát hành xác định hơn.

+0

Câu hỏi không phải là về việc sử dụng Marshal.ReleaseComObject hoặc một cái gì đó tương tự. Tôi đã cố gắng để hiểu cách tính toán tham chiếu hoạt động và vào thời điểm nào nó sẽ được tăng lên. –

+1

Chính xác. Nếu bạn đọc lại, bạn sẽ lưu ý rằng tôi nói chính xác điều này. Chỉ có một tham chiếu COM. Nó không bao giờ được tăng lên. Tất cả việc theo dõi RCW được thực hiện bởi GC. – codekaizen

3

Tôi chưa thấy mã cho RCW - thậm chí không chắc chắn đó là một phần của SSCLI - nhưng tôi phải triển khai một hệ thống tương tự để theo dõi tuổi thọ đối tượng COM trong SlimDX và phải thực hiện một chút nghiên cứu công bằng vào RCW. Đây là những gì tôi nhớ, hy vọng nó hợp lý chính xác nhưng mang nó với một liên lạc của muối.

Khi hệ thống đầu tiên nhìn thấy con trỏ giao diện COM, nó chỉ chuyển đến bộ đệm để xem có RCW cho con trỏ giao diện đó hay không. Có lẽ bộ đệm sẽ sử dụng các tham chiếu yếu, để không ngăn chặn việc hoàn thành và thu thập RCW.

Nếu có trình bao bọc trực tiếp cho con trỏ đó, hệ thống trả về trình bao bọc - nếu giao diện thu được theo cách tăng số lượng tham chiếu của giao diện, có lẽ hệ thống RCW sẽ gọi Release() tại điểm này. Nó đã tìm thấy một trình bao bọc trực tiếp, vì vậy nó biết rằng trình bao bọc là một tham chiếu duy nhất và nó muốn duy trì chính xác một tham chiếu. Nếu không có trình bao bọc trực tiếp trong bộ nhớ cache, nó sẽ tạo một trình bao bọc mới và trả về nó.

Trình bao bọc gọi phát hành trên (các) con trỏ giao diện COM bên dưới từ trình kết thúc.

Trình bao bọc nằm giữa bạn và đối tượng COM và xử lý tất cả các tham số marshaling. Điều này cũng cho phép nó lấy kết quả thô của bất kỳ phương thức giao diện nào mà chính nó là một con trỏ giao diện khác và chạy con trỏ đó qua hệ thống bộ nhớ đệm RCW để xem nó có tồn tại chưa trước khi trả về con trỏ giao diện được bọc.

Thật không may là tôi không hiểu rõ về cách thức hệ thống RCW xử lý việc tạo đối tượng proxy để gửi nội dung qua miền ứng dụng hoặc căn hộ luồng; nó không phải là một khía cạnh của hệ thống mà tôi cần để sao chép cho SlimDX.

0

accepted solution hợp lệ, nhưng dưới đây là một số thông tin cơ bản bổ sung.

Một RCW chứa một hoặc nhiều giao diện đối tượng tài liệu tham khảo COM có nguồn gốc nội bộ cho các đối tượng COM của nó.

Khi RCW phát hành đối tượng COM bên dưới, hoặc do thu gom rác hoặc do Marshal.ReleaseComObject() được gọi, nó giải phóng tất cả các giao diện đối tượng COM được giữ bên trong. Có một số giá trị tham chiếu ở đây - một xác định khi RCW của .NET sẽ giải phóng các giao diện đối tượng COM bên dưới, và sau đó mỗi giao diện COM thô có số tham chiếu riêng của nó như trong COM thông thường.

Dưới đây là code để có được COM thô giao diện IUnknown tính tham khảo:

int getIUnknownReferenceCount(object comobject) 
{ 
    var iUnknown = Marshal.GetIUnknownForObject(comObject); 
    return Marshal.Release(iUnknown); 
} 

Và bạn có thể nhận được cùng cho các giao diện COM khác của đối tượng sử dụng Marshal.GetComInterfaceForObject().

Ngoài các cách được liệt kê trong accepted solution, chúng tôi cũng có thể tăng số lượng tham chiếu .NET RCW giả tạo bằng cách gọi một số thứ như Marshal.GetObjectForIUnknown().

Dưới đây là mã ví dụ cách sử dụng kỹ thuật mà để có được đếm RCW tham khảo một COM đối tượng đưa ra:

int comObjectReferenceCount(object comObject) 
{ 
    var iUnknown = Marshal.GetIUnknownForObject(comObject); 
    Marshal.GetObjectForIUnknown(iUnknown); 
    Marshal.Release(iUnknown); 
    return Marshal.ReleaseComObject(comObject); 
} 
Các vấn đề liên quan