none
Get ErrorProvider message from Third Party Application RRS feed

  • Question

  • I need to get if there are errors in the third party application form. i'm trying to get the message of the "ErrorProvider" control. but my investigation goes like this:

    1-Asking how can i get access to controls in another application: I can get the controls by using UI Automation, actually i can set and get values from external controls.

    2-UI Automation can't get the properties for ErrorProvider (https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-support-for-standard-controls)... so I started to ask how can i get the .net properties for controls that are from external application like TestComplete app does (image at the end)(Actual question)

    3- Also i researched about DLL Injection and DLL Ejection to explore the external process and controls of the third party app form  and get the information. I can get that information, but the external program crash and i can't make it more than 1 time. I need to get the errors just to check that automation process its ok.

    What im looking for? im making an automation tool that i need to stop when there is an error on screen, specifically, ErrorProvider. Just that. How can i do that?

    I had tested applications like "TestComplete" that can get information (.net properties) of a single object... how can they get that information? Any Idea to research? some DLL or maybe a Library?

    This is an actual image from TestComplete:


    Yordy Corrales


    Monday, March 18, 2019 4:13 PM

Answers

  • How? The links doesn't provide any example. it's very high for me. Could you please help me?

    The test I did to display Tooltips (in Output window for testing) from an executable (tested with a C# Form with ErrorProviders) :

    (I did not put the button for the click...)

    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class Form1
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumWindows(ByVal lpEnumFunc As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumThreadWindows(dwThreadId As Integer, ByVal lpfn As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        Public Delegate Function EnumCallBack(ByVal handle As Integer, ByVal lParam As IntPtr) As Boolean
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function GetClassName(ByVal hWnd As IntPtr, ByVal lpClassName As StringBuilder, ByVal nMaxCount As Integer) As Integer
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr
        End Function
    
        Public Const PROCESS_TERMINATE As UInteger = &H1
        Public Const PROCESS_CREATE_THREAD As UInteger = &H2
        Public Const PROCESS_SET_SESSIONID As UInteger = &H4
        Public Const PROCESS_VM_OPERATION As UInteger = &H8
        Public Const PROCESS_VM_READ As UInteger = &H10
        Public Const PROCESS_VM_WRITE As UInteger = &H20
        Public Const PROCESS_QUERY_INFORMATION As UInteger = &H400
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True)>
        Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As Int32) As UInteger
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal flAllocationType As UInteger, ByVal flProtect As UInteger) As IntPtr
        End Function
    
        Public Const PAGE_READWRITE As UInteger = &H4
        Public Const MEM_COMMIT As UInteger = &H1000
        Public Const MEM_RESERVE As UInteger = &H2000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal dwFreeType As UInteger) As Boolean
        End Function
    
        Public Const MEM_DECOMMIT As UInteger = &H4000
        Public Const MEM_RELEASE As UInteger = &H8000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function WriteProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function ReadProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
    
        <DllImport("User32.dll", EntryPoint:="SendMessageW", SetLastError:=True)>
        Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
        End Function
    
        <StructLayout(LayoutKind.Sequential)>
        Public Structure RECT
            Public Left As Integer
            Public Top As Integer
            Public Right As Integer
            Public Bottom As Integer
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
        Public Class TOOLINFO
            Public cbSize As UInteger
            Public uFlags As UInteger
            Public hwnd As IntPtr
            Public uId As IntPtr
            Public rect As RECT
            Public hinst As IntPtr
            Public lpszText As IntPtr
            '<MarshalAs(UnmanagedType.LPTStr)>
            'Public lpszText As String
            Public lParam As IntPtr
            Public lpReserved As IntPtr
        End Class
    
        Public Const WM_USER = &H400
        Public Const TTM_GETTOOLCOUNT = (WM_USER + 13)
        Public Const TTM_GETTEXTW = (WM_USER + 56)
        Public Const TTM_ENUMTOOLSW = (WM_USER + 58)
    
        Public Shared Function EnumWindowsProc(ByVal hwnd As Integer, ByVal lParam As IntPtr) As Boolean
            Dim sbWindowClass As StringBuilder = New StringBuilder(256)
            GetClassName(hwnd, sbWindowClass, 256)
            'WindowsForms10.tooltips_class32.app.0.141b42a_r9_ad1
            If (sbWindowClass.ToString().Contains("tooltips")) Then
                Dim nProcessId As Integer = 0
                GetWindowThreadProcessId(hwnd, nProcessId)
                Dim hProcess As IntPtr = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_WRITE Or PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, False, nProcessId)
                If (hProcess) Then
                    Dim ti As TOOLINFO = New TOOLINFO()
                    ti.cbSize = Marshal.SizeOf(GetType(TOOLINFO))
                    Dim pText As IntPtr = VirtualAllocEx(hProcess, 0, 512, MEM_COMMIT, PAGE_READWRITE)
                    Dim pBuf As IntPtr = VirtualAllocEx(hProcess, 0, Marshal.SizeOf(GetType(TOOLINFO)), MEM_COMMIT, PAGE_READWRITE)
    
                    Dim nToolCount As Integer = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0)
                    For nIndex = 0 To nToolCount - 1
                        Dim pTI As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TOOLINFO)))
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        SendMessage(hwnd, TTM_ENUMTOOLSW, nIndex, pBuf)
                        ReadProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        Marshal.PtrToStructure(pTI, ti)
                        ti.lpszText = pText
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
                        SendMessage(hwnd, TTM_GETTEXTW, nIndex, pBuf)
    
                        Dim pszMessage As IntPtr = Marshal.AllocHGlobal(512 * Marshal.SystemDefaultCharSize)
                        ReadProcessMemory(hProcess, pText, pszMessage, 512 * Marshal.SystemDefaultCharSize, Nothing)
                        Dim sMessage As String = Marshal.PtrToStringUni(pszMessage)
                        Console.WriteLine("Tooltip text : {0}", sMessage)
                        Marshal.FreeHGlobal(pszMessage)
                        Marshal.FreeHGlobal(pTI)
                    Next
    
                    VirtualFreeEx(hProcess, pBuf, Marshal.SizeOf(GetType(TOOLINFO)), MEM_RELEASE)
                    VirtualFreeEx(hProcess, pText, 500, MEM_RELEASE)
                    CloseHandle(hProcess)
                End If
            End If
            Return True
        End Function
    
        Dim ecb As EnumCallBack
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'Dim p As Process = Process.GetProcessesByName("Notepad")(0)
            Dim p As Process = Process.GetProcessesByName("CSharp_ErrorProvider")(0)
            Dim nThreadID As Integer = p.Threads(0).Id
            If (nThreadID) Then
                ecb = AddressOf EnumWindowsProc
                EnumThreadWindows(nThreadID, ecb, IntPtr.Zero)
            End If
        End Sub
    End Class


    • Edited by Castorix31 Thursday, March 21, 2019 11:57 AM
    • Marked as answer by Yordy Corrales Thursday, March 21, 2019 4:12 PM
    Thursday, March 21, 2019 11:56 AM

