5

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?

5
  • The .NET marshalling system is not aware of this (hideous) multi-string format. You'll have to implement that yourself. Commented Aug 13, 2014 at 12:15
  • 3
    Use Marshal.ReadInt16 (IntPtr) method. Read unicode characters until two 0 values are read. Increment IntPtr returned by CreateEnvironmentBlock by two on every iteration. out IntPtr declaration is OK. Don't forget to call DestroyEnvironmentBlock. Commented Aug 13, 2014 at 12:50
  • How is this different then using Environment.GetEnvironmentVariables()? Commented Aug 13, 2014 at 13:40
  • @MariusBancila - by hToken parameter, probably OP needs it for Windows Service. Commented Aug 13, 2014 at 14:29
  • Yes I am running under a spooler service so not under the user account, yet need the user variables, so have to do an impersonation. Commented Aug 14, 2014 at 15:05

1 Answer 1

4

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));
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

A version that returns IEnumerable<string> using yield return might be nice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.