Skip to content

How to minimize lines lose while retriving room text

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #191158
    Chike
    Member

    Follows 2 functions in C++ that attempt to accomplish this goal, and the way to use them.

    // last_char_index
    // parameters:
    // in: richedit control HWND
    // returns: index of last (empty) line
    int last_char_index(HWND rich20)
    {
    int index, lines;
    do {
    if (!IsWindow(rich20)) {
    // invalid window handle
    return -1;
    }
    lines = (int) ::SendMessage(rich20, EM_GETLINECOUNT, 0, 0);
    index = (int) ::SendMessage(rich20, EM_LINEINDEX, lines - 1, 0);
    // loop till no change was changed between the 2 calls
    } while (index == -1 || lines != (int) ::SendMessage(rich20, EM_GETLINECOUNT, 0, 0));
    return index;
    }
    
    // get_next_line
    // parameters:
    // in: richedit control HWND
    // in/out: bext index to get line from
    // out: buffer for line text out
    // in: buffer size
    // returns: true if lines added and text retrived, false otherwise
    bool get_next_line(HWND rich20, int& last_index, char *text_buff, int text_buf_size)
    {
    // last line char index
    int index = last_char_index(rich20);
    // check if text was cleared or haven't changed
    if (index <= last_index) {
    if (index < last_index) {
    // text was cleared
    last_index = index;
    }
    return false;
    }
    int line, next_index;
    do {
    // last index point to the bext line we need read
    line = (int) ::SendMessage(rich20, EM_EXLINEFROMCHAR, 0, last_index);
    // set the dirst word of the buffer to it's size - 1
    // to reserve one byte for null char
    *((WORD*) text_buff) = text_buf_size - 1;
    int len = (int) ::SendMessage(rich20, EM_GETLINE, line, LPARAM(text_buff));
    // the buffer returned is not null terminated unless there's no text
    // sset the null terminator anyway
    if (len > 0) {
    text_buff[len] = '�';
    } else {
    text_buff[0] = '�';
    }
    // get character index for the begining of next line
    next_index = (int) ::SendMessage(rich20, EM_LINEINDEX, line + 1, 0);
    // check if text was cleared
    // without this we may loop forever
    if (last_char_index(rich20) < index) {
    // part of the room text was cleared
    // you may add code here to set last_index to point few lines back
    return false;
    }
    // keep looping if last_index's line changed
    } while (line != (int) ::SendMessage(rich20, EM_EXLINEFROMCHAR, 0, last_index));
    last_index = next_index;
    return true;
    }

    You start by calling last_char_index() when you 1st connect to the room saving the result for further use.
    int my_last_index;
    ….

    my_last_index = last_char_index(rich20);

    Later on in the timer_tick function (or whenever you process room text) call get_next_line() in a loop untill tgere are no more lines left.
    Note that my_last_index is passed by reference and updated with every successful call, or when the text is cleared.

    char line_text[1024]; while (get_next_line(rich20, my_last_index, line_text, sizeof(line_text))) { // process the line here }

    It appears that when the room text grow to near 250k characters, lines are deleted from the beggining of the room text. I cannot see any way around it other to clear the room text completely at that point. Something that will look like this (afrer the loop in the code above):
    if (my_last_index > 240000) { ::SendMessage(rich20, WM_SETTEXT, 0, LPARAM(“”)); }

    I have only tested it briefly with version 9.2 build 236, but it seems to work well even if the text is manually freezed and scrolls fast when resumed.

    Duplicates are possible, but only when resizing the room, and I havn’t observed any.

    I will add that with subclassing most of the cheks done in this code are not needed.

    #191164

    I’m glad I checked this forum and I need to do this exact same thing in vb so this will save me a lot of time. I’m worried about text loss when paltalk clears some text as the room text gets too large. What you have seems to allow for missing lines at that point. The alternative is for you to delete all the lines as you say but that means you have to make sure that no text gets into the room after you check it and before you delete all text. Have you had any experience with it in those cases and are any lines lost?

    #191163
    Chike
    Member

    @usenet7 wrote:

    Have you had any experience with it in those cases and are any lines lost?

    I don’t use this method, nor VB. My dll subclasses the room text control and send the text to the application as it is inserted, track deleted lines, and never miss a line.
    Of course I was aware of races therefore the minimize and not eliminate.

    #191162
    String
    Member

    @Chike wrote:

    My dll subclasses the room text control and send the text to the application

    Is this dll available for download and can it be used within a vb program?

    #191161
    Chike
    Member

    @String wrote:

    @Chike wrote:

    My dll subclasses the room text control and send the text to the application

    Is this dll available for download and can it be used within a vb program?

    Attached the latest DLL I use.
    It should be possible to be used in VB though it’s not trivial nor clean.
    Only one window can be monitored, by one application only.
    The application “connects” to a room window (haven’t tested IMs) and recives events that are sent over a pipe to a native thread that calls the callbacks supplied by the application.
    I don’t use the handle_text callback but last time I tested it it workd fine.

    #191160
    String
    Member

    Thanks a lot Chike……

    #191159
    Chike
    Member

    @String wrote:

    Thanks a lot Chike……

    Don’t thank me yet, it’s not just going to work.

    Since the callbacks are called from a native thread, delegates are needed.
    Type decleration:

    delegate void disconnect_delegate(void);
    
    delegate void text_delegate(const char *text);
    
    delegate void user_delegate(const char *name, int state);

    The following example will only show the steps nesessary for the text callback.
    You can have one delegate and use InvokeRequired or have 2, one that is called from the thread and the other invoked ny the 1st. I use the second method.

    Variables decleration:

    text_delegate^ textHandler;
    
    text_delegate^ textCallback;
    
    GCHandle gchTextCallback;

    Initialize delegates:

    textHandler = gcnew text_delegate(this, &Form1::HandleText);
    
    textCallback = gcnew text_delegate(this, &Form1::TextCallback);
    
    gchTextCallback = GCHandle::Alloc(textCallback); // prevent garbage collection from moving in memory

    Initialize library once, subsequent calls with NULL as parameter:

    pal_lib_callbacks callbacks = {NULL, NULL, NULL};
    
    callbacks.handle_text = (void (__stdcall *)(const char *))
    
    Marshal::GetFunctionPointerForDelegate(textCallback).ToPointer();
    
    PallibAppInit(&callbacks);

    Note: only the callbacks that were set at first call will be called.
    Attache the library with paltalk instance:

    if (PalllibConnectPal(hwnd) == 0) {
    
    // success
    
    }

    Detache the library:

    PallibAppDeinit();
    
    PallibAppInit(NULL); // re-initialize, dirty I know

    Connect to chat room, one room only:

    char *str = static_cast(
    
    Marshal::StringToHGlobalAnsi(roomName).ToPointer());
    
    PalllibConnectRoom(str);
    
    Marshal::FreeHGlobal(IntPtr(str));

    Disconnect room:

    PalllibDisconnectRoom(hwndPalRoom);

    Note: having the room handle to the room that was connected is up to you.

    Disconnect callback will be called if paltalk have been closed, call:

    PallibAppDeinit();
    
    PallibAppInit(NULL);
Viewing 7 posts - 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.