Указатели в C# для получения ссылки из функции DllImport
-
23-09-2019 - |
Вопрос
Я ссылаюсь на DLL в своем проекте на С# следующим образом:
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, string cout, string flimit,
string frate, string fwindow, string fincrement, string fbird,
string fparameter, string fvalidation, string fcoupon);
Функция FeeCalculation экспортируется в DLL следующим образом:
extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin,
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);
Функция DLL возвращает ссылку на свои внутренние структуры в форме char *, поэтому, если бы вы ссылались на эту DLL в C++, вы бы сделали следующее, чтобы выполнить расчет и получить возвращаемые структуры:
FeeCalculation(buff, (char *)&fans, (char *)fl, (char *)ft, (char *)fw, (char *)fi, (char *)fe, (char *)&fm, (char *)val, (char *)cpn);
Теперь, как мне получить те значения, которые возвращаются по ссылке, с помощью C#?То есть, как мне сделать то же самое на C#, чтобы получить возвращаемые структуры для получения возвращенного расчета?Я знаю, что мне нужно создать небезопасный метод, но мне неясно, как обращаться с адресами памяти в C#, как в C++.
Редактировать:Ниже указано, как использовать IntPtr, но как поместить их в идентичную структуру, чтобы на поля структуры можно было ссылаться?
Редактировать:Вот возвращаемая структура, которая меня интересует (cout):
struct feeAnswer {
unsigned int fee;
unsigned int tax1;
unsigned int tax2;
unsigned int tax3;
unsigned int tax4;
unsigned int surcharge1;
unsigned int surcharge2;
unsigned int validationFee;
unsigned int couponFee1;
unsigned int couponFee2;
unsigned int couponFee3;
unsigned int couponFee4;
unsigned short int dstay; //Day Stay
unsigned short int mstay; //Minute Stay
};
Вот (cin), который я бы передал вместе с другими структурами (на данный момент они имеют нулевой размер, я хочу, чтобы это сначала работало, а затем реализую остальное):
struct feeRequest {
unsigned char day;
unsigned char month;
unsigned int year; //2000 ~ 2099
unsigned char hour;
unsigned char minute;
unsigned char rate;
unsigned char validation;
unsigned char coupon1;
unsigned char coupon2;
unsigned char coupon3;
unsigned char coupon4;
};
Решение
Редактировать:теперь, когда у нас есть структуры для работы, возможно лучшее решение.Просто объявите структуры в C#, соответствующие вашим структурам C++, и используйте их в объявлении extern.
[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer {
public uint fee;
public uint tax1;
public uint tax2;
public uint tax3;
public uint tax4;
public uint surcharge1;
public uint surcharge2;
public uint validationFee;
public uint couponFee1;
public uint couponFee2;
public uint couponFee3;
public uint couponFee4;
public ushort dstay; //Day Stay
public ushort mstay; //Minute Stay
};
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct feeRequest {
public byte day;
public byte month;
public uint year; //2000 ~ 2099
public byte hour;
public byte minute;
public byte rate;
public byte validation;
public byte coupon1;
public byte coupon2;
public byte coupon3;
public byte coupon4;
};
[DllImport ("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation (
feeRequest cin,
out feeAnswer cout,
...
....
оригинальный ответ (до того, как у нас появились структуры) ниже
Мне кажется, что это не ссылки на внутренние строки, а скорее указатели на строковые буферы, которые будут заполнены вызовом.Если бы ты был возвращение строковые указатели, тогда они будут объявлены char**
скорее, чем char*
.
Так что я думаю, что это просто стандартные параметры.Их просто много.Итак, ваше взаимодействие с C# будет выглядеть так
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string cout,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string flimit,
или это если ваши «строки» на самом деле не строки
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] cout,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] flimit,
....
Другие советы
Параметры char* в этом случае являются не строками, а указателями на фрагменты необработанных байтов, представляющих данные.Вам следует маршалировать свои параметры как экземпляры типа IntPtr в сочетании с Маршал.AllocHGlobal чтобы создать кусок памяти, а затем Маршал.PtrToStructure чтобы преобразовать этот блок памяти в пригодный для использования тип .NET.
В качестве примера:
[StructLayout(LayoutKind.Sequential)]
struct MyUnmanagedType
{
public int Foo;
public char C;
}
IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyUnmanagedType)));
try
{
FeeCalculation(memory);
MyUnmanagedType result = (MyUnmanagedType)Marshal.PtrToStructure(
memory, typeof(MyUnmanagedType));
}
finally
{
Marshal.FreeHGlobal(memory);
}
Чтобы ответить на ваш Edit, вам нужно создать структуру, а затем использовать StructLayoutAttribute в полях, чтобы порядок байтов и заполнение были такими же, как в исходной dll.