Decoding IntPtr to MultiString - c#

I need to extract the environment strings from a call to CreateEnvironmentBlock( out IntPtr lpEnvironment, IntPtr hToken, bool bInherit ), so as to put them in dictionary based on the variable name and its value.
When this function returns, lpEnvironment receives a pointer to the
new environment block. The environment block is an array of
null-terminated Unicode strings. The list ends with two nulls
(\0\0).
I cannot easily use Marshal.Copy as I do not know the block's length. I'm wondering if there is an easy way to move along it or determine what to copy to something I can then convert more easily. One thought was to pass the out IntPtr lpEnvironment as out char [] lpEnvironment.
Any suggestions?

You dont know the whole length for Marshal.Copy(), however you know the length of the first string if you perform Marshal.PtrToString(). With that knowledge you can advance to the next string and so on, until you read an empty string, that indicates only \0 was present at that address, which is the end of the multi string.
Note that the following code assumes Unicode charset is available and uses it.
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment,IntPtr hToken,bool bInherit);
static IEnumerable<string> ExtractMultiString(IntPtr ptr)
{
while (true)
{
string str = Marshal.PtrToStringUni(ptr);
if (str.Length == 0)
break;
yield return str;
ptr = new IntPtr(ptr.ToInt64() + (str.Length + 1 /* char \0 */) * sizeof(char));
}
}

Related

stringbuilder as out parameter

