Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2006.06.04;
Скачать: [xml.tar.bz2];

Вниз

GetMessage в отдельном потоке   Найти похожие ветки 

 
msg   (2006-03-09 12:11) [0]

В отдельном потоке (TThread) создаю форму:

procedure TTest.Execute;
var
msg:TMsg;
wnd:HWND;
wc:WNDCLASS;
begin
wc.style:=0;
wc.cbClsExtra:=0;
wc.cbWndExtra:=0;
wc.hInstance:=hInstance;
wc.hIcon:=0;
wc.hCursor:=0;
wc.hbrBackground:=GetStockObject(LTGRAY_BRUSH);
wc.lpszMenuName:=nil;
wc.lpszClassName:="asdasd";
wc.lpfnWndProc:=@WndProc;

Windows.RegisterClass(wc);
wnd:=CreateWindow(
  "asdasd",
  "Hello world",
  WS_CAPTION,
  10,
  10,
  200,
  80,
  HWND_DESKTOP,
  0,
  hInstance,
  nil
);

 while GetMessage(Msg,wnd,0,0) do
 begin
   TranslateMessage(Msg);
   DispatchMessage(Msg);
 end;

DestroyWindow(wnd);
Windows.UnRegisterClass("asdasd", hInstance);
end;

WndProc выглядит примерно так:

function WndProc(wnd:HWND; msg:UINT; w:UINT; l:UINT):UINT;stdcall;
var
begin
Result:=0;
case msg of
  WM_QUIT: ;
  WM_PAINT: ;
else
  Result:=DefWindowProc(wnd,msg,w,l);
end;
end;

В основном потоке создаю отдельный поток:

var
T: TTest;
procedure TForm1.Button1Click(Sender: TObject);
begin
T := TTest.Create(true);
with T do
begin
  FreeOnTerminate:=true;
  Resume;
end;
end;

и посылаю сообщение:

procedure TForm1.Button2Click(Sender: TObject);
var
h:hWnd;
begin
 h := FindWindow("asdasd", nil);
 SendMessage(h, WM_QUIT, 0, 0);
end;

GetMessage ни в какую не желает принимать сообщение, даже если написать GetMessage(Msg,0,0,0), в то же время WndProc сообщение принимает. Я так понимаю, что посланные мной сообщения принимает обработчик главного потока. Так ли это? И нужен ли вообще, в таком случае, GetMessage в отдельном потоке?


 
Crash Coredump ©   (2006-03-09 12:16) [1]


> GetMessage ни в какую не желает принимать сообщение


RTFM, RTFM и еще раз RTFM, как завещал великий Ленин, про отличия SendMessage от PostMessage


 
Сергей М. ©   (2006-03-09 12:24) [2]


> GetMessage ни в какую не желает принимать сообщение


Цитата из справки :

If the function retrieves the WM_QUIT message, the return value is zero

Zero - это False

При этом произойдет выход из while-цикла.


> Я так понимаю, что посланные мной сообщения принимает обработчик
> главного потока. Так ли это?


Нет, не так.
Поток, создавший некое окно, ответственен за прием сообщений, адресованных этому окну.


> нужен ли вообще, в таком случае, GetMessage в отдельном
> потоке?


Разумеется нужен !


 
msg   (2006-03-09 16:00) [3]

> RTFM, RTFM и еще раз RTFM, как завещал великий Ленин, про отличия SendMessage от PostMessage

SendMessage ждет ответ, PostMessage нет, впр не о том обсалютно.

> Цитата из справки :

само собой, мне это известно и справку я читать умею. А GetMessage все равно не вызывается, ну никак и из цикла не выходит...

>Нет, не так.
>Поток, создавший некое окно, ответственен за прием сообщений, адресованных этому окну.


Вот и я всегда так думал...


 
Сергей М. ©   (2006-03-09 16:03) [4]


> GetMessage все равно не вызывается


Бред сивой кобылы несешь.


> Вот и я всегда так думал


Что тут думать ? Это - реальность .. концепция, придуманная не мной и не тобой.


 
Crash Coredump ©   (2006-03-09 16:08) [5]


> SendMessage ждет ответ, PostMessage нет, впр не о том обсалютно.


Не ту справку читаешь, почитай еще. Сообщения по SendMessage никогда не будут выбраны вызовом GetMessage


 
Сергей М. ©   (2006-03-09 16:12) [6]


> Сообщения по SendMessage никогда не будут выбраны вызовом
> GetMessage


Бред сивой кобылы.


 
Crash Coredump ©   (2006-03-09 16:23) [7]

Сергей М. ©   (09.03.06 16:12) [6]

Да ну ? Будут ? И дальше провалятся в TranslateMessage|DispatchMessage ?

А ты код напиши для примера, вместе посмотрим.


 
Сергей М. ©   (2006-03-09 16:44) [8]


> Crash Coredump ©   (09.03.06 16:23) [7]


Ты облажался.
По полной программе.


 
Crash Coredump ©   (2006-03-09 16:45) [9]

Сергей М. ©   (09.03.06 16:44) [8]

Ругаться я не хуже тебя умею, поверь.

Пример кода, в котором GetMessage выбирает сообщения, посланные по SendMessage будет ?


 
Сергей М. ©   (2006-03-09 16:46) [10]

Речь идет об интерпоточном взаимодействии.


 
Сергей М. ©   (2006-03-09 16:48) [11]


> Crash Coredump


В пределах одногго и того же кодового потока понятие "очередь" не имеет смысла.


 
Crash Coredump ©   (2006-03-09 16:52) [12]

Сергей М. ©   (09.03.06 16:46) [10]


> В пределах одногго и того же кодового потока понятие "очередь"
> не имеет смысла


Ты это разработчикам Windows расскажи, а мы вместе посмеемся над их ответом тебе. Заодно посмотри реализацию TCustomForm.Release, в котором понятие "очередь" в пределах одного потока весьма уместно используется.


> Речь идет об интерпоточном взаимодействии.


А какая разница ? GetMessage как занимался выборкой сообщений из очереди сообщений, так и занимается, хоть в одном потоке, хоть в десяти.


 
Сергей М. ©   (2006-03-09 16:55) [13]


