How to minimize lines lose while retriving room text

Here you can talk about C++ And C# And Other languages programming.

Moderator: Departure

How to minimize lines lose while retriving room text

Postby Chike » Sat Jan 19, 2008 12:15 am

Follows 2 functions in C++ that attempt to accomplish this goal, and the way to use them.
Code: Select all
// 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] = '\0';
      } else {
         text_buff[0] = '\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.
Code: Select all
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.
Code: Select all
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):
Code: Select all
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.

Image
Chike
imFiles Master
imFiles Master
 
Posts: 511
Joined: Sun May 13, 2007 11:20 pm

Re: How to minimize lines lose while retriving room text

Postby usenet7 » Tue Jan 05, 2010 5:12 am

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?
usenet7
imFiles Newbie
imFiles Newbie
 
Posts: 1
Joined: Tue Jan 05, 2010 4:53 am

Re: How to minimize lines lose while retriving room text

Postby Chike » Tue Jan 05, 2010 8:53 am

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.
Image
Chike
imFiles Master
imFiles Master
 
Posts: 511
Joined: Sun May 13, 2007 11:20 pm

Re: How to minimize lines lose while retriving room text

Postby String » Wed Jan 06, 2010 2:48 am

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?
String
imFiles Senior
imFiles Senior
 
Posts: 189
Joined: Mon Mar 10, 2008 12:06 pm

Re: How to minimize lines lose while retriving room text

Postby Chike » Wed Jan 06, 2010 6:59 am

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.
Attachments
dlls.zip
(12.04 KiB) Downloaded 4 times
Image
Chike
imFiles Master
imFiles Master
 
Posts: 511
Joined: Sun May 13, 2007 11:20 pm

Re: How to minimize lines lose while retriving room text

Postby String » Thu Jan 07, 2010 8:25 am

Thanks a lot Chike......
String
imFiles Senior
imFiles Senior
 
Posts: 189
Joined: Mon Mar 10, 2008 12:06 pm

Re: How to minimize lines lose while retriving room text

Postby Chike » Thu Jan 07, 2010 10:43 am

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:
Code: Select all
      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:
Code: Select all
      text_delegate^ textHandler;
      text_delegate^ textCallback;
      GCHandle gchTextCallback;


Delegate function decleration in the form:
Code: Select all
private: void HandleText(const char *text) {
          // text handling code here
      }
private: void TextCallback(const char *text) {
          array<Object^>^ params = {IntPtr(const_cast<char *>(text))};
          Invoke(textHandler, params);
      }


Initialize delegates:
Code: Select all
         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:
Code: Select all
         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:
Code: Select all
          if (PalllibConnectPal(hwnd) == 0) {
          // success
          }



Detache the library:
Code: Select all
          PallibAppDeinit();
          PallibAppInit(NULL); // re-initialize, dirty I know


Connect to chat room, one room only:
Code: Select all
          char *str = static_cast<char *>(
             Marshal::StringToHGlobalAnsi(roomName).ToPointer());
          PalllibConnectRoom(str);
          Marshal::FreeHGlobal(IntPtr(str));


Disconnect room:
Code: Select all
          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:
Code: Select all
          PallibAppDeinit();
          PallibAppInit(NULL);
Image
Chike
imFiles Master
imFiles Master
 
Posts: 511
Joined: Sun May 13, 2007 11:20 pm


Return to C++, C# And Others Programming

 


  • Related topics
    Replies
    Views
    Last post

Who is online

Users browsing this forum: No registered users and 0 guests