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
}
我正在尝试使用封送处理在我的 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
}