> Crash Coredump ©   (09.03.06 16:52) [12]


Нет слов.

Бегом штудировать MSN !


 
Crash Coredump ©   (2006-03-09 16:59) [14]

Сергей М. ©   (09.03.06 16:55) [13]

А ты скажи, какой именно раздел надо штудировать ?

А то вот я один проштудировал, а там написано:

"The GetMessage function retrieves a message from the calling thread"s message queue and places it in the specified structure. This function can retrieve both messages associated with a specified window and thread messages posted via the PostThreadMessage function."

и еще один раздел проштудировал:

"he SendMessage function sends the specified message to a window or windows. The function calls the window procedure for the specified window and does not return until the window procedure has processed the message. The PostMessage function, in contrast, posts a message to a thread"s message queue and returns immediately"

Или у тебя есть секретное издание MSDN ?


 
Сергей М. ©   (2006-03-09 17:00) [15]

> GetMessage как занимался выборкой сообщений из очереди сообщений,
>  так и занимается


Так точно.
Кодовый поток процесса, используя WinAPI, занимается либо ожиданием/выборкой, либо синхронной отправкой/ожиданием рез-та. Есть иное мнение ?


 
begin...end ©   (2006-03-09 17:00) [16]

> msg   (09.03.06 16:00) [3]

Сообщения, посылаемые окну потока с помощью SendMessage, называются синхронными, а сообщения, посылаемые функцией PostMessage -- асинхронными.

Вызванная функция GetMessage потока-приёмника вначале определяет по особому флагу в структуре потока, посланы ли потоку синхронные сообщения, и, если да, поочерёдно передаёт эти сообщения непосредственно оконным процедурам нужных окон. После этого GetMessage не возвращает управление (что мы и наблюдаем), а проверяет наличие асинхронных сообщений. И до тех пор, пока в очереди не будет обнаружено асинхронное сообщение или в структуре потока не будет обнаружен один из специальных флагов, GetMessage ничего не возвратит.

> И нужен ли вообще, в таком случае, GetMessage в отдельном
> потоке?

Нужен. Без вызова GetMessage WM_QUIT не передалось бы оконной процедуре.


 
Crash Coredump ©   (2006-03-09 17:01) [17]

Сергей М. ©   (09.03.06 17:00) [15]

Так какой раздел мне надо штудировать ?


 
Сергей М. ©   (2006-03-09 17:05) [18]


> Crash Coredump


Ты, чудо,  сам-то себе можешь описать ситуацию. когда один и тот же кодовый поток умудрился одновременно вызвать две ф-ции ?)


 
Crash Coredump ©   (2006-03-09 17:08) [19]

Сергей М. ©   (09.03.06 17:05) [18]

Ты, Серега, повежливее малость, а то опять чтением займешься.

И заодно почитай первый пост из темы:

"GetMessage ни в какую не желает принимать сообщение, даже если написать GetMessage(Msg,0,0,0), в то же время WndProc сообщение принимает"

при ситуации

"GetMessage ни в какую не желает принимать сообщение, даже если написать GetMessage(Msg,0,0,0), в то же время WndProc сообщение принимает"


 
Сергей М. ©   (2006-03-09 17:09) [20]


> Crash Coredump ©   (09.03.06 17:01) [17]


Читать ремарки ! До полного просветления !

Там сказано :

If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine.

Эни квешнз ?


 
Сергей М. ©   (2006-03-09 17:13) [21]


> GetMessage ни в какую не желает принимать сообщение


Бред сивой кобылы.

Эта ф-ция не занимается каким-то там "приемом".

Эта ф-ция занимается ожиданием и выборкой сообщений, отправленных из потоков отличных от того, который вызвал эту ф-цию.


 
begin...end ©   (2006-03-09 17:14) [22]

> Сергей М. ©   (09.03.06 17:09) [20]

> If the specified window was created by the calling thread,
> the window procedure is called immediately as a subroutine.

Это к чему? В вопросе потоки разные, вообще-то.


 
Crash Coredump ©   (2006-03-09 17:15) [23]

Сергей М. ©   (09.03.06 17:09) [20]


> If the specified window was created by the calling thread,
>  the window procedure is called immediately as a subroutine


Кто бы спорил ? И через GetMessage не проходит, об чем я и говорю.

GetMessage сработает на WM_QUIT только если в очереди потока будет стоять флаг QUIT. А если WM_QUIT послано через SendMessage, то в очередь оно не попадет. Недаром придумали вызов PostQuitMessage


 
Сергей М. ©   (2006-03-09 17:23) [24]


> Ты, Серега, повежливее малость, а то опять чтением займешься


О да) .. тебе ли не знать) .. За дурика не держи.

..

Цитирую автора


> в то же время WndProc сообщение принимает.


Ну и ?


 
Crash Coredump ©   (2006-03-09 17:27) [25]

Сергей М. ©   (09.03.06 17:23) [24]


> Ну и ?


Ну и этим все сказано - сообщение, посланное по SendMessage, обрабатывается оконной процедурой, а не появляется в результате вызова GetMessage.


 
Eraser ©   (2006-03-09 18:13) [26]


> Crash Coredump ©   (09.03.06 17:27) [25]


> Ну и этим все сказано - сообщение, посланное по SendMessage,
>  обрабатывается оконной процедурой, а не появляется в результате
> вызова GetMessage.

Что значит "появляется в результате вызова"?
а кто оконную процедуру вызывает, если сообщение из другово потока?


 
Crash Coredump ©   (2006-03-09 18:22) [27]


> Что значит "появляется в результате вызова"?


Значит, что
а) GetMessage возвращает результат (и заполненную структуру MSG при ненулевом результате)
б) Выполнение переходит на команду, следующую за вызовом GetMessage


 
Eraser ©   (2006-03-09 18:26) [28]


> Crash Coredump ©   (09.03.06 18:22) [27]


> Значит, что
> а) GetMessage возвращает результат (и заполненную структуру
> MSG при ненулевом результате)
> б) Выполнение переходит на команду, следующую за вызовом
> GetMessage

так точно.
А второй вопрос?


 
Crash Coredump ©   (2006-03-09 18:32) [29]


