更改IntPtr指向的字符串

时间:2014-02-12 11:59:54

标签: c# interop marshalling unmanaged intptr

在我的C#应用​​程序中,我有一个IntPtr类型的变量lpData(从对非托管代码的调用中获得),它指向一个字符串。

我必须用另一个值替换此字符串。

我试过了:

int RegQueryValueExW_Hooked(
        IntPtr hKey,
        string lpValueName,
        int lpReserved,
        ref Microsoft.Win32.RegistryValueKind lpType,
        IntPtr lpData,
        ref int lpcbData)
{ 
    lpData = Marshal.StringToHGlobalUni("new string"); 
    ...
}

但这似乎并没有取代实际的字符串。

有人能指出我如何做到这一点的正确方向吗?

由于

1 个答案:

答案 0 :(得分:2)

当然它不会替换字符串 - 您将获得指向调用者具有值的字符串的指针。您只是替换“局部变量”(参数)的值,这不会改变调用方的任何内容。

如果您要修改原来的指针值(并确保你其实想的是,这是在“奇怪的错误”潜伏 - 它很容易覆盖周边变量,忘掉空终止等) ,您可以使用Marshal.Copy,例如:

var bytes = Encoding.Unicode.GetBytes("your string\0");

Marshal.Copy(bytes, 0, lpData, bytes.Length);

再次 - 这是一种非常危险的行为,你不应该这样做。你违反了参数传递等隐含的几个合同。

现在,我已经回答了你的问题的,让我谈谈你对真正需要多么错误做到这一点(和这个有很大关系到你的其他职位有关使用StringBuilder)。

您正在尝试修改作为参数传递给您的值。但是,字符串是由调用者分配的。你甚至不知道它有多久!如果您开始将数据复制到该指针,您将覆盖例如数据。完全不同的变量,只是随机发生在字符串之后。这被认为是“非常糟糕”。

相反,您想要做的是遵循RegQueryValueEx具有的正确过程(http://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx)。这意味着您首先必须检查lpcbData值。如果它足够大以容纳您想要写入的所有字节,则只需将数据写入lpData并将lpcbData值设置为适当的长度。如果没有,您仍然设置lpcbData,但返回ERROR_MORE_DATA。然后调用者应该再次使用更大的缓冲区调用RegQueryValueEx。

示例代码如下:

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;
}

请注意,这正是我在快速浏览文档后所写的内容 - 您应该进一步调查,并确保您正在处理所有可能的情况。在这种情况下,.NET不能保护您,可以导致重大问题!