powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
8 сообщений из 8, страница 1 из 1
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38319832
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Расскажу о проблеммке на примере:

Клиент исполняет код:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Public Sub SendDialStrCommand(ph_number As String)
  'отправка WM_ сообщения осн. приложению
  'строка для передачи
  str_data = ph_number
  hwndTarget = FindMyWindow(1) 'WINDOWTITLE_SERVER
  If hwndTarget <> 0 Then
    Dim cds As COPYDATASTRUCT
    
    cds.dwData = 7 'идентифицирует тип передаваемых данных 7-набор номера телефона
    cds.cbData = Len(str_data) + 1
    cds.lpData = VarPtr(StringToMas(str_data).ByteStr(0))
    
    Call SendMessage(hwndTarget, WM_COPYDATA, Form1.hwnd, cds)
  End If
End Sub

Смысл кода:
Команда - набрать номер (cds.dwData = 7)
Параметр - собственно телефонный номер (cds.lpData = адрес памяти откуда его читать)
(cds.cbData -длина номера, включая \0)

Сервер выполняет команду, грубо вот так:
Код: vbnet
1.
2.
3.
4.
      ElseIf cds.dwData = 7 Then 'команда набрать заданный номер
        the_str = PtrToString_len(cds.lpData, cds.cbData)
        ...
        opal_SetupCall call_param, err_text, , the_str



Проблемка:
пока ElseIf cds.dwData = 7 Then
не отработает , т.е. SetupCall не будет выполнена
ВЫЗЫВАЮЩЕЕ приложение будет также висеть .

SetupCall может выполняться при плохой связи с SIP узлом несколько секунд,
а если не дай бог там ошибка в настройках и указанного SIP узла не существует,
то ошибки можно ждать и пару десятков секунд.

То что ВЫЗЫВАЕМОЕ приложение подвешено - с этим здесь приходится мириться.
Но то что подвешено ВЫЗЫВАЮЩЕЕ - непорядок.
Т.е. я нажал на кнопку на Toolbar в ВЫЗЫВАЮЩЕМ приложении - она у меня остается нажатой.

Как бороться?

14517814

Дмитрий77Второй неприятный эффект в таком же духе.
Например посылаю SendMessage (data) в вызываемое приложение.
Если в процедура обработки в вызываемом приложении требует какого-то времени, то опять же в вызывающем приложении будет подвисание.

Как лечить? PostMessage вместо SendMessage? Ну, если ответа не требуется?

А вот не работает PostMessage. И google говорит что это так и есть, то бишь WM_COPYDATA и PostMessage несовместимы.

От вызывающего приложения требуется тупо сообщить вызываемому номер телефона,
на что уходит миллисекунда.
Вызывающее приложение не интересует удалось ли инициировать вызов по этому номеру или нет и прочие подробности.
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38327822
donpauls
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Дмитрий77,

а если попробовать SendNotifyMessage?
или SendMessageTimeout?

Честно говоря у меня точно такая же проблема, пока нормального решения не нашёл:
SendNotifyMessage - работает в ассинхронном режиме только в случае, если вызываемое приложение запущено не вызывающим приложением
SendMessageTimeout -не хватило терпения разобраться с параметрами
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38327874
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Запустить еще одну программу, которая сделает SendMessage и будет висеть вместо вызывающей.

А в дотнете можно было бы элементарно заюзать поток.
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38328414
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
donpaulsа если попробовать SendNotifyMessage?
или SendMessageTimeout?
Пробовал. Ни один из этих вариантов начиная с PostMessage не будет работать конкретно с WM_COPYDATA.
AntonariyЗапустить еще одну программу, которая сделает SendMessage и будет висеть вместо вызывающей.
Ага, на каждую кнопку по exe-шнику. Смешно. Не, ну можно конечно один и тот же с параметрами запускать.

Короче, не настолько актуальная проблема. Я на нее пока забил.
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38328510
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77AntonariyЗапустить еще одну программу, которая сделает SendMessage и будет висеть вместо вызывающей.
Ага, на каждую кнопку по exe-шнику. Смешно. Не, ну можно конечно один и тот же с параметрами запускать.А вариантов не много. Либо отдельный процесс, либо отдельный поток. Если поток, то нужно переделывать в ActiveX exe, но это сложно и не факт, что заработает на восьмерке. На серверах 2008+ не работает. Если отдельный процесс, то это запуск программы, можно другой, а можно своей же, но с параметром отправить сообщение и тут же выйти. В этом случае не нужно дополнительных настроек, запускается уже полностью доверенное приложение.
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38407616
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я тут с другой проблемой столкнулся на эту же тему.