> а кто оконную процедуру вызывает, если сообщение из другово
> потока?


Вызывает ее GetMessage, PeekMessage, WaitMessage, при этом не прерывая своего исполнения до тех пор, пока в очереди нет сообщений.

Напиши пример с окном, которое реагирует на два разных сообщения, из другого потока посылай ему сообщения первого типа через SendMessage, а второго через PostMessage, посмотри, как будет происходить выполнение цикла
while GetMessage .... do begin
   // в этом месте можешь сигнализировать каким-то образом, что GetMessage отработал
   TranslateMessage
   DispatchMessage
end;


 
Eraser ©   (2006-03-09 18:36) [30]


> Crash Coredump ©   (09.03.06 18:32) [29]


> Вызывает ее GetMessage, PeekMessage, WaitMessage

ну вот! правильно. Об чём спор тогда...
а вот это

> Crash Coredump ©   (09.03.06 17:27) [25]


> Ну и этим все сказано - сообщение, посланное по SendMessage,
>  обрабатывается оконной процедурой, а не появляется в результате
> вызова GetMessage.

не верно, что ты и доказал сам.
без Get/PeekMessage никуда.


 
Crash Coredump ©   (2006-03-09 18:42) [31]


> без Get/PeekMessage никуда.


WaitMessage, MsgWaitForMultipleObjects...


> не верно, что ты и доказал сам


Мы сабж будем читать или как ?


 
Leonid Troyanovsky ©   (2006-03-09 18:45) [32]


> Crash Coredump ©   (09.03.06 18:22) [27]

> Значит, что
> а) GetMessage возвращает результат (и заполненную структуру
> MSG при ненулевом результате)
> б) Выполнение переходит на команду, следующую за вызовом
> GetMessage


Никуда оно не перейдет, т.е., выхода из GetMessage при
обработке nonqueued message не будет.

В общем-то, картина не так уж проста, поэтому Рихтер и
писал о том, что у потока целых четыре очереди сообщений,
включая и такие, как очередь синхронных сообщений
и очередь ответных сообщений.

Ну, а собщения, которые SendMessage из другого потока
обрабатываются в оконной процедуре потоком-получателем.
А для того, чтобы оный смог обработать сообщение, он
должен находится в состоянии ожидания, достигаемого,
например, той же GetMessage.

--
Regards, LVT.


 
Eraser ©   (2006-03-09 18:48) [33]

to Crash Coredump ©   (09.03.06 18:42) [31]

> msg   (09.03.06 12:11)
> И нужен ли вообще, в таком случае, GetMessage в отдельном
> потоке?

нужен.

> MsgWaitForMultipleObjects

в данном случае применять эту функцию - извращение.

> WaitMessage

не осуществляет выборку.

так что нормальную очередь сообщений можно построить только на Get/PeekMessage ... остальное - стёб.. imho.


 
Crash Coredump ©   (2006-03-09 18:51) [34]

Leonid Troyanovsky ©   (09.03.06 18:45) [32]


> Никуда оно не перейдет, т.е., выхода из GetMessage при
> обработке nonqueued message не будет.


Об том и речь. С самого начала.


> Ну, а собщения, которые SendMessage из другого потока
> обрабатываются в оконной процедуре потоком-получателем.
> А для того, чтобы оный смог обработать сообщение, он
> должен находится в состоянии ожидания, достигаемого,
> например, той же GetMessage.


Или другими функциями ожидания, связанными с очередью сообщений потока. Вот честно, не проверял, будут ли обрабатываться сообщения, если поток будет ждать какого-нибудь объекта ядра функцией WaitForSingleObject, а что Рихтер на эту тему писал, не помню.


 
Crash Coredump ©   (2006-03-09 18:52) [35]

Eraser ©   (09.03.06 18:48) [33]


> не осуществляет выборку.


Из какой очереди сообщений ? Их несколько у потока.


 
Eraser ©   (2006-03-09 18:52) [36]


> Crash Coredump ©   (09.03.06 18:52) [35]

из любой.


 
Crash Coredump ©   (2006-03-09 18:56) [37]

Eraser ©   (09.03.06 18:52) [36]


> из любой.


Очередь сообщений, куда помещаются сообщения, посланные по SendMessage, WaitMessage тоже не обрабатывает, доставляя их оконной процедуре ?


 
Eraser ©   (2006-03-09 18:57) [38]


> Crash Coredump ©   (09.03.06 18:56) [37]

проспись пойди.


 
Crash Coredump ©   (2006-03-09 19:01) [39]

Eraser ©   (09.03.06 18:57) [38]

Это надо понимать, как отсутствие иных аргументов ?


 
Crash Coredump ©   (2006-03-09 19:12) [40]

Eraser ©   (09.03.06 18:57) [38]

Мне не хочешь верить, может, Рихтеру поверишь, который Джеффри:

"When a worker thread sends a message to your window, the SendMessage function internally queues up the message for the window, and then the worker thread suspends itself. The next time the user-interface thread calls GetMessage, PeekMessage, or WaitMessage, it sees the queued sent message, pulls it from the queue, and processes the window"s window procedure. "

http://www.microsoft.com/msj/1197/win321197.aspx


 
Leonid Troyanovsky ©   (2006-03-09 19:28) [41]


> Crash Coredump ©   (09.03.06 18:51) [34]

> потока. Вот честно, не проверял, будут ли обрабатываться
> сообщения, если поток будет ждать какого-нибудь объекта
> ядра функцией WaitForSingleObject, а что Рихтер на эту тему
> писал, не помню.


Я, конечно, тоже не помню, но работоспособный цикл
можно построить на основе MsgWaitForMultipleObjects + PeekMessage.
Или WaitMessages + PeekMessage.

--
Regards, LVT.


 
Eraser ©   (2006-03-09 21:02) [42]


> Crash Coredump ©   (09.03.06 19:12) [40]

к чему ты вообще тут WaitMessage приплёл... сабж не про это.

> Leonid Troyanovsky ©   (09.03.06 19:28) [41]

именно. обычно WaitMessage применяют балансировки нагрузки. И то довольно редко... видел применение только один раз - в WinVNC.


 
Игорь Шевченко ©   (2006-03-09 21:14) [43]

