Pregunta

Estoy tratando de llamar a un método desde una DLL escrita en C, desde mi aplicación C #. Así es como implementé la llamada:

- > Firma del método C: (de una empresa de terceros)

Int16 SSEXPORT SS_Initialize(Uint16 ServerIds[], Uint16 ServerQty, const char PTR *Binding, const char PTR *LogPath, Uint16 LogDays, Int16 LogLevel,
Uint16 MaxThreads, Uint16 ThreadTTL, Int16 (CALLBACK *ProcessMessage)(Uint16, const char PTR *, Uint32, char PTR **, Uint32 PTR *, Int16 PTR *));

- > Definición de tipos C:

#ifndef _CSISERVERTYPES_
#define _CSISERVERTYPES_

#if defined(OS_NT)

#define CALLBACK __stdcall 
#define SSEXPORT __stdcall
#define PTR      

typedef char                Int8;
typedef unsigned char       Uint8;
typedef short               Int16;
typedef unsigned short      Uint16;
typedef long                Int32;
typedef unsigned long       Uint32;
typedef unsigned char       Byte;
typedef unsigned short      Word;
typedef unsigned long       Dword;
typedef short               Bool;

#endif

typedef Int16 (CALLBACK *PM_Function)
        (Uint16,const char PTR *,Uint32,char PTR **,Uint32 PTR *,Int16 PTR *);

#define SS_OK               0
#define SS_NOT_INIT         -1
#define SS_PROC_ERR         -2
#define SS_ERR_TCP_SRV      -3
#define SS_ERR_OUT_MEM      -4

#ifndef NULL
#define NULL                0
#endif

#endif

- > Declaración de método C # DLL:

[DllImport("CSITCPServer.dll", SetLastError = true)]
    static extern Int16 SS_Initialize(
        UInt16[] ServerIds,
        UInt16 ServerQty,
        ref string Binding,
        ref string LogPath,
        UInt16 LogDays,
        Int16 LogLevel,
        UInt16 MaxThreads,
        UInt16 ThreadTTL,
        ProcessMessageCallback callback);

Llamada de método:

public static void Call_SS_Initialize()
    {   
        Int16 ret;
        UInt16[] serverIds = new UInt16[] { 2020, 2021 };
        string binding = "";
        string logPath = "";

        try
        {
            ret = SS_Initialize(
                serverIds,
                Convert.ToUInt16(serverIds.ToList().Count),
                ref binding,
                ref logPath,
                10,
                0,
                256,
                300,
                ProcessMessage);

            Console.WriteLine("Call_SS_Initialize() -> Result of SS_Initialize:  {0}", ret.ToString());
        }
        catch (Exception ex)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
            //throw;
        }
    }

Y luego obtengo la Win32Exception: 1008 - Se intentó hacer referencia a un token que no existe

Sé que el problema debe estar en la conversión de CHAR a STRING entre los códigos no administrados (C) y administrados (C #). Si modifico los parámetros de Enlace o LogPath para escribir SByte , no da ningún error. Pero como el método espera un texto (cadena), no sé cómo puedo pasar el texto al método ya que espera una variable SByte ...

Soy consciente de que podría tener que usar algo como MarshalAs , pero he probado algunas opciones y no he tenido éxito.

¿Alguien puede decirme qué estoy haciendo mal?

¡Muchas gracias!


Aquí está la definición de devolución de llamada:

 public delegate Int16 ProcessMessageCallback(
        UInt16 ServerId,
        [MarshalAs(UnmanagedType.LPStr)] ref string RequestMsg,
        UInt32 RequestSize,
        [MarshalAs(UnmanagedType.LPStr)] ref string ResponseMsg,
        ref UInt32 ResponseSize,
        ref Int16 AppErrCode);

La cuestión es que el método C DLL está esperando un " REF " parámetro. La llamada a SS_Initialize adjunta la ejecución a la función de devolución de llamada ProcessMessage . En esta función, necesito poder obtener los parámetros modificados (ref) de SS_Initialize ...

¿Podría sugerir cómo cree que debería estructurarse el código?

¡Gracias!

¿Fue útil?

Solución

No necesita usar ref para Binding y LogPath; son constantes * y no cambiarán.

Ese ResponseMessage en la devolución de llamada devolverá una matriz de cadenas, (char **) no solo una sola cadena. El tamaño de respuesta probablemente indicará la profundidad de la matriz. Pruebe [MarshalAs (UnamangedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string [] en su lugar.

Otros consejos

Para las cadenas de cálculo no pasa ref string , solo string . Además, debe decorar cada cadena con [MarshalAs (UnmanagedType.LPStr)] para indicar que está pasando una cadena que contiene caracteres ascii y está terminada en nulo.
Editar: ¿Podría también dar su definición de este procedimiento de devolución de llamada, ya que podría cometer errores similares.

¡Gracias Ravadre y R Ubben , su ayuda combinada hizo el truco!

Al final, se trataba de controlar adecuadamente la cláusula REF en los parámetros y usar la etiqueta MarshalAs. Los parámetros que son como "const char *" en la parte C realmente no necesita la etiqueta REF. También usé [MarshalAs (UnmanagedType.LPStr)] para los parámetros de la cadena. ¡Eso es!

ps: ¡Stackoverflow.com es increíble! Ya he encontrado muchas respuestas a otros problemas aquí simplemente buscando. Esta es mi primera publicación y estoy sorprendido con las respuestas rápidas que tengo para esta. ¡Felicitaciones a todo el equipo y miembros del personal! ;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top