0

I have a .dll without source code. That library is wrote in C... I have only one .h An extract of the library is this:

typedef enum
{
    SSHD_ERR_NONE = 0,
    SSHD_ERR_INVALID_HANDLE,
    SSHD_ERR_NULL_POINTER,
}sshdErr_t;

typedef struct
{
    unsigned long  Year;            // 
    unsigned long  Month;       // 1...12
    unsigned long  Day;         // 1...31

    unsigned long  Hour;        // 0...23
    unsigned long  Min;         // 0...59
    unsigned long  Sec;         // 0...59
    unsigned long  MilliSec;    // 0...999
} sshdDateTime_t;


    DllExport HANDLE sshdCreate(void);
    DllExport sshdErr_t sshdGetDllVersion(char *dllVersion, unsigned long dllVersionMaxLen);

    DllExport sshdErr_t sshdOpen(HANDLE rh);

I have write this code:



      private const String dllPath = @"D:\work\SSHD.dll";

    [DllImport(dllPath,EntryPoint = "sshdOpen")]
        internal static extern eSSHD_Err Extern_sshdOpen();


        [DllImport(dllPath,EntryPoint = "sshdGetDllVersion")]
        //internal static extern string sshdGetDllVersion();
        internal static extern eSSHD_Err Extern_sshdGetDllVersion(string dllVer , Int32 maxLen );

        [DllImport(dllPath, EntryPoint = "sshdCreate")]
        internal static extern IntPtr Extern_sshdCreate();

    public enum eSSHD_Err {
        SSHD_ERR_NONE = 0,
        SSHD_ERR_INVALID_HANDLE,
        SSHD_ERR_NULL_POINTER,
    }

  public class SSHDDateTime {
    public ulong Year { get; set; }     // 
    public ulong Month { get; set; }    // 1...12
    public ulong Day { get; set; }      // 1...31
    public ulong Hour { get; set; }  // 0...23
    public ulong Min { get; set; }      // 0...59
    public ulong Sec { get; set; }    // 0...59
    public ulong MilliSec { get; set; } // 0...999

    public SSHDDateTime() {
      Year = 0;
      Month = 0;
      Day = 0;
      Hour = 0;
      Min = 0;
      Sec = 0;
      MilliSec = 0;
    }
  }

