2012-11-29 48 views
9

Tôi nghĩ rằng về cơ bản tôi đã hiểu cách viết C# đại biểu cho các cuộc gọi lại, nhưng điều này làm tôi bối rối. Các C++ định nghĩa như sau:C# ủy nhiệm cho gọi lại C++

typedef int (__stdcall* Callback)(
long lCode, 
long lParamSize, 
void* pParam 
); 

và C# cách tiếp cận của tôi sẽ là:

unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Mặc dù điều này có vẻ là không chính xác, bởi vì tôi nhận được một lỗi PInvokeStackInbalance, có nghĩa là định nghĩa của tôi về các đại biểu là sai.

Phần còn lại của tham số của hàm là chuỗi hoặc int, có nghĩa là chúng không thể gây ra lỗi, và nếu tôi chỉ chuyển IntPtr.Zero thay cho đại biểu (có nghĩa là tôi trỏ đến một không) chức năng gọi lại tồn tại) Tôi nhận được lỗi AccessViolation, điều này có ý nghĩa tốt.

Tôi đang làm gì sai?

EDIT:

Các đầy đủ C++ chức năng là:

int 
__stdcall 
_Initialize (
const char*   FileName, 
Callback   cbFunction, 
int     Code, 
const char*   Name, 
unsigned int  Option, 
unsigned int  Option2 
); 

C# phiên bản của tôi là:

[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

Chức năng này (để thử nghiệm) vừa gọi bên trong thói quen chính của ứng dụng bảng điều khiển:

static void Main(string[] args) 
{ 
    CallbackDelegate del = new CallbackDelegate(onCallback); 

    Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
    Console.Read(); 
} 

nơi onCallback là thế này:

static int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
    return 0; 
} 

tôi nhận được lỗi PInvokeStackInbalance trên đường, nơi tôi gọi _Initialize, nếu tôi vượt qua một IntPtr.Zero thay vì các đại biểu, và thay đổi định nghĩa của hàm để IntPtr thay vì CallbackDelegate sau đó Tôi nhận được AccessViolationException.

+0

'Phần còn lại của các thông số của hàm là chuỗi hoặc ints'. Không ẩn thông tin. –

+1

Bạn đã thử sử dụng không an toàn chưa? tức là: delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); – DougEC

+0

@ HansPassant Tôi không cố che giấu thông tin, tôi đã cố gắng loại bỏ những thứ mà tôi cho là không liên quan. Tôi sẽ chỉnh sửa câu hỏi và thêm thông tin. – Valandur

Trả lời

3

Một .NET long là 64 bit. C++ long có thể chỉ 32 bit. Kiểm tra với trình biên dịch C++ đã biên dịch định nghĩa đó theo kích thước của nó là long s.

+0

Đầu vào tốt, thật đáng buồn điều này dường như không giải quyết được vấn đề. Tôi không thể kiểm tra trình biên dịch C++, vì tôi chỉ có tệp .dll và .h, nhưng việc thay đổi dài thành Int32 không giúp ích gì. – Valandur

3
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] 
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Nếu .NET giả định cdecl thay vì stdcall, ngăn xếp của bạn chắc chắn sẽ được ẩn.

4

Tôi đã thêm mã của bạn vào dự án hiện tại của mình, nơi tôi đang thực hiện rất nhiều liên kết C#/C++ trong VS2012. Và trong khi tôi ghét sử dụng "nó làm việc trên máy của tôi", nó làm việc tốt cho tôi. Các mã như tôi chạy nó được liệt kê ra dưới đây chỉ để minh họa rằng tôi đã không thực hiện và thay đổi cơ bản.

Đề xuất của tôi là bạn xây dựng một dll gốc mới với hàm sơ khai cho _Initialize như dưới đây và xem nó có hoạt động khi bạn có thể điều khiển cả hai mặt của giao diện. Nếu điều đó làm việc nhưng dll thực sự không, nó đi xuống đến một trong hai cài đặt trình biên dịch ở bên bản địa hoặc của họ là một lỗi về phía bản địa và nó được dậm trên stack.

extern "C" { 

    typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam); 

    TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int     Code, 
           const char*   Name,unsigned int  Option,unsigned int  Option2) 
    { 
     cbFunction(0, 0, nullptr); 
     return 0; 
    } 
} 

Trên C# bên, tôi đã thêm tờ khai đến lớp giao diện của tôi:

public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam); 

[DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

Và sau đó trong tôi chính:

private int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
     return 0; 
} 
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback); 

Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
+0

Cảm ơn bạn đã kiểm tra này, nên đã làm điều đó bản thân mình. Tôi đã thử nó, và nó có vẻ hoạt động. Điều cũng đã phát hiện ra là ứng dụng có vẻ hoạt động nếu tôi chỉ bắt đầu tệp exe, KHÔNG CÓ trình gỡ lỗi ... – Valandur

+0

Điều đó thật kỳ quặc. Nghe có vẻ như có một cơ hội tốt mà có một lỗi trong dll bên C++ đang ảnh hưởng đến ngăn xếp. Có cách nào để bạn có được nguồn cho các dll bản địa để bạn có thể chạy chúng kết hợp theo trình gỡ lỗi? Tôi không biết bất kỳ cách nào tốt để tiếp cận nó và gần như chắc chắn không có cách nào để FIX nó với nguồn. –

+0

Không, thật đáng buồn là tôi không thể lấy được mã nguồn, nếu không tôi nghĩ điều này sẽ dễ dàng hơn nhiều. Tôi đã thử gọi chức năng C++ từ một chương trình C++, và điều này dường như làm việc hoàn toàn tốt đẹp. Do đó tôi nghĩ rằng dll là alright, và tôi chỉ làm một sai lầm với chức năng gọi lại. Cảm ơn sự giúp đỡ của bạn. – Valandur

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