2010-11-10 46 views
5

Hans Passantwishes đây là kịch bản của tôi. Tôi có một ứng dụng chế độ hỗn hợp trong đó mã gốc thực hiện tất cả công việc khó khăn trong khi tôn trọng hiệu suất và mã được quản lý chỉ chịu trách nhiệm đối với GUI. Ngoài ra, người dùng sẽ tham gia bằng cách viết mã C# độc quyền của họ. Tôi có C++ cho các lớp bản địa, C# cho GUI và mã người dùng và C++/Cli cho các lớp bao bọc ở giữa. Trong số tất cả các lớp C++ của tôi có một trong đó có% 90 của các phép tính và được tạo ra một tham số khác nhau mỗi lần. Hãy gọi nó là NativeClass. Có apprx. 2000 phiên bản của lớp NativeClass này và tôi phải tìm đúng cá thể liên quan đến một số tham số trước khi nó tính toán. Vì vậy, tôi nghĩ ra một hash_map, với các tham số là mã băm, cho mục đích này. Khi tôi nhận được một tham số, tôi tìm kiếm một thể hiện đúng trong hash_map, tôi tìm thấy nó và gọi một số phương thức của nó.
Khi người dùng contrubute để tính toán bằng cách viết mã C# và lớp này thực thi các mã này bằng callbacks. Điều này là tầm thường nhưng đôi khi tôi cần một số thông tin về các lớp .Net mà người dùng xây dựng. Vì vậy, tôi cần phải đính kèm cụ thể ManagedClass để NativeClass bằng cách nào đó. Giải pháp đầu tiên của tôi là sử dụng GChandle.Alloc() và chuyển địa chỉ xử lý. Nhưng có một số concerns về GC rằng nó sẽ không được thực hiện công việc của mình đúng cách. Hans đã đề xuất Marshal.AllocCoTaskMem() và Marshal.StructureToPtr() để phân bổ các đối tượng được quản lý trong bộ nhớ không được quản lý, tuy nhiên tôi tin rằng điều này là hợp lệ đối với các lớp hoặc cấu trúc kiểu giá trị. Làm thế nào về các lớp học ref? Làm thế nào tôi có thể vượt qua một tham chiếu đến NativeClass trong khi ngăn chặn chúng được GC thu thập và làm cho GC hoạt động đúng vào thời điểm này?GCHandle, Marshal, bộ nhớ được quản lý và không được quản lý: Để ghim hoặc không ghim

Dưới đây là một số mẫu mã:

class NativeClass 
{ 
private: 
    int AddressOfManagedHandle; 
public: 
    static NativeClass * GetNativeClassFromHashMap(int SomeParameter) 
    { 
// return NativeClass associated with SomeParameter from NativeClassHashMap; 
    } 
    NativeClass(int addr, int SomeParameter) : AddressOfManagedHandle(addr) 
    { 

    } 
    int GetAddress(){return AddressOfManagedHandle;} 
void DoCalculation(){ 
// CALCULATIONS 
} 
}; 


public ref class ManagedClass : MarshalByRefObject 
{ 
private: 
    NativeClass* _nc; 
//GCHandle handle; 
    void FreeManagedClass() 
    { 
     Marshal::FreeHGlobal(IntPtr(_nc->GetAddress())); 
//if(handle.IsAllocated) 
//handle.Free(); 
     delete _nc; 
    } 
public: 
    ManagedClass() 
    { 
     IntPtr addr = (Marshal::AllocHGlobal(Marshal::Sizeof(this))); // Error 
     Marshal::StructureToPtr(this,addr,true); 
//handle = GCHandle.Alloc(this); 
//IntPtr addr = handle.ToIntPtr(); 
     _nc = new NativeClass(addr.ToInt32()); 
    } 
    ~ManagedClass() {FreeManagedClass();} 
    !ManagedClass() {FreeManagedClass();} 
    int GetAddress() {return _nc->GetAddress();}; 
    static ManagedClass^ GetManagedClass(int SomeParameter) 
    { 
int addr = NativeClass::GetNativeClassFromHashMap(SomeParameter)->GetAddress(); 
//Object^obj = GCHandle::FromIntPtr(IntPtr(addr)).Target; 
Object^ obj = Marshal::PtrToStructure(IntPtr(addr), ManagedClass::typeid); 
    return dynamic_cast<ManagedClass^>(obj); 

    } 
}; 

Tôi xin lỗi rằng nó là toooooo dài và vẫn chưa rõ ràng.

+1

Bạn nên sử dụng IntPtr thay vì int để lưu trữ con trỏ gốc. Nếu không, mã của bạn có thể gặp sự cố trên Windows 64 bit. – Elmue

+0

Bạn không nên giải phóng bộ nhớ trong một lớp khác. Viết một hàm hủy (finalizer) ~ NativeClass() gọi FreeHglobal(). – Elmue

Trả lời

3

