2011-01-05 27 views
24

Tôi đã hàm sau trong C++ DLLC# DllImport với C++ boolean chức năng không trở lại một cách chính xác

extern "C" __declspec(dllexport) bool Exist(const char* name) 
{ 
//if (g_Queues.find(name) != g_Queues.end()) 
// return true; 
//else 
// return false; 
return false; 
} 

Bên trong C# lớp tôi có như sau:

[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] 
     public static extern bool Exist(string name); 

Tuy nhiên, bất cứ khi nào tôi gọi tôi chức năng nó luôn luôn trả về đúng, ngay cả khi tôi nhận xét ra chức năng nhỏ của tôi và làm cho nó trở lại sai. Tôi có cảm giác có cái gì đó sai với quy ước gọi điện thoại của tôi hoặc bất kỳ vấn đề khác với P/Gọi DLL của tôi, có thể tương ứng với chuỗi và const char *, nhưng bây giờ tôi hoàn toàn không biết gì. Tôi đang làm gì sai? Tại sao nó trả về true thay vì sai?

EDIT: tôi đã tìm ra này không có gì để làm với các char * const hoặc chuỗi, vì vấn đề vẫn còn có một chức năng có sản phẩm nào. Tôi đã thử thay đổi quy ước gọi giữa Cdecl và StdCall và không hoạt động chính xác. Tôi cũng đã quản lý để gỡ lỗi DLL của tôi và nó được gọi là chính xác và thực sự trở về sai, nhưng một khi trở lại vào C# nó bằng cách nào đó là đúng sự thật. Thay đổi CharSet cũng không có hiệu lực. Tôi đã chắc chắn rằng tôi đã cung cấp chương trình C# của tôi với phiên bản mới nhất và chính xác của DLL của tôi mỗi lần, do đó, đó không phải là một vấn đề là tốt. Một lần nữa, tôi hoàn toàn không biết gì về lý do tại sao kết quả là đúng khi tôi thực sự trở về sai.

EDIT2: SOReader đã cung cấp cho tôi đề xuất khắc phục sự cố quan trọng khác, xem nhận xét của tôi. Đáng buồn thay, nó không khắc phục được vấn đề trả về.

EDIT3: tôi đã kết luận rằng việc thay đổi kiểu trả về của Exist (bool) vào (int) đột nhiên làm cho nó trả lại số đúng (true = 1, false = 0). Điều đó có nghĩa là có thể có một vấn đề giữa bool của C++ và bool của C#. Tôi có thể tiếp tục sử dụng một int như một bool, nhưng điều đó vẫn sẽ không giải thích được vấn đề ban đầu. Có lẽ ai đó khác có thể khai sáng cho tôi về điều này? Có lẽ nó đã làm với thực tế là tôi đang sử dụng x64 (mặc dù cả hai pojects được biên dịch như x86)

+0

Điều đầu tiên cần kiểm tra là chức năng thực tế là 'cdecl'. Nếu makefile của bạn chuyển 'Gz' hoặc' Gr' vào trình biên dịch, thì hàm ở trên không phải là 'cdecl'. Thêm một '__cdecl' vào mã C của bạn, hoặc kích hoạt Trợ lý gỡ lỗi được quản lý' pInvokeStackImbalance'. –

+0

Tôi không nghĩ rằng nó sẽ liên kết nếu/Gr hoặc/Gz được chỉ định. Điểm tốt về Trợ lý gỡ lỗi được quản lý. –

+0

Tôi đã thử nghiệm nó và __fastcall sẽ không liên kết với/clr. Nhưng các liên kết __stdcall (/ Gz) nhưng sau đó không tìm thấy điểm nhập Exist trong thời gian chạy vì chữ ký hàm là khác nhau. –

Trả lời

37

Tôi đã tìm ra giải pháp cho vấn đề của bạn. Tuyên bố của bạn nên được bắt đầu bằng marshaling này: [return:MarshalAs(UnmanagedType.I1)]

để mọi thứ sẽ giống như thế này:

[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] 
[return:MarshalAs(UnmanagedType.I1)] 
public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name); 

Tôi đã thử nghiệm nó trong ví dụ rất đơn giản của tôi và nó làm việc!

EDIT
Tại sao điều này xảy ra? C định nghĩa bool là 4 byte int (như một số bạn đã nói) và C++ định nghĩa nó là 1 byte. Nhóm C# đã quyết định sử dụng bool 4 byte như mặc định trong PInvoke vì hầu hết các hàm API hệ thống sử dụng giá trị 4 byte làm bool. Nếu bạn muốn thay đổi hành vi này, bạn phải làm điều đó với marshaling chỉ định rằng bạn muốn sử dụng 1 giá trị byte.

+0

