2011-07-14 18 views
9

Solo intentaba hacer una interoperabilidad gestionada/no gestionada. Para obtener información sobre el error decidí registrar una devolución de llamada de registro que ofrece el DLL: obrasC# P/Invoke: llamada de delegado Varargs


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

esta definición, pero me sale cadenas como "sondeado formato% s con el tamaño =% dy puntuación =% d". Intenté agregar la palabra clave __arglist, pero no está permitido para los delegados.

Bueno, no es tan dramático para mí, pero solo me da curiosidad si se pueden obtener los parámetros varargs en C#. Sé que podría usar C++ para la interoperabilidad. Entonces, ¿hay alguna manera de hacer esto puramente en C#, con un esfuerzo razonable?

EDITAR: Para aquellos que todavía no lo entendía: estoy NO importadores una función varargs PERO exportarlo como una devolución de llamada, que luego se llamó a mi código nativo. Puedo especificar solo uno a la vez -> solo es posible una sobrecarga y __arglist NO funciona.

+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) podría ser útil. – IllidanS4

Respuesta

4

No hay ninguna manera posible de hacerlo. La razón por la que es imposible se debe a la forma en que las listas de argumentos variables funcionan en C.

En C los argumentos de las variables simplemente se envían como parámetros adicionales a la pila (la pila no administrada en nuestro caso). C no registra en ningún lugar el número de parámetros en la pila, la función llamada toma su último parámetro formal (el último argumento antes de las variables) obtiene su ubicación y comienza a sacar argumentos de la pila.

La manera en que sabe la cantidad de variables que salen de la pila está completamente basada en la convención; algún otro parámetro indica cuántos argumentos variables están en la pila. Para printf lo hace analizando la cadena de formato y saltando de la pila cada vez que ve un código de formato. Parece que tu devolución de llamada es similar.

Para que CLR lo maneje, debería ser capaz de conocer la convención correcta para determinar cuántos argumentos necesita para recuperar. No puede escribir su propio controlador, porque requeriría acceso a la pila no administrada a la que no tiene acceso. Entonces no hay manera de que puedas hacer esto desde C#.

Para obtener más información sobre esto, debe leer las convenciones de llamadas de C.

0

El artículo siguiente cubre un escenario ligeramente diferente y pueden ser útiles:

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

+0

Hay 2 problemas con esto: 1.) Estoy EXPORTANDO un puntero de función/devolución de llamada en lugar de IMPORTARLO a través de DllImport y ya lo intenté __arglist: ¡No funciona con delegados! 2.) Sobrescribir todas las posibilidades no funciona, porque puedo registrar solo una devolución de llamada a la vez. – Zotta

+0

@Floste Lo siento, echo de menos la pregunta que parece. – Justin

+0

Parece que esta es solo otra de mis preguntas, que nadie puede responder ^^ – Zotta

0

Necesitará el soporte de P/invocación Marshaller para que esto sea posible. El Marshaller no proporciona dicho soporte. Por lo tanto, no se puede hacer.

1

En realidad es posible en 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

Aquí es la manera de tratar con él. Puede o no ser aplicable a su caso, dependiendo de si sus argumentos de devolución de llamada están destinados a ser utilizados con la familia de funciones printf.

En primer lugar, la importación vsprintf y _vscprintf de 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); 

A continuación, declarar su delegado con IntPtr puntero args:

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

Ahora, cuando su delegado se invoca a través de código nativo, basta con utilizar vsprintf para formatear el mensaje correctamente:

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

¡Increíble! No estoy seguro de cómo lo descubrió, ¡pero funciona! – Eternal21

+0

¡Gracias, realmente me ayudó a subirme a las vías! Sin embargo, necesitaba una forma de hacerlo en plataforma cruzada ... ¡Así que lo hice yo mismo! Aquí está mi trabajo, no dude en contribuir: https://github.com/jeremyVignelles/va-list-interop-demo. – cube45

Cuestiones relacionadas