OpenProcess in VB.Net

This is driving me nuts. Here's my code (minus all the testing comments and stuff):


Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Boolean, ByVal dwAppProcessId As IntPtr) As IntPtr
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As IntPtr) As IntPtr
Private Const PROCESS_VM_READ = (&H10)

Public Sub TestOpenProcess(ByVal hwnd As IntPtr)

      Dim lRet As IntPtr
      Dim pid As IntPtr
      Dim hProcess as IntPtr

      ' Assign a value to pid - this part works fine
      lRet = GetWindowThreadProcessId(hwnd, pid)

      ' Try to open the process for reading
      hProcess = OpenProcess(PROCESS_VM_READ, False, pid)

      ' At this point:
      ' hProcess = 0
      ' Err.LastDllError = 87 (Parameter incorrect or something like that)      
      Debug.Writeline("Error: " & Err.LastDllError)
End Sub


All the code examples I've found are either in C++ or VB6. I can't find any WORKING VB.Net code for using OpenProcess, and I need to OpenProcess so I can then use ReadProcessMemory.

According to MSDN, OpenProcess returns a HANDLE, which looks like it's IntPtr in VB.Net, but no matter what I try, hProcess stays at 0, and I always get 87 as the LastDLLError code. HELP!!!!!
LVL 36
Who is Participating?
Mike TomlinsonConnect With a Mentor Middle School Assistant TeacherCommented:
Try using Integer for everything...

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal blnheritHandle As Boolean, ByVal dwAppProcessId As Integer) As Integer
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Integer, ByRef lpdwProcessId As Integer) As Integer
Private Const PROCESS_VM_READ = (&H10)

Public Sub TestOpenProcess(ByVal hwnd As Integer)

     Dim lRet As Integer
     Dim pid As Integer
     Dim hProcess as Integer

     ' Assign a value to pid - this part works fine
     lRet = GetWindowThreadProcessId(hwnd, pid)

     ' Try to open the process for reading
     hProcess = OpenProcess(PROCESS_VM_READ, False, pid)

     ' At this point:
     ' hProcess = 0
     ' Err.LastDllError = 87 (Parameter incorrect or something like that)    
     Debug.Writeline("Error: " & Err.LastDllError)
End Sub
Mohamed ZedanCommented:
try this declaration..

Public Declare Function OpenProcess Lib "Kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long

And Call it like this substitute 0 for false
And be sure that  ... Const PROCESS_VM_READ = 16

hProcess = OpenProcess(PROCESS_VM_READ, 0, pid)

also  check that you have read access to the process ...
Keep me informed on your progress :)
gr8gonzoConsultantAuthor Commented:
mohzedan - that didn't work. :/

Idle_Mind - that sort of worked. OpenProcess did finally return a number for hProcess. However, I've now noticed that Err.LastDllError is 2 after I run GetWindowThreadProcessId() and 126 after OpenProcess().

I'm not sure if these are critical errors or just warnings that I can ignore.

The final thing I'm trying to achieve is to read the memory associated with a pop-up messagebox from an app. The next step is to use ReadProcessMemory. If you'd like to help with that, as well, I can double the point value of this question and keep it open.