Eraser ©   (09.03.06 21:02) [42]


> обычно WaitMessage применяют балансировки нагрузки


Это как ? Расскажи, интересно.

Совет - спорь кодом, аргумент неубиенный. И эта..."будьте вежливы".


 
Eraser ©   (2006-03-09 21:29) [44]


> Игорь Шевченко ©   (09.03.06 21:14) [43]


> Совет - спорь кодом, аргумент неубиенный.


vncDesktopThread::run_undetached(void *arg)
{
// Save the thread"s "home" desktop, under NT (no effect under 9x)
HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());

// Attempt to initialise and return success or failure
if (!m_desktop->Startup())
{
 vncService::SelectHDESK(home_desktop);
 ReturnVal(FALSE);
 return NULL;
}

 // Grab the initial display contents
 // *** m_desktop->m_buffer.GrabRegion(m_desktop->m_bmrect);
 // *** m_desktop->m_buffer.Clear(m_desktop->m_bmrect);

// Succeeded to initialise ok
ReturnVal(TRUE);

// START PROCESSING DESKTOP MESSAGES

// We set a flag inside the desktop handler here, to indicate it"s now safe
// to handle clipboard messages
m_desktop->SetClipboardActive(TRUE);

// All changes in the state of the display are stored in a local
// UpdateTracker object, and are flushed to the vncServer whenever
// client updates are about to be triggered
rfb::SimpleUpdateTracker clipped_updates;
rfb::ClippedUpdateTracker updates(clipped_updates, m_desktop->m_bmrect);
clipped_updates.enable_copyrect(true);

// Incoming update messages are collated into a single region cache
// The region cache areas are checked for changes before an update
// is triggered, and the changed areas are passed to the UpdateTracker
rfb::Region2D rgncache = m_desktop->m_bmrect;

// The previous cursor position is stored, to allow us to erase the
// old instance whenever it moves.
rfb::Point oldcursorpos;

// Set the hook thread to a high priority
// *** set_priority(omni_thread::PRIORITY_HIGH);

BOOL idle_skip = TRUE;
ULONG idle_skip_count = 0;
MSG msg;
while (TRUE)
{
 if (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
  //
  // - MESSAGE QUEUE EMPTY
  // Whenever the message queue becomes empty, we check to see whether
  // there are updates to be passed to clients.
  if (idle_skip) {
   idle_skip = FALSE;
   if (idle_skip_count++ < 4) {
    Sleep(5);
    continue;
   }
  }
  idle_skip_count = 0;

  // Clear the triggered flag
  m_desktop->m_update_triggered = FALSE;

  //
  // CHECK SCREEN FORMAT
  // First, we must check that the screen hasnt changed too much.
  if (m_desktop->m_displaychanged || !vncService::InputDesktopSelected())
  {
   rfbServerInitMsg oldscrinfo = m_desktop->m_scrinfo;
   m_desktop->m_displaychanged = FALSE;

   // Attempt to close the old hooks
   if (!m_desktop->Shutdown())
   {
    m_server->KillAuthClients();
    break;
   }

   // Now attempt to re-install them!
   if (!m_desktop->Startup())
   {
    m_server->KillAuthClients();
    break;
   }

   // Check that the screen info hasn"t changed
   vnclog.Print(LL_INTINFO, VNCLOG("SCR: old screen format %dx%dx%d\n"),
    oldscrinfo.framebufferWidth,
    oldscrinfo.framebufferHeight,
    oldscrinfo.format.bitsPerPixel);
   vnclog.Print(LL_INTINFO, VNCLOG("SCR: new screen format %dx%dx%d\n"),
    m_desktop->m_scrinfo.framebufferWidth,
    m_desktop->m_scrinfo.framebufferHeight,
    m_desktop->m_scrinfo.format.bitsPerPixel);
   if ((m_desktop->m_scrinfo.framebufferWidth != oldscrinfo.framebufferWidth) ||
    (m_desktop->m_scrinfo.framebufferHeight != oldscrinfo.framebufferHeight))
   {
    m_server->KillAuthClients();
    break;
   } else if (memcmp(&m_desktop->m_scrinfo.format, &oldscrinfo.format, sizeof(rfbPixelFormat)) != 0)
   {
    m_server->UpdateLocalFormat();
   }

   // Adjust the UpdateTracker clip region
   updates.set_clip_region(m_desktop->m_bmrect);

   // Add a full screen update to all the clients
   rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_bmrect));
   m_server->UpdatePalette();
  }

  //
  // CALCULATE CHANGES
  //

  if (m_desktop->m_server->UpdateWanted())
  {
   // POLL PROBLEM AREAS
   // We add specific areas of the screen to the region cache,
   // causing them to be fetched for processing.
   if (m_desktop->m_server->PollFullScreen())
   {
    rfb::Rect rect = m_desktop->m_qtrscreen;
    rect = rect.translate(rfb::Point(0, m_desktop->m_pollingcycle * m_desktop->m_qtrscreen.br.y));
    rgncache = rgncache.union_(rfb::Region2D(rect));
    m_desktop->m_pollingcycle = (m_desktop->m_pollingcycle + 1) % 4;
   }
   if (m_desktop->m_server->PollForeground())
   {
    // Get the window rectangle for the currently selected window
    HWND hwnd = GetForegroundWindow();
    if (hwnd != NULL)
     PollWindow(rgncache, hwnd);
   }
   if (m_desktop->m_server->PollUnderCursor())
   {
    // Find the mouse position
    POINT mousepos;
    if (GetCursorPos(&mousepos))
    {
     // Find the window under the mouse
     HWND hwnd = WindowFromPoint(mousepos);
     if (hwnd != NULL)
      PollWindow(rgncache, hwnd);
    }
   }


 
Eraser ©   (2006-03-09 21:29) [45]

   // PROCESS THE MOUSE POINTER
   // Some of the hard work is done in clients, some here
   // This code fetches the desktop under the old pointer position
   // but the client is responsible for actually encoding and sending
   // it when required.
   // This code also renders the pointer and saves the rendered position
   // Clients include this when rendering updates.
   // The code is complicated in this way because we wish to avoid
   // rendering parts of the screen the mouse moved through between
   // client updates, since in practice they will probably not have changed.

   // Re-render the mouse"s old location if it"s moved
   BOOL cursormoved = FALSE;
   POINT cursorpos;
   if (GetCursorPos(&cursorpos) &&
    ((cursorpos.x != oldcursorpos.x) ||
    (cursorpos.y != oldcursorpos.y))) {
    cursormoved = TRUE;
    oldcursorpos = rfb::Point(cursorpos);
   }
   if (cursormoved) {
    if (!m_desktop->m_cursorpos.is_empty())
     rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
   }

   {
    // Prevent any clients from accessing the Buffer
    omni_mutex_lock l(m_desktop->m_update_lock);

    // CHECK FOR COPYRECTS
    // This actually just checks where the Foreground window is
    m_desktop->CalcCopyRects(updates);

    // GRAB THE DISPLAY
    // Fetch data from the display to our display cache.
    m_desktop->m_buffer.GrabRegion(rgncache);
     // Render the mouse
     m_desktop->m_buffer.GrabMouse();
    if (cursormoved) {
     // Inform clients that it has moved
     m_desktop->m_server->UpdateMouse();
     // Get the buffer to fetch the pointer bitmap
     if (!m_desktop->m_cursorpos.is_empty())
      rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
    }

    // SCAN THE CHANGED REGION FOR ACTUAL CHANGES
    // The hooks return hints as to areas that may have changed.
    // We check the suggested areas, and just send the ones that
    // have actually changed.
    // Note that we deliberately don"t check the copyrect destination
    // here, to reduce the overhead & the likelihood of corrupting the
    // backbuffer contents.
    rfb::Region2D checkrgn = rgncache.subtract(clipped_updates.get_copied_region());
    rgncache = clipped_updates.get_copied_region();
    rfb::Region2D changedrgn;
    m_desktop->m_buffer.CheckRegion(changedrgn, checkrgn);

    // FLUSH UPDATES TO CLIENTS
    // Add the bits that have really changed to their update regions
    // Note that the cursor is NOT included - they must do that
    // themselves, for the reasons above.
    // This call implicitly kicks clients to update themselves

    updates.add_changed(changedrgn);
    clipped_updates.get_update(m_server->GetUpdateTracker());
   }

   // Clear the update tracker and region cache
   clipped_updates.clear();
  }

  // Now wait for more messages to be queued
  if (!WaitMessage()) {
   vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n"));
   break;

весь код VNC приводить не буду ... много там )

