将结构数组从 C#(.NET Core) 传递到 C++(未管理)
Posted
技术标签:
【中文标题】将结构数组从 C#(.NET Core) 传递到 C++(未管理)【英文标题】:Passing Array of Structures from C#(.NET Core) to C++(unamnaged) 【发布时间】:2018-05-18 08:48:17 【问题描述】:所以我已经阅读了有关如何编组结构数组的文档和无数在线示例。我已经编组了 int 数组,我已经编组了结构,但现在我完全陷入困境,无论我尝试什么都无法让它工作。卡在上面一天多了。
结构/类,两者都试过
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public class SaveDetails
[MarshalAs(UnmanagedType.LPWStr)]
public string Log;
public FILETIME FileTime;
[MarshalAs(UnmanagedType.Bool)]
public bool Saved;
Pinvoke 和调用委托
public class LogSaveFiles : IDisposable
[UnmanagedFunctionPointer(CallingConvention.Winapi,CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles([ In, Out] SaveDetails[] logsToSave, string destinationPath);
private static DLogSaveFiles _dLogSaveFiles;
private IntPtr PLogSaveFiles get; set;
public bool LogSaveFilesAvailable => PLogSaveFiles != IntPtr.Zero;
public LogSaveFiles(Importer importer)
if (importer.dllLibraryPtr!= IntPtr.Zero)
PLogSaveFiles = Importer.GetProcAddress(importer.dllLibrary, "LogSaveFiles");
public Status SaveFiles(SaveDetails[] logsToSave,string destinationPath)
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
result = _dLogSaveFiles(logsToSave, destinationPath);
return result;
public void Dispose()
打电话
private void SaveLogs()
var logsToSave = new[]
new SaveDetails
FileTime = new FILETIME dwHighDateTime = 3,dwLowDateTime = 5,
Log = LogTypes.logDeviceLog,
Saved = true,
new SaveDetails
FileTime = new FILETIME dwHighDateTime = 1,dwLowDateTime = 2,
Log = LogTypes.logDeviceLog,
Saved = false
;
var pathToSave = "C:\\Logs";
_logSaveFiles.SaveFiles(logsToSave, pathToSave);
c++ 暴露调用
typedef struct _LOG_SAVE_DETAILS
LPTSTR szLog;
FILETIME fromFileTime;
BOOL bSaved;
LOG_SAVE_DETAILS, *PLOG_SAVE_DETAILS;
/* Function definitions */
ULY_STATUS _API LogSaveFiles (PLOG_SAVE_DETAILS ppLogs [],
LPCTSTR szDestinationPath);
目标路径正确传递,但结构数组从未通过,导致尝试访问时访问冲突。起初我认为这是 LPTSTR 无法正常通过的问题,但我已经用它自己实现了其他调用并成功编组它。
我已经阅读了https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke 上的所有内容,这一切都表明我的方法是正确的,但它不起作用。
感谢任何帮助。
【问题讨论】:
只是为了确定,C 部分使用的是 unicode 对吗? 其他问题:C++ 怎么知道有多少ppLogs
存在?通常你会将\0
元素作为最后一个元素,但你没有这样做,也没有传递数组长度。
@xanatos yeh 它的 unicode,而 C++ 期望数组为空终止,我也尝试过,但没有奏效。 (仅在执行期间检查,当它遍历数组直到找到 null 时)。我无法编辑 c++ api。
编码前三思!!!我从 unix 上的 c 语言开始,并多次阅读整个 Unix 手册。我从了解标准的 unix 结构中学到了很多东西。数组需要以 null 终止或在数组之前包含一个长度。你的第一个问题是在结构的开头有一个 LPWStr。它实际上是一个 int16[] 以'\0'结尾。所以要找到 FILETIME 的开头,你必须首先在 LPWStr 中找到空值。你不能只查找 '\0' 它必须在 int16 的第一个字节而不是第二个字节中。用c容易做,用c#不容易。
@jdweng Er,这就是LPTSTR
。
【参考方案1】:
简单的解决方案:C端将PLOG_SAVE_DETAILS ppLogs []
改为LOG_SAVE_DETAILS ppLogs []
,然后C#端将public class SaveDetails
改为public struct SaveDetails
。
编组对象数组似乎很困难(我无法做到)。编组结构数组有效。另一种方法是手动进行编组,但这很痛苦。
手动编组的“痛苦”(仅修改代码行):
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles(IntPtr[] logsToSave, string destinationPath);
然后
public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath)
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
if (_dLogSaveFiles == null)
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
int size = Marshal.SizeOf(typeof(SaveDetails));
IntPtr basePtr = IntPtr.Zero;
IntPtr[] ptrs = new IntPtr[logsToSave.Length + 1];
try
basePtr = Marshal.AllocHGlobal(size * logsToSave.Length);
for (int i = 0; i < logsToSave.Length; i++)
ptrs[i] = IntPtr.Add(basePtr, (i * size));
Marshal.StructureToPtr(logsToSave[i], ptrs[i], false);
result = _dLogSaveFiles(ptrs, destinationPath);
finally
if (basePtr != IntPtr.Zero)
for (int i = 0; i < logsToSave.Length; i++)
if (ptrs[i] != IntPtr.Zero)
Marshal.DestroyStructure(ptrs[i], typeof(SaveDetails));
Marshal.FreeHGlobal(basePtr);
return result;
重要提示:这是一个编组器 C#->C++。 C++ 不得以任何方式修改接收到的数组,否则会出现内存泄漏。
【讨论】:
我无法更改 c++ 公开的方法,因为它是许多地方使用的巨大框架的一部分, @AistisTaraskevicius 我可以编写一些临时编组。 C++ 端是否修改了数组?因为如果不是,它会简单得多。 c++ 不会修改数组,它只是使用存储在其中的值 @AistisTaraskevicius 手动编组已发布。封送拆收器在数组末尾添加一个“空”元素,以便 C++ 代码知道何时停止。 抱歉回复慢,周末不在,明天早上去看看!以上是关于将结构数组从 C#(.NET Core) 传递到 C++(未管理)的主要内容,如果未能解决你的问题,请参考以下文章