tôi đã dành nhiều thời gian chiến đấu với một vấn đề tương tự và đây là phác thảo của các giải pháp mà làm việc cho tôi ....

  1. Store xử lý đến lớp quản lý như một cửa hàng void *
  2. một con trỏ đến lớp không được quản lý trong lớp quản lý (như bạn đã làm)
  3. Sử dụng đồng bằng cũ newdelete chứ không phải là bất cứ điều gì như AllocHGlobal
  4. Chuyển đổi GCHandle giữa voi d * và quản lý tài liệu tham khảo đối tượng
  5. Đừng lo lắng về việc suy luận từ MarshalByRefObject - bạn không cần nó như là bạn đang làm marshalling của riêng bạn
  6. Ghi mã hóa phòng thủ khi giải phóng lớp quản lý

tôi mất mã trên của bạn và tấn công vào mã đó để nhận được:

class NativeClass 
{ 
private: 
    void * managedHandle; 
public: 
    static NativeClass * GetNativeClassFromHashMap(int SomeParameter) 
    { 
     // return NativeClass associated with SomeParameter from NativeClassHashMap; 
    } 
    NativeClass(void *handle, int SomeParameter) 
     : managedHandle(handle) 
    { 
    } 
    void * ManagedHandle() 
    { 
     return managedHandle; 
    } 
    void DoCalculation() 
    { 
     // CALCULATIONS 
    } 
}; 

public ref class ManagedClass 
{ 
private: 
    NativeClass* _nc; 
    void FreeManagedClass() 
    { 
     if (_nc) 
     { 
      // Free the handle to the managed object 
      static_cast<GCHandle>(IntPtr(_nc->ManagedHandle)).Free(); 
      // Delete the native object 
      delete _nc; 
      _nc = 0; 
     } 
    } 
public: 
    ManagedClass() 
    { 
     // Allocate GCHandle of type 'Normal' (see doco for Normal, Weak, Pinned) 
     GCHandle gch = GCHandle::Alloc(this, GCHandleType::Normal); 
     // Convert to void* 
     void *handle = static_cast<IntPtr>(gch).ToPointer(); 
     // Initialise native object, storing handle to native object as void* 
     _nc = new NativeClass(handle); 
    } 
    ~ManagedClass() {FreeManagedClass();} 
    !ManagedClass() {FreeManagedClass();} 
    static ManagedClass^ GetManagedClass(int SomeParameter) 
    { 
     // Native class is retrieved from hash map 
     NativeClass *nc = NativeClass::GetNativeClassFromHashMap(SomeParameter); 
     // Extract GCHandle from handle stored in native class 
     // This is the reverse of the process used in the ManagedClass constructor 
     GCHandle gch = static_cast<GCHandle>(IntPtr(nc->ManagedHandle())); 
     // Cast the target of the GCHandle to the managed object 
     return dynamic_cast<ManagedClass^>(gch.Target); 
    } 
}; 

Điều này sẽ giúp bạn đi đúng hướng.

+2

Lợi thế của phương pháp này khi sử dụng gcroot là NativeClass có thể nằm trong một C++ DLL riêng biệt được tiêu thụ bởi trình bao bọc C++/CLI. Đây là những gì tôi cần. – mcdave

+0

Có mã của riêng tôi, tôi đang sử dụng ngay bây giờ, giống như của bạn. Những thứ với lớp Marshal không hoạt động. Nhưng tôi hỏi câu hỏi chỉ để chắc chắn về những gì đang xảy ra. Chỉ có sự khác biệt là bạn không lưu trữ xử lý để ManagedClass trong ManagedClass và NativeClass có địa chỉ xử lý như một con trỏ không phải là một số nguyên. Hãy để tôi hỏi 3 câu hỏi. mã này đã chạy bao lâu rồi? Bạn có bất kỳ vấn đề liên quan đến bộ nhớ? Ứng dụng của bạn là gì? –

+0

Đã làm việc trong ~ 3 năm; Không có vấn đề về bộ nhớ trừ khi bạn quên giải phóng tay cầm được quản lý, trong trường hợp các đối tượng được quản lý không bao giờ được thu gom rác; Ứng dụng này là một ứng dụng kỹ thuật, giống như ứng dụng của bạn, yêu cầu hiệu suất của mã gốc trong khi có giao diện người dùng trong .NET. – mcdave

0

Hmmm.

GCHandle là cấu trúc.

Trong một số trường hợp, việc chuyển giao một GCHandle được bỏ ghim sẽ làm những gì bạn muốn.

+0

Tôi cũng nghĩ thế. GCHandle.Alloc với GCHandleTypes.Có vẻ như ok với tôi. Đó là những gì MSDN nói 'Loại xử lý này đại diện cho một xử lý mờ, có nghĩa là bạn không thể giải quyết địa chỉ của đối tượng được ghim thông qua tay cầm. Bạn có thể sử dụng loại này để theo dõi một đối tượng và ngăn chặn bộ sưu tập của nó bằng bộ thu gom rác. Thành viên liệt kê này rất hữu ích khi một máy khách không được quản lý giữ tham chiếu duy nhất, mà không thể phát hiện được từ bộ thu gom rác, tới đối tượng được quản lý.' trên GCHandle.Normal. –

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