>  И эта..."будьте вежливы".

я и так вежлив, особенно когда со мной спорят не ради спора, а по сабжу )


 
Игорь Шевченко ©   (2006-03-09 21:55) [46]

Eraser ©   (09.03.06 21:29) [44]
Eraser ©   (09.03.06 21:29) [45]


> весь код VNC приводить не буду


Надо приводить код Windows.

А этот код ты зачем привел ?


> по сабжу


А по сабжу все сказано в посте [1]

С потоком в данной ситуации надо обмениваться через Post[Thread]Message


 
Eraser ©   (2006-03-09 22:03) [47]


> Игорь Шевченко ©   (09.03.06 21:55) [46]

в [1] автор прав насчёт RTFM - согласен - это никогда не лишнее )
PostThreadMessage однако он не упаминул.. ну да Бог с ним с PostThreadMessage.
Далее ведь тов. Crash Coredump начал доказывать, что Get/PeekMessage не нужна в доп. потоке.

Crash Coredump ©   (09.03.06 16:08) [5]
Не ту справку читаешь, почитай еще. Сообщения по SendMessage никогда не будут выбраны вызовом GetMessage


на что Сергей М. вполне резонно заметил
Бред сивой кобылы.

:-)

> Надо приводить код Windows.

а где его взять? )

вот по этой теме хорошая ветка
http://www.rsdn.ru/Forum/Message.aspx?mid=1410117


 
Игорь Шевченко ©   (2006-03-09 22:18) [48]

Eraser ©   (09.03.06 22:03) [47]


> Сообщения по SendMessage никогда не будут выбраны вызовом
> GetMessage


Так, как ожидает автор (см. сабж) они выбраны не будут, разумеется. Автор (см. сабж), ожидает, что после того, как он послал окну сообщение, у него функция GetMessage возвратит результат.

А про то, как обрабатываются сообщения на самом деле, и автор поста [1] и я знаем довольно неплохо :)

Впрочем, окончательную точку ставит MSDN (VS 2005 beta 2)

"The GetMessage function retrieves a message from the calling thread"s message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.

....

During this call, the system delivers pending messages that were sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. The system may also process internal events. Messages are processed in the following order:

Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages "

в отличие от Win32.Hlp

"he GetMessage function retrieves a message from the calling thread"s message queue and places it in the specified structure. This function can retrieve both messages associated with a specified window and thread messages posted via the PostThreadMessage function. The function retrieves messages that lie within a specified range of message values. GetMessage does not retrieve messages for windows that belong to other threads or applications"


 
Eraser ©   (2006-03-09 22:31) [49]


> Игорь Шевченко ©   (09.03.06 22:18) [48]


> GetMessage does not retrieve messages for windows that belong
> to other threads or applications"

странно.. не обращал внимание на этот ляп )
пользуюсь тоже MSDN.

> А про то, как обрабатываются сообщения на самом деле, и
> автор поста [1] и я знаем довольно неплохо :)

ну что ж... раз вы говорите, что тот товарисч вкурсе - приму на веру...
однако с формулировками по осторожнее быть надо.. а то тов. Crash Coredump раза 3 упамянул, что
>  Сообщения по SendMessage никогда не будут выбраны вызовом
> GetMessage

...


 
Игорь Шевченко ©   (2006-03-09 22:42) [50]

Eraser ©   (09.03.06 22:31) [49]

Они будут, согласно MSDN, отдиспетчированы - непосредственно переданы оконной процедуре нужного окна. Но при этом GetMessage не закончит своего выполнения как того ожидает автор. Поэтому ему в данном случае лучше выполнять PostMessage, а для конкретного примера в [0] и вовсе PostQuitMessage.

