1

Here is my dilemma. I have a program that uses SendMessage to get text from a chat program. Now when I did this in Visual Basic I just put the SendMessage in a loop and whenever the chat was updated so would my output.

This is not the case with C++. When I put the SendMessage in a loop it loops forever. Say the Chat is something like:

Apples
Bananas
Cheerios

Now lets say I run my program it finds the text and starts looping. Now in Visual Basic it would keep looping until it hit Cheerios and it would stop and wait until someone in the chat typed something else.

With C++ it would output:

Apples
Bananas
Cheerios
Apples
Bananas
Cheerios
...

And go on forever. Is there a way to stop this? I was thinking along the terms of EOF but that wouldn't help because as soon as it hit the last line it would go out of the loop and wouldn't pick up anything else that people typed in chat.

Here is the portion of the code that gets the text. Now not that I do have an loop that won't end until I set bLoop to True.

cout << "   + found RichEdit20W window at: " << hwndRichEdit20W << endl << endl;
cout << "- get text " << endl;
bool bLoop = false;
int textLen = (int)SendMessage(hwndRichEdit20W, WM_GETTEXTLENGTH, 0, 0);

while (bLoop == false)
{

    const int MAXSIZE = 32678;
    wchar_t szBuf[MAXSIZE];

    SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);

    wcout << szBuf;

}

Thanks for any help!

VB CODE UPDATE

This is how I did it in my VB program. This is for a game console and not a chat window but it should be the same concept right?

Note Do While PARENThwnd <> IntPtr.Zero The handle is never 0 so it will be an infinite loop.

Private Sub bwConsole_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwConsole.DoWork
    Dim PARENThwnd As IntPtr
    Dim CHILDhwnd As IntPtr
    PARENThwnd = FindWindow(Nothing, "ET Console")

    If PARENThwnd = IntPtr.Zero Then
        txtConsole.Text = "ET Console is not availble."
    Else
        Do While PARENThwnd <> IntPtr.Zero
            CHILDhwnd = GetDlgItem(PARENThwnd, 100)

            Dim Handle As IntPtr = Marshal.AllocHGlobal(32767)

            Dim NumText As Integer = CInt(SendMessage(CType(CHILDhwnd, IntPtr), WM_GETTEXT, CType(32767 \ Marshal.SystemDefaultCharSize, IntPtr), Handle))

            Dim Text As String = Marshal.PtrToStringAuto(Handle)

            txtConsole.Text = Text

            Marshal.FreeHGlobal(Handle)

            txtConsole.SelectionStart = txtConsole.TextLength
            txtConsole.ScrollToCaret()

            rtxtDefinition.Text = ""
            Call GetDefinitions(txtConsole.Text)
        Loop
    End If
End Sub

Also will not be able to answer any questions until later tonight. Off to work.

3 Answers3

1

You never update your bLoop variable, causing the loop to run infinitely. If you want to break your loop, implement a loop termination condition that changes the value of bLoop.

A condensed version of your code does the following:

bool bLoop = false;
while ( bLoop == false ) {
    // This loop never terminates, unless bLoop is set to true
}

It is somewhat hard to understand, why you are looping to begin with. I suppose you hope the SendMessage call would somehow guess that you don't want it to run, until some condition is met. There is no such magic built into functions.

To respond to text changes you should choose a different solution altogether: An event-based model. The supported way to implement this is through UI Automation.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • From "Now not that I do have an loop that won't end until I set bLoop to True." it seemed as though the OP was aware of his loop condition... – GnomeDePlume Jan 05 '14 at 16:07
  • 1
    @Gnome That may or may not be the case. The question *"Is there a way to stop this [the loop]?"* is answered by *"Implement a loop termination condition"*. I also provided a solution how to properly implement this. – IInspectable Jan 05 '14 at 16:13
  • @IInspectable I know that the loop doesn't end. I had it programmed that way because that was how I did it in my VB program. It would constantly update so that when anything was typed it would automatically update. I just want to know is there a way with `SendMessage` to know if a line has been added to the chat window I'm hooked onto. And if so how do I just add that 1 line and not loadthe entire thing again. – Unreliaable Jan 05 '14 at 16:19
  • @Unreliaable As I said: Use UI Automation. Any other way? Not that I know of. – IInspectable Jan 05 '14 at 16:21
1

The first thing you need to understand is that the two versions of the code are doing very different things with the contents of the chat window. The VisualBasic version is taking that text and stuffing it into a text control. This has the effect of replacing the existing text with the new text from the chat window so you never see duplicates. The C++ version outputs the text to the output stream (console) each time it retrieves it. Unlike the text control used in the VB version the output stream appends the text rather than replaces it.

To get around this you need to keep tract of the previous text retrieved from the chat window and only output the difference. This may mean appending new text from the chat or outputting the entire string. The code below maintains a string buffer and manages appending or replacing the history of the chat. It also creates a new string containing only the differences from the last change allowing you to easily deal with updates.

class ChatHistory
{
    std::wstring    history;
public:

    std::wstring update(const std::wstring& newText)
    {
        const std::wstring::size_type historySize = history.size();
        if(newText.compare(0, historySize, history.c_str()) == 0)
        {
            history.append(newText.c_str() + historySize, newText.size() - historySize);
            return history.c_str() + historySize;
        }
        else
        {
            history = newText;
        }

        return newText;
    }
};

Below are the necessary changes to your code to use it. I haven't had a chance to test it with your exact set up but it should work.

