2011-12-16 51 views
8

Trong dll của tôi có một phương pháp mà tôi muốn xuất.Xuất dll từ C++ sang C#. Tại sao tôi cần: "extern" C ""

// Làm việc:

extern "C" __declspec(dllexport) 

// Wont việc

__declspec(dllexport) 

C++ Xuất:

extern "C" __declspec(dllexport) int Test(); 

C# nhập khẩu:

[DllImport("CircleGPU2_32.DLL", EntryPoint = "Test", 
    CallingConvention = CallingConvention.StdCall)] 
public static extern int Test(); 

Tại sao tôi cần bên ngoài "C"?

+0

Tôi đã thêm một lời giải thích chi tiết hơn về lý do tại sao nó là cần thiết, nếu bạn quan tâm. – greatwolf

Trả lời

11

Lý do chính là ngăn chặn trình quản lý tên C++ khỏi xâu chuỗi tên của hàm.

Hãy thử xuất nó mà không cần extern "C" và kiểm tra DLL kết quả trong Phụ thuộc Walker và bạn sẽ thấy một tên hoàn toàn khác cho hàm được xuất.

+0

Tên có thể là bất cứ điều gì hoặc có một số mẫu? Thật là một hành vi kỳ lạ .. Cảm ơn – Pedro77

+0

Có một mẫu. Việc mangling mã hóa các loại tham số. Nhưng nó thực hiện cụ thể và thay đổi từ trình biên dịch sang trình biên dịch. Vì vậy, nếu bạn muốn khả năng tương thích nhị phân, bạn cần phải tránh điều đó. –

+0

Mangling được giới thiệu khi quá tải hàm được thêm vào ngôn ngữ. Bjarne muốn sử dụng công cụ liên kết hiện có mà không thể hỗ trợ quá tải (tức là nhiều chức năng khác nhau có cùng tên), vì vậy mangling được phát minh. –

20

Điều này là do tên mangling được thực hiện bởi C++.

extern "C" vs không extern "C"

Như một ví dụ ở đây là những gì CFF Explorer lãm cho bảng xuất khẩu của một dll. Việc đầu tiên được xây dựng với trình biên dịch C++ của borland. Thứ hai được xây dựng với msvc.

Ordinal  FunctionRVA  Name RVA Name 
00000001 0020E140  0032C2B6 createDevice 
00000002 0020E244  0032C2C3 createDeviceEx 
0000000D 00328DA4  0032C0C1 @[email protected]@IdentityMatrix 
0000000E 00328DE4  0032C28A @[email protected]@IdentityMaterial 

0000000C 000F9C80  001EE1B6 createDevice 
0000000D 000F9CE0  001EE1C3 createDeviceEx 
00000001 00207458  001EDDC7 [email protected]@[email protected]@[email protected]@A 
00000002 001F55A0  001EDDF5 [email protected]@[email protected]@[email protected]@[email protected] 

2 chức năng đầu tiên createDevicecreateDeviceEx chứa chữ ký extern "C" trong nguyên mẫu của nó trong khi những người khác thì không. Chú ý sự khác biệt trong mã hóa khi sử dụng C++ mangling. Sự khác biệt thực sự là sâu hơn hơn thế.

ABI & Tiêu chuẩn

Như đã giải thích trong câu trả lời khác, tiêu chuẩn C++ không không định một MộtbstractBinarytôinterface. Điều đó có nghĩa là các nhà cung cấp thiết kế các công cụ của họ có thể làm bất cứ điều gì họ muốn khi nói đến các cuộc gọi chức năng được xử lý như thế nào và quá tải hoạt động như thế nào - miễn là nó thể hiện hành vi mong đợi theo tiêu chuẩn.

Với tất cả các lược đồ mã hóa khác nhau, không có cách nào ngôn ngữ khác có thể có hy vọng làm việc với các mô-đun được biên dịch bằng C++. Các mô-đun Heck được biên dịch với một trình biên dịch C++ không thể làm việc với trình biên dịch khác! Các nhà cung cấp trình biên dịch được tự do thay đổi mã hóa giữa các phiên bản theo quyết định của họ.