extern "C"
__declspec(dllexport)
bool FillString(LPWSTR OutName)
{
LPWSTR out = L"TheName\0";
int len = wcslen(out);
memcpy(
OutName,
out,
len * sizeof(wchar_t));
return true;
}
That is function in my c/c++ dll, the following is my call from c#...
[DllImport(#"My.dll", EntryPoint = "FillString", CallingConvention = CallingConvention.Cdecl)]
public static extern bool MyFunction([MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder Name);
var fromdll = new StringBuilder(64);
// I do not know length of out string (Name), but it is null terminated
bool IsFilled = MyFunction(fromdll);
Console.WriteLine(fromdll);
The output is
TheName???
Can anyone help me to get the output...?
TheName
You need to copy one more character. '\0' is called string terminator. Without it C, C++ and PInvoke in .NET do not recognize end of string. If you are copying wcslen characters, zero is not copied. There are many solutions:
Use mentioned wcscpy to copy string with \0
Copy one more
character with memcpy(OutName, out, (len + 1) * sizeof(wchar_t)).
Moreover, it's a good idea to pass buffer (StringBuilder) size. C/C++ lets you write outside variable boudaries. This size enables to avoid that.

Marshalling a fixed-size buffer from C# to extern

I'm having some difficulties using a struct to call some Windows kernel32 function. I'm trying to call the function FindFirstStreamW, which return, among others, a WIN32_FIND_STREAM_DATA struct.
I use this struct in C# to represent it :
[StructLayout(LayoutKind.Sequential)]
public unsafe struct WIN32_FIND_STREAM_DATA
{
public long StreamSize;
public fixed char StreamName [MAX_PATH + 32];
}
But I have problems using the StreamName afterward. After a call to the function, the StreamName buffer seems to contains a semicolon (which is expected) and then only random data.
I experimented by replacing the fixed buffer by consecutives char such as s1, s2, s3 etc... and it worked (s1 contained ':', then s2 '$' etc... all correct !).
[StructLayout(LayoutKind.Sequential)]
public unsafe struct WIN32_FIND_STREAM_DATA
{
public long StreamSize;
public char s1;
public char s2;
public char s3;
}
I must therefore miss something but can't find what and after 8 firefox windows filled with google researches, I'm pretty desperate.
Here is my DLLImport :
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstStreamW(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
StreamInfoLevels infoLevel,
out WIN32_FIND_STREAM_DATA data,
int reserved = 0
);
And there is my test code :
WIN32_FIND_STREAM_DATA data = default(WIN32_FIND_STREAM_DATA);
IntPtr search = FindFirstStreamW(D_TMP_TESTHANDLE_TXT, StreamInfoLevels.FindStreamInfoStandard, out data);
Debug.WriteLine(data.StreamSize);
Debug.WriteLine(new string(data.StreamName));
FindClose(search);
Thanks in advance for your help.
The problem with your struct - compared to the variant where you actually have distinct characters - is probably that it does not get marshalled as expected because arrays are by reference and not by value by default when using .NET.
Read this MSDN article about string marshalling for more information.
So I'd try it with something like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct WIN32_FIND_STREAM_DATA
{
public long StreamSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH+36)]
public string StreamName;
}

Changing the string to which an IntPtr is pointing

In my C# application I have a variable lpData of type IntPtr (received from a call to unmanaged code), and it points to a string.
I have to replace this string with another value.
I tried:
int RegQueryValueExW_Hooked(
IntPtr hKey,
string lpValueName,
int lpReserved,
ref Microsoft.Win32.RegistryValueKind lpType,
IntPtr lpData,
ref int lpcbData)
{
lpData = Marshal.StringToHGlobalUni("new string");
...
}
but this doesn't seem to replace the actual string.
Can someone point me in the right direction on how to do this?
Thanks
Of course it doesn't replace the string - you're getting the pointer to the string where your caller has the value. You're only replacing the value of your "local variable" (the parameter), this doesn't change anything on the caller's side.
If you want to modify the value on the original pointer (and make sure you actually do want that, this is where "weird errors" lurk - it's very easy to overwrite surrounding variables, forget about the null terminator, etc.), you can use Marshal.Copy, for example:
var bytes = Encoding.Unicode.GetBytes("your string\0");
Marshal.Copy(bytes, 0, lpData, bytes.Length);
Again - this is a very dangerous behaviour and you shouldn't be doing this. You're violating several contracts implied by parameter passing etc.
Now that I've answered your question, let me talk about how wrong you are about actually needing to do this (and this is very much related to your other post about using the StringBuilder).
You are trying to modify a value passed to you as a parameter. However, the string was allocated by the caller. You don't even know how long it is! If you start copying data to that pointer, you're going to overwrite the data of eg. completely different variables, that just randomly happened to be allocated just after the string. This is considered "very bad".
Instead, what you want to do, is follow the proper process that RegQueryValueEx has (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx). That means you first have to check the lpcbData value. If it is large enough to hold all the bytes you want to write, you just write the data to the lpData and set the lpcbData value to the proper length. If not, you still set lpcbData, but return ERROR_MORE_DATA. The caller should then call RegQueryValueEx again, with a larger buffer.
The sample code would be something like this:
string yourString = "Your string";
int RegQueryValueExW_Hooked(
IntPtr hKey,
string lpValueName,
int lpReserved,
ref Microsoft.Win32.RegistryValueKind lpType,
IntPtr lpData,
ref int lpcbData)
{
var byteCount = Encoding.Unicode.GetByteCount(yourString);
if (byteCount > lpcbData)
{
lpcbData = byteCount;
return ERROR_MORE_DATA;
}
if (lpData == IntPtr.Zero)
{
return ERROR_SUCCESS;
}
lpcbData = byteCount;
var bytes = Encoding.Unicode.GetBytes(yourString);
Marshal.Copy(bytes, 0, lpData, bytes.Length);
return ERROR_SUCCESS;
}
Do note that this is just what I've written after quickly glancing through the documentation - you should investigate further, and make sure you're handling all the possible cases. .NET doesn't protect you in this case, you can cause major issues!

P/Invoke mapping for complicated C function (const void *text)

I'm trying to create a wrapper for the eSpeak speech synthesis library, and so far I've managed to make simple methods like single character synthesis work. However, the main synthesis method is too complex for me to wrap my head around. Does anyone have a good suggestion for a C# mapping of this method signature? (Especially the first variable!)
ESPEAK_API espeak_ERROR espeak_Synth(const void *text,
size_t size,
unsigned int position,
espeak_POSITION_TYPE position_type,
unsigned int end_position,
unsigned int flags,
unsigned int* unique_identifier,
void* user_data);
espeak_POSITION_TYPE I've mapped to an enum and I think it works. The following is the description of the text variable if that is helpful:
text: The text to be spoken, terminated by a zero character. It may be either 8-bit characters,
wide characters (wchar_t), or UTF8 encoding. Which of these is determined by the "flags"
parameter.
It depends.
assuming espeak_ERROR and espeak_POSITION_TYPE are integers:
[DllImport("...", CharSet=CharSet.Ansi)
static extern int espeak_Synth(string text,
int text_length,
int position,
int position_type,
int end_position,
int flags,
IntPtr unique_identifier,
IntPtr user_data);
this should work, if you pass to flags, that text is 8bit (and not UTF).
[DllImport("...")
static extern int espeak_Synth(IntPtr text,
int text_length,
int position,
int position_type,
int end_position,
int flags,
IntPtr unique_identifier,
IntPtr user_data);
// vv this can be some parameter
byte[] dat = Encoding.UTF8.GetBytes("some utf-8 string" + "\0"); // note the trailing '\0'!
IntPtr pnt = Marshal.AllocHGlobal(dat.Length);
try {
Marshal.Copy(dat, 0, pnt, dat.Length);
espeak_Synth(pnt, dat.Length, .....); // assuming the text_length is in bytes. If in octets, pass the string's length
}
finally
{
Marshal.FreeHGlobal(pnt);
}
if you want to pass UTF-8 string.

How to marshal unmanaged buffer of packed structs in c#

I am (successfully) calling the Windows FilterSendMessage function in c# using the following pinvoke signature:
[DllImport("fltlib.dll")]
public static extern IntPtr FilterSendMessage(
IntPtr hPort,
IntPtr inBuffer,
UInt32 inBufferSize,
IntPtr outBuffer,
UInt32 outBufferSize,
out UInt32 bytesReturned);
The outBuffer parameter is populated with an arbitrary number of structs (packed one after the other), defined in C as:
typedef struct _BAH_RECORD {
int evt
int len;
WCHAR name[1];
} BAH_RECORD, *PBAH_RECORD;
The name field is assigned a variable length, null-terminated unicode string. The len field describes the total size of the struct in bytes (including the name string). I am confident there's nothing wrong with how the structs are being handled in the unmanaged side of things.
My problem arises when I try and marshal the outBuffer to an instance of the BAH_RECORD struct, defined in c# as:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct BAH_RECORD
{
public UInt32 evt;
public UInt32 len;
public string name;
}
IntPtr outBuffer = Marshal.AllocHGlobal(OUT_BUFFER_SIZE);
hResult = Win32.FilterSendMessage(hPortHandle, inBuffer, IN_BUFFER_SIZE, outBuffer, OUT_BUFFER_SIZE, out bytesReturned);
BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));
<snip>
If I try and print/view/display bah.name, I get garbage...
To confirm that outBuffer does actually contain valid data, I did some crude pointer hackery in c# to step though it, calling Marshal.ReadInt32 twice (to cover the first 2 struct fields), and then Marshal.ReadByte a few times to populate a byte[] which I then use as an argument to Encoding.Unicode.GetString()...the string comes out fine, so it's definitely in there, I just can't seem to get the marshaller to handle it correctly (if it even can?)
Any help appreciated
Steve
The problem is that the 'name' string in your C# BAH_RECORD struct is marshaled as a pointer to a string (WCHAR*) but on the C side it is an inline WCHAR buffer. So when you marshal your struct the runtime reads the first four bytes of the buffer as a pointer and then attempts to read the string that it points to.
Unfortunately, there is no way for the runtime to automatically marshal variable sized buffers inside structs so you will need to use manual marshaling (or as you say "pointer hackery"). But when you advance the pointer to point at the buffer you don't need to read in the bytes individually and then convert them to a string - just call Marshal.PtrToStringUni.

Categories

Resources