2014-02-12 42 views
0

在我的C#應用​​程序中,我有一個IntPtr類型的變量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"); 
    ... 
} 

,但這似乎並沒有取代實際的字符串。

有人能指出我如何做到這一點的正確方向嗎?

感謝

+0

替換實際的字符串是什麼?你想改變註冊表中的值嗎?字符串是不可變的,所以你只能用一個新引用替換一個字符串。但是,對於那個特定的API調用,您可以使用「StringBuilder」 - 也許這將適用於您想要執行的操作? –

+0

在我的情況下,StringBuilder崩潰(請參閱http:// stackoverflow。com/questions/21722555/easyhook-32-bit-application),所以我嘗試使用IntPtr代替,它不會崩潰;但後來我不知道如何替換字符串。 –

+0

我看到你的其他帖子在這裏:http://stackoverflow.com/questions/21722555/easyhook-32-bit-application ....問題是,它是該API的「調用者」分配緩衝區(和它的大小)並提供指針。由於它不是指向指針/引用參數的指針,因此不能將指針更改回調用方。 –

回答

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在這種情況下不保護你,你可以引起重大問題!

+1

@ L-Three您不能像打印字符串一樣打印出字節數組。您必須使用['Encoding.GetString()'](http://msdn.microsoft.com/zh-cn/library/744y86tc%28v=vs.110%29.aspx)將其轉換。 –

+0

謝謝!!!!!現在我更好地理解應該怎麼做。你的實現似乎可行,但仍然有一件奇怪的事情:如果調用者獲得了註冊表值(被覆蓋),它將以字節數組的格式獲取。如果我做Encoding.Default.GetString(registryvalue),那麼我會得到'你好'。你知道如何使它成爲字符串而不是字節數組嗎? –

+0

@ L-Three我在unicode中編碼了字符串,所以爲了得到正確的字符串,你也必須在unicode中解碼,例如。 'Encoding.Unicode.GetString(registryvalue)'。在任何情況下,您都應該使用'lpType'參數報告正確的編碼,並且在調用者方面,按照這樣的方式讀取它。 – Luaan