Here's the expanded version of the code, along with the debugging output:
    Declare Function OpenProcess Lib "kernel32" ( _
            ByVal dwDesiredAccess As Integer, _
            ByVal blnheritHandle As Boolean, _
            ByVal dwAppProcessId As Integer) As Integer

    Declare Function GetWindowThreadProcessId Lib "user32" ( _
            ByVal hwnd As Integer, _
            ByRef lpdwProcessId As Integer) As Integer

    Declare Function ReadProcessMemory Lib "kernel32" ( _
            ByVal hProcess As Integer, _
            ByVal lpBaseAddress As Long, _
            ByRef lpBuffer As Byte(), _
            ByRef nSize As Integer, _
            ByRef lpNumberOfBytesWritten As Integer) As Integer

    Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer

    Declare Function VirtualQueryEx Lib "kernel32" ( _
            ByVal hProcess As Integer, _
            ByVal lpAddress As Integer, _
            ByRef lpBuffer As MEMORY_BASIC_INFORMATION, _
            ByVal dwLength As Integer) As Integer

    Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
            ByVal hWnd As Integer, _
            ByVal Msg As Integer, _
            ByVal wParam As Integer, _
            ByVal lParam As Integer) As Integer

    Public Const VBM_WINDOWTITLEADDR = &H1091
    Public Const PROCESS_ALL_ACCESS = &H1F0FFF

    Public Structure MEMORY_BASIC_INFORMATION ' 28 bytes
        Public BaseAddress As Long
        Public AllocationBase As Long
        Public AllocationProtect As Long
        Public RegionSize As Long
        Public State As Long
        Public Protect As Long
        Public lType As Long
    End Structure

    Public Sub TestOpenProcess(ByVal hwnd As Integer)

        Dim lRet As Integer
        Dim pid As Integer
        Dim hProcess As Integer
        Dim startAddress As Integer
        Dim bytesToRead As Integer = 4096
        Dim abBuffer(bytesToRead) As Byte
        Dim lBytesRead As Integer

        Dim lLenMBI As Integer = Len(mbi)

        lRet = GetWindowThreadProcessId(hwnd, pid)
        Debug.WriteLine("GetWindowThreadProcessId Error: " & Err.LastDllError)
        Debug.WriteLine("GetWindowThreadProcessId pid: " & pid)
        Debug.WriteLine("GetWindowThreadProcessId lRet: " & lRet)

        hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
        Debug.WriteLine("OpenProcess Error: " & Err.LastDllError)
        Debug.WriteLine("OpenProcess hProcess: " & hProcess)

        startAddress = GetHeapAddress(hwnd)
        Debug.WriteLine("GetHeapAddress Error: " & Err.LastDllError)
        Debug.WriteLine("GetHeapAddress startAddress: " & startAddress)

        mbi.AllocationBase = startAddress
        mbi.BaseAddress = startAddress
        lRet = VirtualQueryEx(hProcess, startAddress, mbi, lLenMBI)
        Debug.WriteLine("VirtualQueryEx Error: " & Err.LastDllError)
        Debug.WriteLine("VirtualQueryEx lRet: " & lRet)
        Debug.WriteLine("VirtualQueryEx mbi.AllocationBase: " & mbi.AllocationBase)

        lRet = ReadProcessMemory(hProcess, mbi.AllocationBase, abBuffer, bytesToRead, lBytesRead)
        Debug.WriteLine("ReadProcessMemory Error: " & Err.LastDllError)
        Debug.WriteLine("ReadProcessMemory abBuffer.Length: " & abBuffer.Length)
        Debug.WriteLine("ReadProcessMemory lBytesRead: " & lBytesRead)

        lRet = CloseHandle(hProcess)
        Debug.WriteLine("CloseHandle Error: " & Err.LastDllError)

    End Sub

    Function GetHeapAddress(ByVal hwnd As Integer) As Integer
        Dim lFormCaptionHeapAddress As Integer = SendMessage(hwnd, VBM_WINDOWTITLEADDR, 0, 0)
        Return lFormCaptionHeapAddress
    End Function

GetWindowThreadProcessId Error: 2
GetWindowThreadProcessId pid: 3332
GetWindowThreadProcessId lRet: 3352
OpenProcess Error: 126
OpenProcess hProcess: 3744
GetHeapAddress Error: 126
GetHeapAddress startAddress: 0
VirtualQueryEx Error: 126
VirtualQueryEx lRet: 28
VirtualQueryEx mbi.AllocationBase: 281474976710656
ReadProcessMemory Error: 299
ReadProcessMemory abBuffer.Length: 1
ReadProcessMemory lBytesRead: 0
CloseHandle Error: 299

Again, if you'd rather just get your points and have me re-post this, let me know. Thank you!

- Jonathan
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

gr8gonzoConsultantAuthor Commented:
Just an FYI - I think that the Err.LastDllError doesn't get cleared, so GetHeapAddress and VirtualQueryEx probably worked fine. I'm thinking that GetHeapAddress (a trick I saw from another experts exchange post) probably just doesn't work as described or something, and VirtualQueryEx seemed like it populated the AllocationBase, as well as the AllocationProtect.

According to MSDN library pages that list the error codes (

Error 2 = The system cannot find the file specified.
Error 126 = The specified module could not be found.
Error 299 = Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

All thoughts are welcome - Idle_Mind already has 250 points guaranteed to him, but if I get an answer, I'll double the points and distribute as appropriate.
Mike TomlinsonMiddle School Assistant TeacherCommented:
I don't have any personal experience with the more exotic APIs you're using here...

I just know that in MOST cases, the Long datatype is incorrect in VB.Net.

You still have one in your ReadProcessMemory() declaration.

Also the MEMORY_BASIC_INFORMATION Structure probably needs Integer instead of Long.

Mohamed ZedanCommented:
Here is a working class that opens a process and reads it's memory :)
After it there is a form class that reads from minesweeper game the location of the mines and displays it .
These we ported from a codeproject article in C#
Hope I've helped :)

Imports System.Runtime.InteropServices

Public Class ProcessMemoryReader
    <DllImport("kernel32.dll")> _
    Public Shared Function OpenProcess(ByVal dwDesiredAccess As UInt32, ByVal bInheritHandle As Int32, ByVal dwProcessId As UInt32) As IntPtr
    End Function

    <DllImport("kernel32.dll")> _
    Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, <[In](), Out()> ByVal buffer As Byte(), ByVal size As UInt32, <Out()> ByVal lpNumberOfBytesRead As IntPtr) As Int32
    End Function

    <DllImport("kernel32.dll")> Public Shared Function CloseHandle(ByVal hObject As IntPtr) As Int32
    End Function

    Public Const PROCESS_VM_READ As Int32 = (&H10)

    Public Property ReadProcess() As Process
            Return m_ReadProcess
        End Get
        Set(ByVal Value As Process)
            m_ReadProcess = Value
        End Set
    End Property

    Private m_ReadProcess As Process = Nothing
    Private m_hProcess As IntPtr = IntPtr.Zero

    Public Sub New()
    End Sub

    Public Sub OpenProcess()
        m_hProcess = OpenProcess(Convert.ToUInt32(PROCESS_VM_READ), 1, Convert.ToUInt32(m_ReadProcess.Id))
    End Sub

    Public Sub CloseHandle()
        Dim iRetValue As Integer
        iRetValue = CloseHandle(m_hProcess)
        If (iRetValue = 0) Then Throw New Exception("CloseHandle failed")
    End Sub

    Public Function ReadProcessMemory(ByVal MemoryAddress As IntPtr, ByVal bytesToRead As UInt32, ByRef bytesReaded As Integer) As Byte()
        Dim buffer(Convert.ToInt32(bytesToRead)) As Byte
        Dim ptrBytesReaded As New IntPtr
        ReadProcessMemory(m_hProcess, MemoryAddress, buffer, bytesToRead, ptrBytesReaded)
        bytesReaded = ptrBytesReaded.ToInt32()
        Return buffer
    End Function
End Class

