I'm trying to create a PInvoke for FbwfFindFirst and am struggling with the struct FbwfCacheDetail.
In short, I'm not sure how to marshal WCHAR fileName[1]; seeing as it's a variable length array and a non-null terminated.
Any help would be welcomed
Since the whole structure is of variable size, one way to do this is like this (I can't test it because I don't have this dll on my system):
string volume = "";
int size = 0;
// ask for whole structure size
FbwfFindFirst(volume, IntPtr.Zero, ref size); // this call should return ERROR_MORE_DATA which is ok
// allocate for the structure
var ptr = Marshal.AllocHGlobal(size);
try
{
FbwfFindFirst(volume, ptr, ref size); // should not return error
// get the easy part
var detail = Marshal.PtrToStructure<FbwfCacheDetail>(ptr);
// compute filename offset and get the string
// file name length is in bytes, per documentation
var fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf<FbwfCacheDetail>("fileName").ToInt32(), detail.fileNameLength / 2);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
[DllImport("fbwflib", CharSet = CharSet.Unicode)]
static extern int FbwfFindFirst(string volume, IntPtr cacheDetail, ref int size);
[StructLayout(LayoutKind.Sequential)]
struct FbwfCacheDetail
{
public int cacheSize;
public int openHandleCount;
public int fileNameLength;
byte fileName; // don't use this member
}
Simon Mourier's answer is 99% correct, and with normal APIs it would have definitely worked, but it appears as if this particular API doesn't follow "normal rules", whatever that might be ;). As such, I needed to modify a couple things and this is what worked for me:
const int ERROR_INSUFFICIENT_BUFFER = 122;
const int ERROR_MORE_DATA = 234;
var volume = "C:";
var fileName = string.Empty;
var size = 0;
while (true)
{
var ptr = Marshal.AllocHGlobal(size); // FbwfFindFirst fails if given IntPtr.Zero - regardless of what the value of size is.
try
{
var result = FbwfFindFirst(volume, ptr, ref size);
// Despite documentation saying otherwise, it can return either of these
if (result == ERROR_MORE_DATA || result == ERROR_INSUFFICIENT_BUFFER)
{
continue;
}
if (result != 0)
{
throw new Exception($"Failed with {result}");
}
// get the easy part
var detail = (FbwfCacheDetail) Marshal.PtrToStructure(ptr, typeof(FbwfCacheDetail));
// compute filename offset and get the string
// file name length is in bytes, per documentation
fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf(typeof(FbwfCacheDetail), "fileName").ToInt32(), detail.fileNameLength / 2);
break;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
EDIT
Forgot to say that the pinvoke for FbwfFindFirst needs to be as follows, else it will return ERROR_INVALID_PARAMETER:
[DllImport("fbwflib.dll")]
public static extern uint FbwfFindFirst(
[MarshalAs(UnmanagedType.LPWStr)]
string volume,
IntPtr cacheDetail,
ref int size
);
Related
I am having the most difficult time marshaling this struct between C# and C++.
What makes it very hard to troubleshoot is that SOMETIMES the strings are populated with data (wtf), but most of the time they are not.
I've tried sending over an Array of structs as well as a IntPtr, but the results are similar, the strings in the struct are almost always empty and I can't figure out what I'm doing wrong in the marshaling. The code is posted below. Any help would be appreciated.
Edit***
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans. ***
C++:
#pragma pack (push, 1)
typedef struct
{
char FirmwareVers[FS_MAX_FIRMWARE_VER];
char SerialNum[FS_MAX_SERIAL_NUM];
char HardwareVers[FS_MAX_HW_VER];
ULONG StatusFlags;
int LMIndex;
} FS_LMON_STATUS, *PFS_LMON_STATUS;
DllExport int _stdcall FS_GetLMs(PFS_LMON_STATUS pLaunchMonInfo, int MaxLaunchMons, int *pNumLaunchMons)
{
int Cnt;
FS_LMON_STATUS LMStatus;
if(!g_IsInitalized)
return FS_NOT_INITALIZED;
*pNumLaunchMons = 0;
if(MaxLaunchMons == 0)
return FS_ERROR;
for(Cnt = 0; Cnt < MAX_LM_CONNECTIONS; Cnt++)
{
if(g_CreatedClasses.pLMList->GetLMStatus(Cnt, &LMStatus) != FS_SUCCESS)
continue;
if(LMStatus.LMIndex != INVALID_LM_INDEX)
{
memcpy(pLaunchMonInfo, &LMStatus, sizeof(LMStatus));
pLaunchMonInfo++;
(*pNumLaunchMons)++;
MaxLaunchMons--;
if(MaxLaunchMons == 0)
{
return FS_SUCCESS;
}
}
}
return FS_SUCCESS;
}
C#:
[DllImport("FSADLL", SetLastError = false)]
private static extern int FS_GetLMs([Out] IntPtr pLaunchMonInfo, int MaxLaunchMons, ref int pNumLaunchMons);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] //, Size = 38)]
public struct FS_LMON_STATUS
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_FIRMWARE_VER)] //10 bytes
public string FirmwareVers;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_SERIAL_NUM)] // 15 bytes
public string SerialNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_HW_VER)] // 5 bytes
public string HardwareVers;
public uint StatusFlags; //4 bytes
public int LMIndex; // identifies which index //4 bytes
}
const int max_launch_monitors = 8;
FS_LMON_STATUS[] dev_info = new FS_LMON_STATUS[max_launch_monitors];
int num_launch_monitors = 0;
IntPtr pAddr = Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS)));
Marshal.StructureToPtr(dev_info, pAddr, false);
int result = FS_GetLMs(pAddr, max_launch_monitors, ref num_launch_monitors);
UnityEngine.Debug.Log("Result of FS_GetLMs: " + result);
FS_LMON_STATUS[] device_info = new FS_LMON_STATUS[max_launch_monitors];
//Marshal.Copy(pAddr, device_info, (int)0, num_launch_monitors * (int)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
//Marshal.ReadIntPtr(pAddr, 0);
//device_info = (FS_LMON_STATUS[]) Marshal.PtrToStructure(Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS[]))), typeof(FS_LMON_STATUS[]));
if (num_launch_monitors > 0)
UnityEngine.Debug.Log("GC2 Device Found.");
else // If there is no devices found, remove the previous device from the holder variable
GC2Device = null;
for (int i = 0; i < num_launch_monitors; i++)
{
device_info[i] = (FS_LMON_STATUS)Marshal.PtrToStructure(pAddr, typeof(FS_LMON_STATUS));
pAddr = new IntPtr(Marshal.SizeOf(typeof(FS_LMON_STATUS)) + pAddr.ToInt64());
}
//*** There will only ever be 1 device in the list until the old SDK is fixed ***
for (int lm_index = 0; lm_index < num_launch_monitors; lm_index++)
{
if (device_info[lm_index].StatusFlags != LM_STATUS_DISCONNECTED)
{
UnityEngine.Debug.Log("device_info.SerialNum: " + device_info[lm_index].SerialNum);
//assign each LM to a LM data structure
LaunchMonitor logical_device = new LaunchMonitor(inst);
logical_device.mLaunchMonitorType = LaunchMonitorType.LAUNCH_MONITOR_TYPE_GC2;
logical_device.mConnectionType = ConnectionType.USB_CONNECTION;
IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(device_info[lm_index]));
Marshal.StructureToPtr(device_info[lm_index], pnt, false);
//Marshal.Copy(device_info[lm_index], dv_info, 0, (uint)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
logical_device.mConnectionToken = pnt;
//GC2Devices.Add(logical_device);
logical_device.Serial = logical_device.GetSerialNumber();
GC2Device = logical_device;
}
}
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans.
First of all I want to say that I know enough about c++ and pinvoke to be frustrated.
I am currently working on a c++ wrapper and pinvoke that is not returning the info I need.
Below are the pieces of what I am doing:
I have a c++ method with the signature of:
DllExport int LookupFamilyName(__in PCWSTR familyName,__out_opt wchar_t* result, int lengthIn)
With the internal code:
UINT32 count = 0;
UINT32 length = 0;
LONG rc = GetPackagesByPackageFamily(familyName, &count, NULL, &length, NULL);
if (rc == ERROR_SUCCESS)
{
return int(rc);
}
else if (rc != ERROR_INSUFFICIENT_BUFFER) //122
{
return int(rc);
}
PWSTR *fullNames = (PWSTR *) malloc(count * sizeof(*fullNames));
if (fullNames == NULL)
{
return 700010; //faile to allocate memeory
}
PWSTR buffer = (PWSTR) malloc(length * sizeof(WCHAR));
if (buffer == NULL)
{
return 700011; //faile to allocate memeory
}
rc = GetPackagesByPackageFamily(familyName, &count, fullNames, &length, buffer);
if (rc != ERROR_SUCCESS)
{
return int(rc);
}
else
{
result = new wchar_t[lengthIn];
for (UINT32 i = 0; i < count; ++i)
{
wcscpy_s(result,lengthIn,fullNames[i]);
}
}
free(buffer);
free(fullNames);
I am then using the following to invoke:
[DllImport("ClassLibrary1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int LookupFamilyName(string familyName, ref StringBuilder sbPackageFullName, int len);
And then I am calling it from C# with the following:
string packagename = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe";
StringBuilder showmethemoney = new StringBuilder(64);
int x = LookupFamilyName(packagename, ref showmethemoney,showmethemoney.Capacity);
I'm passing in a StringBuilder for the wchar_t* and that value is filled in with what I want by the time we get to the end of the LookupFamilyName method but when we get back to the c# code, the StringBuilder does not have the value that the wchar_t* had in the c++ code.
For those of you that know far more then I, what do I need to do to make sure that the the value in the wchar_t* result gets passed back to the StringBuilder?
I'd like to interpret the output of the following unmanaged function:
afc_error_t afc_get_device_info (afc_client_t client, char ***device_information)
I import the dll with the code:
[DllImport("libimobiledevice.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern short afc_get_device_info(IntPtr client, out IntPtr info);
As long as I only needed to convert response to string Marshal.PtrToStringAnsi was okay. However I have no idea how to convert that IntPtr back to char array.
It should be something like:
IntPtr di;
int result = afc_read_directory(client, #"C:\", out di);
if (di == IntPtr.Zero)
{
throw new Exception();
}
IntPtr di2 = di;
while (true)
{
IntPtr ptr = Marshal.ReadIntPtr(di2);
if (ptr == IntPtr.Zero)
{
break;
}
string str = Marshal.PtrToStringAnsi(ptr);
if (str == string.Empty)
{
break;
}
di2 = di2 + IntPtr.Size;
}
Try if it works, then I'll explain how...
important you are leaking memory here...
I've found this example in C:
char **dirs = NULL;
afc_read_directory(afc, "/eafaedf", &dirs);
if (!dirs)
afc_read_directory(afc, "/", &dirs);
printf("Directory time.\n");
for (i = 0; dirs[i]; i++) {
printf("/%s\n", dirs[i]);
free(dirs[i]);
}
if (dirs)
free(dirs);
you are responsible for freeing the memory (see the free inside the cycle and the final free?). In this case (and for other methods that return arrays of C-strings you can use afc_dictionary_free. Note that other methods like afc_receive_data that return a single block of memory you can't use it.
I have been banging my head against a problem for days now. I would like your help.
I am trying to interface to I2C from a board running Windows CE7. The board is a Boundary Devices Nitrogen6X.
I am trying to code this in C#.
After a lot of googling and trial and error I can now do almost everything with the I2C (by that I mean I wrapped most commands in methods that work). Of course, the one thing that I cannot do yet is Reading/Writing. I have been trying a few different implementations, porting C and C++ code that supposedly worked. To no avail. Currently I am putting more effort in the two implementations I will copy here.
Neither of these implementations work for me. Both enter the error management portion, and both report error number 87 (ERROR_INVALID_PARAMETER).
Does anyone have experience on these kind of issues? could someone point out what I am doing wrong?
Edit 1: I should probably mention that I am trying to "see" some signals on the SDA and SCL pins of I2C3: of the board by simply plugging an oscilloscope to them. There is no actual device connected on the I2C bus. I would expect this to give me some sort of error after the first byte (addres+Read/Write) has been sent, because no acknowledge bit would be received. However, I see that error 87 in my code, and no change on the signals as seen from the scope (both remain high on idle).
(Code snippets follow)
The first one uses pointers and stuff, and is probably closer to C++ code:
StructLayout(LayoutKind.Sequential)]
unsafe public struct UNSAFE_I2C_PACKET
{
//[MarshalAs(UnmanagedType.U1)]
public byte byAddr; //I2C slave device address
//[MarshalAs(UnmanagedType.U1)]
public byte byRw; //Read = I2C_Read or Write = I2C_Write
//[MarshalAs(UnmanagedType.LPArray)]
public byte* pbyBuf; //Message Buffer
//[MarshalAs(UnmanagedType.U2)]
public Int16 wLen; //Message Buffer Length
//[MarshalAs(UnmanagedType.LPStruct)]
public int* lpiResult; //Contain the result of last operation
}
[StructLayout(LayoutKind.Sequential)]
unsafe public struct UNSAFE_I2C_TRANSFER_BLOCK
{
//public I2C_PACKET pI2CPackets;
public UNSAFE_I2C_PACKET* pI2CPackets;
public Int32 iNumPackets;
}
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
unsafe internal static extern int DeviceIoControlCE(
IntPtr hDevice, //file handle to driver
uint dwIoControlCode, //a correct call to CTL_CODE
[In, Out]byte* lpInBuffer,
uint nInBufferSize,
byte* lpOutBuffer,
uint nOutBufferSize,
uint* lpBytesReturned,
int* lpOverlapped);
unsafe public void ReadI2C(byte* pBuf, int count)
{
int ret;
int iResult;
UNSAFE_I2C_TRANSFER_BLOCK I2CXferBlock = new UNSAFE_I2C_TRANSFER_BLOCK();
UNSAFE_I2C_PACKET i2cPckt = new UNSAFE_I2C_PACKET();
//fixed (byte* p = pBuf)
{
i2cPckt.pbyBuf = pBuf;// p;
i2cPckt.wLen = (Int16)count;
i2cPckt.byRw = I2C_READ;
i2cPckt.byAddr = BASE;
i2cPckt.lpiResult = &iResult;
I2CXferBlock.iNumPackets = 1;
//fixed (I2C_PACKET* pck = &i2cPckt)
{
I2CXferBlock.pI2CPackets = &i2cPckt; // pck;
//fixed (I2C_TRANSFER_BLOCK* tBlock = &I2CXferBlock)
{
if (DeviceIoControlCE(_i2cFile,
I2C_IOCTL_TRANSFER,
(byte*)&I2CXferBlock,//tBlock,
(uint)sizeof(UNSAFE_I2C_TRANSFER_BLOCK),//Marshal.SizeOf(I2CXferBlock),
null,
0,
null,
null) == 0)
{
int error = GetLastError();
diag("Errore nella TRANSFER");
diag(error.ToString());
}
}
}
}
}
The second option I am working on marshals stuff around between managed and unmanaged:
[StructLayout(LayoutKind.Sequential)]
public struct I2C_PACKET
//public class I2C_PACKET
{
//[MarshalAs(UnmanagedType.U1)]
public Byte byAddr; //I2C slave device address
//[MarshalAs(UnmanagedType.U1)]
public Byte byRw; //Read = I2C_Read or Write = I2C_Write
//[MarshalAs(UnmanagedType.LPArray)]
public IntPtr pbyBuf; //Message Buffer
//[MarshalAs(UnmanagedType.U2)]
public Int16 wLen; //Message Buffer Length
//[MarshalAs(UnmanagedType.LPStruct)]
public IntPtr lpiResult; //Contain the result of last operation
}
[StructLayout(LayoutKind.Sequential)]
public struct I2C_TRANSFER_BLOCK
{
//public I2C_PACKET pI2CPackets;
//[MarshalAs(UnmanagedType.LPArray)]
public IntPtr pI2CPackets;
//[MarshalAs(UnmanagedType.U4)]
public Int32 iNumPackets;
}
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern int DeviceIoControlCE(
IntPtr hDevice, //file handle to driver
uint dwIoControlCode, //a correct call to CTL_CODE
[In] IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
unsafe public void ReadI2C(byte[] buffer)
{
int count = buffer.Length;
I2C_TRANSFER_BLOCK I2CXFerBlock = new I2C_TRANSFER_BLOCK();
I2C_PACKET I2CPckt = new I2C_PACKET();
I2CPckt.byAddr = BASE;
I2CPckt.byRw = I2C_READ;
I2CPckt.wLen = (Int16)count;
I2CPckt.lpiResult = Marshal.AllocHGlobal(sizeof(int));
I2CPckt.pbyBuf = Marshal.AllocHGlobal(count);
//GCHandle packet = GCHandle.Alloc(I2CPckt, GCHandleType.Pinned);
I2CXFerBlock.iNumPackets = 1;
I2CXFerBlock.pI2CPackets = Marshal.AllocHGlobal(Marshal.SizeOf(I2CPckt)); //(Marshal.SizeOf(typeof(I2C_PACKET)));// //(sizeof(I2C_PACKET));//
Marshal.StructureToPtr(I2CPckt, I2CXFerBlock.pI2CPackets, false);
//I2CXFerBlock.pI2CPackets = packet.AddrOfPinnedObject();
//GCHandle xferBlock = GCHandle.Alloc(I2CXFerBlock, GCHandleType.Pinned);
IntPtr xfer = Marshal.AllocHGlobal(Marshal.SizeOf(I2CXFerBlock)); //(sizeof(I2C_TRANSFER_BLOCK)); //
Marshal.StructureToPtr(I2CXFerBlock, xfer, false);
//IntPtr xfer = xferBlock.AddrOfPinnedObject();
uint size = (uint)sizeof(I2C_TRANSFER_BLOCK);//Marshal.SizeOf(I2CXFerBlock);
uint getCnt = 0;
if ((DeviceIoControlCE(_i2cFile,
I2C_IOCTL_TRANSFER,
xfer,
size,
xfer, //IntPtr.Zero,
size, //0,
out getCnt,
IntPtr.Zero)) == 0)
{
int error = GetLastError();
diag("Errore nella TRANSFER.");
diag(error.ToString());
}
else
{
//success
I2CXFerBlock = (I2C_TRANSFER_BLOCK)Marshal.PtrToStructure(xfer, typeof(I2C_TRANSFER_BLOCK));
I2CPckt = (I2C_PACKET)Marshal.PtrToStructure(I2CXFerBlock.pI2CPackets, typeof(I2C_PACKET));
Marshal.Copy(I2CPckt.pbyBuf, buffer, 0, count);
diag("Success in TRANSFER: " + buffer[0].ToString());
}
Marshal.FreeHGlobal(I2CPckt.pbyBuf);
Marshal.FreeHGlobal(I2CXFerBlock.pI2CPackets);
Marshal.FreeHGlobal(xfer);
//packet.Free();
//xferBlock.Free();
}
Edit 2: The (supposedly) working code I have (which I am unable to run) comes from drivers I have been given, which might be partially proprietary (hence I cannot share). However I found online the header for an I2C bus, that contains the following definition:
#define I2C_MACRO_TRANSFER(hDev, pI2CTransferBlock) \
(DeviceIoControl(hDev, I2C_IOCTL_TRANSFER, (PBYTE) pI2CTransferBlock, sizeof(I2C_TRANSFER_BLOCK), NULL, 0, NULL, NULL))
I initially tried giving "null" to parameters as it's done here, but I still got the same error code.
Edit 3: From the same driver, the struct definitions:
// I2C Packet
typedef struct
{
BYTE byAddr; // I2C slave device address for this I2C operation
BYTE byRW; // Read = I2C_READ or Write = I2C_WRITE
PBYTE pbyBuf; // Message Buffer
WORD wLen; // Message Buffer Length
LPINT lpiResult; // Contains the result of last operation
} I2C_PACKET, *PI2C_PACKET;
// I2C Transfer Block
typedef struct
{
I2C_PACKET *pI2CPackets;
INT32 iNumPackets;
} I2C_TRANSFER_BLOCK, *PI2C_TRANSFER_BLOCK;
Edit 4: I tried implementing version passing a ref to my structures, as #ctacke suggested in his comment. I still get the same error, so I guess I must have done womthing different from the way he thought it. Here is the snippet:
[StructLayout(LayoutKind.Sequential)]
public struct REF_I2C_PACKET //public class REF_I2C_PACKET //
{
//[MarshalAs(UnmanagedType.U1)]
public Byte byAddr; //I2C slave device address
//[MarshalAs(UnmanagedType.U1)]
public Byte byRw; //Read = I2C_Read or Write = I2C_Write
//[MarshalAs(UnmanagedType.LPArray)]
public IntPtr pbyBuf; //Message Buffer
//[MarshalAs(UnmanagedType.U2)]
public Int16 wLen; //Message Buffer Length
//[MarshalAs(UnmanagedType.LPStruct)]
public IntPtr lpiResult; //Contain the result of last operation
}
[StructLayout(LayoutKind.Sequential)]
public struct REF_I2C_TRANSFER_BLOCK //public class REF_I2C_TRANSFER_BLOCK //
{
//public I2C_PACKET pI2CPackets;
public IntPtr pI2CPackets;
//[MarshalAs(UnmanagedType.U4)]
public Int32 iNumPackets;
//[MarshalAs(UnmanagedType.LPArray)]
//[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = 2)]
//public REF_I2C_PACKET[] pI2CPackets;
}
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
unsafe internal static extern int DeviceIoControlCE(
IntPtr hDevice, //file handle to driver
uint dwIoControlCode, //a correct call to CTL_CODE
//[MarshalAs(UnmanagedType.AsAny)]
//[In] object lpInBuffer, //
ref REF_I2C_TRANSFER_BLOCK lpInBuffer,
uint nInBufferSize,
byte* lpOutBuffer, //ref REF_I2C_TRANSFER_BLOCK lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned, //uint* lpBytesReturned,
int* lpOverlapped);
unsafe public void RefReadI2C(byte[] buffer)
{
int count = buffer.Length;
REF_I2C_TRANSFER_BLOCK I2CXFerBlock = new REF_I2C_TRANSFER_BLOCK();
REF_I2C_PACKET[] I2CPckt = new REF_I2C_PACKET[2];
I2CPckt[0] = new REF_I2C_PACKET();
I2CPckt[1] = new REF_I2C_PACKET();
I2CPckt[0].byAddr = BASE;
I2CPckt[0].byRw = I2C_READ;
I2CPckt[0].wLen = (Int16)count;
I2CPckt[0].lpiResult = Marshal.AllocHGlobal(sizeof(int));
I2CPckt[0].pbyBuf = Marshal.AllocHGlobal(count);
Marshal.Copy(buffer, 0, I2CPckt[0].pbyBuf, count);
I2CPckt[1].byAddr = BASE;
I2CPckt[1].byRw = I2C_READ;
I2CPckt[1].wLen = (Int16)count;
I2CPckt[1].lpiResult = Marshal.AllocHGlobal(sizeof(int));
I2CPckt[1].pbyBuf = Marshal.AllocHGlobal(count);
Marshal.Copy(buffer, 0, I2CPckt[0].pbyBuf, count);
I2CXFerBlock.iNumPackets = 2;
I2CXFerBlock.pI2CPackets = Marshal.AllocHGlobal(Marshal.SizeOf(I2CPckt[0])*I2CPckt.Length);
uint size = (uint)Marshal.SizeOf(I2CXFerBlock); //sizeof(REF_I2C_TRANSFER_BLOCK);//Marshal.SizeOf(I2CXFerBlock);
//size += (uint)(Marshal.SizeOf(I2CPckt[0]) * I2CPckt.Length);
uint getCnt = 0;
if ((DeviceIoControlCE(_i2cFile,
I2C_IOCTL_TRANSFER,
ref I2CXFerBlock,
size,
null, //IntPtr.Zero,
0, //0,
out getCnt,
null)) == 0)
{
int error = GetLastError();
diag("Errore nella TRANSFER.");
diag(error.ToString());
}
else
{
//success
I2CPckt = (REF_I2C_PACKET[])Marshal.PtrToStructure(I2CXFerBlock.pI2CPackets, typeof(REF_I2C_PACKET[]));
Marshal.Copy(I2CPckt[0].pbyBuf, buffer, 0, count);
diag("Success in TRANSFER: " + buffer[0].ToString());
}
Marshal.FreeHGlobal(I2CPckt[0].pbyBuf);
Marshal.FreeHGlobal(I2CPckt[0].lpiResult);
}
Edit 5:
I found online (http://em-works.googlecode.com/svn/trunk/WINCE600/PLATFORM/COMMON/SRC/SOC/COMMON_FSL_V2_PDK1_9/I2C/PDK/i2c_io.cpp) The following code:
BOOL I2C_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut)
{
/*stuff*/
case I2C_IOCTL_TRANSFER:
{
#define MARSHAL 1
#if MARSHAL
DuplicatedBuffer_t Marshalled_pInBuf(pBufIn, dwLenIn, ARG_I_PTR);
pBufIn = reinterpret_cast<PBYTE>( Marshalled_pInBuf.ptr() );
if( (dwLenIn > 0) && (NULL == pBufIn) )
{
return FALSE;
}
#endif
I2C_TRANSFER_BLOCK *pXferBlock = (I2C_TRANSFER_BLOCK *) pBufIn;
if (pXferBlock->iNumPackets<=0)
{
return FALSE;
}
#if MARSHAL
MarshalledBuffer_t Marshalled_pPackets(pXferBlock->pI2CPackets,
pXferBlock->iNumPackets*sizeof(I2C_PACKET),
ARG_I_PTR);
I2C_PACKET *pPackets = reinterpret_cast<I2C_PACKET *>(Marshalled_pPackets.ptr());
if( (NULL == pPackets) )
{
return FALSE;
}
#else
I2C_PACKET *pPackets = pXferBlock->pI2CPackets;
#endif
#if MARSHAL
struct Marshalled_I2C_PACKET
{
MarshalledBuffer_t *pbyBuf;
MarshalledBuffer_t *lpiResult;
} *Marshalled_of_pPackets;
Marshalled_of_pPackets = new Marshalled_I2C_PACKET[pXferBlock->iNumPackets];
if (Marshalled_of_pPackets==0)
{
return FALSE;
}
MarshalledBuffer_t *pMarshalled_ptr;
int i;
// Map pointers for each packet in the array
for (i = 0; i < pXferBlock->iNumPackets; i++)
{
switch( pPackets[i].byRW & I2C_METHOD_MASK )
{
case I2C_RW_WRITE:
pMarshalled_ptr = new MarshalledBuffer_t(
pPackets[i].pbyBuf,
pPackets[i].wLen,
ARG_I_PTR,
FALSE, FALSE);
if (pMarshalled_ptr ==0)
{
bRet = FALSE;
goto cleanupPass1;
}
if (pMarshalled_ptr->ptr()==0)
{
bRet = FALSE;
delete pMarshalled_ptr;
goto cleanupPass1;
}
break;
case I2C_RW_READ:
pMarshalled_ptr = new MarshalledBuffer_t(
pPackets[i].pbyBuf,
pPackets[i].wLen,
ARG_O_PTR, FALSE, FALSE);
if (pMarshalled_ptr ==0)
{
bRet = FALSE;
goto cleanupPass1;
}
if (pMarshalled_ptr->ptr()==0)
{
bRet = FALSE;
delete pMarshalled_ptr;
goto cleanupPass1;
}
break;
default:
{
bRet = FALSE;
goto cleanupPass1;
}
}
pPackets[i].pbyBuf = reinterpret_cast<PBYTE>(pMarshalled_ptr->ptr());
Marshalled_of_pPackets[i].pbyBuf = pMarshalled_ptr;
}
for (i = 0; i < pXferBlock->iNumPackets; i++)
{
pMarshalled_ptr = new MarshalledBuffer_t(
pPackets[i].lpiResult, sizeof(INT),
ARG_O_PDW, FALSE, FALSE);
if (pMarshalled_ptr ==0)
{
bRet = FALSE;
goto cleanupPass2;
}
if (pMarshalled_ptr->ptr()==0)
{
bRet = FALSE;
delete pMarshalled_ptr;
goto cleanupPass2;
}
pPackets[i].lpiResult = reinterpret_cast<LPINT>(pMarshalled_ptr->ptr());
Marshalled_of_pPackets[i].lpiResult = pMarshalled_ptr;
}
#endif
bRet = pI2C->ProcessPackets(pPackets, pXferBlock->iNumPackets);
#if MARSHAL
DEBUGMSG (ZONE_IOCTL|ZONE_FUNCTION, (TEXT("I2C_IOControl:I2C_IOCTL_TRANSFER -\r\n")));
i = pXferBlock->iNumPackets;
cleanupPass2:
for (--i; i>=0; --i)
{
delete Marshalled_of_pPackets[i].lpiResult;
}
i = pXferBlock->iNumPackets;
cleanupPass1:
for (--i; i>=0; --i)
{
delete Marshalled_of_pPackets[i].pbyBuf;
}
delete[] Marshalled_of_pPackets;
#endif
break;
}
/*stuff*/
}
I cannot claim to understand 100% of it, but from the Windows naming conventions (http://msdn.microsoft.com/en-us/library/windows/desktop/aa378932(v=vs.85).aspx) it would appear that the size parameter I should send is the total number of bytes of my transfer, including everything. I have tried to figure that number out myself, but I have so far not been able to. Alternatively, I guess it would be possible to try and do something to the structures I have to turn them into a byte array. Only I guess that it would need to have a specific order of the bytes in it for the system to understand it.
Can anyone pitch in on that?
I'm trying to implement a custom collation in SQLite for Windows Runtime.
The create_collation method is implemented as follows:
SQLITE_API int sqlite3_create_collation(
sqlite3*,
const char *zName,
int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
So far I have the following C# signature:
[DllImport("sqlite3", EntryPoint = "sqlite3_create_collation", CallingConvention = CallingConvention.Cdecl)]
public static extern int CreateCollation(IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string name, int textRep, object state, Compare callback);
public delegate int Compare(object pCompareArg, int size1, IntPtr Key1, int size2, IntPtr Key2);
This is the implementation:
int i = CreateCollation(db, "unicode_nocase", SQLITE_UTF8, null, CompareMethod);
/* ... */
public static int CompareMethod(object o, int i1, IntPtr s1, int i2, IntPtr s2)
{
return string.Compare(Marshal.PtrToStringUni(s1), Marshal.PtrToStringUni(s2));
}
The application compiles without errors. The call to create_collation returns zero (SQLITE_OK), but if I use the collation in a statement the following error message is returned:
no such collation sequence: unicode_nocase
source reference: https://github.com/doo/SQLite3-WinRT/tree/master/SQLite3Component
Can somebody please help me?
Thank you!
After some time looking around inside Mono.Android.SQLite, which also uses the C implementation of SQLite, I found the solution:
The problem was that the call to sqlite3_create_collation has a void* parameter which I incorrectly defined as object in C# where it should be IntPtr.
I have posted the current implementation I have below. I partially reverse engineered the solution from the Mono implementation, which calls sqlite3_create_collation twice for every collation to be registered - once with the parameter eTextRep set to SQLITE_UTF16LE and a second time with SQLITE_UTF8. I could only imagine that this might help the SQLite core to find a fast implementation for different formats in which the string values are stored. However, these require different decoding when they are converted to C# strings.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int CompareCallback(IntPtr pvUser, int len1, IntPtr pv1, int len2, IntPtr pv2);
[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
private static extern int sqlite3_create_collation(IntPtr db, byte[] strName, int nType, IntPtr pvUser, CompareCallback func);
private const int SQLITE_UTF8 = 1;
private const int SQLITE_UTF16LE = 2;
private const int SQLITE_UTF16BE = 3;
private const int SQLITE_UTF16 = 4; /* Use native byte order */
private const int SQLITE_ANY = 5; /* sqlite3_create_function only */
private const int SQLITE_UTF16_ALIGNED = 8; /* sqlite3_create_collation only */
public void Register(IntPtr db)
{
if (db == IntPtr.Zero)
throw new ArgumentNullException("db");
//create null-terminated UTF8 byte array
string name = Name;
var nameLength = System.Text.Encoding.UTF8.GetByteCount(name);
var nameBytes = new byte[nameLength + 1];
System.Text.Encoding.UTF8.GetBytes(name, 0, name.Length, nameBytes, 0);
//register UTF16 comparison
int result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF16LE, IntPtr.Zero, CompareUTF16);
if (result != 0)
{
string msg = SQLite3.GetErrmsg(db);
throw SQLiteException.New((SQLite3.Result)result, msg);
}
//register UTF8 comparison
result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF8, IntPtr.Zero, CompareUTF8);
if (result != 0)
{
string msg = SQLite3.GetErrmsg(db);
throw SQLiteException.New((SQLite3.Result)result, msg);
}
}
private string GetUTF8String(IntPtr ptr, int len)
{
if (len == 0 || ptr == IntPtr.Zero)
return string.Empty;
if (len == -1)
{
do
{
len++;
}
while (Marshal.ReadByte(ptr, len) != 0);
}
byte[] array = new byte[len];
Marshal.Copy(ptr, array, 0, len);
return Encoding.UTF8.GetString(array, 0, len);
}
private string GetUTF16String(IntPtr ptr, int len)
{
if (len == 0 || ptr == IntPtr.Zero)
return string.Empty;
if (len == -1)
{
return Marshal.PtrToStringUni(ptr);
}
return Marshal.PtrToStringUni(ptr, len / 2);
}
internal int CompareUTF8(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
{
return Compare(GetUTF8String(ptr1, len1), GetUTF8String(ptr2, len2));
}
internal int CompareUTF16(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
{
return Compare(GetUTF16String(ptr1, len1), GetUTF16String(ptr2, len2));
}