Я советую кроме MSDN еще исходниками пользоваться. Например, ReactOS. Поскольку они ставят своей целью совместимость с Windows, то их исходники можно рассматривать, как отправную точку для углубленных знаний.

Из их исходников видно, что GetMessage (NtUserGetMessage), если в очереди потока нет сообщений, вызывает внутреннюю функцию WaitMessage, которая и осуществляет диспетчеризацию Sent-сообщений - это к спору о WaitMessage :)


 
Eraser ©   (2006-03-09 22:50) [51]


> Игорь Шевченко ©   (09.03.06 22:42) [50]


> Из их исходников видно, что GetMessage (NtUserGetMessage),
>  если в очереди потока нет сообщений, вызывает внутреннюю
> функцию WaitMessage, которая и осуществляет диспетчеризацию
> Sent-сообщений - это к спору о WaitMessage :)

ну я подозоевал, что-то вроде

while(true)
{
 WaitMessage();
 if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)
 {
   if(msg.message==WM_QUIT) break;
   TranslateMessage(&msg);
   DispatchMessage(&msg);
 }

а исходники ReactOS доступны всем для скачивания?
ссылочку можно? )


 
Игорь Шевченко ©   (2006-03-09 23:05) [52]

Eraser ©   (09.03.06 22:50) [51]

Сначала проверка очереди, если пусто, то проверка, доставка и очистка Sent-сообщений, затем WaitMessage


> а исходники ReactOS доступны всем для скачивания?
> ссылочку можно? )


http://www.reactos.org


 
Eraser ©   (2006-03-09 23:27) [53]


> Игорь Шевченко ©   (09.03.06 23:05) [52]

выделю трафик.. скачаю.
интересно однако:)


 
Игорь Шевченко ©   (2006-03-09 23:31) [54]

Eraser ©   (09.03.06 23:27) [53]

На самом деле интересно, так как дает представление о том, как реализуется поведение тех или иных функций Windows. Кроме того, если по механизмам ядра написана отличная книжка Руссиновича и Соломона, то по части оконной подсистемы довольно большое белое пятно. Даже по механизмам GDI написана книжка Фэня Юаня, а по USER и его ядерной части в Win32k.sys я ничего не видел.


 
Leonid Troyanovsky ©   (2006-03-10 08:33) [55]


> Eraser ©   (09.03.06 22:50) [51]

>  WaitMessage();
>  if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)


Скорее уж while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)

--
Regards, LVT.


 
msg   (2006-03-10 12:45) [56]

Ага, GetMessage действительно нужен, потому, что если

 while GetMessage(Msg,wnd,0,0) do
 begin
   TranslateMessage(Msg);
   DispatchMessage(Msg);
 end;

заменить на

 while true do
  sleep(1);

в оконную процедуру сообщение не передается, понял.

ранее SendMessage пытался заменить на PostMessage, но GetMessage управление всеравно не возвращала, щас возвращает... фиг с ним, спишем на собственную невнимательность.

С поведением GetMessage при PostMessage и SendMessage более менее разобрался.

Заметил еще одну интересную особенность, оконная процедура, при SendMessage(WM_QUIT) получает сообщение с кодом 18(WM_QUIT), а при PostMessage(WM_QUIT) оконная процедура получает два сообщения 2(WM_DESTROY) и 130(WM_NCDESTROY).

Теперь есть непонятки с PostQuitMessage, если ее вызываю в оконной процедуре в ответ на WM_DESTROY(PostMessage) или WM_QUIT(SendMessage), GetMessage возвращает управление, мы выходим из цикла...
Если же написать:

type
TTest=class(TThread)
...
public
  Procedure ExitTest;
...

implementation

procedure TTest.ExitTest;
begin
PostQuitMessage(0);
end;

и из основного потока вызвать ExitTest:

T.ExitTest;

то мы вываливаемся из программы (т.е. завершает работу основной поток), почему? в справке написано:
Функция PostQuitMessage указывает Windows, что ПОТОК (в нашем случае не основной) послал запрос на завершение (выход).


 
Сергей М. ©   (2006-03-10 12:49) [57]


> ПОТОК (в нашем случае не основной)


Ну как же не основной-то ?

Сам же говоришь , что


> и из основного потока вызвать ExitTest


 
Crash Coredump ©   (2006-03-10 12:50) [58]


> и из основного потока вызвать ExitTest:
>
> T.ExitTest;


То процедура будет выполнена в контексте основного потока, что неудивительно.

msg   (10.03.06 12:45) [56]

А что ты вообще хочешь сделать, если не секрет, в связи с чем вопрос ?
Может есть готовое решение.


 
msg   (2006-03-10 14:58) [59]

Сергей М., Crash Coredump
а, да, точно, понял. этот вопрос снят.

Crash Coredump
Если вы спрашиваете о T.ExitTest;, то это просто эксперимент.

А вообще задача такая: есть консольное приложение в момент работы оно должно принимать и обрабатывать ряд команд данных сторонними программами. Т.е., к примеру, примерно так будет выглядеть команда моей программе из стороннего процесса:

const
 WM_ASDASD_COMMAND = WM_USER + 3;
 _ASDASD_TEST      = 1;

...

h := FindWindow("asdasd", nil);
SendMessage(h, WM_ASDASD_COMMAND, _ASDASD_TEST, 0);


 
Сергей М. ©   (2006-03-10 15:02) [60]


> msg   (10.03.06 14:58) [59]


Насколько принципиален при решении этой задачи отказ от борландовских классов в пользу "чистого WinAPI" ?


 
Сергей М. ©   (2006-03-10 15:03) [61]


> msg


Ведь так или иначе классы ты задействуешь, коль скоро задействовал TThread ..


 
Leonid Troyanovsky ©   (2006-03-10 15:19) [62]


> msg   (10.03.06 14:58) [59]

> А вообще задача такая: есть консольное приложение в момент
> работы оно должно принимать и обрабатывать ряд команд данных
> сторонними программами. Т.е., к примеру, примерно так будет
> выглядеть команда моей программе из стороннего процесса:


Для взаимодействия с консольным приложением можно взять, например,
pipes, memory mapped files, APC & etc.