VB6 шлет запрос #100 в сторону C++:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
  Debug.Print SendDataToC(100, "Hello C++ Console Application")
...
Public Function SendDataToC(ByVal lng_command As Long, ByVal str_data As String) As Boolean
  Dim hwndTarget As Long
  
  hwndTarget = FindWindowEx(HWND_MESSAGE, 0, "C_WINDOW_CLASS", "C_Message_Receiver")
  If hwndTarget <> 0 Then
    Dim cds As COPYDATASTRUCT
    
    cds.dwData = lng_command 'идентифицирует тип передаваемых данных (номер команды)
    cds.cbData = Len(str_data) + 1
    cds.lpData = VarPtr(StringToMas(str_data).ByteStr(0))
    
    SendDataToC = CBool(SendMessage(hwndTarget, WM_COPYDATA, Form1.hwnd, cds))
  Else
    SendDataToC = False
  End If
End Function


и получает от C++ ответ через WndProc:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
  Select Case Msg
    Case WM_COPYDATA
      Dim cds As COPYDATASTRUCT
      Dim the_str As String
      
      Call CopyMemory(cds, ByVal lParam, Len(cds))
      If cds.dwData = 101 Then 'тест-ответ
        the_str = PtrToString_len(cds.lpData, cds.cbData)
        Debug.Print the_str
      End If



C++ код делает следующее:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
  switch( message )
  {
    case WM_COPYDATA:
      cout << "WM_COPYDATA" << endl;
      COPYDATASTRUCT* pcds;
      pcds = (COPYDATASTRUCT*)lparam;
...
      } else if (pcds->dwData == 100) {//тест запрос
        PString t = PString((LPCTSTR)(pcds->lpData));
        HWND hwndTarget = (HWND)wparam; //wparam = HWND
        std::string buffer;
        stringstream to_buffer;
        to_buffer << "Answer:" << t << "\r\n";
        buffer = to_buffer.str();
        to_buffer.str("");

        COPYDATASTRUCT cds;
        cds.dwData=101; //тест ответ на запрос
        cds.cbData = static_cast<DWORD>(buffer.length()) + 1;
        cds.lpData = const_cast<char *>((buffer.c_str()));

        Sleep(5000);
        //Здесь я нарочно добавил ВИСЯК, реальная ф-ция формирующая ответ занимает ощутимое ВРЕМЯ
        SendMessage(hwndTarget, WM_COPYDATA, 0, (LPARAM) &cds);
      }
      return 1;



Когда я даю команду #100 из VB6,
то
1) VB6 висит 5 сек (Sleep(5000); в C++ коде)
2) потом пишет следующее в Debug:
Код: vbnet
1.
2.
Answer:Hello C++ Console Application 'сначала результат из WinProc в VB6
True 'затем код возврата исходной SendMessage(100) посланной из VB6




И здесь непонятно 2 вещи:
1) Если VB6 спит (ждет результата от отосланной SendMessage -строчка True),
то как VB6 вообще умудряется отработать свою WndProc
По идее там вообще должна быть "мертвая петля":
SendMessage посланный из VB6 не получит результат, т.к. не сумеет вернуть результат SendMessage посланной из C++

2) Я хочу чтоб C++ сразу возвращал результат SendMessage (чтоб VB6 не висела 5 сек),
а "ответ" приходил независимо.
То что "висит" C++ мне наплевать - там многопоточная прога.

Что я должен сделать?
Создать поток в C++,
вернуть ответ из C++ сразу (чтоб исх. SendMessage в VB6 не висела)
А потом в потоке выполнить в C++ чего надо и отсылать "Answer:Hello C++ Console Application" из этого потока
потом убить поток в C++, как отдал ответ
???
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38407955
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77Что я должен сделать?
Создать поток в C++,
вернуть ответ из C++ сразу (чтоб исх. SendMessage в VB6 не висела)
А потом в потоке выполнить в C++ чего надо и отсылать "Answer:Hello C++ Console Application" из этого потока
потом убить поток в C++, как отдал ответ
???

