2011-01-19 26 views
11

Tôi đã tạo ra một dự án C DLL ++, trong đó có một lớp "myCppClass" và cố gắng DLL xuất nó bằng cách sử dụng đoạn mã sau như mô tả của: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspxLàm thế nào để dllexport một ++ Class C để sử dụng trong một C# ứng dụng

class __declspec(dllexport) CExampleExport : //public CObject 
{ ... class definition ... }; 

Tôi đã bỏ qua "CObject công khai" như yêu cầu afx.h và ngụ ý nó là một dll MFC. Tôi không chắc chắn nếu điều này là một điều tốt hay không nhưng nó khác với các thiết lập mặc định của dự án DLL.

Từ tài liệu được liên kết ở trên, tôi đã dẫn đến tin rằng tất cả "hàm công cộng và biến thành viên" đều có sẵn để nhập. Làm thế nào để thực hiện điều này trong C#? Đơn giản có thể khởi tạo lớp học?

Chỉnh sửa: Tôi vừa nhận ra rằng Tiêu đề của bài đăng có thể gây hiểu nhầm. Sự nhấn mạnh nên được trên DllImport-ing từ C# và đảm bảo rằng tôi đã theo tài liệu chính xác trong C++

+1

Bạn sẽ phải hiển thị DLL COM. Tôi đăng bài này như một bình luận bởi vì tôi không có thời gian để đưa ra một lời giải thích dài dòng hơn. –

Trả lời

13

C# không thể nhập trực tiếp các lớp C++ (đó là giao diện C mang tên hiệu quả).

Tùy chọn của bạn đang hiển thị lớp qua COM, tạo trình bao bọc được quản lý bằng cách sử dụng C++/CLI hoặc hiển thị giao diện kiểu C. Tôi muốn giới thiệu các wrapper quản lý, vì điều này là dễ nhất và sẽ cung cấp cho các loại an toàn nhất.

Một giao diện C-style sẽ giống như thế này (cảnh báo: Mã chưa được kiểm tra):

extern "C" __declspec(dllexport) 
void* CExampleExport_New(int param1, double param2) 
{ 
    return new CExampleExport(param1, param2); 
} 

extern "C" __declspec(dllexport) 
int CExampleExport_ReadValue(void* this, int param) 
{ 
    return ((CExampleExport*)this)->ReadValue(param) 
} 

Một C++/CLI kiểu wrapper sẽ trông như thế này (cảnh báo: Mã chưa được kiểm tra):

ref class ExampleExport 
{ 
private: 
    CExampleExport* impl; 
public: 
    ExampleExport(int param1, double param2) 
    { 
     impl = new CExampleExport(param1, param2); 
    } 

    int ReadValue(int param) 
    { 
     return impl->ReadValue(param); 
    } 

    ~ExampleExport() 
    { 
     delete impl; 
    } 
}; 
+0

Bạn không thể 'dynamic_cast' từ' void * '. Cũng có thể sử dụng kiểu con trỏ thực, C# sẽ sử dụng 'IntPtr' trong p/gọi các chữ ký. –

+0

@Ben Bạn nói đúng. Tôi thay đổi nó từ một '(T *)' cast khi proofreading ... thay đổi trở lại ngay bây giờ. Cũng có thể sử dụng kiểu con trỏ thực, nhưng tôi muốn nhấn mạnh sự thiếu an toàn trong cách tiếp cận này. – Zooba

+0

Nếu bạn thích dàn diễn viên kiểu mẫu C++, thì 'static_cast' là phần được sử dụng. –

4

Bạn không thể tạo một cá thể lớp C++ thông qua pinvoke từ C#. Đây là một chi tiết thực hiện rắc rối, chỉ có trình biên dịch C++ biết bao nhiêu bộ nhớ cần được cấp phát và khi nào và làm thế nào để gọi đúng hàm tạo và hàm hủy. Kích thước vật thể là hạt khó nhất để crack, không có cách nào để làm cho điều đó đáng tin cậy.