Ну, а если уж так хочется GUI-thread можно, например,

http://www.rsdn.ru/Forum/?mid=883505

только, пользы от конгуевых гибридов значительно меньше.

--
Regards, LVT.


 
Crash Coredump ©   (2006-03-10 15:32) [63]

msg   (10.03.06 14:58) [59]


> А вообще задача такая: есть консольное приложение в момент
> работы оно должно принимать и обрабатывать ряд команд данных
> сторонними программами. Т.е., к примеру, примерно так будет
> выглядеть команда моей программе из стороннего процесса


Не обязательно консольное:

unit WorkerMain;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
 TfWorker = class(TForm)
   procedure FormCreate(Sender: TObject);
 public
   procedure DefaultHandler(var Message); override;
 end;

var
 fWorker: TfWorker;

implementation
uses
 WorkerMessages;

{$R *.DFM}

{ TfWorker }

{ TfWorker }

procedure TfWorker.DefaultHandler(var Message);
begin
 with TMessage(Message) do
   if Msg = UM_WORKER1 then
     MessageBeep (0)
   else if Msg = UM_WORKER2 then
     MessageBox(0, "message 2", "Worker", MB_OK)
   else
     inherited;
end;

procedure TfWorker.FormCreate(Sender: TObject);
begin
 Application.ShowMainForm := false;
 ShowWindow (Application.Handle, SW_HIDE);
end;

end.


unit WorkerMessages;

interface
uses
 Messages;

const
 UM_WORKER1 = WM_USER + 100;
 UM_WORKER2 = WM_USER + 101;

implementation

end.


Это один EXEшник.

unit ControlMain;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls, ActnList;

type
 TfWorkerControl = class(TForm)
   Button1: TButton;
   Button2: TButton;
   Button3: TButton;
   ActionList: TActionList;
   actMessage1: TAction;
   actMessage2: TAction;
   actTerminate: TAction;
   procedure FormCreate(Sender: TObject);
   procedure ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
   procedure actMessage1Execute(Sender: TObject);
   procedure actMessage2Execute(Sender: TObject);
   procedure actTerminateExecute(Sender: TObject);
 private
   FWorkerWnd: HWND;
 end;

var
 fWorkerControl: TfWorkerControl;

implementation
uses
 WorkerMessages;

{$R *.DFM}

procedure TfWorkerControl.FormCreate(Sender: TObject);
begin
 FWorkerWnd := FindWindow ("TfWorker", "fWorker");
end;

procedure TfWorkerControl.ActionListUpdate(Action: TBasicAction;
 var Handled: Boolean);
begin
 actMessage1.Enabled := IsWindow(FWorkerWnd);
 actMessage2.Enabled := IsWindow(FWorkerWnd);
 actTerminate.Enabled := IsWindow(FWorkerWnd);
end;

procedure TfWorkerControl.actMessage1Execute(Sender: TObject);
begin
 SendMessage(FWorkerWnd, UM_WORKER1, 0, 0);
end;

procedure TfWorkerControl.actMessage2Execute(Sender: TObject);
begin
 SendMessage(FWorkerWnd, UM_WORKER2, 0, 0);
end;

procedure TfWorkerControl.actTerminateExecute(Sender: TObject);
begin
 PostMessage(FWorkerWnd, WM_CLOSE, 0, 0);
end;

end.


Это второй EXEшник - та самая стороняя программа


 
msg   (2006-03-10 17:37) [64]

Сергей М.
несомненно, VCL-ные библиотеки используются, но дело все в том, что само приложение мало по килобайтам, поэтому просто не хотелось бы его "утяжелять" использованием к примеру таких модулей как: Controls, Forms, Menus, Dialogs ...

Leonid Troyanovsky
В данном случае команды оч. простые и даже если будет какое-то расширение команд, ничего сложного не будет, максимум передача строки(пути), но эта проблема решается в моем случае с помощью CopyDataStruct, так же ни когда не потребуется удаленное управление. В связи с этим есть желание cделать все настолько просто, насколько это возможно (легче отлаживать, писать приложение(я) отдающее(ие) команды, модифицировать...).

а к примеру с тем же MMF надо решать проблему отслеживания изменившейся/пополнившейся информации, проблему синхронизации приложений, структуры размещаемой информации и пр.

AllocateHWND, ага, что может быть проще, вот только бы еще класс окна можно было бы определять самому.

Crash Coredump
Это понятно. Так бы и сделал если бы было бы гуишное приложение, но у консоли то ведь нет своей очереди оконных сообщений...


 
Leonid Troyanovsky ©   (2006-03-10 18:19) [65]


> msg   (10.03.06 17:37) [64]

> В данном случае команды оч. простые и даже если будет какое-
> то расширение команд, ничего сложного не будет, максимум
> передача строки(пути), но эта проблема решается в моем случае
> с помощью CopyDataStruct, так же ни когда не потребуется
> удаленное управление. В связи с этим есть желание cделать
> все настолько просто, насколько это возможно (легче отлаживать,
>  писать приложение(я) отдающее(ие) команды, модифицировать.
> ..).


С помощью mmf оно решается не сложней.
Дополнительно потребуется лишь (тоже именованные) mutex и, возможно,
event (CreateEvent).
Читатель ждет (периодически проверяет) состояние event, и при
сигнале читает проекцию файла. Писатель пишет и устанавливает
событие по окончанию. Всякое чтение-запись в проекцию начинается
с захвата мьютекса и завершается его release. Вот, собс-но, и все.
Отладить оное взамодействие можно в обычном гуевом приложении
с кодом консоли, вынесенным в поток.

Т.е., для консольного приложения - это самое то, ничего лишнего
и громоздкого (включая TThread ;).

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2006-03-10 18:32) [66]


> msg   (10.03.06 17:37) [64]

> AllocateHWND, ага, что может быть проще, вот только бы еще
> класс окна можно было бы определять самому.


Особенной нужды в этом нет. Для того, чтобы его можно было проще
найти среди дельфирожденных окон ему можно сделать SetWindowText
or SetProp с уникальным идентификатором.