Create a new form and paste the following code replacing everything in the code view and try it with minesweeper

Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Namespace MineSweeperReader

    Public Class Form1
        Inherits System.Windows.Forms.Form
        Private WithEvents lblWidth As System.Windows.Forms.Label
        Private WithEvents lblHeight As System.Windows.Forms.Label
        Private WithEvents lblMines As System.Windows.Forms.Label
        Private WithEvents txtWidth As System.Windows.Forms.TextBox
        Private WithEvents txtHeight As System.Windows.Forms.TextBox
        Private WithEvents txtMines As System.Windows.Forms.TextBox
        Private WithEvents btnRead As System.Windows.Forms.Button
        Private components As System.ComponentModel.Container = Nothing
        Private ButtonArray As System.Windows.Forms.Button(,) = Nothing
        Private WithEvents button1 As System.Windows.Forms.Button
        Private MainControls As System.Windows.Forms.Control() = Nothing

        Public Sub New()
        End Sub

        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                End If
            End If
        End Sub

        Private Sub InitializeComponent()
            Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1))
            Me.lblWidth = New System.Windows.Forms.Label
            Me.lblHeight = New System.Windows.Forms.Label
            Me.lblMines = New System.Windows.Forms.Label
            Me.txtWidth = New System.Windows.Forms.TextBox
            Me.txtHeight = New System.Windows.Forms.TextBox
            Me.txtMines = New System.Windows.Forms.TextBox
            Me.btnRead = New System.Windows.Forms.Button
            Me.button1 = New System.Windows.Forms.Button
            Me.lblWidth.AutoSize = True
            Me.lblWidth.Location = New System.Drawing.Point(8, 16)
            Me.lblWidth.Name = "lblWidth"
            Me.lblWidth.Size = New System.Drawing.Size(36, 16)
            Me.lblWidth.TabIndex = 0
            Me.lblWidth.Text = "Width:"
            Me.lblHeight.AutoSize = True
            Me.lblHeight.Location = New System.Drawing.Point(112, 16)
            Me.lblHeight.Name = "lblHeight"
            Me.lblHeight.Size = New System.Drawing.Size(40, 16)
            Me.lblHeight.TabIndex = 1
            Me.lblHeight.Text = "Height:"
            Me.lblMines.AutoSize = True
            Me.lblMines.Location = New System.Drawing.Point(216, 16)
            Me.lblMines.Name = "lblMines"
            Me.lblMines.Size = New System.Drawing.Size(38, 16)
            Me.lblMines.TabIndex = 2
            Me.lblMines.Text = "Mines:"
            Me.txtWidth.Location = New System.Drawing.Point(48, 12)
            Me.txtWidth.Name = "txtWidth"
            Me.txtWidth.ReadOnly = True
            Me.txtWidth.Size = New System.Drawing.Size(48, 20)
            Me.txtWidth.TabIndex = 3
            Me.txtWidth.Text = ""
            Me.txtHeight.Location = New System.Drawing.Point(152, 12)
            Me.txtHeight.Name = "txtHeight"
            Me.txtHeight.ReadOnly = True
            Me.txtHeight.Size = New System.Drawing.Size(48, 20)
            Me.txtHeight.TabIndex = 4
            Me.txtHeight.Text = ""
            Me.txtMines.Location = New System.Drawing.Point(256, 12)
            Me.txtMines.Name = "txtMines"
            Me.txtMines.ReadOnly = True
            Me.txtMines.Size = New System.Drawing.Size(48, 20)
            Me.txtMines.TabIndex = 5
            Me.txtMines.Text = ""
            Me.btnRead.Location = New System.Drawing.Point(8, 40)
            Me.btnRead.Name = "btnRead"
            Me.btnRead.Size = New System.Drawing.Size(296, 24)
            Me.btnRead.TabIndex = 6
            Me.btnRead.Text = "Read MineSweeper Memory"
            'Me.button1.Image = CType(resources.GetObject("button1.Image"), System.Drawing.Image)
            Me.button1.Location = New System.Drawing.Point(0, 0)
            Me.button1.Name = "button1"
            Me.button1.Size = New System.Drawing.Size(16, 16)
            Me.button1.TabIndex = 7
            Me.button1.Text = "button1"
            Me.button1.Visible = False
            Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
            Me.ClientSize = New System.Drawing.Size(320, 273)
            Me.Name = "Form1"
            Me.Opacity = 0.800000011920929
            Me.Text = "MineSweeper Memory Reader"

        End Sub

        <STAThread()> _
        Shared Sub Main()
            Application.Run(New Form1)
        End Sub

        Private Sub btnRead_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRead.Click
            Dim iWidthAddress As Integer
            Dim iHeightAddress As Integer
            Dim iMinesAddress As Integer
            Dim iCellBaseAddress As Integer
            If Environment.OSVersion.Version.Major = 5 Then
                If Environment.OSVersion.Version.Minor = 0 Then
                    iWidthAddress = &H10056F8
                    iHeightAddress = &H1005A68
                    iMinesAddress = &H1005A6C
                    iCellBaseAddress = &H1005700
                    iWidthAddress = &H1005334
                    iHeightAddress = &H1005338
                    iMinesAddress = &H1005330
                    iCellBaseAddress = &H1005340
                End If
                MessageBox.Show("Sorry, only winXP and win2K are supported!")
            End If
            Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1))
            Dim pReader As ProcessMemoryReader = New ProcessMemoryReader
            Dim myProcesses As System.Diagnostics.Process() = System.Diagnostics.Process.GetProcessesByName("winmine")
            If myProcesses.Length = 0 Then
                MessageBox.Show("No MineSweeper process found!")
            End If
            pReader.ReadProcess = myProcesses(0)
            Dim bytesReaded As Integer
            Dim iWidth As Integer
            Dim iHeight As Integer
            Dim iMines As Integer
            Dim iIsMine As Integer
            Dim iCellAddress As Integer
            Dim memory As Byte()
            memory = pReader.ReadProcessMemory(New IntPtr(iWidthAddress), Convert.ToUInt32(1), bytesReaded)
            iWidth = memory(0)
            txtWidth.Text = iWidth.ToString
            memory = pReader.ReadProcessMemory(New IntPtr(iHeightAddress), Convert.ToUInt32(1), bytesReaded)
            iHeight = memory(0)
            txtHeight.Text = iHeight.ToString
            memory = pReader.ReadProcessMemory(New IntPtr(iMinesAddress), Convert.ToUInt32(1), bytesReaded)
            iMines = memory(0)
            txtMines.Text = iMines.ToString
            ButtonArray = New System.Windows.Forms.Button(iWidth, iHeight) {}
            Dim x As Integer
            Dim y As Integer
            y = 0
            y = 0
            While y < iHeight
                x = 0
                x = 0
                While x < iWidth
                    ButtonArray(x, y) = New System.Windows.Forms.Button
                    ButtonArray(x, y).Location = New System.Drawing.Point(20 + x * 16, 70 + y * 16)
                    ButtonArray(x, y).Name = ""
                    ButtonArray(x, y).Size = New System.Drawing.Size(16, 16)
                    iCellAddress = iCellBaseAddress + (32 * (y + 1)) + (x + 1)
                    memory = pReader.ReadProcessMemory(New IntPtr(iCellAddress), Convert.ToUInt32(1), bytesReaded)
                    iIsMine = memory(0)
                    If iIsMine = &H8F Then
                        ButtonArray(x, y).Text = "M" '= CType((resources.GetObject("button1.Image")), System.Drawing.Bitmap)
                    End If
                    Me.Controls.Add(ButtonArray(x, y))
                    System.Math.Min(System.Threading.Interlocked.Increment(x), x - 1)
                End While
                System.Math.Min(System.Threading.Interlocked.Increment(y), y - 1)
            End While
        End Sub

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            MainControls = New Control(Me.Controls.Count) {}
            Me.Controls.CopyTo(MainControls, 0)
        End Sub
    End Class
End Namespace
Mohamed ZedanCommented:
I tested it ... it's a cool cheat in MineSweeper ... but remember that you need to know the memory Addresses of the strings that you want to read
gr8gonzoConsultantAuthor Commented:
Sorry - I tried to post a response but was getting site errors and I got carried away with something else. Unfortunately, I still couldn't get things to work using those suggestions.
Mike TomlinsonMiddle School Assistant TeacherCommented:
We fixed the initial OpenProcess() error.

I would close this question (with lower points if you want) and open a new more specific question to address the ReadProcessMemory() issue.
gr8gonzoConsultantAuthor Commented:
That's okay - I'll leave the points as is and award the answer. I appreciate that help - it at least helped me understand a bit more about what was going on. Thank you!
All Courses

From novice to tech pro — start learning today.