How does one pass nullptr through P/Invoke declared with a StringBuilder? - c#

Consider a typical P/Invoke declaration like this:
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool LookupAccountName(
string SystemName,
string accountName,
IntPtr pSid,
ref uint cbSid,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder ReferencedDomainName,
ref uint ReferencedDomainNameCount,
out SID_NAME_USE SIDUse);
MSDN documentation for LookupAccountName says that pSid and ReferencedDomainName may be nullptr if the customer wishes. Passing nullptr for pSid is easy; just pass IntPtr.Zero. But what should one pass for a StringBuilder?
I don't want to pass an empty StringBuilder, because I don't want this call to fail with ERROR_INSUFFICIENT_BUFFER.

You can just pass null for this parameter; it will be marshalled as a null pointer.

Related

How to handle Windows API LPTSTR output as a formal parameter output

I'm trying to determine the classname of a window by calling GetClassName() on the hWnd that I receive from FindWindowByCaption(). But it's not working; all I see for the classname is garbage characters (specifically, four question marks and then some strange non-alphanumeric character). GetClassName(), as far as I understand it, writes data into the caller's buffer.
Here's my code:
[DllImport("user32.dll", SetLastError = true)]
static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(System.IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
static extern int GetClassName(System.IntPtr hWnd,
[param: MarshalAs(UnmanagedType.LPTStr)]
System.Text.StringBuilder lpClassName, int nMaxCount);
...
System.IntPtr hWndMyWindow = FindWindowByCaption(System.IntPtr.Zero, "My Window");
if (hWndMyWindow != null)
{
System.Console.WriteLine("hWndMyWindow = {0}", hWndMyWindow);
System.Text.StringBuilder lpClassName = new System.Text.StringBuilder(256);
lpClassName.Length = lpClassName.Capacity - 2;
int len = GetClassName(hWndMyWindow, lpClassName, lpClassName.Length - 2);
lpClassName.Length = len; // set length to the actual length of the classname
System.Console.WriteLine("hWndMyWindow = {0}, classname = {1}", hWndMyWindow, lpClassName.ToString());
}
else
{
System.Console.WriteLine("No such window.");
}
What am I doing wrong? The lpClassName param of GetClassName() is an output... I expected GetClassName() to write the classname into the StringBuilder, but it's not working right.
[param: MarshalAs(UnmanagedType.LPTStr)]
LPTStr does not mean what you think it does. With the way you wrote these declarations, only UnmanagedType.LPStr can work. Or better yet, completely omit this attribute, it is not necessary at all, the pinvoke marshaller already understands StringBuilder.
You are invoking the 1990s with these declarations, you are using the "Ansi" versions of these winapi functions. The default for [DllImport] unfortunately. It does not make sense to use them, nobody runs Windows 98 anymore. Windows is a Unicode operating system at its heart, write the declarations to match:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(System.IntPtr hWnd,
StringBuilder lpClassName, int nMaxCount);
FindWindowByCaption() is a bit too cute, just don't, pass null as the 1st argument. Note that you have a bug in your error handling, FindWindow fails with IntPtr.Zero, not null. And don't ignore error handling for GetClassName(). Just make a loud bang by throwing System.ComponentModel.Win32Exception.

Is there an equivalent to WinAPI GetColorDirectory in .NET?

Is there an analogue of the function GetColorDirectory?
Or should I just call through a DLL?
The purpose is to get the path to the system directory with color profiles
As per MSDN you call it using the API:
[DllImport(DllImport.Mscms, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern bool GetColorDirectory(IntPtr pMachineName, StringBuilder pBuffer, ref uint pdwSize);

C# how to specify SetupDiGetDeviceRegistryProperty field value SPDRP_HARDWAREID

I am using this function SetupDiGetDeviceRegistryProperty in C # to query the hardware ID and to know how big a buffer to allocate for the data. But I am getting error at `SPDRP_HARDWAREID. Error message is
"The name "SPDRP_HARDWAREID" does not exist in current context.
I have tried declaring SPDRP_HARDWAREID as enum, but it didn't worked.
Does anyone have idea?
Just enter 0x00000001 and forget about the variable name if you only want to use the hardwareid function. Remember to have the other parameter types valid as well, your declaration is wrong, you should use this:
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
out UInt32 PropertyRegDataType,
byte[] PropertyBuffer,
uint PropertyBufferSize,
out UInt32 RequiredSize
);

Access violation calling Win API from C# using DllImport

The task is to determine the last write time for the registry key. As standard RegistryKey class doesn't provide this feature, I have to use WinAPI function "RegQueryInfoKey". To get the key handle I open it by "RegOpenKeyEx".
This is the WinAPI prototype of the function (taken from MSDN):
LONG WINAPI RegOpenKeyEx(
__in HKEY hKey,
__in LPCTSTR lpSubKey,
DWORD ulOptions,
__in REGSAM samDesired,
__out PHKEY phkResult
);
I use the following declaration:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey, uint samDesired, ref UIntPtr phkResult);
Then I call it in the following way:
UIntPtr hKey = UIntPtr.Zero;
string myKeyName = "blablabla";
UIntPtr HKEY_USERS = (UIntPtr)0x80000003;
uint KEY_READ = 0x20019;
RegOpenKeyEx(HKEY_USERS, myKeyName, KEY_READ, ref hKey);
At this point I get "Access violation" exception. What am I doing wrong?
I think something is wrong with parameters passing, but how to do it right?
Thank you.
There are 5 parameters in the native function's prototype, and only 4 in your P/Invoke signature.
In particular, you're missing DWORD ulOptions. This parameter is "reserved and must be zero" according to the MSDN documentation, but it must still be passed in the function call.
Also, you don't need to set the SetLastError field because the RegOpenKeyEx function returns its error code; you don't have to retrieve it by calling GetLastError. Consequently, you don't need the marshaler to save that value for you automatically. Just check the return value for the error code.
Change your P/Invoke signature to look like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey,
uint ulOptions, uint samDesired,
out UIntPtr phkResult);
The wrong P/Invoke signature is almost always the cause of "access violation" errors. When you see one of those, make sure you double-check it twice!
You've missed ulOptions from your P/Invoke signature.

A Problem in Pinvoke

I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...

Categories

Resources