Хотя, конечно, возможно, что лучше его сделать Message-Only Window
(see SetParent with HWND_MESSAGE)

--
Regards, LVT.


 
Игорь Шевченко ©   (2006-03-10 20:42) [67]


> Так бы и сделал если бы было бы гуишное приложение, но у
> консоли то ведь нет своей очереди оконных сообщений...


А что, консоль является необходимым внешним условием ?


 
msg   (2006-03-10 21:21) [68]

>Читатель ждет (периодически проверяет) состояние event, и при
>сигнале читает проекцию файла. Писатель пишет и устанавливает
>событие по окончанию. Всякое чтение-запись в проекцию начинается
>с захвата мьютекса и завершается его release. Вот, собс-но, и все.
>Отладить оное взамодействие можно в обычном гуевом приложении
>с кодом консоли, вынесенным в поток.

Здесь плохо, что "Читатель ждет (периодически проверяет)", потому как происходит дополнительная нагрузка на процессор (ни кто не спорт, она не значительна) и в зависимости от частоты проверки, получение сообщения у нас может занять какое-то время. Так же увеличение частоты проверки, ради сокращения времени получения сообщения, все больше увеличивает нагрузку на процессор.

Если же использовать WaitForSingleObject, то нужен дополнительный поток. Хотя в принципе, ожидание сообщений консольного буфера у меня построено на WaitForSingleObjectEx, можно попробовать объединить ожидание event и буфера функцией WaitForMultipleObjects, как считаете? Оба события же должны ожидаться бесконечно (INFINITE).

>Хотя, конечно, возможно, что лучше его сделать Message-Only Window
>(see SetParent with HWND_MESSAGE)

Кстати, действительно замечательный флаг. Благодарю.


 
msg   (2006-03-10 21:23) [69]

>А что, консоль является необходимым внешним условием ?

Основное приложение: да. Приложение отдающее команды: нет, хотя оно и не важно от куда SendMessage слать.


 
Игорь Шевченко ©   (2006-03-10 21:29) [70]

msg   (10.03.06 21:23) [69]


> Основное приложение: да.


Собственно, как уже отмечал LVT, в консольном приложении создать окно тоже труда не составит. Единственное, на мой взгляд, неудобное место будет с организацией выборки сообщений.


 
begin...end ©   (2006-03-10 21:54) [71]

> Игорь Шевченко ©   (10.03.06 21:29) [70]

Я, возможно, глупость сморожу, но у консоли разве затруднительно организовать цикл с GetMessage подобно тому, как это делается в GUI? Я, собсно, к тому, что не совсем понятно, зачем ещё окно создавать :)


 
Игорь Шевченко ©   (2006-03-10 22:11) [72]

begin...end ©   (10.03.06 21:54) [71]

А насколько я знаю, этот цикл уже организовывает тот, кто поддерживает ConsoleWindowClass для окна консоли, то есть, winsrv.dll

Я вот не знаю (если ты попробуешь, напиши, интересно), как интерференция с ним себя покажет, поэтому и спросил автора о необходимости именно консольного приложения.


 
msg   (2006-03-10 23:51) [73]

У консольного приложения же как такового нет своего окна, консольное окно создаеться вызовом AllocConsole и заним следит система, к консольному окну может "приотачится" сколь угодо процессов (AttachConsole), и уничтожается оно после того, как все процессы от него отключатся вызвав FreeConsole.

Ну покрайней мере вот такой код не дает ни каких результатов:
в таймере (timeSetEvent):
 h := FindWindow("ConsoleWindowClass", nil);
 PostMessage(h, WM_PAINT, 0, 0);
или
 SetConsoleTitle("{9F0C9451-118F-4A3E-9170-6C1989797463}");
 h := FindWindow(nil, "{9F0C9451-118F-4A3E-9170-6C1989797463}");
 PostMessage(h, WM_PAINT, 0, 0);

в цикле:
 if PeekMessage(Mess,0,0,0,PM_REMOVE) then
 begin
   case Mess.message of
     WM_PAINT: beep;
   end;
 end;


 
begin...end ©   (2006-03-11 08:27) [74]

> Игорь Шевченко ©   (10.03.06 22:11) [72]

Сообщения, посылаемые окну консоли через PostMessage, собственноручно организованным GetMessage-циклом не выбираются. Но у меня в [71] была мысль посылать не окнам (стандартному консольному или самостоятельно созданному), а потоку (через PostThreadMessage). Эти сообщения, на первый взгляд, нормально доходят.


 
begin...end ©   (2006-03-11 08:51) [75]

К [74]:

> Сообщения, посылаемые окну консоли...

Разумеется, имеется в виду стандартное консольное окно. Если создать дополнительное своё, то сообщения, адресованные ему, вроде бы, нормально принимаются в цикле (тоже на первый взгляд).


 
Leonid Troyanovsky ©   (2006-03-13 09:06) [76]


> msg   (10.03.06 21:21) [68]

> Если же использовать WaitForSingleObject, то нужен дополнительный
> поток. Хотя в принципе, ожидание сообщений консольного буфера
> у меня построено на WaitForSingleObjectEx, можно попробовать
> объединить ожидание event и буфера функцией WaitForMultipleObjects,
>  как считаете? Оба события же должны ожидаться бесконечно


Конечно, WaitForMultipleObjects.
Если же используется WaitForSingleObjectEx, то можно извещать
ожидающий поток не с помощью event (SetEvent), а QueueUserAPC.

--
Regards, LVT.



Страницы: 1 2 вся ветка

Форум: "WinAPI";
Текущий архив: 2006.06.04;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.73 MB
Время: 0.043 c
2-1147762028
ANB
2006-05-16 10:47
2006.06.04
Как отправить письмо с вложением без файла ?


8-1135515760
Glex
2005-12-25 16:02
2006.06.04
Как управлять Z-Index ом у TPanel?


15-1146926799
Mozart
2006-05-06 18:46
2006.06.04
Жилье - реально ли приобрести "с нуля" - без помощи...


2-1147894133
Dstr
2006-05-17 23:28
2006.06.04
PopMenu


15-1147247197
WhiteBarin
2006-05-10 11:46
2006.06.04
Компонент Бегущая строка





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский