C# 编组、不平衡堆栈和正确获取 PInvoke 签名

C# Marshaling, ubalanced stack and getting PInvoke signature correct

我正在尝试使用封送处理在我的 C# 项目中调用 C DLL,并且有一些功能可以正常工作,但我在使用其他功能时遇到了问题。喜欢下面的。

我的第一个问题是让结构正确,下一个问题是 return PROFILE_INFO 作为一个包含程序文件列表的数组,或者它可能不会 return 一个列表proNum 是一个索引。

C 中的函数

extern "C" __declspec(dllexport) int WINAPI GetProgramFileList (unsigned long proNum, PROFILE_INFO *proFile);

typedef struct{
    PROINFO         proInfo;
    __int64             proSize;
    PROGRAM_DATE    createDate;
    PROGRAM_DATE    writeDate;
}PROFILE_INFO;

typedef struct{
    char wno[33];
    char dummy[7];
    char comment[49];
    char dummy2[7];
    char type;
    char dummy3[7];
}PROINFO;

typedef struct{
    short   year;
    char    month;
    char    day;
    char    hour;
    char    min;
    char    dummy[2];
}PROGRAM_DATE;

我的函数

[DllImport(@".\IFDLL.dll", EntryPoint = "GetProgramFileList", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern int GetProgramFileListTest(ulong proNum, ref PROFILE_INFO pro);

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROFILE_INFO
{
    [MarshalAs(UnmanagedType.Struct)]
    public PROINFO proInfo;            // WNo/name/type
    public long proSize;               // Program size
    [MarshalAs(UnmanagedType.Struct)]
    public PROGRAM_DATE createDate;    // Program creating date
    [MarshalAs(UnmanagedType.Struct)]
    public PROGRAM_DATE writeDate;     // Program updating date
}

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROINFO
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno;         // WNo.
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy;       // dummy
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment;     // program name
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2;      // dummy
    public char type;                                                               // program type
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3;      // dummy
}

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROGRAM_DATE
{
    public short year;                 // Date (Year) 4-digit
    public char month;                 // Date (Month)
    public char day;                   // Date (Day)
    public char hour;                  // Date (Time)
    public char min;                   // Date (Minutes)
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private char dummy; // Dummy
}

C# 结构 PROFILE_INFO 中的 PROGRAM_DATE createDate 将抛出:

Cannot marshal field 'createDate' of type 'CClient.Models.PROFILE_INFO': The type definition of this field has layout information but has an invalid managed/unmanaged type combination or is unmarshalable.

将 PROGRAM_DATE 字段更改为字符串使其接受它,但函数 return 是一个参数 (-60) 错误。虽然不确定我是否离成功更近了。

其他尝试,包括尝试将 PROFILE_INFO 变为 return 作为数组(ref PROFILE_INFO[]),落在:

A call to PInvoke function has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature.

我在 C dll 之后得到了这些描述:

Explanation

Obtain the program list in the standard area

Argument

proNum [in]

Specify the number of the data to be obtained.

proFile [out]

Store the program information in the standard area, in PROFILE_INFO type.

执行该函数前,请务必保证要获取的数据个数所在的数据区

Return值

如果成功,则 returned 为“0”。如果有错误,则 returned.

不是“0”

我使用的其他函数是 GetProgramDirInfo、SendProgram、ReceiveProgram、SearchProgram 等,但它们 return 没有任何数组,所以我假设编组数组是我的问题。我也试图避免使用不安全的指针,我不确定是否需要自己进行复制。

感谢任何帮助。

使用 p/invoke 时的几个要点:

  • 不要添加对 .NET 而言显而易见的内容(结构就是结构,...)
  • 如果您不确定,请不要添加 pack,默认情况下 .NET 在这方面的行为应该类似于 C/C++
  • 如果定义中没有字符串,则不要添加Ansi信息(只有当有字符串或TStr等时)。它不会引起问题但没用
  • 一般情况下,如果您不知道它们的用途,请不要添加属性
  • int 和 C/C++ 中的 long (通常)是 32 位的。 long 在 C/C++
  • 中不是 64 位

这里有一个代码应该更好:

  [DllImport(@".\IFDLL.dll", EntryPoint = "GetProgramFileList")]
  public static extern int GetProgramFileListTest(uint proNum, ref PROFILE_INFO pro);

  [StructLayout(LayoutKind.Sequential)]
  public struct PROFILE_INFO
  {
      public PROINFO proInfo;            // WNo/name/type
      public long proSize;               // Program size
      public PROGRAM_DATE createDate;    // Program creating date
      public PROGRAM_DATE writeDate;     // Program updating date
  }

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  public struct PROINFO
  {
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno;         // WNo.
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy;       // dummy
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment;     // program name
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2;      // dummy
      public char type;                                                               // program type
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3;      // dummy
  }

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  public struct PROGRAM_DATE
  {
      public short year;                 // Date (Year) 4-digit
      public char month;                 // Date (Month)
      public char day;                   // Date (Day)
      public char hour;                  // Date (Time)
      public char min;                   // Date (Minutes)
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private string dummy; // Dummy
  }