Thật lạ lùng.Tôi đã thử nghiệm nó trong VS 2010 với 32bit xây dựng của DLL và C# ứng dụng, và 64bit xây dựng của cả hai và không có vấn đề Shammah mô tả. Những gì bạn đang nói sẽ có ý nghĩa trong một máy tính của Big Endian, nhưng không có trong các kiến ​​trúc Little Endian như máy tính để bàn dựa trên x86 mà chúng tôi sử dụng. Hãy nhớ rằng, byte ít quan trọng nhất, 1 hoặc 0, trong cả hai trường hợp cho bool và 4 byte int sẽ giống nhau. Đó có lẽ là lý do tại sao nó hoạt động hoàn hảo cho tôi, tôi đoán vậy. –

+0

@SimonBrangwin: Trừ khi bạn muốn đọc hội đồng được tạo ra hoặc là một nhà cung cấp trình biên dịch cho mình, nó là khá nhiều không thể hiểu những gì sẽ xảy ra khi bạn nhận được kiểu trả về sai. Nó không hoạt động. Có một lý do tại sao C++ và C sẽ đánh dấu điều này là hành vi không xác định. – Puppy

+0

Tôi sẽ chấp nhận lý do của bạn: C bools là 4 byte và C++ bools là 1 byte, nhưng điều này cũng xảy ra khi CallingConvention được đặt thành ThisCall, điều này sẽ làm rõ rằng đó là phương thức C++. Tuy nhiên, bạn chỉ là người đưa tin, và sau khi tất cả, giải pháp của bạn đã khắc phục được sự cố của tôi, vì vậy cảm ơn bạn! – ulatekh

0

Tôi đã thử nghiệm mã của bạn và nó trả về false cho tôi. Vì vậy, phải có một cái gì đó khác đang xảy ra.

Bạn có chắc là bạn đang biên dịch lại DLL đúng cách không? Hãy thử xóa .DLL và thực hiện việc xây dựng lại.

Khác với mọi thứ có vẻ là giả định tốt. Theo mặc định, marshalling sẽ xử lý chuỗi .NET thành const char * mà không cần phải trang trí nó với các thuộc tính Marshal, cho dù DLL được biên dịch như ANSI hay Unicode.

Xem http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor5

2

lẽ marshaling đối số của hàm có thể giúp:

 
[MarshalAs(UnmanagedType.LPStr)] 

Sau đây là cách tuyên bố sẽ giống như thế:

 
[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)] 
     public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name); 
+0

Trong khi gỡ lỗi này tôi phát hiện ra rằng điều này thực sự nên được sử dụng. Nếu chuỗi của tôi là "my_name", chỉ "m" sẽ được chuyển thành một đối số mà không có marshalling. Với marshalling, toàn bộ "my_name" sẽ được thông qua. Đáng buồn thay, điều này vẫn không khắc phục được vấn đề trả về: ( – Shammah

+0

Tôi đã xem xét dự án của mình đã viết vài năm trước đây và tôi thấy rằng tôi đã sử dụng PreserveSig khi bools quay trở lại. Hãy thử điều này và cho tôi biết nếu nó hoạt động: [PreserveSig] bool foo(); – SOReader

+0

Thực ra tôi chỉ sử dụng các chức năng giao diện - không phải chức năng giao diện tĩnh, vì vậy tôi không biết liệu đầu của tôi có hoạt động hay không; ( – SOReader

4

C bool thực sự là int, như không có kiểu boolean trong ngôn ngữ C gốc. Điều đó có nghĩa rằng nếu C# 's DLLImport được thiết kế để interop với mã C, sau đó họ sẽ mong đợi rằng C#' s bool tương ứng với C của int.Trong khi điều này vẫn không giải thích tại sao sai sẽ trở thành sự thật, sửa chữa nó sẽ khắc phục vấn đề.

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx

này nói rằng UnmanagedType.Bool là Win32 BOOL, mà là một int.

2

Điều này thực sự gây ra bởi EAX không được xóa hoàn toàn bằng mã C++ điển hình trả về bool. Nó là điển hình cho EAX có chứa một số giá trị không có thật khi nhập một hàm, và để return false trình biên dịch thường sẽ phát ra xor al, al. Điều này chỉ xóa chỉ số LSB của EAX và làm cho mã C# giải thích giá trị khác không phải là true thay vì false.

0

tôi gửi các biến boolean, sử dụng hệ thống sau

__declspec(dllexport) const bool* Read(Reader* instance) { 
    try { 
     bool result = instance->Read(); 
     bool* value = (bool*)::CoTaskMemAlloc(sizeof(bool)); 
     *value = result; 
     return value; 
    } catch (std::exception exp) { 
     RegistryException(exp); 
     return nullptr; 
    } 
} 

Trong C#, tôi làm

DllImport(WrapperConst.dllName)] 
public static extern IntPtr Read(IntPtr instance); 

public bool Read() { 
    IntPtr intPtr = ReaderWrapper.Read(instance)); 
    if(intPtr != IntPtr.Zero) { 
     byte b = Marshal.ReadByte(intPtr); 
     Marshal.FreeHGlobal(intPtr); 
     return b != 0; 
    } else { 
     throw new Exception(GetLastException()); 
    } 
} 
Các vấn đề liên quan