Когда вы вызываете функции Windows API, вы должны самостоятельно реализовать код проверки ошибок. Они не будут генерировать исключения, если что-то пойдет не так, как стандартные функции .NET Framework.
Требуемая проверка ошибок зависит от функции. (Такое несоответствие именно поэтому .NET использует вместо этого исключения.) Проверка документации необходима.
WriteProcessMemory
работает таким образом, что довольно распространено. Возвращаемым значением является BOOL
(целое число, которое библиотеки Win32 рассматривают как логическое значение, где 0 = FALSE и 1 = TRUE). Если функция завершается ошибкой, возвращаемое значение равно 0 (ЛОЖЬ). В случае успеха возвращаемое значение равно 1 (ИСТИНА):
Если функция завершается успешно, возвращаемое значение отлично от нуля.
Если функция завершается ошибкой, возвращаемое значение равно 0 (ноль). Чтобы получить расширенную информацию об ошибке, позвоните GetLastError
. Функция завершается ошибкой, если запрошенная операция записи пересекает область недоступного процесса.
Интересно, конечно, что нам говорят, что мы должны вызвать функцию GetLastError
, чтобы определить , почему функция не сработала. Частично это правда. Вы только звоните GetLastError
, когда пишете неуправляемый код C ++ . В .NET вам нужно сделать что-то еще. Есть два шага:
- Пометьте подпись P / Invoke с помощью
SetLastError = True
, что заставляет CLR автоматически вызывать GetLastError
и сохранять его значение.
- Позвоните
Marshal.GetLastWin32Error
, чтобы получить это значение.
Итак, для его отладки вам нужно переписать ваш код следующим образом (используя стандартный синтаксис P / Invoke, а не более ограниченное ключевое слово Declare
, уникальное для VB.NET):
<DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
ByVal hProcess As Integer, _
ByVal lpBaseAddress As Integer, _
ByVal lpBuffer As Integer, _
ByVal nSize As Integer, _
ByVal lpNumberOfBytesWritten As Integer _
) As Integer
End Function
Dim proc() As Process = Process.GetProcessesByName("process")
If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
Dim p = proc(0)
Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
Dim address As Integer
address = &H98544
Dim memory As IntPtr
Call ReadProcessMemory(pH, address, memory, 4, 0)
Console.WriteLine(memory.ToString)
Dim data As Integer = 2000
If WriteProcessMemory(pH, address, data, Len(data), 0&) = 0 Then
' The function failed, so find out what the error was
MessageBox.Show("WriteProcessMemory failed with error " & Marshal.GetLastWin32Error)
End If
Как только вы узнаете код ошибки, вы можете найти его в списке кодов системных ошибок , чтобы увидеть, что он означает.
Оказывается, ошибка номер 299, ERROR_PARTIAL_COPY
, что в документации просто означает:
Была выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.
Хм, не очень полезно ... Я надеялся, что вы получите что-то лучше. C'est la vie .
Вам понадобится немного дополнительных знаний, чтобы решить эту проблему. Намек дается, еще раз, в документации. Каждый раз, когда он говорит, что параметр является «указателем» (указывается в синтаксисе C ++ звездочкой *
), это означает, что в VB.NET его необходимо передать как ссылку , а не как значение. Чтобы указать, что что-то передается в качестве ссылки, используйте ключевое слово ByRef
. Конечно, для передачи по значению вы указываете ключевое слово по умолчанию ByVal
.
Кроме того, обратите внимание, что когда в документации говорится, что что-то является handle или address , вы должны объявить это в VB.NET как IntPtr
type , а не как целое число. IntPtr
особенный, потому что его размер изменяется в зависимости от того, является ли базовая операционная система 32-битной или 64-битной (в отличие от целого числа, которое является фиксированным размером, независимо от того, на какой платформе вы работаете). Даже если это не вызывает ваших непосредственных проблем, это будет вызывать проблемы совместимости, когда вы компилируете код для 64-бит.
Так что получается, что ваше заявление на самом деле неверно. Переписать это так:
<DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
ByVal hProcess As IntPtr, _
ByVal lpBaseAddress As IntPtr, _
ByVal lpBuffer As Integer, _
ByVal nSize As Integer, _
ByRef lpNumberOfBytesWritten As Integer _
) As Integer
End Function
Можно также учитывать, что третий параметр (lpBuffer
) должен быть массивом байтов (Byte()
), поскольку он указывает данные, которые должны быть записаны в адресное пространство.