I kindly wanted to ask if it is recommendable to add properties for fields in a struct scope, if the struct is used for an winAPI call (f.ex. dllimport("xxx.dll")).
The question poped up when I let run the code Analysis - saying me, that I should use property accessors instead having public fields in struct.
And true, onthinking I would recommend that too, but - is an API call still possible with the 'so code optimized' struct?
May be I ought to show what I mean:
as everybody knows it:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct someAPICallStructure
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
as the code analyzer wishes:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct someAPICallStructure
{
public CBSize
{
get
{
return cbSize;
}
set
{
cbSize = value;
}
}
.... and properties for all other fields too ...
int cbSize;
IntPtr hwndParent;
string pszMessageText;
string pszCaptionText;
IntPtr hbmBanner;
}
Or could I even use like this?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct someAPICallStructure
{
public CBSize {get; set;}
... and with all other fields too ...
}
And even further; can I add equals(..) and other suggested methods to the struct and it works still for the API Call?
Related
I'm trying to move a folder from directory to another directory , but I'm getting an exception of access to path is denied , so I found a c++ SHFileOperation method that can be used in this case, the problem is that I didn't have any idea how to use it:
public void MoveFolder(string oldPath,string newPath)
{
SHFILEOPSTRUCT hFILEOPSTRUCT = new SHFILEOPSTRUCT();
hFILEOPSTRUCT.pFrom = oldPath;
hFILEOPSTRUCT.pTo = newPath;
int res = SHFileOperation(ref hFILEOPSTRUCT);
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true,
ThrowOnUnmappableChar = true)]
[Obsolete]
static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);
Struct definition of SHFILEOPSTRUCT:
[Obsolete("Use this element with caution and only if you know what you are doing. It's only meant to be used internally by MahApps.Metro and Fluent.Ribbon. Breaking changes might occur anytime without prior warning.")]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public FO wFunc;
public string pFrom;
public string pTo;
[CLSCompliant(false)]
public FOF fFlags;
public int fAnyOperationsAborted;
public IntPtr hNameMappings;
public string lpszProgressTitle;
}
}
This code throw an exception
Cannot marshal field 'fAnyOperationsAborted' of type 'ControlzEx.Standard.SHFILEOPSTRUCT': Invalid managed/unmanaged type combination (Int32/UInt32 must be paired with I4, U4, or Error).
How can I make this works?
How do native methods initialize structures that they return through an out IntPtr parameter?
Given the following native code:
[DllImport("foo.dll", EntryPoint = "bar", CallingConvention = CallingConvention.Cdecl)]
internal static extern int GetBars(out int count, out IntPtr output);
and the following struct definition:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public readonly record struct BarOut
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public readonly string FieldA;
[MarshalAs(UnmanagedType.U1)]
public readonly bool FieldB;
}
the managed implementation is able to make the native call and read all the structs found at the pointer.
The entire code base also works with regular structs such as:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct BarOut
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string FieldA;
[MarshalAs(UnmanagedType.U1)]
public bool FieldB;
}
I cannot find any documentation that describes how the structs are initialized and therefore I am not clear whether these two output definitions are safely equivalent where the only difference is the record is immutable.
Does substituting records in the codebase present any problems?
In DLL struct is:
typedef struct tagEKIDinfo{
short usbNo;
short printerID;
CHAR serialNo[6];
WORD mediaType;
} EKIDinfo, *PEKIDinfo;
In C# :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public int usbNo;
public int printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
And the function is:
DLL:
DWORD WINAPI EKSearchPrinters( PEKIDinfo pIDInfo, DWORD infoSize, LPDWORD pSizeNeeded, LPDWORD pinfoNum )
C#:
[DllImport("EKUSB.dll", EntryPoint = "EKSearchPrinters", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int EKSearchPrinters(IntPtr hPrinter, Int32 infoSize, ref Int32 pSizeNeeded, ref Int32 pNumber);
The serialNo true value is "218699", but I got "99?" with C#.
Why about this? Can anyone help me? Thans a lot!
Kind of a guess, but if you're off by four bytes, maybe the first two fields are too big? Try this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public System.Int16 usbNo; //<--- changed
public System.Int16 printerID; //<--- changed
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
I think you've got wrong types. DLL interface says
struct tagEKIDinfo {
short usbNo; <- short (2 bytes), not int (4bytes)
short printerID; <- short (2 bytes), not int (4bytes)
CHAR serialNo[6];
WORD mediaType;
}
Note the short. This is likely to be int16 on C# side, not int as you have now. Since there are 2 such fields, all before char-array, 2x(4-2)=4 and that would account exactly to the 218699->99 = 4 chars missing
The struct is declared incorrectly. The first two members should be short.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public short usbNo;
public short printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
Rather than manually marshal the struct I'd pass it by reference.
[DllImport("EKUSB.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint EKSearchPrinters(
ref EKIDinfo pIDInfo,
uint infoSize,
ref uint pSizeNeeded,
ref uint pNumber
);
I'm not sure why you named the first parameter to suggest a handle, but it certainly doesn't look like one to me.
It's entirely possible that some of the ref parameters should be out depending on the data flow intent which has not been shown.
The other potential issue is how you call the function. You didn't show the call or any details of how it must be done.
I'm using native functions and have small problem with marshaling structs in c#. I have pointer to struct in another struct - e.g. C# declarations:
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Auto)]
public struct PARENT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string Name;
[MarshalAs(UnmanagedType.Struct, SizeConst=8)]
public CHILD pChild;
}
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Auto)]
public struct CHILD
{
public UInt32 val1;
public UInt32 val2;
}
In PARENT struct I should have a pointer to CHILD struct. I need to pass a 'pointer to reference' (of PARENT struct) as argument of API function.
There's no problem with single reference ("ref PARENT" as argument of imported dll function) but how to pass "ref ref" ? Is it possible without using unsafe code (with C pointer) ?
greetings
Arthur
If you do not want to use unsafe code, then you need to define Child as IntPtr and add a property which access then the values from the Child IntPtr.
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Auto)]
public struct PARENT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string Name;
public IntPtr pChild;
public CHILD Child{
get {
return (CHILD)Marshal.PtrToStructure(pChild, typeof(CHILD));
}
}
}
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Auto)]
public struct CHILD
{
public UInt32 val1;
public UInt32 val2;
}
I think it is easier and cleaner with unsafe code/pointers.
This is a combination of safe and unsafe, as I believe is reasonable here.
fixed (void* pt = new byte[Marshal.SizeOf(myStructInstance)])
{
var intPtr = new IntPtr(pt);
Marshal.StructureToPtr(myStructInstance, intPtr, true);
// now "pt" is a pointer to your struct instance
// and "intPtr" is the same, but wrapped with managed code
}
Guys I am having difficulties on retrieving struct member values after calling a function in the DLL. I tried to convert the C++ codes into C# but I’m not sure if it is correct or not. Please help me understand my mistakes here (if there is) and how to correct.
My problem here is I can’t correctly retrieved the values of the INNER STRUCTS (Union) after I called the ReceiveMessage function from the DLL. Like for example m_objMsg.MsgData.StartReq.MsgID is always 0.
But when I try to use the C++ .exe program, the MsgID has a correct value. (not 0)
C++ Code:
extern int ReceiveMessage(SESSION, int, Msg*);
typedef struct
{
char SubsId[15];
int Level;
char Options[12];
} ConxReq;
typedef struct
{
char MsgId[25];
} StartReq;
typedef struct
{
long Length;
short Type;
union
{
ConxReq oConxReq;
StartReq oStartReq;
} Data;
} Msg;
/////////////////////////////////////////////////////
Msg oMsg;
int rc=ReceiveMessage(Session, 0, &oMsg);
switch(rc)
{
case 0:
switch(oMsg.Type)
{
case 0: // ConxReq
…
break;
case 1: // StartReq
…
break;
…
}
And here is my attempt to convert this into c#:
[DllImport("MyDLL.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
protected static extern Int32 ReceiveMessage(IntPtr session,
Int32 nTimeOut,
[MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public StartReq oStartReq;
}
Msg m_objMsg = new Msg();
m_objMsg.MsgData = new Data();
m_objMsg.MsgData.oConxReq = new ConxReq();
m_objMsg.MsgData.oStartReq = new StartReq();
int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg);
then the SWITCH Condition
And If I add this struct inside the UNION for c++ and c#...
I've got an error stating the "... incorrectly align" or "...overlapped..."
c++
ConxNack oConxNack;
typedef struct
{
int Reason;
} ConxNack;
[StructLayout(LayoutKind.Sequential)]
public struct ConxNack
{
public int nReason;
}
[FieldOffset(0)]
public ConxNack oConxNack;
Thank you so much in advance for your time and help...
Akash is right, have a look here: http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3
Another option is create two structs and use an appropriate cast once you know which type it is.
hth
Mario
In C++, we know that all members of UNION shared the same memory chunk and can only have one member of an object at a time.
In order to implement this in C#, we need to use the LayoutKind to Explicit and set all the starting point of each member to 0.
In my previous example, An error message is displayed stating that the offset of an object type is incorrectly aligned or overlapped by a non-object type.
Answer is we cannot set all the members to FieldOffSet to 0 since it is not allowed to combine the reference type with the value type.
- Thanks to the explanation of Hans Passant
What I did is to create a copy of the UNION Member Structs and change the type of all the String Member Variables to bytes.
I used bytes since this is a value type so I can put this struct into FieldOffSet(0).
Take note, i adjust the FieldOffSet of the next member variable so i can still get the same size of my string variable.
And also for the struct size since i have byte member at the last.
Thanks to Akash Kava and Mario The Spoon for giving me an idea and providing me a useful link.
After calling the function in the DLL and passed this Struct Obj (ref m_objMsg) as a paramter, I need to extract the values.
One way is to have a pointer that points to the address of the struct in the unmanaged memory and convert this pointer a new
Struct with the corresponding member variables (my original structs).
NEW STRUCTS (BYTES)
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)]
public struct ConxReq
{
[FieldOffSet(0)]
public byteSubsId;
[FieldOffSet(15)]
public Int32 Level;
[FieldOffSet(19)]
public byte Options;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ConxNack
{
[FieldOffSet(0)]
public int nReason;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]
public struct StartReq
{
[FieldOffSet(0)]
public byte MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public ConxNack oConxNack;
[FieldOffset(0)]
public StartReq oStartReq;
}
////////////////////////////////////////////////////////////////
MY ORIGINAL STRUCTS
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyConxNack
{
public int nReason;
}
///////////////////////////////////////////////////////////////
Since I have a Msg.Type, i know what kind of struct (type) I could cast the object.
Like for example
ReceiveMessage(m_Session, nTimeOut, ref oMsg);
switch (oMsg.Type)
{
case 0: // ConxReq
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes)
Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false);
MyConxReq oMyConxReq = new MyConxReq;
oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct
Marshal.FreeHGlobal(ptr);
Then you can use now the oMyConxReq object to acccess the member variables directly.
Please let me know if you have other or better way to do this...
Kindly advise if what I did is correct or if I missed something.
Thank you so much!!! :)
You have to use StructLayout(LayoutKind.Explicit) and FieldOffsets to make union.