All replies

  • You already posted this question in a recent thread

    My test with ReadProcessMemory and TTM_GETTEXT works (Windows 10 with an ErrorProvider from a Winform)

    but I don't think it is a good method to detect an "error on screen"... (the Tooltip can be filled and not displayed for example)

    Monday, March 18, 2019 6:39 PM
  • You already posted this question in a recent thread

    My test with ReadProcessMemory and TTM_GETTEXT works (Windows 10 with an ErrorProvider from a Winform)

    but I don't think it is a good method to detect an "error on screen"... (the Tooltip can be filled and not displayed for example)

    Yeap, I ask a similar question, but what i need it's to get .net properties as shown in the picture of this question. ReadProcessMemory and TTM_GETTEXT in your test, i guess, it's for get the tooltip text, but i'm asking to get error status in a windows .net form and its text. the control it's ErrorProvider. or how can i get the full .net properties from a third party app object (any control, principally ErrorProvider)

    I want to get in my application this: (This properties are from the external app):


    this is what i imagine (I know that this is not like this)

    I want to instance the external object as: exterrprov = extapp.form1.errorprovider1

    and then get the text: dim strmsg=exterrprov .geterror(extapp.form1.textbox1)

    or maybe i want to get the blinkrate property, or something like that.


    Yordy Corrales

    Monday, March 18, 2019 7:03 PM
  • Hi,

    Can your Third Party Application be edited? If you can,you can bind the ErrorProvider value to the label and then monitor the value of the label.

    Third Party Application:

    Public Class Form1
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            If TextBox1.Text = "12" Then
                Me.ErrorProvider1.SetError(Me.TextBox1, "error")
            End If
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Label1.Visible = False
            Me.Text = "TestForm"
            Dim t As New Timer With {.Interval = 100, .Enabled = True}
            AddHandler t.Tick, Sub()
    
                                   Label1.Text = ErrorProvider1.GetError(TextBox1)
    
                               End Sub
        End Sub
    End Class

    monitor application:

    Imports System.Runtime.InteropServices
    Imports System.Text
    Public Class Form1
        <DllImport("User32.dll", EntryPoint:="FindWindow")>
        Public Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("User32.dll", EntryPoint:="FindWindowEx")>
        Public Shared Function FindWindowEx(ByVal hwndParent As IntPtr, ByVal hwndChildAfter As IntPtr, ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("User32.dll", EntryPoint:="FindEx")>
        Public Shared Function FindEx(ByVal hwnd As IntPtr, ByVal hwndChild As IntPtr, ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("user32.dll", EntryPoint:="SendMessageA")>
        Private Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As StringBuilder) As Integer
        End Function
        Public Shared WM_GETTEXT As Integer = &HD
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim t As New Timer With {.Interval = 100, .Enabled = True}
            AddHandler t.Tick, Sub()
                                   getinfor()
    
                               End Sub
        End Sub
    
        Private Sub getinfor()
            Dim maindHwnd As IntPtr = FindWindow(Nothing, "TestForm")
    
            If maindHwnd <> IntPtr.Zero Then
                'MsgBox("Find Main Window")
                Dim maindHwndp As IntPtr = FindWindowEx(maindHwnd, 0, "WindowsForms10.STATIC.app.0.141b42a_r10_ad1", vbNullString)
    
                If maindHwndp <> IntPtr.Zero Then
                    'MsgBox("Find Child Window!")
                    Const buffer_size As Integer = 1024
                    Dim buffer As StringBuilder = New StringBuilder(buffer_size)
                    SendMessage(maindHwndp, WM_GETTEXT, buffer_size, buffer)
                    TextBox1.Text = buffer.ToString()
                    'SendMessage(maindHwndp, BM_CLICK, 0, Nothing)
                End If
            Else
                'MsgBox("No Find Main Window")
                'Environment.Exit(0)
            End If
        End Sub
    End Class

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Tuesday, March 19, 2019 5:22 AM
    Moderator
  • I did some tests and  I could get controls/properties by injecting a DLL

    I created a DLL in C++/CLI with a Test function to do the work, I injected the DLL then called the function with CreateRemoteThread

    (the ErrorProvider is not in child controls by is a Component)

    But it is complicated....

    Tuesday, March 19, 2019 12:09 PM
  • Hi,

    Can your Third Party Application be edited? If you can,you can bind the ErrorProvider value to the label and then monitor the value of the label.

    Third Party Application:

    Public Class Form1
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            If TextBox1.Text = "12" Then
                Me.ErrorProvider1.SetError(Me.TextBox1, "error")
            End If
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Label1.Visible = False
            Me.Text = "TestForm"
            Dim t As New Timer With {.Interval = 100, .Enabled = True}
            AddHandler t.Tick, Sub()
    
                                   Label1.Text = ErrorProvider1.GetError(TextBox1)
    
                               End Sub
        End Sub
    End Class

    monitor application:

    Imports System.Runtime.InteropServices
    Imports System.Text
    Public Class Form1
        <DllImport("User32.dll", EntryPoint:="FindWindow")>
        Public Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("User32.dll", EntryPoint:="FindWindowEx")>
        Public Shared Function FindWindowEx(ByVal hwndParent As IntPtr, ByVal hwndChildAfter As IntPtr, ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("User32.dll", EntryPoint:="FindEx")>
        Public Shared Function FindEx(ByVal hwnd As IntPtr, ByVal hwndChild As IntPtr, ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("user32.dll", EntryPoint:="SendMessageA")>
        Private Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As StringBuilder) As Integer
        End Function
        Public Shared WM_GETTEXT As Integer = &HD
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim t As New Timer With {.Interval = 100, .Enabled = True}
            AddHandler t.Tick, Sub()
                                   getinfor()
    
                               End Sub
        End Sub
    
        Private Sub getinfor()
            Dim maindHwnd As IntPtr = FindWindow(Nothing, "TestForm")
    
            If maindHwnd <> IntPtr.Zero Then
                'MsgBox("Find Main Window")
                Dim maindHwndp As IntPtr = FindWindowEx(maindHwnd, 0, "WindowsForms10.STATIC.app.0.141b42a_r10_ad1", vbNullString)
    
                If maindHwndp <> IntPtr.Zero Then
                    'MsgBox("Find Child Window!")
                    Const buffer_size As Integer = 1024
                    Dim buffer As StringBuilder = New StringBuilder(buffer_size)
                    SendMessage(maindHwndp, WM_GETTEXT, buffer_size, buffer)
                    TextBox1.Text = buffer.ToString()
                    'SendMessage(maindHwndp, BM_CLICK, 0, Nothing)
                End If
            Else
                'MsgBox("No Find Main Window")
                'Environment.Exit(0)
            End If
        End Sub
    End Class

    Best Regards,

    Alex


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    This is nice! But the Third Party Application isn't editable

    Yordy Corrales

    Tuesday, March 19, 2019 12:53 PM
  • I did some tests and  I could get controls/properties by injecting a DLL

    I created a DLL in C++/CLI with a Test function to do the work, I injected the DLL then called the function with CreateRemoteThread

    (the ErrorProvider is not in child controls by is a Component)

    But it is complicated....

    I already create a DLL to get all the third party controls in the same language (C++/CLI), (Then maybe I can create a TXT file to explore it and see if there was any error in the external app). but here my problem is that i need to inject and eject every time i need to check for errors without closing the Third Party Application. When I Eject and try to inject it again the DLL, the external app crash :(.

    Yordy Corrales


    Tuesday, March 19, 2019 12:56 PM
  • When I Eject and try to inject it again the DLL, the external app crash :(

    I used about the same function I posted in VC++ forum and translated into VB.NET (no need of EnumProcessModules in VB) and I can Inject/Eject in loop without crashing...
    Tuesday, March 19, 2019 1:53 PM

  • My test with ReadProcessMemory and TTM_GETTEXT works (Windows 10 with an ErrorProvider from a Winform)

    How? The links doesn't provide any example. it's very high for me. Could you please help me?

    Yordy Corrales

    Wednesday, March 20, 2019 7:53 PM
  • How? The links doesn't provide any example. it's very high for me. Could you please help me?

    The test I did to display Tooltips (in Output window for testing) from an executable (tested with a C# Form with ErrorProviders) :

    (I did not put the button for the click...)

    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class Form1
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumWindows(ByVal lpEnumFunc As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumThreadWindows(dwThreadId As Integer, ByVal lpfn As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        Public Delegate Function EnumCallBack(ByVal handle As Integer, ByVal lParam As IntPtr) As Boolean
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function GetClassName(ByVal hWnd As IntPtr, ByVal lpClassName As StringBuilder, ByVal nMaxCount As Integer) As Integer
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr
        End Function
    
        Public Const PROCESS_TERMINATE As UInteger = &H1
        Public Const PROCESS_CREATE_THREAD As UInteger = &H2
        Public Const PROCESS_SET_SESSIONID As UInteger = &H4
        Public Const PROCESS_VM_OPERATION As UInteger = &H8
        Public Const PROCESS_VM_READ As UInteger = &H10
        Public Const PROCESS_VM_WRITE As UInteger = &H20
        Public Const PROCESS_QUERY_INFORMATION As UInteger = &H400
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True)>
        Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As Int32) As UInteger
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal flAllocationType As UInteger, ByVal flProtect As UInteger) As IntPtr
        End Function
    
        Public Const PAGE_READWRITE As UInteger = &H4
        Public Const MEM_COMMIT As UInteger = &H1000
        Public Const MEM_RESERVE As UInteger = &H2000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal dwFreeType As UInteger) As Boolean
        End Function
    
        Public Const MEM_DECOMMIT As UInteger = &H4000
        Public Const MEM_RELEASE As UInteger = &H8000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function WriteProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function ReadProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
    
        <DllImport("User32.dll", EntryPoint:="SendMessageW", SetLastError:=True)>
        Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
        End Function
    
        <StructLayout(LayoutKind.Sequential)>
        Public Structure RECT
            Public Left As Integer
            Public Top As Integer
            Public Right As Integer
            Public Bottom As Integer
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
        Public Class TOOLINFO
            Public cbSize As UInteger
            Public uFlags As UInteger
            Public hwnd As IntPtr
            Public uId As IntPtr
            Public rect As RECT
            Public hinst As IntPtr
            Public lpszText As IntPtr
            '<MarshalAs(UnmanagedType.LPTStr)>
            'Public lpszText As String
            Public lParam As IntPtr
            Public lpReserved As IntPtr
        End Class
    
        Public Const WM_USER = &H400
        Public Const TTM_GETTOOLCOUNT = (WM_USER + 13)
        Public Const TTM_GETTEXTW = (WM_USER + 56)
        Public Const TTM_ENUMTOOLSW = (WM_USER + 58)
    
        Public Shared Function EnumWindowsProc(ByVal hwnd As Integer, ByVal lParam As IntPtr) As Boolean
            Dim sbWindowClass As StringBuilder = New StringBuilder(256)
            GetClassName(hwnd, sbWindowClass, 256)
            'WindowsForms10.tooltips_class32.app.0.141b42a_r9_ad1
            If (sbWindowClass.ToString().Contains("tooltips")) Then
                Dim nProcessId As Integer = 0
                GetWindowThreadProcessId(hwnd, nProcessId)
                Dim hProcess As IntPtr = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_WRITE Or PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, False, nProcessId)
                If (hProcess) Then
                    Dim ti As TOOLINFO = New TOOLINFO()
                    ti.cbSize = Marshal.SizeOf(GetType(TOOLINFO))
                    Dim pText As IntPtr = VirtualAllocEx(hProcess, 0, 512, MEM_COMMIT, PAGE_READWRITE)
                    Dim pBuf As IntPtr = VirtualAllocEx(hProcess, 0, Marshal.SizeOf(GetType(TOOLINFO)), MEM_COMMIT, PAGE_READWRITE)
    
                    Dim nToolCount As Integer = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0)
                    For nIndex = 0 To nToolCount - 1
                        Dim pTI As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TOOLINFO)))
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        SendMessage(hwnd, TTM_ENUMTOOLSW, nIndex, pBuf)
                        ReadProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        Marshal.PtrToStructure(pTI, ti)
                        ti.lpszText = pText
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
                        SendMessage(hwnd, TTM_GETTEXTW, nIndex, pBuf)
    
                        Dim pszMessage As IntPtr = Marshal.AllocHGlobal(512 * Marshal.SystemDefaultCharSize)
                        ReadProcessMemory(hProcess, pText, pszMessage, 512 * Marshal.SystemDefaultCharSize, Nothing)
                        Dim sMessage As String = Marshal.PtrToStringUni(pszMessage)
                        Console.WriteLine("Tooltip text : {0}", sMessage)
                        Marshal.FreeHGlobal(pszMessage)
                        Marshal.FreeHGlobal(pTI)
                    Next
    
                    VirtualFreeEx(hProcess, pBuf, Marshal.SizeOf(GetType(TOOLINFO)), MEM_RELEASE)
                    VirtualFreeEx(hProcess, pText, 500, MEM_RELEASE)
                    CloseHandle(hProcess)
                End If
            End If
            Return True
        End Function
    
        Dim ecb As EnumCallBack
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'Dim p As Process = Process.GetProcessesByName("Notepad")(0)
            Dim p As Process = Process.GetProcessesByName("CSharp_ErrorProvider")(0)
            Dim nThreadID As Integer = p.Threads(0).Id
            If (nThreadID) Then
                ecb = AddressOf EnumWindowsProc
                EnumThreadWindows(nThreadID, ecb, IntPtr.Zero)
            End If
        End Sub
    End Class


    • Edited by Castorix31 Thursday, March 21, 2019 11:57 AM
    • Marked as answer by Yordy Corrales Thursday, March 21, 2019 4:12 PM
    Thursday, March 21, 2019 11:56 AM
  • How? The links doesn't provide any example. it's very high for me. Could you please help me?

    The test I did to display Tooltips (in Output window for testing) from an executable (tested with a C# Form with ErrorProviders) :

    (I did not put the button for the click...)

    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class Form1
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumWindows(ByVal lpEnumFunc As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function EnumThreadWindows(dwThreadId As Integer, ByVal lpfn As EnumCallBack, ByVal lParam As IntPtr) As Boolean
        End Function
    
        Public Delegate Function EnumCallBack(ByVal handle As Integer, ByVal lParam As IntPtr) As Boolean
    
        <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function GetClassName(ByVal hWnd As IntPtr, ByVal lpClassName As StringBuilder, ByVal nMaxCount As Integer) As Integer
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr
        End Function
    
        Public Const PROCESS_TERMINATE As UInteger = &H1
        Public Const PROCESS_CREATE_THREAD As UInteger = &H2
        Public Const PROCESS_SET_SESSIONID As UInteger = &H4
        Public Const PROCESS_VM_OPERATION As UInteger = &H8
        Public Const PROCESS_VM_READ As UInteger = &H10
        Public Const PROCESS_VM_WRITE As UInteger = &H20
        Public Const PROCESS_QUERY_INFORMATION As UInteger = &H400
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True)>
        Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As Int32) As UInteger
        End Function
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal flAllocationType As UInteger, ByVal flProtect As UInteger) As IntPtr
        End Function
    
        Public Const PAGE_READWRITE As UInteger = &H4
        Public Const MEM_COMMIT As UInteger = &H1000
        Public Const MEM_RESERVE As UInteger = &H2000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal dwFreeType As UInteger) As Boolean
        End Function
    
        Public Const MEM_DECOMMIT As UInteger = &H4000
        Public Const MEM_RELEASE As UInteger = &H8000
    
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function WriteProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
        <DllImport("Kernel32.dll", SetLastError:=True)>
        Public Shared Function ReadProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef vNumberOfBytesRead As UInteger) As Boolean
        End Function
    
        <DllImport("User32.dll", EntryPoint:="SendMessageW", SetLastError:=True)>
        Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
        End Function
    
        <StructLayout(LayoutKind.Sequential)>
        Public Structure RECT
            Public Left As Integer
            Public Top As Integer
            Public Right As Integer
            Public Bottom As Integer
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
        Public Class TOOLINFO
            Public cbSize As UInteger
            Public uFlags As UInteger
            Public hwnd As IntPtr
            Public uId As IntPtr
            Public rect As RECT
            Public hinst As IntPtr
            Public lpszText As IntPtr
            '<MarshalAs(UnmanagedType.LPTStr)>
            'Public lpszText As String
            Public lParam As IntPtr
            Public lpReserved As IntPtr
        End Class
    
        Public Const WM_USER = &H400
        Public Const TTM_GETTOOLCOUNT = (WM_USER + 13)
        Public Const TTM_GETTEXTW = (WM_USER + 56)
        Public Const TTM_ENUMTOOLSW = (WM_USER + 58)
    
        Public Shared Function EnumWindowsProc(ByVal hwnd As Integer, ByVal lParam As IntPtr) As Boolean
            Dim sbWindowClass As StringBuilder = New StringBuilder(256)
            GetClassName(hwnd, sbWindowClass, 256)
            'WindowsForms10.tooltips_class32.app.0.141b42a_r9_ad1
            If (sbWindowClass.ToString().Contains("tooltips")) Then
                Dim nProcessId As Integer = 0
                GetWindowThreadProcessId(hwnd, nProcessId)
                Dim hProcess As IntPtr = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_WRITE Or PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, False, nProcessId)
                If (hProcess) Then
                    Dim ti As TOOLINFO = New TOOLINFO()
                    ti.cbSize = Marshal.SizeOf(GetType(TOOLINFO))
                    Dim pText As IntPtr = VirtualAllocEx(hProcess, 0, 512, MEM_COMMIT, PAGE_READWRITE)
                    Dim pBuf As IntPtr = VirtualAllocEx(hProcess, 0, Marshal.SizeOf(GetType(TOOLINFO)), MEM_COMMIT, PAGE_READWRITE)
    
                    Dim nToolCount As Integer = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0)
                    For nIndex = 0 To nToolCount - 1
                        Dim pTI As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TOOLINFO)))
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        SendMessage(hwnd, TTM_ENUMTOOLSW, nIndex, pBuf)
                        ReadProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
    
                        Marshal.PtrToStructure(pTI, ti)
                        ti.lpszText = pText
                        Marshal.StructureToPtr(ti, pTI, False)
                        WriteProcessMemory(hProcess, pBuf, pTI, Marshal.SizeOf(GetType(TOOLINFO)), Nothing)
                        SendMessage(hwnd, TTM_GETTEXTW, nIndex, pBuf)
    
                        Dim pszMessage As IntPtr = Marshal.AllocHGlobal(512 * Marshal.SystemDefaultCharSize)
                        ReadProcessMemory(hProcess, pText, pszMessage, 512 * Marshal.SystemDefaultCharSize, Nothing)
                        Dim sMessage As String = Marshal.PtrToStringUni(pszMessage)
                        Console.WriteLine("Tooltip text : {0}", sMessage)
                        Marshal.FreeHGlobal(pszMessage)
                        Marshal.FreeHGlobal(pTI)
                    Next
    
                    VirtualFreeEx(hProcess, pBuf, Marshal.SizeOf(GetType(TOOLINFO)), MEM_RELEASE)
                    VirtualFreeEx(hProcess, pText, 500, MEM_RELEASE)
                    CloseHandle(hProcess)
                End If
            End If
            Return True
        End Function
    
        Dim ecb As EnumCallBack
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'Dim p As Process = Process.GetProcessesByName("Notepad")(0)
            Dim p As Process = Process.GetProcessesByName("CSharp_ErrorProvider")(0)
            Dim nThreadID As Integer = p.Threads(0).Id
            If (nThreadID) Then
                ecb = AddressOf EnumWindowsProc
                EnumThreadWindows(nThreadID, ecb, IntPtr.Zero)
            End If
        End Sub
    End Class


    You are my hero, now i can get the tooltips, i made some ajustments for my needs, but this is the Holy Grail. it's very difficult to get this! Thank you for your help!

    Yordy Corrales

    Thursday, March 21, 2019 4:16 PM