2009-10-07 29 views
8

Hy vọng đây là một câu hỏi dễ dàng, nhưng nó cho thấy sự thiếu chuyên môn của tôi với C++. Tôi là một lập trình viên C#, và tôi đã thực hiện công việc mở rộng với P/Invoke trong quá khứ với các tập tin C++/C của người khác. Tuy nhiên, thời gian này tôi đã quyết định viết một wrapper C + + dll (unmanaged) bản thân mình, và sau đó gọi dll wrapper của tôi từ C#.Làm thế nào để thiết lập một hàm C++ sao cho nó có thể được sử dụng bởi p/invoke?

Sự cố tôi ngay lập tức gặp phải là tôi không thể xác định hàm C++ có thể được tìm thấy bằng lệnh p/invoke. Tôi không biết những gì các cú pháp cho việc này, nhưng đây là những gì tôi đang cố gắng cho đến nay:

extern bool __cdecl TestFunc() 
{ 
    return true; 
} 

Ban đầu tôi chỉ đơn giản là có này, nhưng nó đã không làm việc một trong hai:

bool TestFunc() 
{ 
    return true; 
} 

Và sau đó trên C# bên, tôi có:

public const string InterfaceLibrary = @"Plugins\TestDLL.dll"; 

    [DllImport(InterfaceLibrary, CallingConvention = CallingConvention.Cdecl, 
     EntryPoint = "TestFunc"), SuppressUnmanagedCodeSecurity] 
    internal static extern bool TestFunc(); 

Tất cả mọi thứ biên dịch, nhưng khi tôi thực hiện điều này C# p/gọi cuộc gọi, tôi nhận được một System.EntryPointNotFoundException: Không thể tìm thấy một điểm vào tên 'TestFunc' trong DLL ' Plugins \ TestDLL.dll '.

Chắc chắn đây phải là điều cực kỳ đơn giản trên đầu C++ mà tôi không biết cú pháp.

Trả lời

12

Bạn sẽ muốn sử dụng extern "C" cũng như __declspec(export), như vậy:

extern "C" _declspec(dllexport) bool TestFunc() 
{ 
    return true; 
} 

Để biết chi tiết đầy đủ, xem MSDN on Marshalling Types.

+0

Tuyệt vời, điều đó đã làm được! Tôi cũng đã cố gắng chỉ có bên ngoài "C" trong quá khứ, nhưng điều đó đã không làm việc. Nó không thành công cho đến khi _declspec (dllexport) được thêm vào. – x4000

1

Bạn phải hiển thị chức năng này với extern "C" nếu không tên sẽ bị xáo trộn.

1

Trình biên dịch C++ sửa đổi tên của các hàm của bạn để kết hợp thông tin về các tham số và kiểu trả về. Điều này được gọi là mangling tên. Mặt khác, trình biên dịch C không mangle tên hàm của bạn.

Bạn có thể nói với các trình biên dịch C++ để làm việc như một trình biên dịch C sử dụng extern "C":

extern "C" __declspec(dllexport) bool TestFunc { return true; } 

Để gọi chức năng từ C# sử dụng P/Invoke, tên của bạn phải không được đọc sai. Do đó, bạn thực sự có thể xuất các hàm C sang C#. Nếu bạn muốn các chức năng được thực hiện trong C++, bạn có thể viết một hàm C mà chỉ cần gọi hàm C++ thực hiện chức năng.

6

Mở rộng câu trả lời chính xác của Reed.

Một vấn đề khác bạn có thể gặp phải khi hiển thị hàm C++ qua PInvoke đang sử dụng các loại không hợp lệ. PInvoke thực sự chỉ có thể hỗ trợ marshalling của các kiểu nguyên thủy và các kiểu dữ liệu/lớp dữ liệu cũ.

Ví dụ, giả TestFunc có chữ ký sau

void TestFunc(std::string input); 

Thậm chí thêm extern "C" và __declspec(dllexport) sẽ không đủ để lộ những ++ chức năng C. Thay vào đó, bạn sẽ cần phải tạo ra một hàm trợ giúp chỉ hiển thị các loại tương thích PInvoke và sau đó được gọi vào hàm chính.Ví dụ

void TestFunc(const std::string& input) { ... } 

extern "C" _declspec(dllexport) void TestFuncWrapper(char* pInput) { 
    std::string input(pInput); 
    TestFunc(input); 
} 
+0

Vâng, chính vì lý do này mà tôi đang viết một trình bao bọc C++, thay vì cố gắng gọi tất cả các hàm tôi cần trực tiếp. Mục tiêu của tôi là tạo ra một giao diện đơn giản hơn cho P/Invoke, trong khi cho phép tất cả sự phức tạp cần thiết với các callbacks và các kiểu phức tạp, vv, vẫn ở bên C++. Điểm tốt! – x4000

+0

@ x4000, tôi đã thực hiện chính xác cách tiếp cận tương tự trước đây. Ngoại trừ thay vì một DLL mới, tôi chỉ cần thêm các hàm wrapper vào cùng một DLL. Thêm DLL sẽ có được sạch hơn. – JaredPar

+0

DLL ban đầu mà tôi bao bọc là bởi một bên thứ ba, do đó, đó không phải là một lựa chọn cho tôi. Nhưng bạn nói đúng, tôi nghĩ rằng nó sạch hơn trong nhiều khía cạnh để tách các trình bao bọc khỏi nội dung được bao bọc khi có thể. – x4000

1

Do something like this:

#define EXPORT extern "C" __declspec(dllexport) 

Và sau đó tuyên bố bất kỳ chức năng với từ khóa XUẤT KHẨU, ví dụ một C++ chức năng

BOOL getString(TCHAR* string, DWORD size); 

sẽ trở thành

EXPORT BOOL getString(TCHAR* string, DWORD size); 

sau đó là niềm vui phần: Tới console VS của bạn và gõ:

dumpbin /EXPORTS <PATH_TO_GENERATED_DLL> 

và you'l thấy tên đọc sai và thứ tự của tất cả các chức năng một cách dễ dàng xuất khẩu của bạn, sau đó nó chỉ là một vấn đề o pInvoking họ

0

xây dựng tất cả các dự án với Nền tảng Win32 và bit thích hợp (ví dụ x86 hoặc x64) tùy chọn xây dựng.

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