So I use Dll import, convert enum in enum (C#) and struct in class...

When I try to use I have an error...

Now the code for use the library:

  IntPtr handle;
  handle = Extern_sshdCreate();

this MAYBE work... It means that value of "handle" change.... but I really not know if it work!

when I try to use the other function, in this way:

int retProva = (int) (Extern_sshdOpen(handle));

or

eSSHD_Err retProva = Extern_sshdOpen(handle);

I have an error of stack corrupted...

Using the other function is the same results:

string dllVer="xxxxxxxxxxxxxxx";
Int32 maxLen = 10;
Extern_sshdGetDllVersion(dllVer,maxLen);

I try to use string or String for the dllVer or to use = " " ... I try to store the retruned value and not I try to use Int32, string, ulong for maxLen nut all try without result...

the error returned, in Italian, is this:

Managed Debugging Assistant 'PInvokeStackImbalance' : 'Una chiamata alla funzione PInvoke 'TestSTSWavetronix!TestSTSWavetronix.FormTestWavetronix::Extern_sshdGetDllVersion' ha sbilanciato lo stack. Questo problema può verificarsi quando la firma PInvoke gestita non corrisponde alla firma di destinazione non gestita. Verificare che la convenzione di chiamata e i parametri della firma PInvoke corrispondano alla firma di destinazione non gestita.'

So I ask: 1. How to use correctly this call? 2. I transform enum C in enum C#, but for return value, how to adapt? 3. similar to previous, how to pass struct C to function, if in C# I have class instead? 4. I have a pointer on C parameter function call... how to manage with him? I suppose that the C code try to modify the string that I pass....

edit: other try: - use unsafe in compilation proprierties and before prototype - use "enum : int" on definition of enum

edit 2: ********************** WORKING SOLUTION *************

  public enum eSSHD_Err {
    SSHD_ERR_NONE = 0,
    SSHD_ERR_INVALID_HANDLE,
    SSHD_ERR_NULL_POINTER,
}
    [DllImport(dllPath,EntryPoint = "sshdGetDllVersion",CallingConvention = CallingConvention.Cdecl)]
    unsafe internal static extern eSSHD_Err Extern_sshdGetDllVersion(ref char dllVer,Int32 maxLen);
char[] dllVer = new char[250];
Int32 maxLen = 250;
retErrGetDllVer = Extern_sshdGetDllVersion(ref dllVer[0],maxLen);

Thanks to all

Massimiliano

holyhope
  • 35
  • 8
  • Like in https://stackoverflow.com/questions/20419415/c-sharp-call-c-dll-passing-pointer-to-pointer-argument Have you tried tu put unsafe before internal static extern and in DllImport: CallingConvention = CallingConvention.Cdecl? – Jean-Claude Colette Aug 08 '17 at 15:15
  • The stack error usually means the return variable size from calling function doesn't match the parent return size. So specify the size in the enumeration : public enum eSSHD_Err : int //or the size from the c++ library. – jdweng Aug 08 '17 at 15:23
  • Hi, I try to compile with unsafe (build option AND before the prototype) I try to use CallingConvention in Cdec, now I try to keep in my code.... @jdweng I have try now in this way: typedef enum : int { SSHD_ERR_NONE = 0, SSHD_ERR_INVALID_HANDLE, SSHD_ERR_NULL_POINTER, } but without success... The error remain the same. I try to call the fnction without expecting return.. So both: eSSHD_Err retProva = Extern_sshdOpen(handle); and Extern_sshdOpen(handle); – holyhope Aug 08 '17 at 15:41
  • I don't think call will work with unsafe code. Never tried it. – jdweng Aug 08 '17 at 16:15
  • Can you get the value of handle after calling Extern_sshdCreate(); ? – Jean-Claude Colette Aug 08 '17 at 17:18
  • Hi, yes Handle value is this (I don't know if it is valid or not): Before: 0x00000000 (all zero) After: 0x077d2ac0 – holyhope Aug 09 '17 at 06:59

1 Answers1

0

Try adding the StructLayoutAttribute to your managed struct declaration. The C# compiler is permitted to re-order struct members for efficiency if it deems it beneficial, so this will prevent that. Since long is usually a 32-bit integer in C, I have changed the field types to uint.

[StructLayout(LayoutKind.Sequential)]
public class SSHDDateTime {
  public uint Year { get; set; }     // 
  public uint Month { get; set; }    // 1...12
  public uint Day { get; set; }      // 1...31
  public uint Hour { get; set; }  // 0...23
  public uint Min { get; set; }      // 0...59
  public uint Sec { get; set; }    // 0...59
  public uint MilliSec { get; set; } // 0...999

  public SSHDDateTime() {
    Year = 0;
    Month = 0;
    Day = 0;
    Hour = 0;
    Min = 0;
    Sec = 0;
    MilliSec = 0;
  }
}

Next, pass a StringBuilder to the Extern_sshdGetDllVersion function. This function is expecting a character buffer, and the managed equivalent of a character buffer is a StringBuilder. The PInvoke marshaler has detailed knowledge of the StringBuilder class to handle interop marshaling of strings. You can add the MarshalAsAttribute as a hint to the marshaler that you are dealing with ANSI strings in the native library. Also, the default calling convention for the DllImportAttribute is StdCall, which is likely not what you want here. This revised signature specifies the Cdecl calling convention, which is the standard convention for C programs. If your C library is in fact compiled to use StdCall, then you can take that part out, but I think that is the source of your stack corruption.

    [DllImport(dllPath, EntryPoint = "sshdGetDllVersion", CallingConvention = CallingConvention.Cdecl)]
    internal static extern eSSHD_Err Extern_sshdGetDllVersion([MarshlAs(UnmanagedType.LPStr)]StringBuilder dllVer , Int32 maxLen );

The PInvoke signatures for the other functions would be as follows:

[DllImport(dllPath, EntryPoint = "sshdOpen", CallingConvention = CallingConvention.Cdecl))]
internal static extern eSSHD_Err Extern_sshdOpen();

[DllImport(dllPath, EntryPoint = "sshdCreate", CallingConvention = CallingConvention.Cdecl))]
internal static extern IntPtr Extern_sshdCreate();
Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31
  • Hi to all! Now work! I use the calling convention and the ref instead char *... Now I write the working code – holyhope Aug 09 '17 at 08:18