Ngoài ra, không có ABI chung có nghĩa là không có cách nào dự kiến ​​phổ biến để gọi vào các chức năng/phương pháp này. Ví dụ, một trình biên dịch có thể vượt qua các đối số của nó trên ngăn xếp trong khi trình biên dịch khác có thể truyền nó trên thanh ghi. Người ta có thể vượt qua các đối số từ trái sang phải trong khi một đối số khác có thể được đảo ngược. Nếu chỉ một trong những khía cạnh này không khớp chính xác giữa người gọi và callee thì ứng dụng của bạn sẽ bị lỗi ... đó là nếu bạn may mắn. Thay vì đối phó với điều này, các nhà cung cấp chỉ đơn giản nói không bằng cách buộc một lỗi xây dựng với các mã hóa khác nhau.

OTOH, trong khi C không có ABI chuẩn hóa, C là ngôn ngữ đơn giản hơn nhiều để giải quyết bằng cách so sánh. Hầu hết các nhà cung cấp trình biên dịch C xử lý trang trí chức năng và cơ chế gọi theo cách tương tự. Kết quả là có một loại tiêu chuẩn 'không thực tế' ngay cả khi ABI không được chỉ định rõ ràng trong tiêu chuẩn. Với tính phổ biến này, nó giúp dễ dàng hơn cho các ngôn ngữ khác giao tiếp với các mô-đun được biên dịch bằng C.

Ví dụ, trang trí __stdcall được ký theo một quy ước cuộc gọi cụ thể. Đối số được đẩy sang phải sang trái và callee có trách nhiệm làm sạch ngăn xếp sau đó. __cdecl tương tự nhưng được hiểu rằng người gọi gọi có trách nhiệm làm sạch ngăn xếp.

Các bottomline

Nếu module trong câu hỏi phải tương thích với các ngôn ngữ bên ngoài của C++, trang trí nó một cách thích hợp và phơi bày nó như là một C API là đặt cược tốt nhất của bạn. Lưu ý rằng bạn đang từ bỏ một số tính linh hoạt bằng cách thực hiện việc này. Đặc biệt, bạn sẽ không thể quá tải các chức năng đó vì trình biên dịch không còn có thể tạo ra các ký hiệu duy nhất cho mỗi quá tải với tên mangling.

Nếu khả năng tương tác không quan trọng đối với mô-đun được đề cập - nó chỉ được sử dụng với cùng công cụ được tạo, sau đó bỏ qua trang trí extern "C" từ nguyên mẫu của bạn.

+0

C không có ABI chuẩn hóa. http://stackoverflow.com/questions/4489012/does-c-have-a-standard-abi –

+0

@Charles doh! Tôi đoan la bạn đung. Tôi đoán tiêu chuẩn de-facto là phù hợp hơn ở đây. – greatwolf

1

Trình biên dịch thông thường trang trí tên đã xuất của bạn để bao gồm thông tin về lớp và chữ ký. extern "C" yêu cầu trình biên dịch không thực hiện điều đó.

+1

Trang trí là một thuật ngữ rộng hơn. Việc sử dụng 'extern" C "' dừng lại một dạng trang trí, mangling, nhưng trang trí khác vẫn có thể tồn tại. Ví dụ, các phương thức 'stdcall' thường được trang trí với tiền tố' _' và hậu tố '@ N', trong đó N là kích thước ngăn xếp. http://msdn.microsoft.com/en-us/library/zxk0tw93(v=VS.100).aspx –

0

Tôi đã thử điều này với một hàm với 1 tham số và bạn phải sử dụng

[DllImportAttribute ("whatever.Dll", CallingConvention = CallingConvention. cdecl)]

để nhập trong C# để hoạt động. Stdcall tuyên bố chỉ hoạt động (trong tình hình trình bày, không nói chung) cho các chức năng không có tham số và trả về void. Được thử nghiệm trong phiên bản express2012.

Là một mặt lưu ý, phụ thuộc walker có thể được tải về từ http://www.dependencywalker.com/