Не ну если это сделать, то VB6 не будет висеть 5 сек.
Вроде сделал.
Но я задолбался параметры в поток передавать:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
struct ForSend{
   HWND hwnd;
   char * msg;
};

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
  switch( message )
  {
    case WM_COPYDATA:
      COPYDATASTRUCT* pcds;
      pcds = (COPYDATASTRUCT*)lparam;
...
      } else if (pcds->dwData == 100) {//тест запрос

        DWORD dwThreadId;
        HANDLE hThread;

        char * the_str = new char[pcds->cbData];
        *the_str=0;
        strncat(the_str, (char *)(pcds->lpData), pcds->cbData);

        ForSend *SendPr = new ForSend;
        SendPr->hwnd = (HWND)wparam;
        SendPr->msg = the_str;

        hThread = CreateThread(
         NULL,         // атрибуты безопасности по умолчанию
         0,            // размер стека используется по умолчанию
         ThreadFunc_Answer,   // функция потока
         (LPVOID)SendPr, // аргумент функции потока
         0,            // флажки создания используются по умолчанию
         &dwThreadId); // возвращает идентификатор потока

        if (hThread == NULL)
          cout << "CreateThread failed.\n"; 
        else {
          cout << "CreateThread OK(" << hThread << ")\n"; 
          CloseHandle( hThread );
        }
      }
      return 1;
...
DWORD WINAPI ThreadFunc_Answer(LPVOID lpParam)
{
  //ForSend * in = (ForSend *)lpParam;
  const ForSend* in = reinterpret_cast<const ForSend*>(lpParam);
  PString the_str = PString(in->msg);
  Sleep(5000);
  cout << "Answer:" << the_str << "@\r\n";
  cout << "Hwnd:" << in->hwnd << "\r\n";

  HWND hwndTarget = in->hwnd;
  std::string buffer;
  stringstream to_buffer;
  to_buffer << "Answer:" << the_str << "\r\n";
  buffer = to_buffer.str();
  to_buffer.str("");

  COPYDATASTRUCT cds;
  cds.dwData=101; //тест ответ на запрос
  cds.cbData = static_cast<DWORD>(buffer.length()) + 1;
  cds.lpData = const_cast<char *>((buffer.c_str()));
  SendMessage(hwndTarget, WM_COPYDATA, 0, (LPARAM) &cds);

  delete [] in->msg; //освобождаю память под строку
  delete in; //освобождаю память под структуру
  ExitThread(0);    // Специальный вызов для закрытия потока
}



P.S. На C-шном форуме хрен помогут , строко-указателе-копатели хреновы. VB6 да и .NET - это видимо чтобы оградить себя от больных людей, которые бредят тем как устроена память и указатели, вместо чтоб получать быстрый практический результат относительно простым путем.
...
Рейтинг: 0 / 0
Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
    #38528958
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Столкнулся с этим же багом, но с более серьезными последствиями.
Код C++ посылает SendMessage (WM_COPYDATA) с информацией о ходе своих процессов в приложение VB6. Причем подобные отправки происходят из разных мест C-шного кода, в т.ч. из dll и т.п.

Код: plaintext
1.
2.
3.
4.
5.
6.
процесс
---
SendMessage (WM_COPYDATA) ->
<- возврат результата SendMessage из VB
---
продолжение процесса


На стороне VB6 код был типа:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
Public Function NewFormWindowProc _
... 
  Select Case Msg
    Case WM_COPYDATA
      Dim cds As COPYDATASTRUCT
      Dim the_str as String
      Call CopyMemory(cds, ByVal lParam, Len(cds))
      the_str = PtrToString_len(cds.lpData, cds.cbData)
     Select Case cds.dwData
        Case 101
           Function1(the_str )
        Case 102
           Function2(the_str )
     End Select
   Case Else
  End Select
  
  NewFormWindowProc = CallWindowProc _


Причем Function1 например может конвертировать файл запустив через Shell с ожиданием результата отдельный exe,
Function2 например может отправлять e-mail.
Т.е. это требующие времени операции, и при такой архитектуре
процесс C++ будет висеть (пока SendMessage не вернет результат).
А процесс на это такую грубость не рассчитан, в моем случае при нагрузке C++ мог свалиться на 100% CPU Usage с невозможностью вообще что либо сделать с компьютером.

