файл abc.h
typedef struct sp_BankNoteTypeList
{
int cim_usNumOfNoteTypes;
struct sp_notetype
{
USHORT cim_usNoteID;
CHAR cim_cCurrencyID[3];
ULONG cim_ulValues;
bool cim_bConfigured;
}SP_CIMNOTETYPE[12];
}SP_CIMNOTETYPELIST,*SP_LPCIMNOTETYPELIST;BNA_API int BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType);
abc.cpp (файл DLL)
int BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType)
{
LPWFSCIMNOTETYPE fw_notetypedata;
LPWFSCIMNOTETYPELIST lpNoteTypeList; //output param
hResult = WFSGetInfo(hService, WFS_INF_CIM_BANKNOTE_TYPES, (LPVOID)NULL, 400000, &res);
lpNoteTypeList=(LPWFSCIMNOTETYPELIST)res->lpBuffer;
if(hResult!=0)
{
return (int)hResult;
}
sp_BankNoteType->cim_usNumOfNoteTypes = lpNoteTypeList->usNumOfNoteTypes;
for(int i=0;i<lpNoteTypeList->usNumOfNoteTypes;i++)
{
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_usNoteID = lpNoteTypeList->lppNoteTypes[i]->usNoteID;
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_ulValues = lpNoteTypeList->lppNoteTypes[i]->ulValues;
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_bConfigured = lpNoteTypeList->lppNoteTypes[i]->bConfigured;
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[0] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[0];
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[1] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[1];
sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[2] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[2];
}
return (int)hResult;
}
Состав :-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct sp_notetype
{
public ushort cim_usNoteID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] cim_cCurrencyID;
public ulong cim_ulValues;
public bool cim_bConfigured;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct sp_BankNoteTypeList
{
public int cim_usNumOfNoteTypes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public sp_notetype[] SP_CIMNOTETYPE;
};
public sp_notetype[] SP_CIMNOTETYPE;
public sp_BankNoteTypeList SP_CIMNOTETYPELIST;
Вызов функции: —
[DllImport(@"abc.dll")]
public static extern int BanknoteType(out sp_BankNoteTypeList SP_CIMNOTETYPELIST);public string BNA_BankNoteType(out int[] NoteID,out string[]CurrencyID,out string[] Values,out bool[] Configured)
{
NoteID = new int[12];
CurrencyID = new string[12];
Values = new string[12];
Configured = new bool[12];
try
{
trace.WriteToTrace(" Entered in BNA_BankNoteType ", 1);
hResult = BanknoteType(out SP_CIMNOTETYPELIST);
for (int i = 0; i < 12; i++)
{
NoteID[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_usNoteID);
CurrencyID[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[0]).ToString() + (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[1]).ToString() + (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[2]).ToString();
Values[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_ulValues).ToString();
Configured[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_bConfigured);
}
return DicErrorCode(hResult.ToString());
}
catch (Exception ex)
{
return "FATAL_ERROR";
}
когда я пытаюсь вызвать их в C #, я получаю значение мусора в C #.
Пока я отлаживаю их, я считаю правильным хранить значения.
Любая помощь относительно того, как значения должны быть переданы, была бы очень полезна.
Заранее спасибо.
Декларации C # находятся на правильном пути, просто детали не так точны. Хорошая отправная точка эта почта, показывает, как написать некоторый тестовый код, чтобы убедиться, что объявления структуры хорошо соответствуют. Делаем так на этом конкретном:
C++: auto len = sizeof(SP_CIMNOTETYPELIST); // 196 bytes
C# : var len = Marshal.SizeOf(typeof(sp_BankNoteTypeList)); // 296 bytes
Не близко, вы должны получить точное совпадение, чтобы надеяться на правильное распределение. Объявление C # для внутренней структуры должно выглядеть так:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct sp_notetype {
public ushort cim_usNoteID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] cim_cCurrencyID;
public uint cim_ulValues;
private byte _cim_bConfigured;
public bool cim_bConfigured {
get { return _cim_bConfigured != 0; }
}
};
[DllImport(@"abc.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern int BanknoteType([Out]out sp_BankNoteTypeList list);
Объявление sp_BankNoteTypeList в порядке. Перезапустите тест, и теперь вы должны получить также 196 байт в C #. Аннотирование изменений:
CharSet = CharSet.Ansi
char
8-битный тип. CharSet.Auto был бы правильным выбором, если бы нативная структура использовала WCHAR. public uint cim_ulValues;
ULONG
на 32 бита в 32-битных и 64-битных программах. Что делает uint
правильный эквивалент C # вместо ulong.private byte _cim_bConfigured;
bool
это очень сложный тип с плохой стандартизацией. Это 1 байт в C ++, 4 байта в C, 2 байта в COM-взаимодействии, 1 байт в управляемом коде. Маршалинг по умолчанию предполагает BOOL
как соответствующий нативный тип, как это делается в winapi. Объявление его закрытым как байт с получателем общедоступных свойств — это один из способов сделать это, и я предпочел здесь один, применение атрибута [MarshalAs (UnmanagedType.U1)] к полю было бы другим.CallingConvention = CallingConvention.Stdcall
[Out]out sp_BankNoteTypeList list
out
достаточно. Но это деталь языка C #, о которой маршаллер не знает. Копирование обратно должно быть запрошено явным образом, структура не является «blittable». Или, другими словами, собственный макет не совпадает с внутренним управляемым макетом. ByValArray делает это неизбежным.Весьма прачечная, надеюсь, я их всех получил. Получение одинакового размера структуры — 95% битвы.
Я воссоздал C ++ часть, так как я получил ошибки, связанные с WFSGetInfo
и связанных с ним структур, я заполняю поля некоторыми случайными данными (примечание о char[3]
поле: я заполняю его 3 случайными заглавными буквами). С вдохновением от MSDN и многие другие вопросы ТАК, Я идентифицировал список проблем в коде; После устранения этих проблем я смог получить право данные из C #. Как примечание, я использую VStudio2015.
Пара наземных нот:
BNA_API
для меня это __declspec(dllexport)
Объявление функции находится внутри
#if defined(__cplusplus)
extern "C" {
#endif
// Function declaration comes here
#if defined(__cplusplus)
}
#endif
блок, чтобы избежать Искажение имени в C ++. Для более подробной информации, проверьте [MSDN]: украшенные имена
Проблемы:
__cdecl
в то время как C # маршалер использует __stdcall
, Это не работает хорошо, это повреждает стек, когда От себяИНГ /попаргументы пинга. Чтобы это исправить, вы должны изменить значение по умолчанию в только одно место:
BNA_API int
__stdcall
BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType);
— это метод, который я выбралDllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)]
— это тоже работаетstruct sp_notetype
«s C # определение, вы должны иметь: public
uint
cim_ulValues;
, Проверьте [MSDN]: маршалинг аргументов для полного спискаCharset
ваших структур — в С, char
(Шириной 8 бит) — измените его с Charset.Auto
в Charset.Ansi
, Проверьте [SO]: C # вызывает C DLL, передайте char * как параметр, неверный