Передача массива структур из C # (.NET Core) в C ++ (без изменений) - PullRequest
0 голосов
/ 18 мая 2018

Итак, я прочитал документацию и бесчисленные примеры онлайн, как распаковать массив структур.Я собрал массив 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;
}

Пинвокировать и вызывать делегата

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,все это говорит о том, что мой подход правильный, но он не работает.

Любая помощь приветствуется.

1 Ответ

0 голосов
/ 18 мая 2018

Простое решение: изменение стороны 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 ++ не должен каким-либо образом изменять полученный массив, иначе произойдет утечка памяти.

...