Question

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

Was it helpful?

Solution

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top