Подтвердить что ты не робот

Как обрабатывать нулевые или необязательные параметры структуры dll в С#

Как мне обрабатывать необязательные аргументы struct в dll-методах, вызванных с С#, используя pinvoke? Например, параметр lpSecurityAttributes здесь должен быть передан null при отсутствии.

Правильный способ передачи struct, по-видимому, использует ref, но он не может иметь необязательных параметров или вообще принимает null.

Какие способы достижения этого?

4b9b3361

Ответ 1

У вас есть несколько вариантов

1) Используйте class вместо struct

Я думаю, что этот метод является самым простым. Просто объявите struct как class:

[StructLayout(LayoutKind.Sequential)]
public class CStruct
{
    //member-list
}

а затем объявите свой метод:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(CStruct cStruct, ...);

Если ваш необязательный параметр является последним, вместо этого вместо CStruct cStruct = null можно использовать параметр. Это позволяет исключить его, а не передавать null явно. Вы также можете написать метод-обертку, который использует это и гарантирует, что дополнительные параметры будут последними.

2) Используйте IntPtr и IntPtr.Zero

Используйте struct:

[StructLayout(LayoutKind.Sequential)]
public struct CStruct
{
    //member-list
}

и объявите свой метод как:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);

В случае не null маршал struct указателю и вызовите метод:

IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CStruct)));
try{
    Marshal.StructureToPtr(myCStruct, ptr, false);
    DLLFunction(ptr, ...);
} finally {
    Marshal.FreeHGlobal(ptr);
}

В случае null вызовите метод с IntPtr.Zero:

DLLFunction(IntPtr.Zero, ...);

Опять же, вы можете сделать этот параметр опционным, если это окажется последним в списке (или вы используете обертку, чтобы сделать это). Сделайте это, используя IntPtr cStruct = default(IntPtr) в качестве параметра. (As default(IntPtr) создает IntPtr.Zero.)

3) Перегрузите свой метод, чтобы избежать маршалинга

Используйте struct, как в 2).

Просто объявите один вариант для случая null:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(ref cStruct, ...);

и другой для случая null:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);

Первый метод будет автоматически вызван при передаче struct, а второй при передаче IntPtr.Zero. Если объявить версию IntPtr с необязательным параметром (как показано внизу 2) выше), она автоматически вызывается, когда вы исключаете параметр cStruct.

4) Исходные указатели с использованием unsafe

Используйте структуру как в 2) и объявите свой метод (обратите внимание на ключевое слово unsafe):

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static unsafe extern int DLLFunction(CStruct* cStruct, ...);

В случае с null вы передаете &myCStruct и просто null в случае null. Как и в 1), если этот необязательный параметр последний, вы можете объявить параметр как CStruct* cStruct = null для автоматического пропуска null, когда cStruct исключен.

Спасибо @dialer за предложение этого метода.