ChatHistory history;
while (bLoop == false)
{

    const int MAXSIZE = 32678;
    wchar_t szBuf[MAXSIZE];

    SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);

    std::wcout << history.update(szBuf);
}
Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74
  • +1 Nice! I can't argue with that :D @Unreliaable Give my version a shot if that doesn't work out / you're feeling retro. – GnomeDePlume Jan 05 '14 at 22:32
  • @Captain Obvlious It works really nice. It cuts of the last character of the last work but I will work on fixing that. Also for some reason it does not want to update when new text is typed but I will work on that too. Thanks for the code! – Unreliaable Jan 06 '14 at 01:52
0

In both VBA and C++ that structure will cause an infinite loop.

It sounds like you want an infinite loop, but don't want an infinite print out of the contents of the buffer.

As @IInspectable has pointed out (thanks!) sendmessage will copy the contents of the chat program into the buffer at every iteration of the loop.

So, either purge the chat window or let the program know what content is new and needs to be printed.

I'm on a linux box at the moment, so can't test this very easily. Please let me know if the following works...

#include <cwchar> // so we can use wcschr function

const int MAXSIZE = 32678;     // moved outside of the loop
wchar_t szBuf[MAXSIZE];        // moved outside of the loop
szBuf[0] = L'\0';              // set sentinel (wide NULL!)
wchar_t* end_printed_message = szBuf; 
//pointer to the end of the content that has been printed

while (bLoop == false)
{

    SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);

    wchar_t* end_message_buffer = wcschr(szBuf, L'\0'); // find end of the string using wchar version of strchr()

    if(end_printed_message != end_message_buffer){   // if the buffer contains something new
        wcout << end_printed_message;                // print out the new stuff (start at old end)
        end_printed_message = end_message_buffer;    // update the pointer to the end of the content
    }
}

I don't know what headers you've already included, hence the use of 'ol faithful <cstring>

As an aside (yes, I will still bang on about this) mixed use of (w)cout is generally bad and may cause problems.

Community
  • 1
  • 1
GnomeDePlume
  • 1,642
  • 2
  • 15
  • 31
  • If i use only `cout` and not `wcout` then I get a bunch of random numbers since it is in `UNICODE` and they are wide-characters – Unreliaable Jan 05 '14 at 16:24
  • as @IInspectable points out, UI Automation is probably the correct way to go. But... if you want a 'dirty' solution (and running inside an infinite loop will probably fall inside that category) then you can probably use your approach, but changing what gets printed out on every iteration of the loop... – GnomeDePlume Jan 05 '14 at 16:27
  • Uhm... what's this supposed to do? You just wrote your contents to cout. Setting the buffer's first character to `NUL` isn't going to do anything. Besides, `WM_GETTEXT` will overwrite the buffer anyway... – IInspectable Jan 05 '14 at 16:28
  • You only want to print out szBuf if it contains new text. If the contents of szBuf persists then every loop will print that out. Setting the first character of the array to NUL will act as a sentinal, denoting an empty string. You raise a good point - if the buffer gets filled with the entire chat contents every loop cycle then clearing the buffer this way won't do anything. – GnomeDePlume Jan 05 '14 at 16:32
  • Every loop will call `SendMessage` with `WM_GETTEXT`, essentially retrieving the entire contents. Neither of your proposed solutions is. – IInspectable Jan 05 '14 at 16:41
  • So if SendMessage re-fills the entire buffer (and we can't clear the chat window) then the best thing seems to be to look at the buffer an detect new text... – GnomeDePlume Jan 05 '14 at 18:09
  • @GnomeDePlume I was going to try your code but `strchr` gives me `Error: no instance of overloaded function "strchr" matches the argument list argument types are:(wchar_t[32678], char)` Will also mess with this code to see if I can make it work. – Unreliaable Jan 06 '14 at 01:55
  • Oh yes - we're using wchar. Try using `#include ` then `wcschr(szBuf, L'\0')`. We'll also need to change the sentinel to a wchar - the `szBuf[0] = L'\0'`. I'll update my answer... – GnomeDePlume Jan 06 '14 at 09:41
  • @Unreliaable I've tested this with wcin (since I don't have access to SendMessage on my machine) and it seems to work. – GnomeDePlume Jan 06 '14 at 11:08
  • @GnomeDePlume Yeah I don't know why it doesn't want to work... I mean it finds the text and stops at the end but it doesn't update when new next is typed. Quite odd since when I add breakpoints it is going through the loop. – Unreliaable Jan 06 '14 at 14:24
  • @Un That's Bizarre. If you're using a debugger (do!) can you check that the contents of `szBuf` changes when new text is added? If you're not using a debugger, you could add a `wcout << L"szBuf value: " << szBuf << endl;` in the loop to check that the buffer is changing. You could also add (labelled) print statements to check that each of the pointers is being correctly updated. – GnomeDePlume Jan 06 '14 at 16:08
  • @Un P.S. Please do upvote this answer if it has been helpful (and save me the shame of negative reputation!). You can upvote multiple answers and then accept the one that gets you the best working solution. – GnomeDePlume Jan 06 '14 at 16:16
  • @GnomeDePlume I can't upvote yet but since I am getting further with yours I will mark. It is still cutting off the last charater for some reason and when debugging it the `end_message_buffer` and the `end_printed_buffer` stay the same even though it goes through the loop. – Unreliaable Jan 06 '14 at 16:40
  • Thanks! The missing last character is strange. The two pointers will only differ between `wcschr` and the `if` statement when new text has been added. I'd try to chase the problem down by checking that (1) the message inside `szBuf` definitely changes when new text is added to the chat window. (2) When it changes, `wcschr` workes as expected and the `end_message_buffer` pointer is incremented past `end_printed_message`. If we manage to fix it I'll tidy up the answer... – GnomeDePlume Jan 06 '14 at 16:53