Nếu bạn không thể làm phẳng lớp C++ thành các phương thức tĩnh thì bạn cần viết một trình bao bọc được quản lý. Điều đó được thực hiện với ngôn ngữ C++/CLI, bạn sẽ viết một "lớp ref" có đối tượng lớp không được quản lý được lưu trữ như một con trỏ, được tạo trong hàm dựng và bị xóa trong trình phá hủy và trình kết thúc.

9

Theo như tôi biết, C# chỉ có thể tương tác với giao diện COM. May mắn nó không cần phải là một đối tượng COM đầy đủ thổi với registry, nó có thể là bất kỳ đồng bằng cũ C + + lớp thực hiện IUnknown.

Vì vậy, làm một cái gì đó như thế này trong C++:

#include <Windows.h> 

// Generate with from VisualStudio Tools/Create Guid menu 
static const GUID IID_MyInterface = 
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } }; 

struct MyInterface: public IUnknown 
{ 
    // add your own functions here 
    // they should be virtual and __stdcall 
    STDMETHOD_(double, GetValue)() = 0; 
    STDMETHOD(ThrowError)() = 0; 
}; 

class MyClass: public MyInterface 
{ 
    volatile long refcount_; 

public: 
    MyClass(): refcount_(1) { } 

    STDMETHODIMP QueryInterface(REFIID guid, void **pObj) { 
     if(pObj == NULL) { 
      return E_POINTER; 
     } else if(guid == IID_IUnknown) { 
      *pObj = this; 
      AddRef(); 
      return S_OK; 
     } else if(guid == IID_MyInterface) { 
      *pObj = this; 
      AddRef(); 
      return S_OK; 
     } else { 
      // always set [out] parameter 
      *pObj = NULL; 
      return E_NOINTERFACE; 
     } 
    } 

    STDMETHODIMP_(ULONG) AddRef() { 
     return InterlockedIncrement(&refcount_); 
    } 

    STDMETHODIMP_(ULONG) Release() { 
     ULONG result = InterlockedDecrement(&refcount_); 
     if(result == 0) delete this; 
     return result; 
    } 

    STDMETHODIMP_(DOUBLE) GetValue() { 
     return 42.0; 
    } 

    STDMETHODIMP ThrowError() { 
     return E_FAIL; 
    } 
}; 

extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance() 
{ 
    return new MyClass(); 
} 

Và trên C# bên bạn làm điều gì đó như thế này:

[ComImport] 
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")] 
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
interface MyInterface 
{ 
    [PreserveSig] double GetValue(); 
    void ThrowError(); 
} 

class Program 
{ 
    [DllImport("mylib.dll")] 
    static extern MyInterface CreateInstance(); 

    static void Main(string[] args) 
    { 
     MyInterface iface = CreateInstance(); 
     Console.WriteLine(iface.GetValue()); 
     try { iface.ThrowError(); } 
     catch(Exception ex) { Console.WriteLine(ex); } 
     Console.ReadKey(true); 
    } 
} 

Bạn có thể làm khá nhiều bất cứ điều gì bạn muốn bằng cách này, miễn khi giao tiếp giữa C++ và C# đi qua giao diện ảo.

0

Thực ra, bạn có thể trực tiếp tham khảo tên bị cắt xén bằng cách sử dụng thuộc tính EntryPoint thuộc tính DllImport. Xem this answer để biết thêm chi tiết.

0

C# và C++ KHÔNG ABI tương thích như C++ và Delphi, vì vậy bạn không thể xuất thành viên lớp ảo (phương pháp) và tuyên bố họ hoàn toàn ảo ở phía bên gọi một gọi họ, bởi vì C# không thể xử lý của vtbl của các đối tượng C++. Tôi sẽ đề nghị bạn quấn các lớp C++ của bạn bằng COM, vì vậy bạn có một hiệu ứng phụ tích cực, rằng các ngôn ngữ tương thích COM khác cũng có thể sử dụng các lớp của bạn.