locked
Reading text in third party software to visual studio 2010 express RRS feed

  • Question

  • Hi

     

    I’m writing a small pet program in VB2010 Express and need some help please.

     

    I have 7 labels

     

    Form1

    Label1 to Label7

     

    I want these labels to display the text from a third party Software program.

    The text is displayed in a child form of the main form the text is in

    “window handle is 000E0678”” RichEdit20W”

    The text scroll up continuously,

     every time - let’s say

    John (10year)              'is displayed, it takes the 10y and place it in Label1

    Ann (22year 7Street)  ‘is displayed, it takes the 22y & place it in Label2 7St  in Label3

     

    Please help me get that text in the labels I only know a little VB if you need more info please ask. I have used Spy++ to get the window handle

    I know there are lots of people struggling with this and there are no info out there that help a beginner with this. Yes lots for the pro's but we dont understand it, if its just code we dont know where to start or make changes to that code. This is something simple that beginners will understand

    Friday, November 19, 2010 8:05 AM

Answers

  • This is how I would do that;

    You want it to automaticly update the label when a new transaction appears in the 3rd party application

    so

    A) I would be using a class to create a new objet, a "TransactionReader"

    B) I would create a new event, let call it "TransacrionReader.NewTransactionAdded". This event would be raised every time a new transaction is added in the 3rd party application

    C) I would create a new event argument "NewTransactionEventArgs" This would be the event argument of our new event. I would contains the text of the new transaction ONLY

    ie

           Transaction Send 2545

           Client: Frank New Client(6d nr)                   

           Client: Frank Is Client(7y 5r 2d)                  

           Client: Frank Potential Sale Client (5j)        

           Client: Frank Sale(5s)          

                         

    D) Since it is impossible to have the 3rd party application to notify when a new transaction is available, for the "TransactionReader" to know if there is, I would put in the TransactionReader a timer that will go at regular interval to see if a new transaction was added. Probably setting up the timer to check 2 or 4 times per seconds would be a good setting.

    E) I would use the code of  Step 1,2,3,4 from the previous post in the timer to go check. Step5 have to be different.

    F) In step 1, I would add a condition that if the 3rd party application is not found, a message tels the user to start the application and skip the rest of the code, So if it is not found or closed, your application dont crash

    G) I would also add to step 2 a condition that if the list of child already exist and is valid, that this step 2 gets skip and the existing list of child get used. (this will save a lot of processor time for both application, yours and the 3rd party one

    H) I would add to the reader 2 methods. "StartReading" and "StopReading" so you can control the reader

    I) in the step 4, I would only get the last 300 charactere from the 3rd Party application, there is no reason to get all the text, 300 caractere is more than necessary

    J) In step 5, I would use the string function "Split" to create an array of string where each string is a transaction. So, the last item of this array will be the last transaction added in the 3rd party application

    It will be something like this

            Send 2545                    <===The word "Transaction" is removed here

            Client: Frank New Client(6d nr)                   

            Client: Frank Is Client(7y 5r 2d)                  

            Client: Frank Potential Sale Client (5j)        

            Client: Frank Sale(5s)          

                         

    K) Then I would put this text in the event argument we have created and raise the event.

    L) In Form1, I would put a method that will handle this event. This method would extract the info from the text of the transaction and put it in the Label ( I will let check in the example how I did that with only one line of code) for each different Type "New", "Sale", "Is Client, etc..

    This example should work good if I understand what you need and automaticaly update your labels when a new transaction is added in the 3rd party application.

    ++++DONT FORGET TO SET THE WINDOW TEXT AND THE INDEX OF LIST OF CHILD IN MY EXAMPLE+++

     

    Public Class Form1
    
     Private WithEvents TReader As New TransactionReader
    
     '====This is for the Button to Start to check for the text
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      'Start the timer
      TReader.StartReading()
     End Sub
    
     '====Event Handler called when the text change
     Private Sub TReader_NewTransactionAdded(ByVal Sender As Object, ByVal e As NewTransactionEventArgs) _
               Handles TReader.NewTransactionAdded
    
      'make an array of all the lines in the transaction
      Dim Lines() As String = e.NewText.Split(New String() {vbCrLf}, StringSplitOptions.RemoveEmptyEntries)
      For Each line As String In Lines
       If line.Contains("New") Then
        Dim Index = line.IndexOf("("c) + 1
        Dim Info() As String = line.Substring(Index, line.Length - Index - 1).Split(" "c)
        Label1.Text = Info(0)
        Label2.Text = Info(1)
       ElseIf line.Contains("Potential") Then
        Dim Index = line.IndexOf("("c) + 1
        Label6.Text = line.Substring(Index, line.Length - Index - 1)
       ElseIf line.Contains("Sale") And Not line.Contains("Potential") Then
        Dim Index = line.IndexOf("("c) + 1
        Label7.Text = line.Substring(Index, line.Length - Index - 1)
       ElseIf line.Contains("Is Client") Then
        Dim Index = line.IndexOf("("c) + 1
        Dim Info() As String = line.Substring(Index, line.Length - Index - 1).Split(" "c)
        Label3.Text = Info(0)
        Label4.Text = Info(1)
        Label5.Text = Info(2)
       End If
      Next
     End Sub
    
    End Class
    '
    '
    '============== Class Transaction Reader =====================
    Class TransactionReader
    
     '=====Declare a delegate for the function EnumChildWindowsProc
     Delegate Function EnumChildWindowsCallBack(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
     '=====Create the new instance of the delegate for the EnumChildWindowsProc
     Private Callback As New EnumChildWindowsCallBack(AddressOf EnumChildWindowsProc)
     '=====This is the list of child controls
     Private ListOfChild As New List(Of IntPtr)
     '======Creates a new event
     Public Event NewTransactionAdded(ByVal Sender As Object, ByVal e As NewTransactionEventArgs)
     '=====Creates a timer to automaticaly check if the text changed
     Private WithEvents TheTimer As New Timer With {.Interval = 250, .Enabled = False}
     '=====This will be used for the comparaison to se if the text has changed
     Private OldText As String = ""
    
    
     Public Sub StopReading()
      TheTimer.Enabled = False
     End Sub
    
     Public Sub StartReading()
      ListOfChild.Clear()
      TheTimer.Enabled = True
     End Sub
    
    
     '===== This is the callback function
     Private Function EnumChildWindowsProc(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
      ListOfChild.Add(Child_hWnd)
      Return True
     End Function
    
    
     '===== this is the timer method
     Private Sub TheTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TheTimer.Tick
      Dim Window_hWnd As IntPtr = FindWindow(Nothing, "Test Application") '<=Replace "Test Application"
      '=====This will tell the user if the 3rd party application is not found
      If CInt(Window_hWnd) = 0 Then
       TheTimer.Enabled = False
       MsgBox("Application Not Found -- Press the button to restart", MsgBoxStyle.OkOnly)
       Exit Sub
      End If
      '=====If the list of child is not filled, Fill it
      If ListOfChild.Count = 0 Then
       Dim NotUsed As Boolean = EnumChildWindows(Window_hWnd, Callback, Nothing)
      End If
      '=====Get the length of the text
      Dim MaxTextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero) + 1
      '=====Create an unmanaged array
      Dim Pointer As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(MaxTextLength)
      '=====Get the text in the unmanaged array
      Dim TextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXT, CType(MaxTextLength, IntPtr), Pointer)
      '=====Create a managed array
      Dim ByteString(MaxTextLength) As Byte
      '=====Copy the unmanaged array to the managed on
      System.Runtime.InteropServices.Marshal.Copy(Pointer, ByteString, 0, TextLength)
      '=====Destroy the unmanaged array
      System.Runtime.InteropServices.Marshal.FreeHGlobal(Pointer)
      '====Let only use the last 300 characters
      Dim StartIndex As Integer = 0
      If TextLength > 300 Then StartIndex = TextLength - 300
      '=====make a string using the array
      Dim theText As String = ""
      For x As Integer = StartIndex To TextLength
       theText &= Chr(ByteString(x))
      Next
      '===If the text has changed
      If OldText <> theText Then
       '===make an array that contains the transactions
       Dim Transactions() As String = theText.Split(New String() {"Transaction "}, StringSplitOptions.RemoveEmptyEntries)
       'Keep only the last transaction
       Dim TempText = Transactions.Last
       '===The new text becomes the old text
       OldText = theText
       '===Raise the new event because the text has changed
       RaiseEvent NewTransactionAdded(Me, New NewTransactionEventArgs(TempText))
      End If
     End Sub
    
     '======== COM Region
     Private Declare Function SendMessageA Lib "user32" (ByVal hwnd As IntPtr, _
           ByVal wMsg As Integer, _
           ByVal wParam As IntPtr, _
           ByVal ByVallParam As IntPtr) _
           As Integer
    
     Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, _
               ByVal lpWindowName As String) _
               As IntPtr
    
     Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As IntPtr, _
            ByVal lpEnumFunc As EnumChildWindowsCallBack, _
            ByVal lParam As Integer) _
            As Boolean
    
     Private Const WM_GETTEXT As Integer = &HD
     Private Const WM_GETTEXTLENGTH As Integer = &HE
    
    End Class
    '
    '
    '===========This is the New Event Argument=============
    Class NewTransactionEventArgs : Inherits EventArgs
     Public NewText As String
     Public Sub New(ByVal S As String)
      NewText = S
     End Sub
    End Class
    
    • Edited by Crazypennie Tuesday, November 23, 2010 4:36 PM Pennie
    • Marked as answer by Buyer_SA Friday, November 26, 2010 9:05 AM
    Tuesday, November 23, 2010 4:34 PM

