2011-07-14 33 views
9

Tôi chỉ đang cố gắng thực hiện một số giao diện được quản lý/không được quản lý. Để có được thông tin mở rộng lỗi tôi quyết định đăng ký một callback log được cung cấp bởi các dll: công trìnhC# P/Gọi: Varargs ủy nhiệm gọi lại


[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt); 

Định nghĩa này, nhưng tôi nhận được chuỗi như "Format% s dò với size =% d và điểm số =% d". Tôi đã cố gắng thêm từ khóa __arglist, nhưng nó không được phép cho các đại biểu.

Vâng, nó không phải là quá ấn tượng đối với tôi, nhưng tôi chỉ tò mò wether người ta có thể nhận được các thông số varargs trong C#. Tôi biết rằng tôi có thể sử dụng c + + cho interop. Vì vậy: Có cách nào để làm điều này hoàn toàn trong C#, với hiệu quả hợp lý không?

EDIT: Đối với những người vẫn không nhận được nó: Tôi KHÔNG NHẬP KHẨU một hàm varargs NHƯNG KHẨU nó như là một callback, sau đó được gọi là mã nguồn gốc của tôi. Mỗi lần tôi chỉ có thể chỉ định một lần -> chỉ một quá tải có thể và __arglist KHÔNG hoạt động.

+1

[This] (http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/301588-how-hooking-functions-wow- variable-arguments-c.html) có thể hữu ích. – IllidanS4

Trả lời

4

Không không có cách nào có thể làm điều đó. Lý do không thể là do danh sách đối số biến hoạt động trong C.

Trong đối số biến C chỉ được đẩy như tham số bổ sung vào ngăn xếp (ngăn xếp không được quản lý trong trường hợp của chúng tôi). C không bất cứ nơi nào ghi lại số lượng tham số trên stack, hàm được gọi có tham số chính thức cuối cùng của nó (đối số cuối cùng trước varargs) lấy vị trí của nó và bắt đầu popping đối số ra khỏi ngăn xếp.

Cách thức mà nó biết có bao nhiêu biến để thoát khỏi ngăn xếp là hoàn toàn dựa trên quy ước - một số tham số khác cho biết có bao nhiêu đối số biến được đặt trên ngăn xếp. Đối với printf nó làm điều đó bằng cách phân tích chuỗi định dạng và popping ra khỏi ngăn xếp mỗi khi nó nhìn thấy một mã định dạng. Nó có vẻ như gọi lại của bạn là tương tự.

Để CLR xử lý điều đó, nó phải có khả năng biết quy ước chính xác để xác định có bao nhiêu đối số cần thiết để đón. Bạn không thể viết trình xử lý của riêng mình vì nó sẽ yêu cầu quyền truy cập vào ngăn xếp không được quản lý mà bạn không có quyền truy cập. Vì vậy, không có cách nào bạn có thể làm điều này từ C#.

Để biết thêm thông tin về điều này, bạn cần đọc kỹ các quy ước gọi điện của C.

0

Bài viết sau đây bao gồm một kịch bản hơi khác nhau và có thể hữu ích:

How to P/Invoke VarArgs (variable arguments) in C#

+0

Có 2 vấn đề với điều này: 1.) Đang xuất một con trỏ hàm/gọi lại thay vì nhập nó qua DllImport và tôi đã thử __arglist: Nó không hoạt động với các đại biểu! 2.) Ghi đè tất cả các khả năng không hoạt động, vì tôi chỉ có thể đăng ký một lần gọi lại một lần. – Zotta

+0

@Floste Xin lỗi, tôi đã bỏ lỡ câu hỏi này. – Justin

+0

Có vẻ như đây chỉ là một câu hỏi khác của tôi, mà không ai có thể trả lời ^^ – Zotta

0

Bạn cần hỗ trợ từ P/gọi trình khắc phục sự cố để có thể thực hiện điều này. Người marshaller không cung cấp sự hỗ trợ như vậy. Do đó nó không thể được thực hiện.

1

Tôi đã gặp phải câu hỏi này trước khi đăng Marshal va_list in C# delegate. Tôi nghĩ rằng nó tương tự, và tôi đã tìm thấy một thực hiện mà làm việc cho tôi. HTH.

1

Trên thực tế nó có thể trong CIL:

.class public auto ansi sealed MSIL.TestDelegate 
     extends [mscorlib]System.MulticastDelegate 
{ 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor(object 'object', 
           native int 'method') runtime managed 
    { 
    } 
    .method public hidebysig newslot virtual 
      instance vararg void Invoke() runtime managed 
    { 
    } 
} 
2

Dưới đây là cách để đối phó với nó. Nó có thể hoặc không thể áp dụng cho trường hợp của bạn, tùy thuộc vào việc các đối số gọi lại của bạn có nghĩa là được sử dụng với họ của các chức năng printf hay không.

Thứ nhất, nhập khẩu vsprintf_vscprintf từ msvcrt:

[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
public static extern int vsprintf(
    StringBuilder buffer, 
    string format, 
    IntPtr args); 

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern int _vscprintf(
    string format, 
     IntPtr ptr); 

Tiếp theo, tuyên bố đại diện của bạn với IntPtr con trỏ args:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public unsafe delegate void LogCallback(
    void* arg1, 
    int level, 
    [In][MarshalAs(UnmanagedType.LPStr)] string fmt, 
    IntPtr args); 

Bây giờ khi đại biểu của bạn được gọi qua mã gốc, chỉ cần sử dụng vsprintf để định dạng tin nhắn chính xác:

private void LogCallback(void* data, int level, string fmt, IntPtr args) 
{ 
    var sb = new StringBuilder(_vscprintf(fmt, args) + 1); 
    vsprintf(sb, fmt, args); 

    //here formattedMessage has the value your are looking for 
    var formattedMessage = sb.ToString(); 

    ... 
} 
+0

Thật tuyệt vời! Bạn không chắc chắn làm thế nào bạn figured nó ra, nhưng nó hoạt động! – Eternal21

+0

Cảm ơn, nó thực sự đã giúp tôi để có được trên đường đua! Tuy nhiên tôi cần một cách để làm cho nó vượt qua nền tảng ... Vì vậy, tôi đã làm điều đó bản thân mình! Đây là công việc của tôi, cảm thấy tự do để đóng góp: https://github.com/jeremyVignelles/va-list-interop-demo. – cube45

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