How do I handle null or optional DLL struct parameters
You have a few options
1) Use a class
instead of a struct
I think this method is the easiest. Simply declare the struct
as a class
:
[StructLayout(LayoutKind.Sequential)]
public class CStruct
{
//member-list
}
and then declare your method:
[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(CStruct cStruct, ...);
If your optional parameter happens to be the last one, you can instead use CStruct cStruct = null
as the parameter. This allows you to exclude it instead of passing null
explicitly. You can also write a wrapper method that uses this and ensures the optional parameters come last.
2) Use IntPtr
and IntPtr.Zero
Use a struct
:
[StructLayout(LayoutKind.Sequential)]
public struct CStruct
{
//member-list
}
and declare your method as:
[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);
In the non-null
case, marshal the struct to a pointer and call the method:
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CStruct)));
try{
Marshal.StructureToPtr(myCStruct, ptr, false);
DLLFunction(ptr, ...);
} finally {
Marshal.FreeHGlobal(ptr);
}
In the null
case, call the method with IntPtr.Zero
:
DLLFunction(IntPtr.Zero, ...);
Again, you can make this parameter optional if this happens to be the last in the list (or you use a wrapper to make it so). Do this by using IntPtr cStruct = default(IntPtr)
as the parameter. (As default(IntPtr)
creates a IntPtr.Zero
.)
3) Overload your method to avoid marshaling
Use a struct
as in 2).
Simply declare one option for the non-null
case:
[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(ref cStruct, ...);
and another for the null
case:
[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);
The first method will automatically get called when passing a struct
, and the second when passing IntPtr.Zero
. If declaring the IntPtr
version with an optional parameter (as shown at the bottom of 2) above), it will automatically call it when you exclude the cStruct
parameter.
4) Raw pointers using unsafe
Use a struct as in 2) and declare your method (note the unsafe
keyword):
[DllImport("mydll.dll", OptionName = optionValue, ...)]
static unsafe extern int DLLFunction(CStruct* cStruct, ...);
In the non-null
case, you pass &myCStruct
, and simply null
in the null
case. As in 1), if this optional parameter is last, you can declare the parameter as CStruct* cStruct = null
to automatically pass null
when cStruct
is excluded.
Thanks to @dialer for suggesting this method.
How to pass an Optional parameter that is a struct in C#
For this, you should declare the 3rd argument as a IntPtr
.
When you want to pass it null, give it IntPtr.Zero
.
If you want to pass a real structure to it, Marshal
the structure into memory - i.e. something like this
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
// set anything you want in the sa structure here
IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES)));
try {
Marshal.StructureToPtr(sa, pnt, false)
// call RegSaveKey here
} finally {
Marshal.FreeHGlobal(pnt);
}
How to handle optional struct parameters when calling C functions from C#
You can change the struct
to a class
and then pass null
. Remember that a C# struct
is quite different from a C++ struct
, and here your really want to use a C# class
.
Or if you always want to ignore pioRecvRequest
change the signature of SCardTransmit
so that pioRecvRequest
is of type IntPtr
. Then pass IntPtr.Zero
for the value.
Actually, the SCARD_IO_REQUEST
is just a header and if you want to pass it in the call you will have to manage this structure and the additional buffer space yourself anyway so IntPtr
is the right choice. You will then have to use the Marshal
functions to allocate and fill the structure before the call and unmarshal the data and free it after the call.
How to pass a nullable type to a P/invoked function
It's not possible to pass a Nullable type into a PInvoke'd function without some ... interesting byte manipulation in native code that is almost certainly not what you want.
If you need the ability to pass a struct value as NULL to native code declare an overload of your PInvoke declaration which takes an IntPtr in the place of the struct and pass IntPtr.Zero
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, ref int enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr enumerator, IntPtr hwndParent, uint Flags);
Note: I added a ref class to the first signature. If the native signature can take NULL, it is likely a pointer type. Hence you must pass value types by reference.
Now you can make calls like the following
if (enumerator.HasValue) {
SetupDiGetClassDevs(someGuid, ref enumerator.Value, hwnd, flags);
} else {
SetupDiGetClassDevs(someGuid, IntPtr.Zero, hwnd, flags);
}
C#: How to pass null to a function expecting a ref?
I'm assuming that Mapping is a structure? If so you can have two versions of the FILES_GetMemoryMapping()
prototype with different signatures. For the second overload where you want to pass null
, make the parameter an IntPtr
and use IntPtr.Zero
public static extern uint FILES_GetMemoryMapping(
[MarshalAs(UnmanagedType.LPStr)] string pPathFile,
out ushort Size,
[MarshalAs(UnmanagedType.LPStr)] string MapName,
out ushort PacketSize,
IntPtr oMapping,
out byte PagesPerSector);
Call example:
FILES_GetMemoryMapping(MapFile, out size, MapName,
out PacketSize, IntPtr.Zero, out PagePerSector);
If Mapping is actually a class instead of a structure, just set the value to null before passing it down.
How do I assign a null value to a struct for a pinvoke call?
Declare LSA_UNICODE_STRING
to be a class
rather than a struct
. By doing so you make it a reference type. That matches the declaration of LSA_OBJECT_ATTRIBUTES
because ObjectName
has type PLSA_UNICODE_STRING
which is a pointer to the struct. You do need to specify LayoutKind.Sequential
when you do this since that's not the default for a class. Once you've made this change, you can set the variable to null
.
[StructLayout(LayoutKind.Sequential)]
class PLSA_UNICODE_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}
You can adopt the same policy for LSA_OBJECT_ATTRIBUTES
to allow that to be passed as null
.
[StructLayout(LayoutKind.Sequential)]
class PLSA_OBJECT_ATTRIBUTES
{
public uint Length;
public IntPtr RootDirectory;
public PLSA_UNICODE_STRING ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[DllImport("advapi32.dll")]
static extern uint LsaOpenPolicy(
PLSA_UNICODE_STRING SystemName,
PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
uint DesiredAccess,
out IntPtr PolicyHandle
);
Note that the declaration on pinvoke.net erroneously uses SetLastError=true
on LsaOpenPolicy
. That's wrong because the error code comes back in the return value. I also removed the setting of PreserveSig
to true
since that is the default. Their declaration of LSA_OBJECT_ATTRIBUTES
also appears wrong since the ObjectName
parameter has type LSA_UNICODE_STRING
which is the struct rather than a pointer to it.
I would advise you to treat what you find on pinvoke.net with extreme scepticism. A large proportion of the declarations on that site are simply incorrect.
You ask about the possibility of using nullable types. According to @JaredPar's answer here, that's not an option.
SQL Query Advice - Most recent item
Assuming that "latest" is determined by date (rather than by issue number), this method is usually pretty fast, assuming decent indexes:
SELECT
T1.prodid,
T1.issue
FROM
Sales T1
LEFT OUTER JOIN dbo.Sales T2 ON
T2.custid = T1.custid AND
T2.prodid = T1.prodid AND
T2.datesold > T1.datesold
WHERE
T1.custid = @custid AND
T2.custid IS NULL
Handling 500k rows is something that a laptop can probably handle without trouble, let alone a real server, so I'd stay clear of denormalizing your database for "performance". Don't add extra maintenance, inaccuracy, and most of all headaches by tracking a "last sold" somewhere else.
EDIT: I forgot to mention... this doesn't specifically handle cases where two issues have the same exact datesold. You might need to tweak it based on your business rules for that situation.
How to P/Invoke SetFileTime with null parameters?
You cannot model that with the ref keyword in C#, that always produces a non-null pointer at runtime. You'll actually have to declare it long*
, which requires using the unsafe keyword. Pass the argument with the &local-var
syntax or use null
.
One possible trick to avoid using unsafe is to declare 3 versions of this method with different names, using the DllImport's EntryPoint property to map them all to SetFileTime(). Specifying the ones you don't want to set as IntPtr so you can pass IntPtr.Zero, the one you do want to set as ref long. For example:
[DllImport("kernel32.dll", SetLastError = true, EntryPoint="SetFileTime", ExactSpelling=true)]
internal static extern bool SetFileCreateTime(
IntPtr hFile,
ref long lpCreationTime,
IntPtr lpLastAccessTimeUnused,
IntPtr lpLastWriteTimeUnused);
Repeat for SetFileAccessTime and SetFileWriteTime.
Calling Win API in C# with P/Invoke when _Out_ parameters can be NULL or non-NULL
When you have an optional parameter that is an enum, as you have here, then I think there is little alternative but to declare two separate overloads. The overload that you use to pass null declares the parameter like this:
IntPtr pCurrentTopologyId
You always pass IntPtr.Zero
when calling this overload.
The other overload, the one that you call when you pass a non-null value declares the parameter like this:
out DISPLAYCONFIG_TOPOLOGY_ID pCurrentTopologyId
where DISPLAYCONFIG_TOPOLOGY_ID
is a C# enum
with base type of int
.
Your code gets this second variant wrong because you declare it as out IntPtr pCurrentTopologyId
. Well IntPtr
is the wrong size on 64 bit (8 bytes rather than 4).
If you wish to declare just a single p/invoke and not use overloads then you just transfer the work elsewhere. To do that you have to pick the first option:
IntPtr pCurrentTopologyId
Fine when you want to pass IntPtr.Zero
. But when you need to pass the address of an enum variable you need to pin your variable using GCHandle
and AddrOfPinnedObject
. All perfectly possible but yet more boilerplate. So, take your pick!
Related Topics
How to Convert an Iso8601 Timespan to a C# Timespan
Set Default Global JSON Serializer Settings
How to Make the Value of a Variable Track the Value of Another
Two-Way/Bidirectional Dictionary in C#
Could Not Load File or Assembly 'Microsoft.Reportviewer.Common, Version=11.0.0.0
Exception When Addwithvalue Parameter Is Null
Does .Net Ftpwebrequest Support Both Implicit (Ftps) and Explicit (Ftpes)
Request Windows Vista Uac Elevation If Path Is Protected
SQL Where Clause Matching Values with Trailing Spaces
Modify Struct Variable in a Dictionary
C# Adding Button with Value at Runtime
Programmatically Get a Screenshot of a Page
Why/When Would It Be Appropriate to Override Tostring