All replies

  • 1)The handle for the window will change each time this third party will be restarted.

    You can use the API "FindWindow" to find this this handle programaticaly

    2) once you have this handle, you need to do a serie of call to the API "EnumChildWindows" to go down in the tree Parent/Child of the application until you find the handle of the control that hold text you want

    3) then, you need to create an unmanage array and send to the handle of this control a window message "WM_GETTEXT" with in the lParam the intptr value returned from the marshal function you used to create the unmanage array.

    4) then You need to marshal the unmanage array to a managed one.

    5) convert this arrray to a string and put the string in your textbox

    Friday, November 19, 2010 12:23 PM
  • Hi

    I did some work on it. The  MessageBox.Show("can't find") dont show but   MessageBox.Show(childhandle.ToString) Bring up a " 0 " value

    and MessageBox.Show(WindowText(childhandle)) dont have any text in it.

    Please help

    Imports System.IO
    Imports System.Runtime.InteropServices
    Imports System.Text
    Public Class Form1
      Const WM_GETTEXTLENGTH = &HE
      Const WM_GETTEXT = &HD
      <DllImport("USER32.DLL", CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
      Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
      End Function
      <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
      Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
               ByVal childAfter As IntPtr, _
               ByVal lclassName As String, _
               ByVal windowTitle As String) As IntPtr
      End Function
      <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
      Private Shared Function SendMessage(ByVal hWnd As IntPtr, _
       ByVal Msg As UInteger, _
       ByVal wParam As IntPtr, _
       ByVal lParam As IntPtr) As Int32
      End Function
      Declare Auto Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
        ByVal wparam As Integer, ByVal lparam As System.Text.StringBuilder) As IntPtr
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim nHandle As IntPtr
        nHandle = FindWindow("QWidget", Nothing)
        Dim childhandle As IntPtr = FindWindowEx(nHandle, IntPtr.Zero, "QTool", Nothing)
        If nHandle = IntPtr.Zero Then
          MessageBox.Show("can't find")
        Else
          MessageBox.Show(childhandle.ToString)
          MessageBox.Show(WindowText(childhandle))
    
        End If
      End Sub
      Public Function WindowText(ByVal window_hwnd As IntPtr) As String
        Dim txtlen As Int32
        WindowText = ""
        If window_hwnd = 0 Then Exit Function
        txtlen = SendMessage(window_hwnd, WM_GETTEXTLENGTH, 0, _
          0)
        If txtlen = 0 Then Exit Function
        txtlen = txtlen + 1
        Dim txt As StringBuilder = New StringBuilder(txtlen)
        SendMessage(window_hwnd, WM_GETTEXT, txtlen, txt)
        WindowText = txt.ToString
      End Function
    End Class
    
    
    
    Saturday, November 20, 2010 9:05 AM
  • Not bad !!

    I'll be back home tonight, and I'll check this. and help

    Your problem is in the point 2) :  This is not an easy one to do

    the rest, 3) 4) and 5) are just a few lines of code

     

    If you can post a screen shot of your third party application, it will help me/other a lot to help you

    Saturday, November 20, 2010 1:30 PM
  • Hi  Thanks

    Cant find a way to add screen shots here.

    Class Name   QWidget

    Class Styles   00000008  CS_DBLCLKS

    Dont show Parent or Child Window

    Window Handle   004A0570

    Window Proc    Unavailable Unicode

     

    With Spy++  I can drag the + any ware on the window and only get the mansioned info there is no difference between the Chat or main screen info

    With the code I added I can get almost any text from any app but this one got me.

    If I google QWidget it looks like its part of windows Class functions thats never used by windows

    Saturday, November 20, 2010 3:50 PM
  • This is the code you need.

    At:

    STEP 1)    Replace "Test Application with the text in the title bar of your application

    STEP 3)    The index of "ListOfChild" in the SendMessage statement will have to be changed to the right one. ListOfChild is a list that contains all the controls of the 3rd party application. You have to find which index value is the textbox that you are trying to get the text from. The only way to fin it is to use the "Trial and error method" and try them all until you fin the right one

    Also, if the text you are reading may be more than 200 characters, Adjust the value of "MaxTextLength" so the text can fit  in the array

     

    Public Class Form1
    
      Private Declare Function SendMessageA Lib "user32" (ByVal hwnd As IntPtr, _
                                ByVal wMsg As Integer, _
                                ByVal wParam As IntPtr, _
                                ByVal ByVallParam As IntPtr) _
                                As Integer
    
      Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, _
                                            ByVal lpWindowName As String) _
                                            As IntPtr
    
      Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As IntPtr, _
                                  ByVal lpEnumFunc As EnumChildWindowsCallBack, _
                                  ByVal lParam As Integer) _
                                  As Boolean
    
      Private Callback As New EnumChildWindowsCallBack(AddressOf EnumChildWindowsProc)
      Delegate Function EnumChildWindowsCallBack(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
      Private ListOfChild As New List(Of IntPtr)
      Private Const WM_GETTEXT As Integer = &HD
    
    
      Private Function EnumChildWindowsProc(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
        ListOfChild.Add(Child_hWnd)
        Return True
      End Function
    
    
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
        'STEP 1)
        'get the third party main window handle
        'Replace "Test Application" with the text in the 3rd party application title bar
        Dim Window_hWnd As IntPtr = FindWindow(Nothing, "Test Application") '<=Replace "Test Application"
    
        'STEP 2)
        'Using a callback function and "EnumChildWindows", 
        'get the handle of all the children and grand children of the window
        Dim NotUsed As Boolean = EnumChildWindows(Window_hWnd, Callback, Nothing)
    
        'STEP 3)
        'get the text
        'You will have to find the right index to use for ListOfChild in the "SendMessage"
        'The only way will be using the "Trial and error" method
        'Unless some controls get added and removed dynamicly in that 3rd party application, the
        'index should always remain the same
        'MaxTextLength has 201 bytes : 200 char max + terminating null character.
        Dim MaxTextLength As Integer = 200
        Dim Pointer As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(MaxTextLength) '
        Dim TextLength As Integer = SendMessageA(ListOfChild(5), WM_GETTEXT, CType(MaxTextLength, IntPtr), Pointer)
    
        'STEP 4)
        'Bring the unmanaged array to a managed one
        Dim ByteString(MaxTextLength) As Byte
        System.Runtime.InteropServices.Marshal.Copy(Pointer, ByteString, 0, TextLength)
        System.Runtime.InteropServices.Marshal.FreeHGlobal(Pointer)
    
        'STEP 5)
        'Recreate the string that contains the text
        Dim theText As String = ""
        For x As Integer = 0 To TextLength
          theText &= Chr(ByteString(x))
        Next
        Label1.Text = theText
      End Sub
    End Class
    • Edited by Crazypennie Sunday, November 21, 2010 2:44 AM Pennie
    Sunday, November 21, 2010 2:19 AM
  • Also. since you will be reading the text many times,

      Don't forget to clear the "ListOfChild" list before each new reading if you reuse the entire code.

    However, You could keep this list of child and just repeat the Steps 3, 4 and 5 for the subsequent reading. (Step 1 and 2 are only required the first time since they are used the create the ListOfChild and repeating them will always give the same list [Unless that the application creates and remove some controls dynamicaly])

    Sunday, November 21, 2010 3:24 AM
  • Thanks this helps a lot

    One thing I don’t understand is this

    'You will have to find the right index to use for ListOfChild in the "SendMessage"

    Dim TextLength As Integer = SendMessageA(ListOfChild(5), WM_GETTEXT, CType(MaxTextLength, IntPtr), Pointer)

     

    I know the answer is in (5)

    I take it that all the code stay the same “ SendMessageA(ListOfChild(5)”

    I just need to change the (5) to the right index.

                Where do I start looking, Listofchild “Private ListOfChild As New List(Of IntPtr)”  was create here. But how do the index look like is it between 0 and 100 is it numbers and character in the index. How do you look for something you can’t see.

              

    I know it’s a stupid question to ask you and you must be laughing. But it not the hard stuff that gets us “mortals” it’s the simple stuff that never get discussed or explained. Hard stuff you Google and 1000 WebPages popup Simple stuff not 1

     

    I really appreciate your help and thank again for your trouble

     

    Sunday, November 21, 2010 10:17 AM
  • ListOfChild is a list that contains the handle of all the controls on your third party Form

    Let say the there is 10 controls on that form, you will then have 10 handles in the list. However, to find which one is the handle of the particular control you want to read the text , you have it to try each one

        SendMessageA(ListOfChild(0)” ... SendMessageA(ListOfChild(1)” ... SendMessageA(ListOfChild(2)” ... SendMessageA(ListOfChild(3)” ...

    until you get to the right one. After you find it, the index should always be the same.

    This will work for 99% of the application. The only time the index will change, is when that 3rd party application does add or remove some controls progarmaticly. If it is the case, there is some API that can be called to get some hint to find which one is the right one (GetWindowInfo). If you need to use GetWindowInfo, let me know, I'LL help with that.(but I will need a screen shot of your application to help)

     

     

     

     

    Sunday, November 21, 2010 1:02 PM
  • I thought so thanks

     

    For me to get the index I need to get this right first. To see if label1 retrieve the text from the textbox 

     

      'STEP 5)
        'Recreate the string that contains the text
        Dim theText As String = ""
        For x As Integer = 0 To TextLength
          theText &= Chr(ByteString(x))
        Next
        Label1.Text = theText
      End Sub
    End Class

     

    As I mentioned the text I’m looking for scroll up the textbox and most is just garble

    The text I’m looking for is in this format “Client” is a constant and the two (  ) are constant the rest change.  

    ·          

    ·         Client John (10year)              'is displayed, it takes the 10y and place it in Label1

    Client Ann (22year 7Street)  ‘is displayed, it takes the 22y & place it in Label2 7St  in Label3

     

    I have to search the text for “ ) “ then retrieve the 10y

    set Label1 = to 10y    “but it could be 8y or any 8m or 5d”

     

    ASCII code for “)“ is “ 41 “

    strChar = Chr$(41)    But the length of “Client John” can change I can’t use strSubstr = Left$ will have to use Right$ But I can’t count the characters because “10year could be 9year”

     

    I will have to find a way to get the (???????) and then set Label1 to ??

    And

    (????  ????)  and then set Label2 to ??  and Label3 to ??

     

    I will figure it out but maybe you have a better way of handling this situation  

     

     

     

     

     

    Transaction 1254            ' The search have to start here Transaction 1254 the next search >1254 

    Monday, November 22, 2010 8:58 AM
  • Can you give me a few lines example of this text. That will help me to help you

    When searching to extract some words from a text, the problem is often more about what the garbage text look like.. than what the searched words look like

    Will the pathern always be:              Client XXX ( xyear XXXXX)

     

    Monday, November 22, 2010 10:31 AM
  • Hi

     

    It’s a chat window where you can sit and talk to other Buyers wail waiting for the Client information to come up.

    The text can be anything.

     

    Client: Hi jon hou are u

    Client: Good Thanks

    Client: Is Client(7y 5r 2d)                                “ This is what I want 7y= 7 year 5r=5Star rate 2d=2Deal”

    Client: and u

    Client: Any new client 2 day

     

     “ This is what I want 7y= 7 year 5r=5Star rate 2d=2Deal” But 7y can be 2d=2 Day

     

    Transaction Send 2545

    Client: Hi jon how are u

    Client: Good Thanks

    Client: Ann Is Client(7y 5r 2d)                         

    Client: and u

    Client: Any new client 2 day

    Client: no

    Client: Frank New Client(6d nr)                              “6d=6 Day nr=No Rate”

    Client: Going on Tea Break

    Client: Frank Potential Sale Client (5j)                   “9j=5Year

    Client: Talk Later

    Client: Piet Sale(5s)                                                “5 Star

    Transaction Send 2654

     

    The text can be

    Client: Frank New Client(6d nr)                    “New Client will always be in this format

    Client: Ann Is Client(7y 5r 2d)                      “Is Client will always be in this format

    Client: Frank Potential Sale Client (5j)         “Potential Sale Client will always be in this format                     

    Client: Piet Sale(5s)                                      “Sale will always be in this format         

     

    Label1 = 6d

    Label2 = nr

    Label3 = 7y

    Label4 = 5r

    Label5 = 2d

    Label6 = 5j

    Label7 = 5s

    Looking at this now can't I count from ) back and how will it look in this part of your code

      'STEP 5)
        'Recreate the string that contains the text
        Dim theText As String = ""
        For x As Integer = 0 To TextLength
          theText &= Chr(ByteString(x))
        Next
        Label1.Text = theText
      End Sub
    End Class

    I will have to clear the Labels when "Transaction Send” appear on screen with a larger number as the previous one and only use text under that for the next set of label info. The data is saved to a Database but that is working fine just fed up entering that manually if I can get the info into the labels I’m fine can handle everything from there.      

    I know it must be the most boring part of any job filling in useless info into a database but its part of my job and I can’t do anything about it except getting this to work. Or die trying.

    Monday, November 22, 2010 1:35 PM
  • I was thinking at this also.

    Since the text in your 3rd party RichTextBox is scrolling all the time, the text may get big and this may cause you a problem to set the "MaxTextLength" value

    so what you can do is to replace this line in the code:

     Dim MaxTextLength As Integer = 200
    
    

    by this one

     Dim MaxTextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero) + 1
    
    and add this
     Private Const WM_GETTEXTLENGTH As Integer = &HE
    This will make the value of MaxTextLength to adjust automaticaly the the length of the text you are reading
    • Edited by Crazypennie Monday, November 22, 2010 2:25 PM Pennie
    Monday, November 22, 2010 2:22 PM
  • This is what I thought, all your lines are similar

    How do you choose which one is the line that you need ?

    Since new lines are continualy added in the 3rd party application, when these lines are added, does your application need to automaticaly update your Label with the new values?

    These questions have to be answered before you can search in the text from the third party application

    • Edited by Crazypennie Monday, November 22, 2010 3:31 PM Pennie
    Monday, November 22, 2010 3:26 PM
  • The text will always be in this order.

     

    Transaction Send 2545

    Client: Frank New Client(6d nr)                    

    Client: Frank Is Client(7y 5r 2d)                   

    Client: Frank Potential Sale Client (5j)        

    Client: Frank Sale(5s)          

                          

     

    Transaction Send 2654

    I did put the code in a other 3p application just to see what it does and how it does things.

    It gets the text but dont update "text dont scrole".

    If we search the string for “New” then use count back we can find Label1 and Label2 Then

    search the string for “Is” then use count back we can find Label3, Label4 and Label5 Then

    search the string for “Potential Sale” then use count back we can find the Label6 Then

    search the string for “Sale(” then use count back we can find the Label7      "I add Sale"(" to distinguish between Potential Sale and Sale

     

    If the text scroll we can use “Transaction Send” to trigger the labels to update, but it will capture the same “Transaction Send” if it’s still part of the text window. The button click can start the program but after that it must be on its own. If we can replace button click with Form Load it will be nice. 

     

    Is it possible to delete all Text above “Transaction Send 2654” before update Labels on Transaction Send 2654.

    I can use Label7 to triger my application to use label data and clear all Labels before new Text capture start.

     

     

    I don't know if this is possseble or how this will look in code but it sounds logical and logical is always possible

     

     

     

    Monday, November 22, 2010 5:55 PM
  • I have fixed the above problem, sorry did not look at the placement.

    How do we get this from the text string     " New Client(6d nr)" in this code

     strTest = "New Client(6d nr)"
    
     Mid$(strTest,11, 2) = "6d"
    
     strTest = " New Client(6d nr)"
    
     Mid$(strTest,14, 2) = "nr" 
    Tuesday, November 23, 2010 5:12 AM
  • Hi

    If you could just help me get the text string in the code. I cant find a way the set Label1 to the text string.

    Thanks

     

    Tuesday, November 23, 2010 3:07 PM
  • This is how I would do that;

    You want it to automaticly update the label when a new transaction appears in the 3rd party application

    so

    A) I would be using a class to create a new objet, a "TransactionReader"

    B) I would create a new event, let call it "TransacrionReader.NewTransactionAdded". This event would be raised every time a new transaction is added in the 3rd party application

    C) I would create a new event argument "NewTransactionEventArgs" This would be the event argument of our new event. I would contains the text of the new transaction ONLY

    ie

           Transaction Send 2545

           Client: Frank New Client(6d nr)                   

           Client: Frank Is Client(7y 5r 2d)                  

           Client: Frank Potential Sale Client (5j)        

           Client: Frank Sale(5s)          

                         

    D) Since it is impossible to have the 3rd party application to notify when a new transaction is available, for the "TransactionReader" to know if there is, I would put in the TransactionReader a timer that will go at regular interval to see if a new transaction was added. Probably setting up the timer to check 2 or 4 times per seconds would be a good setting.

    E) I would use the code of  Step 1,2,3,4 from the previous post in the timer to go check. Step5 have to be different.

    F) In step 1, I would add a condition that if the 3rd party application is not found, a message tels the user to start the application and skip the rest of the code, So if it is not found or closed, your application dont crash

    G) I would also add to step 2 a condition that if the list of child already exist and is valid, that this step 2 gets skip and the existing list of child get used. (this will save a lot of processor time for both application, yours and the 3rd party one

    H) I would add to the reader 2 methods. "StartReading" and "StopReading" so you can control the reader

    I) in the step 4, I would only get the last 300 charactere from the 3rd Party application, there is no reason to get all the text, 300 caractere is more than necessary

    J) In step 5, I would use the string function "Split" to create an array of string where each string is a transaction. So, the last item of this array will be the last transaction added in the 3rd party application

    It will be something like this

            Send 2545                    <===The word "Transaction" is removed here

            Client: Frank New Client(6d nr)                   

            Client: Frank Is Client(7y 5r 2d)                  

            Client: Frank Potential Sale Client (5j)        

            Client: Frank Sale(5s)          

                         

    K) Then I would put this text in the event argument we have created and raise the event.

    L) In Form1, I would put a method that will handle this event. This method would extract the info from the text of the transaction and put it in the Label ( I will let check in the example how I did that with only one line of code) for each different Type "New", "Sale", "Is Client, etc..

    This example should work good if I understand what you need and automaticaly update your labels when a new transaction is added in the 3rd party application.

    ++++DONT FORGET TO SET THE WINDOW TEXT AND THE INDEX OF LIST OF CHILD IN MY EXAMPLE+++

     

    Public Class Form1
    
     Private WithEvents TReader As New TransactionReader
    
     '====This is for the Button to Start to check for the text
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      'Start the timer
      TReader.StartReading()
     End Sub
    
     '====Event Handler called when the text change
     Private Sub TReader_NewTransactionAdded(ByVal Sender As Object, ByVal e As NewTransactionEventArgs) _
               Handles TReader.NewTransactionAdded
    
      'make an array of all the lines in the transaction
      Dim Lines() As String = e.NewText.Split(New String() {vbCrLf}, StringSplitOptions.RemoveEmptyEntries)
      For Each line As String In Lines
       If line.Contains("New") Then
        Dim Index = line.IndexOf("("c) + 1
        Dim Info() As String = line.Substring(Index, line.Length - Index - 1).Split(" "c)
        Label1.Text = Info(0)
        Label2.Text = Info(1)
       ElseIf line.Contains("Potential") Then
        Dim Index = line.IndexOf("("c) + 1
        Label6.Text = line.Substring(Index, line.Length - Index - 1)
       ElseIf line.Contains("Sale") And Not line.Contains("Potential") Then
        Dim Index = line.IndexOf("("c) + 1
        Label7.Text = line.Substring(Index, line.Length - Index - 1)
       ElseIf line.Contains("Is Client") Then
        Dim Index = line.IndexOf("("c) + 1
        Dim Info() As String = line.Substring(Index, line.Length - Index - 1).Split(" "c)
        Label3.Text = Info(0)
        Label4.Text = Info(1)
        Label5.Text = Info(2)
       End If
      Next
     End Sub
    
    End Class
    '
    '
    '============== Class Transaction Reader =====================
    Class TransactionReader
    
     '=====Declare a delegate for the function EnumChildWindowsProc
     Delegate Function EnumChildWindowsCallBack(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
     '=====Create the new instance of the delegate for the EnumChildWindowsProc
     Private Callback As New EnumChildWindowsCallBack(AddressOf EnumChildWindowsProc)
     '=====This is the list of child controls
     Private ListOfChild As New List(Of IntPtr)
     '======Creates a new event
     Public Event NewTransactionAdded(ByVal Sender As Object, ByVal e As NewTransactionEventArgs)
     '=====Creates a timer to automaticaly check if the text changed
     Private WithEvents TheTimer As New Timer With {.Interval = 250, .Enabled = False}
     '=====This will be used for the comparaison to se if the text has changed
     Private OldText As String = ""
    
    
     Public Sub StopReading()
      TheTimer.Enabled = False
     End Sub
    
     Public Sub StartReading()
      ListOfChild.Clear()
      TheTimer.Enabled = True
     End Sub
    
    
     '===== This is the callback function
     Private Function EnumChildWindowsProc(ByVal Child_hWnd As IntPtr, ByVal lParamCalled As IntPtr) As Boolean
      ListOfChild.Add(Child_hWnd)
      Return True
     End Function
    
    
     '===== this is the timer method
     Private Sub TheTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TheTimer.Tick
      Dim Window_hWnd As IntPtr = FindWindow(Nothing, "Test Application") '<=Replace "Test Application"
      '=====This will tell the user if the 3rd party application is not found
      If CInt(Window_hWnd) = 0 Then
       TheTimer.Enabled = False
       MsgBox("Application Not Found -- Press the button to restart", MsgBoxStyle.OkOnly)
       Exit Sub
      End If
      '=====If the list of child is not filled, Fill it
      If ListOfChild.Count = 0 Then
       Dim NotUsed As Boolean = EnumChildWindows(Window_hWnd, Callback, Nothing)
      End If
      '=====Get the length of the text
      Dim MaxTextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero) + 1
      '=====Create an unmanaged array
      Dim Pointer As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(MaxTextLength)
      '=====Get the text in the unmanaged array
      Dim TextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXT, CType(MaxTextLength, IntPtr), Pointer)
      '=====Create a managed array
      Dim ByteString(MaxTextLength) As Byte
      '=====Copy the unmanaged array to the managed on
      System.Runtime.InteropServices.Marshal.Copy(Pointer, ByteString, 0, TextLength)
      '=====Destroy the unmanaged array
      System.Runtime.InteropServices.Marshal.FreeHGlobal(Pointer)
      '====Let only use the last 300 characters
      Dim StartIndex As Integer = 0
      If TextLength > 300 Then StartIndex = TextLength - 300
      '=====make a string using the array
      Dim theText As String = ""
      For x As Integer = StartIndex To TextLength
       theText &= Chr(ByteString(x))
      Next
      '===If the text has changed
      If OldText <> theText Then
       '===make an array that contains the transactions
       Dim Transactions() As String = theText.Split(New String() {"Transaction "}, StringSplitOptions.RemoveEmptyEntries)
       'Keep only the last transaction
       Dim TempText = Transactions.Last
       '===The new text becomes the old text
       OldText = theText
       '===Raise the new event because the text has changed
       RaiseEvent NewTransactionAdded(Me, New NewTransactionEventArgs(TempText))
      End If
     End Sub
    
     '======== COM Region
     Private Declare Function SendMessageA Lib "user32" (ByVal hwnd As IntPtr, _
           ByVal wMsg As Integer, _
           ByVal wParam As IntPtr, _
           ByVal ByVallParam As IntPtr) _
           As Integer
    
     Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, _
               ByVal lpWindowName As String) _
               As IntPtr
    
     Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As IntPtr, _
            ByVal lpEnumFunc As EnumChildWindowsCallBack, _
            ByVal lParam As Integer) _
            As Boolean
    
     Private Const WM_GETTEXT As Integer = &HD
     Private Const WM_GETTEXTLENGTH As Integer = &HE
    
    End Class
    '
    '
    '===========This is the New Event Argument=============
    Class NewTransactionEventArgs : Inherits EventArgs
     Public NewText As String
     Public Sub New(ByVal S As String)
      NewText = S
     End Sub
    End Class
    
    • Edited by Crazypennie Tuesday, November 23, 2010 4:36 PM Pennie
    • Marked as answer by Buyer_SA Friday, November 26, 2010 9:05 AM
    Tuesday, November 23, 2010 4:34 PM
  • Hi

     

    Fantastic piece of code THANKS. With a little tweaking I do get the text from other 3party applications and the Labels update but with this one I get this error

    Dim MaxTextLength As Integer = SendMessageA(ListOfChild(0), WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero) + 1

     

     Index was out of range. Must be non-negative and less than the size of the collection.

    Parameter name: index

     

    If tried larger number up to 100 but same error, if I deliberately place the wrong  name in "Test Application"

    It show the MsgBox("Application Not Found -- Press the button to restart", MsgBoxStyle.OkOnly)

    I take it from this that it do find the "Test Application" but that there are no child in the index.

     

    I also tried all window captions I could find associating with this program but nothing

     

    But thanks again I have a 1002 uses for this code just as it is but “the application” its suppose to work on are just too clever hiding its children "Good Mom"

     

    Is there a way or am I just wasting your time.  

     

    Wednesday, November 24, 2010 10:43 AM
  • I found this code but its not VB mybe you can use it.

    list all the children of a QWidget: can use QObject::children() which can return a list of all the children of any QWidget

    Wednesday, November 24, 2010 11:14 AM
  • So, it work with every application, BUT the one you need it to work with !!!

    Sometime it is just a matter of being a bit more clever

    I will try to help you to debug,... but it may be hard over a forum.

    -----

    Just under the line :  Dim NotUsed as Boolean = ....

    add a line and write:  Stop

    then setup your application so it try to read the 3rd party you need to read from and  start your application. It will stop on the line you just added Stop

    click anywhere on the code, so it get focus

    then hover your mouse over the word "NotUsed" . A comment will pop up with the value of "NotUsed" . tell me if it is True or false

    then hover your mouse over the word "Window_hWnd" in the line "Dim Window_hWnd as IntPtr = ..."

    Tell me the value

    and then, go up a bit and hover your mouse over the word ListOfChildren in the line "Private ListOfChildren as New List(Of IntPtr)"

    and again, tell me what it show

    -----

    Then, follow this link, it explain how to post an image on this forum. and give me a screen shut of your 3rd party application.

    http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/33bf2119-5f56-40b2-a689-d437ec09e550

     

    With all that, I may be able to help you

     

    • Edited by Crazypennie Wednesday, November 24, 2010 12:18 PM Pennie
    Wednesday, November 24, 2010 12:14 PM
  • False

    5965812

    0

    Wednesday, November 24, 2010 1:02 PM
  • Understand for the screen shut..

    We got a handle and no child .. Let me think about that a bit

    Wednesday, November 24, 2010 2:34 PM
  • I do have Olly "OllyDBG" and can look inside the application but I wont have a clue what to look for.
    Wednesday, November 24, 2010 3:56 PM
  • If you have Olly, try to see if the form calls the API "CreateWindow" --- If not, try to locate what does ---

    Is the control on a MDI form ?

    Wednesday, November 24, 2010 4:38 PM
  • Windows                 I did this to protect the Company Name  xxxxxxxxxxxxxx

     

    Handle         Title                             Parent     WinProc    ID         Style      ExtStyle   Thread     ClsProc    Class

    000604C4       xxxxxxxxxxxxxx - Logged In As B  Topmost                          17CF0000   00000100   Main       FFFF0489   QWidget

    E001C0112      Default IME                       000604C4                         8C000000              Main       76F93A04   IME

    NE00130570     MSCTFIME UI                       001C0112                         8C000000              Main       FFFF0479   MSCTFIME UI

    000606C2                                         Topmost                          04C00000   00000100   00000F74   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E00190672      Default IME                       000606C2                         8C000000              00000F74   76F93A04   IME

    000705F2       Last Client History                 Topmost                          06CF0000   00000100   Main       FFFF0489   QWidget

    000A013E                                         Topmost                          04C00000   00000100   00000B08   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E005E0586      Default IME                       000A013E                         8C000000              00000B08   76F93A04   IME

    000A0174                                         Topmost                          04C00000   00000100   00000CF4   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E005006F2      Default IME                       000A0174                         8C000000              00000CF4   76F93A04   IME

    000A02DE                                         Topmost                          04C00000   00000100   0000058C   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E000A05A6      Default IME                       000A02DE                         8C000000              0000058C   76F93A04   IME

    000B0592       xxxxxxxxxxxxxx                    Topmost                          06CF0000   00000100   Main       FFFF0489   QWidget

    00120560                                         Topmost                          04C00000   00000100   Main       FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    001405A0       MCI command handling window       Topmost                          04C00000   00000100   0000096C   736D27FA   #43

    E001602C2      Default IME                       001405A0                         8C000000              0000096C   76F93A04   IME

    001605B4                                         Topmost                          04C00000   00000100   000007DC   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E00180528      Default IME                       001605B4                         8C000000              000007DC   76F93A04   IME

    001705DA                                         Topmost                          04C00000   00000100   00000EE0   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E001D055E      Default IME                       001705DA                         8C000000              00000EE0   76F93A04   IME

    001A052A       P C 1363                Topmost                          16CF0000   00000100   Main       FFFF0489   QWidget

    001A058C       ?????n???                         Topmost                          04C80000   00000100   Main       007A9270   FTCLobby

    00210542                                         Topmost                          04C00000   00000100   000008F8   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E004B0716      Default IME                       00210542                         8C000000              000008F8   76F93A04   IME

    003F0332       xxxxxxxxxxxxxx                     Topmost                          06CF0000   00000100   Main       FFFF0489   QWidget

    00500282                                         Topmost                          04C00000   00000100   00000B7C   FFFF077D   QEventDispatcherWin32_Internal_Widget1729040032

    E001E04EC      Default IME                       00500282                         8C000000              00000B7C   76F93A04   IME

     

     

    ?????n???   This was not me

    P C 1363                Topmost    is the one where the chat window is, I used it when we run the dubug I THINK                    

     

    Wednesday, November 24, 2010 6:56 PM
  • QWidget !!!

    Is your application is a web application?

    Thursday, November 25, 2010 1:29 AM
  • The program is installed on my pc. But we have offices in a lot of countries all over. I think the application use the internet, and some clients contact us using the internet. I think that’s why we enter new data in by hand to keep it out of the wrong hands. But if it’s a second application like this one (Mine) it’s not part of the main program and don’t hold any risks. No one will even know about the application and it only grabs junk data from a chat window.    

    That’s the other problem if I move from 1 country say USA to China the title name change cant we get the title by grabbing top window.

    Thursday, November 25, 2010 5:23 AM
  • I have no experience with Qt application

    What I think you should do is to go to the Qt developper forum. You sould get an answer there

    http://developer.qt.nokia.com/forums

    You may also get an answer in a C++ forum,(QWidget was associated with C++ for a long time)

    In both of these forum, you will probably get code sample in C++

    If it is the case and need translation, just come back here, I'll help you with that

    Thursday, November 25, 2010 4:43 PM
  • Thanks for all your trouble and effort, will try.
    Thursday, November 25, 2010 6:00 PM
  • Hi      Here are 4 ways but its not VB do this help

    ========

    QWidget* widget = QWidget::find((WId)hwnd); QListView* listview = (QListView)widget; QAbstractItemModel model = listview->model();

    ========

    for (int i = 0; i < model->rowCount(); ++i) { QString s = model->index(i, 0).data(Qt::DisplayRole).toString(); System::String^ string = gcnew System::String((wchar_t*)s.utf16()); chatLines->Add(string); }

     ========

    code: HWND hwnd = FindWindow(L"QWidget", NULL); QWidget* widget = QWidget::find((WId)hwnd); QListWidget* chatBox = (QListWidget*)widget; // I've found that QListWidget is easier to use than QListView

     ========

    using QObject::children() which returns, you guess it, a list of all the children of any QObject.

    I've tested it and it works like a charm :)

    ========

    Thursday, November 25, 2010 7:20 PM
  • Dim widget As Pointer(Of QWidget) = QWidget.find(DirectCast(hwnd, WId)) 
    Dim listview As Pointer(Of QListView) = DirectCast(widget, QListView) 
    Dim model As QAbstractItemModel = listview.model() 
    
    Saturday, November 27, 2010 8:10 AM
  • Please start a new thread to ask a new question.

    This is required by the forum rules

    Saturday, December 4, 2010 6:48 PM
  • You posted your question in the "Off Topic" forum ... Cannot answer you in that forum.

    Please post your question in "Visual Basic General" forum

    Saturday, December 4, 2010 9:44 PM



  • I have the same problem
    Please send me address the issue in vb. Net or help me . :)

    I really need help

    i can't read text of Qwidget class

    and can't sendmessage to press button on this class

    but in other application  no problem .

    please help me .

    tnx

















    Wednesday, August 1, 2012 5:56 AM