I need to process the bytes[] when I get from external application. The external application is also done in C# and they send the bytes thru UDP. They are sending the bytes converted from struct which is stated below :
public struct DISPATCH_MESSAGE
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] federation_name; // Units: nil Range: nil
}
So, when I get the bytes, I need to take out the char[] inside that,
and get the string out of that char[].
I am new to this kind of unmanaged coding.
Probably you should declare it as ByValTStr (depending on the nature of the string, it might be different):
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPATCH_MESSAGE{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string federation_name;
}
UPDATE: If it's already giving out a char[], it's probably doing the necessary conversion (includes handling encoding) correctly, so I think you'd just need:
string str = new string(instance.federation_name);
Related
I'm trying to access the Nikon image SDK(for those interested see: 1) to implement access to *.nef file in a programm. I'm stuck at a return code from the dll which should be interpreted as "invalid parameter" and I'm running out of ideas.
Yes I know the chance, that somebody is exactly using this dll is sparse, but I'm rather looking for "writing"/"thinking" errors... I'm still learning (so excuse to any wrong used terms, etc...) and also for this reason this is a little "longer" post (some "aloud thinking" on my side ;-) )
1.) the dll has an entry function where you pass a identifier and a struct as parameter. The identifier stands for a specific command (like open,close,etc....). The struct is used for data exchange with the camera.
2.) I do have everything together and working (since, I'm getting a "return code") but I can't figure out the reason for the return code (maybe some datatype is incompatible?)
So first the "C++"-part:
c++ function definition:
extern "C" unsigned long __declspec(dllexport) WINAPI Nkfl_Entry(unsigned long ulCommand, void* pParam );
this is stdcall, so I do need to worry about any further options to dllimport, since usigned long(c++) corresponds to uint(c#) i get two uints one "out" and one "in"...
c++ struct defintion:
typedef struct tagNkflLibraryParam
{
unsigned long ulSize; // Size of structure
unsigned long ulVersion; // Version
unsigned long ulVMMemorySize; // Size of vertual memory
NkflPtr* pNkflPtr; // Pointer of StratoObject
unsigned char VMFileInfo[ MAX_PATH ]; // Swap file info
} NkflLibraryParam, *NkflLibraryPtr;
so I do need to pass 3 times uints, one pointer to an "StratoObject" ((1.) the doc says "typedef void* NkflPtr" so this is "just" a void* pointer 2.) the doc says if this is zero it will be filled up by the sdk) and finally one byte (since unsigned char(c++) corresponds to byte(c#)).
So first question: Is this correct?
Then going to the "coding-part":
c# struct defintion:
namespace NikonStruct
{
[StructLayout(LayoutKind.Sequential)]
public struct NkflLibraryParam
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
}
}
now this should correspond to my defintions above...
c# Program class:
class Program
{
public enum eNkflCommand : int
{
kNkfl_Cmd_OpenLibrary = 1,
kNkfl_Cmd_CloseLibrary = 2,
};
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_OpenLibrary(eNkflCommand ulCommand, ref NikonStruct.NkflLibraryParam data);
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_CloseLibrary(eNkflCommand ulCommand, IntPtr close);
static void Main(string[] args)
{
try
{
// specify return value of entry function
uint result1, result2;
/// call the kNkfl_Cmd_OpenLibrary Function
// generate data structure, which is used to communicate with kNkfl_Cmd_OpenLibrary function
NikonStruct.NkflLibraryParam _NkflLibraryParam = new NikonStruct.NkflLibraryParam();
// fill the fields of _NkflLibraryParam structure for kNkfl_Cmd_OpenLibrary function
_NkflLibraryParam.ulVersion = 16777216;
_NkflLibraryParam.ulSize = ((uint)Marshal.SizeOf(_NkflLibraryParam)); ;
// call the entry function with parameters for kNkfl_Cmd_OpenLibrary
result1 = kNkfl_Cmd_OpenLibrary(eNkflCommand.kNkfl_Cmd_OpenLibrary, ref _NkflLibraryParam);
Console.WriteLine(result1);
/// call the kNkfl_Cmd_CloseLibrary Function
result2 = kNkfl_Cmd_CloseLibrary(eNkflCommand.kNkfl_Cmd_CloseLibrary, IntPtr.Zero);
Console.WriteLine(result2);
}
catch
{
string errorMsg = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()).Message;
throw new ArgumentException(errorMsg);
}
}
}
So nothing specific here:
eNkflCommand is from the doc
the structure is passed by reference so ref...
the "close" function expects "null pointer" (according to doc)
ulVersion is 0x01000000 (according to doc)
all other struct values are not set (and are zero by default if I understood the clr doc correctly)
Compiles and runs as already mentioned but result1 returns wrong "status-code" which translates to "invalid param" as already mentioned.
Any help appreciated....
FOUND IT:
never trust a documentation of a software developer: there was actually a missing parameter (not declared in the documentation BUT in an additional header definition file which was in another sub-directory of the sdk-package...)
so actually the struct defintion in the c# should be:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct struktur
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] DefProfPath; // <- this one is not included in the doc of NIKON (I still don't now what this should hold but it works if it's empty...)
}
Thanks to jszigeti and DavidHeffernan for trying...
So, there have been many variants of this question, and after looking at several I still can't figure it out.
This is the C code:
typedef struct
{
unsigned long Identifier;
char Name[128];
} Frame;
Frame GetFrame(int index);
This is the C# code:
struct Frame
{
public ulong Identifier;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 128)]
public char[] Name;
}
[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern Frame GetFrame(int index);
This is the last attempt I tried in C#, and it seems pretty logical, but I get the error "Method's signature is not PInvoke compatible." So, I'm kind of lost on what to try next. Any help is appreciated.
Thanks,
Kevin
Updated Kevin added this as an edit to my answer
I should instead change my C code:
void GetFrame(int index, Frame * f);
and use instead for C#:
struct Frame
{
public uint Identifier;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
public string Name;
}
[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void GetFrame(int index, ref Frame f);
There are two problems with the PInvoke signature that you've chosen.
The first is easy to fix. You have a mistranslation of unsigned long. In C an unsigned long is typically only 4 bytes. You chose the C# type long which is 8 bytes. Changing the C# code to use uint will fix this.
The second is a bit harder. As Tergiver pointed out the CLR Marshaller only supports a struct in the return position if it's blittable. Blittable is a fancy way of saying that it has the exact same memory representation in native and managed code. The struct definition you've chosen isn't blittable because it has a nested array.
This can be worked around though if you remember that PInvoke is a very simple process. The CLR Marshaller really just needs you to answer 2 questions with the signature of your types and pinvoke methods
How many bytes am I copying?
In which direction do they need to go?
In this case the number of bytes is sizeof(unsigned long) + 128 == 132. So all we need to do is build up a managed type that is blittable and has a size of 132 bytes. The easiest way to do this is to define a blob to handle the array portion
[StructLayout(LayoutKind.Sequential, Size = 128)]
struct Blob
{
// Intentionally left empty. It's just a blob
}
This is a struct with no members that will appear to the marshaller as having a size of 128 bytes (and as a bonus it's blittable!). Now we can easily define the Frame structure as a combination of an uint and this type
struct Frame
{
public int Identifier;
public Blob NameBlob;
...
}
Now we have a blittable type with a size the marshaller will see as 132 bytes. This means it will work just fine with the GetFrame signature you've defined
The only part left is giving you access to the actual char[] for the name. This is a bit tricky but can be solved with a bit of marshal magic.
public string GetName()
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(128);
Marshal.StructureToPtr(NameBlob, ptr, false);
return Marshal.PtrToStringAnsi(ptr, 128);
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
}
}
}
Note: I can't comment on the calling convention portion because I'm unfamiliar with the GetFrame API but that's something i would definitely check on.
The problem is that the native function returns a non-blittable type as a return value.
http://msdn.microsoft.com/en-us/library/ef4c3t39.aspx
P/Invoke cannot have non-blittable types as a return value.
You cannot p/Invoke that method. [EDIT It is actually possible, see JaredPar's answer]
Returning 132 bytes by value is a bad idea. If this native code is yours, I would fix it. You can fix it by allocating the 132 bytes and returning a pointer. Then add a FreeFrame method to release that memory. Now it can be p/Invoked.
Alternately, you could change it to accept a pointer to the Frame memory that it will fill in.
Another option to JaredPar's is to utilize the C# fixed size buffer feature. This does however require you to turn on the setting to allow unsafe code, but it avoids having 2 structs.
class Program
{
private const int SIZE = 128;
unsafe public struct Frame
{
public uint Identifier;
public fixed byte Name[SIZE];
}
[DllImport("PinvokeTest2.DLL", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern Frame GetFrame(int index);
static unsafe string GetNameFromFrame(Frame frame)
{
//Option 1: Use if the string in the buffer is always null terminated
//return Marshal.PtrToStringAnsi(new IntPtr(frame.Name));
//Option 2: Use if the string might not be null terminated for any reason,
//like if were 128 non-null characters, or the buffer has corrupt data.
return Marshal.PtrToStringAnsi(new IntPtr(frame.Name), SIZE).Split('\0')[0];
}
static void Main()
{
Frame a = GetFrame(0);
Console.WriteLine(GetNameFromFrame(a));
}
}
I'm trying to call SetupDiGetDriverInfoDetail from a C# application. The call fails and the win32 error I get back is 0x6F8 ("The supplied user buffer is not valid for the requested operation."). Up to this point I have been able to call other setupdi functions with success so I think the problem is with the way that I marshal either the function or SP_DRVINFO_DETAIL_DATA struct.
I'm not sure, but I think the problem may be with the HardwareID member of the SP_DRVINFO_DETAIL_DATA struct. I've tried specifying the HardwareID as different types (ex. a byte array and allocating the buffer before setting the size and calling the function), but always the same error. If anyone has any experience with this call or has any pointers, I would appreciate the help.
Below is my structure definition, function import and code snippet. In this version I use a fixed size HardwareID buffer. I've also tried specifying a buffer size of 1 expecting an "buffer too small" error, but I always get the "invalid buffer" error.
[DllImport("setupapi.dll", SetLastError = true)]
internal static extern Int32 SetupDiGetDriverInfoDetail(
IntPtr DeviceInfoSet,
SP_DEVINFO_DATA DeviceInfoData,
SP_DRVINFO_DATA DriverInfoData,
ref SP_DRVINFO_DETAIL_DATA DriverInfoDetailData,
Int32 DriverInfoDetailDataSize,
ref Int32 RequiredSize);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct SP_DRVINFO_DETAIL_DATA
{
public Int32 cbSize;
public System.Runtime.InteropServices.ComTypes.FILETIME InfDate;
public Int32 CompatIDsOffset;
public Int32 CompatIDsLength;
public IntPtr Reserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String SectionName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String InfFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String DrvDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String HardwareID;
};
SetupApiWrapper.SP_DRVINFO_DETAIL_DATA DriverInfoDetailData = new SetupApiWrapper.SP_DRVINFO_DETAIL_DATA();
DriverInfoDetailData.cbSize = Marshal.SizeOf(DriverInfoDetailData);
result = SetupApiWrapper.SetupDiGetDriverInfoDetail(
DevInfo,
DeviceInfoData,
DriverInfoData,
ref DriverInfoDetailData,
DriverInfoDetailData.cbSize,
ref reqSize);
oAlthough I agree that the error code seems unexpected, I think the problem is that cbSize should be set to sizeof(SP_DRVINFO_DETAIL_DATA) (that's the proper C sizeof, not Marshal.SizeOf on your p/invoke structure.)
A quick test with a two line C program gives:
ANSI 797
UNICODE 1570
For the two proper sizeof values (you need to work out which one you need yourself...)
In contrast Marshal.SizeOf(typeof(SP_DRVINFO_DETAIL_DATA)) for your structure gives 1048 as a length.
I think you need to get that lined up before you go any further.
I suspect that it might be that the buffer-too-small error is returned if DriverInfoDetailDataSize is too small, but the invalid-buffer error is returned if cbSize is wrong.
The help for SetupDiGetDriverInfoDetail is also explicit that cbSize and DriverInfoDetailDataSize are not supposed to be the same value (because ANYSIZE_ARRAY is just defined as 1 as a placeholder), so you should not expect to get Marshal.SizeOf to work correctly with your deliberately oversized structure.
Additional correction:
Your InfFilename member is also the wrong length - a structure which exactly matches the structure from SETUPAPI.H is:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Unicode)]
internal struct SP_DRVINFO_DETAIL_DATA
{
public Int32 cbSize;
public System.Runtime.InteropServices.ComTypes.FILETIME InfDate;
public Int32 CompatIDsOffset;
public Int32 CompatIDsLength;
public IntPtr Reserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String SectionName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String InfFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String DrvDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public String HardwareID;
};
This gives the correct lengths, both in the ANSI and UNICODE versions. However, you don't want to use this as-is, because you need HardwareID to be longer, so you'll have to adjust the length of that and then live with Marshal.SizeOf giving the wrong value for plugging directly into cbSize.
The function declaration is wrong, the 2nd and 3rd arguments are passed by ref. Explains "invalid buffer", the API wants a pointer. Careful with Pack, it is only 1 on 32-bit operating systems. Set the Platform target to x86 to be sure. You are supposed to measure the required structure size first. Tricky to do, make the HardwareID nice and big, don't be frugal and throw 16K at it.
I have a structure
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SERVER_USB_DEVICE
{
USB_HWID usbHWID;
byte status;
bool bExcludeDevice;
bool bSharedManually;
ulong ulDeviceId;
ulong ulClientAddr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string usbDeviceDescr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string locationInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string nickName;
}
When I pass it in a win32 DLL function as below:
[DllImport ("abc.dll", EntryPoint="EnumDevices", CharSet=CharSet.Ansi)]
public static extern bool EnumDevices(IntPtr lpUsbDevices,
ref ulong pulBufferSize,
IntPtr lpES);
I get some missing text in the string members of the structure.
Suppose SERVER_USB_DEVICE.usbDeviceDescr contains value "Mass Storage Device" which is wrong it should contain value "USB Mass Storage Device"
What is wrong in the code?
Actually i was making a small mistake here ulong is 8 bytes in c# where as it is 4 bytes in c++ (as we all know). converting ulong to uint solved the problem.
Try with ByValTStr instead of ByValArray
Have you verified that the fields in the structure that are located before usbDeviceDescr (status, bExcludeDevice, bSharedManually, ulDeviceId, and ulClientAddr) get their correct values? Could it be that it is the marshalling of the USB_HWID structure that is wrong, so that the offsets are off by 4 bytes for the rest of the structure?
You can look at the structure in a byte array to make sure you have everything aligned properly. Try this:
int size = Marshal.SizeOf(typeof(SERVER_USB_DEVICE));
byte[] buffer1 = new byte[size];
SERVER_USB_DEVICE[] buffer2 = new SERVER_USB_DEVICE[1];
// put instance of SERVER_USB_DEVICE into buffer2
Buffer.BlockCopy(buffer2, 0, buffer1, 0, size);
Yet another one of my idiotic P/Invoke questions... Sometimes I think this stuff is going to be a piece of cake, but it blows up in my face.
I have a simple unmanaged method that takes a destination array and fills it.
unsigned short NvtlCommon_GetAvailableDevices(SdkHandle session,
DeviceDetail * pDev_list, unsigned long * dev_list_size)
typedef struct
{
DeviceTechType eTechnology;
DeviceFormFactorType eFormFactor;
char szDescription[NW_MAX_PATH];
char szPort[NW_MAX_PATH];
char szFriendlyName[NW_MAX_PATH];
} DeviceDetail;
I converted these to C#:
[DllImport(NvtlConstants.LIB_CORE, EntryPoint = "NvtlCommon_GetAvailableDevices")]
public static extern NvtlErrorCode GetAvailableDevices(IntPtr session,
DeviceDetail[] pDev_list, ref long dev_list_size);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceDetail {
public DeviceTechType eTechnology;
public DeviceFormFactorType eFormFactor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szPort;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szFriendlyName;
}
The documentation states that on input, dev_list_size should be the size of the allocated array. On output, it's the number of items that were actually filled. If I use out or ref on the pDev_list argument, my application crashes. This seems to be the only signature that even begins to work.
DeviceDetail[] devices = new DeviceDetail[5];
long count = 5;
GetAvailableDevices(coreHandle, devices, ref count);
That code returns a success message and count is set to 1, which is indeed the number of available devices. However, the array is still 5 uninitialized DeviceDetail structs.
Am I doing something wrong here, or is this a problem with the underlying unmanaged library?
Have you tried [Out]?
public static extern NvtlErrorCode GetAvailableDevices(IntPtr session,
[Out] DeviceDetail[] pDev_list, ref long dev_list_size);
↑