Изменение строки, на которую указывает IntPtr
В моем приложении на C# у меня есть переменная lpData типа IntPtr (полученная при вызове неуправляемого кода), и она указывает на строку.
Я должен заменить эту строку другим значением.
Я старался:
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 ответ
Конечно, она не заменяет строку - вы получаете указатель на строку, в которой ваш вызывающий имеет значение. Вы только заменяете значение вашей "локальной переменной" (параметр), это ничего не меняет на стороне вызывающей стороны.
Если вы хотите изменить значение в исходном указателе (и убедитесь, что вы действительно этого хотите, именно здесь скрываются "странные ошибки" - очень легко перезаписать окружающие переменные, забыть о нулевом терминаторе и т. Д.), Вы можете использование 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 не защищает вас в этом случае, вы можете вызвать серьезные проблемы!