Хорошо, моя серия проблем с контроллером лазерного устройства продолжается ... Я хочу вызвать следующую функцию C++ в DLL из моего кода C#:
extern "C" _declspec(dllimport) int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);
Указатель pData
указывает на массив лазерных точек, которые определены в заголовочном файле C++ следующим образом:
#pragma pack (1)
struct LaserPoint {
WORD x;
WORD y;
byte colors[6];
};
На стороне C# я определил импорт функции следующим образом:
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, ref byte[] pData, UInt32 numOfPoints, UInt32 scanrate);
... и такую структуру LaserPoint
:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
public UInt16 x;
public UInt16 y;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] colors;
}
Затем я вызываю функцию SendFrame
следующим образом:
LaserPoint point = new LaserPoint();
point.x = 16384;
point.y = 32768;
point.colors = new byte[] {255, 0, 0, 0, 0, 0};
byte[] arr = point2array(points);
SendFrame(0, ref arr, 1, 30000);
Это моя функция для преобразования экземпляра структуры LaserPoint
в массив байтов:
private static byte[] point2array(object obj) {
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
Но мое лазерное устройство не получает правильный ввод, так как лазер ведет себя очень странно. Использование того же кода в проекте C++ отлично работает. Так что ошибка где-то в этом коде взаимодействия C#.
Любые идеи?
Без проблем. : -] Извините за краткий ответ, в данный момент на мобильном телефоне.
Кстати, полностью ли вы контролируете как сторону C++, так и сторону C# кода? Поскольку SendFrame
можно легко заменить, чтобы избежать необходимости в point2array
(довольно ужасно как есть) ...
Массивы уже являются ссылочными типами, поэтому ref byte[]
- это двойное косвенное обращение, сродни byte**
- потеряйте ref
.
Если вы намереваетесь использовать здесь только структуры LaserPoint, вы можете определить функцию как
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);
И избавьтесь от копирования, и пусть среда выполнения сделает это за вас.
В противном случае, как говорит ildjarn, избавление от ref должно сработать, поскольку байтовые массивы уже являются ссылочными (указательными) типами.
В качестве примеров того, как это сделать, PInvoke вики имеет структуры и подписи для большинства API Windows, поэтому, хотя вашего вызова там не будет, есть много похожих примеров.
Большое спасибо за ваш ответ и предложение. У меня был ref LaserPoint[] pData
, который раньше не работал. Но LaserPoint[] pData
тоже работает нормально :-) Кажется, я раньше делал еще одну ошибку с указателями. Откажусь от своей дурацкой функции point2array
.
Вау, вот и все :)! Пожалуйста, ответьте, и я проверю его как решение. Спасибо!