Идея следующая и вроде получилось.
Обработчик WndProc вместо тупого запуска Function1 из-под себя пишет Message в массив-буфер, запускает одноразовый Timer (1 мс!!!) и отфутболивает SendMessage(результат получен).
А ф-ция таймера работает с очередью сообщений.
При этом как в ф-ции таймера, так и в Function1 если она использует цикл ожидания чего-либо используется DoEvents() с целью как минимум не препятствовать приему новых сообщений в буфер-массив.
С буфером-массивом используется та же идея как при создании внутренней очереди Tray Notifications.
Т.е. если принятое сообщение единственное, то оно обрабатывается сразу по тику таймера.
Если не единственное, то просто кладется в конец очереди.
А обработка происходит из ранее запущенной ф-ции таймера, кот. обработав предыдущее сообщение преступает к обработке следующего при его наличии.

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
Public Type MyDataMessage
  dwData As Long 'тип сообщения
  lpData As String 'содержание сообщения
End Type

Public AppDataMessages() As MyDataMessage
===========
  'инициализация сообщений
  ReDim AppDataMessages(0 To 0) 'очистка-0-й элемент не используется
===========
Public Function NewFormWindowProc _
 (ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
 
  Select Case Msg
    Case WM_COPYDATA
      Dim cds As COPYDATASTRUCT
      
      Call CopyMemory(cds, ByVal lParam, Len(cds))
      
      Dim the_message As MyDataMessage
      the_message.dwData = cds.dwData
      the_message.lpData = PtrToString_len(cds.lpData, cds.cbData)
      AddDataMessageToQueue the_message
    Case Else
  End Select
  
  NewFormWindowProc = CallWindowProc _
   (OldFormWindowProc, hwnd, Msg, wParam, lParam)
End Function

Private Sub TimerDataMes_Timer()
  'Используется первый тик таймера. Интервал = 1 МИЛЛИСЕКУНДА
  TimerDataMes.Enabled = False
  Do
    'обрабатываем первое сообщение
    Dim the_message As MyDataMessage
    the_message = AppDataMessages(1) 'работаем с копией сообщения, иначе будет temp. locked
    ProcessDataMessage the_message
    RemoveDataMessageFromQueue
    If UBound(AppDataMessages) > 0 Then
      DoEvents 'обработка оконных сообщений в промежутке
    Else
      Exit Do
    End If
  Loop
End Sub


Private Sub AddDataMessageToQueue(new_message As MyDataMessage)
  'добавление сообщения в конец массива
  ReDim Preserve AppDataMessages(0 To UBound(AppDataMessages) + 1)
  AppDataMessages(UBound(AppDataMessages)) = new_message
  
  'запуск сообщения на исполнение, если нет текущего активного
  If UBound(AppDataMessages) = 1 Then 'кроме добавленного сообщения ничего нет
    'запуск тика таймера 1мс
    Form1.TimerDataMes.Enabled = True
  End If
End Sub

Public Sub RemoveDataMessageFromQueue()
  'удаляем сообщение из очереди сдвигом элементов массива на единицу и удалением крайнего элемента
  'сдвиг на единицу если оно не единственное
  If UBound(AppDataMessages) > 1 Then '0-не используется, 1-удаляемое, 2-следующее и т.д.
    Dim i As Long
    For i = 1 To UBound(AppDataMessages) - 1
      AppDataMessages(i) = AppDataMessages(i + 1)
    Next
  End If
  
  'удаляем старший элемента массива
  ReDim Preserve AppDataMessages(0 To UBound(AppDataMessages) - 1)
End Sub

Public Sub ProcessDataMessage(the_message As MyDataMessage)
  Dim the_str As String
  the_str = the_message.lpData
  Select Case the_message.dwData
    Case 101: '
      Function1 the_str ' сама Function1 может использовать DoEvents(), если в ней цикл
    Case 102: '
      Function2 the_str ' сама Function2 может использовать DoEvents(), если в ней цикл



Можно конечно использовать MailSlot вместо SendMessage.
Сообщение кладется в MailSlot без подтверждения его приема (т.е. пославший ничего не ждет и не тормозит).

но есть одно различие.
MailSlot надо долбить по таймеру (на стороне приемника),
а SendMessage принимается как только так сразу. В приведенном коде таймер используется только по факту прихода сообщения и создает минимальную задержку в 1 миллисекунду.
...
Рейтинг: 0 / 0
8 сообщений из 8, страница 1 из 1
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Как убрать "подвешивание" вызывающего приложения при передаче SendMessage(WM_COPYDATA)?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]