Таким, что в ядре COM уже реализован маршаллинг аргументов всех типов, которые есть в VARIANT. А типы аргументов она возьмет из typelib. Думаю, работать будет довольно быстро, учитывая, что вся эта байда затевалась для офисных продуктов мс, которые процентов на 80 из нее и состоят. Этот код толпа индусов уже лет 20 полирует
В общем, померил я скорость SendMessage. Если окно не найдено, то выполняется очень быстро (буквально за 50 с небольшим тактов). Если это "моё" окно (и оно обрабатывает это сообщение), скорость сравнима со скоростью SetEvent/ResetEvent (даже чуть быстрее: 1220 vs 1400-1550 тактов, причём это тестировалось в Delphi). Другие окна – в среднем раз в 40-47 дольше (порядка 50-57К тактов у меня... если не брать во внимание Excel, например, у которого, судя по всему, обработчик тяжёлый). SignalObjectAndWait в цикле в 2-х разных потоках (одного процесса) выдают около 2700 тактов. Вот только без shared-memory и в разных потоках не проверял...
Jin X, Ваши тесты не корректны. Во первых при невалид хэндле не произойдёт никакой ядерный вызов - проверка валидности описателей выполняется в юм. Во вторых есть у теневых сервисов механизм пакетной обработки. Так например рисуется текст. Работает следующим образом. Некоторые действия отлаживаются, необходимое событие сохраняется в буфер. Буфер может держать множество событий. Далее при вызове любого теневого сервиса ядро перед его обработкой развернёт все события из буфера. Это механизм оптимизации. Таким образом если вы ничего в цикле не вызываете, кроме тестовой апи, то профайл будет существенно отличаться от того же вызова сервиса, но при нормальной работе гуя.
> скорость сравнима со скоростью SetEvent/ResetEvent Скорость чего именно - сервисной обработки или доставки события ? Гуй асинхронный, сигналящий поток быстро вернётся из ядра, но сообщение неизвестно когда будет доставлено, так как существует их очередь - событие будет добавлено в очередь.
Так, я и говорю, что там 50 тактов всего затрачивается. Выполнения всего SendMessage и всего SetEvent (SignalObjectAndWait и пр). Ну ок, есть много "но", тем не менее общая картина ясна: через Event'ы всё работает многократно быстрее, чем через SendMessage.
В общем, вот такую штуку сделал. Это вариант без передачи параметров и получения результата. Просто запуск команд в родительском процессе. На работает долго: от 40-60 (если повезёт) до 200+ тыс. тиков ЦП (бывало даже 300 тыс). Запускать так: IPCEventServer.exe IPCEventUser.exe p.s. Всё равно громоздко выходит (для дочки). Открытие Event'ов с проверкой чего только стоит... IPCEventServer: Код (ASM): format PE Console 4.0 entry start include 'win32axp.inc' ;-- CODE SECTION ------------------------------------------------------------------------------------------------------- .code start: cinvoke printf, <'SERVER: Starting...',13,10> stdcall ParseCommandLine test eax,eax jz .noparam mov ebx,eax cinvoke printf, <'SERVER: Creating events...',13,10> stdcall CreateEvents test eax,eax jnz .ev_error cinvoke printf, <'SERVER: Running specified command line...',13,10> invoke CreateProcess, 0, ebx, 0, 0, 0, 0, 0, 0, sinfo, pinfo test eax,eax jz .run_error mov eax,[pinfo.hProcess] mov [pid],eax cinvoke printf, <'SERVER: Listening to command events...',13,10> .repeat stdcall WaitForCommandEvent mov ebx,eax .if ebx = 0 cinvoke printf, <'SERVER: Process is finished.',13,10> .elseif ebx = 1 cinvoke printf, <'SERVER: Quit command is fetched.',13,10> .elseif ebx = 2 cinvoke printf, <'SERVER: Init command is fetched.',13,10> .elseif ebx = 3 cinvoke printf, <'SERVER: First command is fetched.',13,10> .elseif ebx = 4 cinvoke printf, <'SERVER: Second command is fetched.',13,10> .else cinvoke printf, <'SERVER: Error is occured.',13,10> .endif .until signed ebx <= 1 invoke Sleep, 1200 cinvoke printf, <'SERVER: Finishing...',13,10> invoke CloseHandle, [pinfo.hThread] invoke CloseHandle, [pinfo.hProcess] .finish: invoke CloseHandle, [dwEvQuit] invoke CloseHandle, [dwEvInit] invoke CloseHandle, [dwEvFirst] invoke CloseHandle, [dwEvSecond] .exit: invoke Sleep, 2000 invoke ExitProcess, 0 .noparam: cinvoke printf, <'SERVER: No command line is specified!',13,10> jmp .exit .ev_error: cinvoke printf, <'SERVER: Event #%i create error!',13,10>, eax jmp .exit .run_error: invoke GetLastError cinvoke printf, <'SERVER: Run error #%i!',13,10>, eax jmp .finish ; Return pointer to filename string in EAX (0 if no params) proc ParseCommandLine uses edi invoke GetCommandLine mov edi,eax cinvoke strlen, edi test eax,eax jz .noparam lea ecx,[eax-1] mov al,'"' scasb je @F mov al,' ' @@: test al,al repne scasb jne .noparam mov al,' ' cmp al,al repe scasb je .noparam lea eax,[edi-1] ret .noparam: xor eax,eax ret endp ; Return EAX = 0 if ok or EAX = event number with error proc CreateEvents uses ebx xor ebx,ebx invoke CreateEvent, 0, 0, 0, 'IPC test "Quit" event' inc ebx test eax,eax jz .error mov [dwEvQuit],eax invoke CreateEvent, 0, 0, 0, 'IPC test "Init" event' inc ebx test eax,eax jz .error mov [dwEvInit],eax invoke CreateEvent, 0, 0, 0, 'IPC test "First" event' inc ebx test eax,eax jz .error mov [dwEvFirst],eax invoke CreateEvent, 0, 0, 0, 'IPC test "Second" event' inc ebx test eax,eax jz .error mov [dwEvSecond],eax invoke CreateEvent, 0, 0, 0, 'IPC test "Continue" event' inc ebx test eax,eax jz .error mov [dwEvContinue],eax xor ebx,ebx .error: mov eax,ebx ret endp ; Return command number in EAX (0 = process is finished, -1 = error) proc WaitForCommandEvent invoke WaitForMultipleObjects, EventCount, Events, 0, -1 push eax invoke SetEvent, [dwEvContinue] pop eax cmp eax,EventCount jb @F or eax,-1 @@: ret endp ;-- DATA SECTION ------------------------------------------------------------------------------------------------------- struc STARTUPINFO { . = $ .cb dd .size .lpReserved dd ? .lpDesktop dd ? .lpTitle dd ? .dwX dd ? .dwY dd ? .dwXSize dd ? .dwYSize dd ? .dwXCountChars dd ? .dwYCountChars dd ? .dwFillAttribute dd ? .dwFlags dd ? .wShowWindow dw ? .cbReserved2 dw ? .lpReserved2 dd ? .hStdInput dd ? .hStdOutput dd ? .hStdError dd ? .size = $-. } struc PROCESS_INFORMATION { . = $ .hProcess dd ? .hThread dd ? .dwProcessId dd ? .dwThreadId dd ? .size = $-. } .data sinfo STARTUPINFO pinfo PROCESS_INFORMATION Events label pid rd 1 dwEvQuit rd 1 dwEvInit rd 1 dwEvFirst rd 1 dwEvSecond rd 1 EventCount = ($-Events)/4 dwEvContinue rd 1 ;-- IMPORT SECTION ----------------------------------------------------------------------------------------------------- section '.idata' import data readable library kernel32, 'kernel32.dll',\ msvcrt, 'msvcrt.dll' import_kernel32 all_api import msvcrt,\ printf, 'printf',\ strlen, 'strlen' IPCEventUser: Код (ASM): format PE Console 4.0 entry start include 'win32axp.inc' SYNCHRONIZE = 0x00100000 EVENT_MODIFY_STATE = 2 ;-- CODE SECTION ------------------------------------------------------------------------------------------------------- .code start: cinvoke printf, <13,10,' USER: Starting...',13,10> cinvoke printf, <' USER: Opening events...',13,10> stdcall OpenEvents test eax,eax jnz .ev_error invoke Sleep, 1000 cinvoke printf, <13,10,' USER: Testing Init command...',13,10> stdcall SendCommandEvent, [dwEvInit] stdcall ShowResult, eax invoke Sleep, 1000 cinvoke printf, <13,10,' USER: Testing First command...',13,10> stdcall SendCommandEvent, [dwEvFirst] stdcall ShowResult, eax invoke Sleep, 1000 cinvoke printf, <13,10,' USER: Testing Second command...',13,10> stdcall SendCommandEvent, [dwEvSecond] stdcall ShowResult, eax invoke Sleep, 1000 invoke MessageBox, 0, "Send 'Quit' command?", '', MB_YESNO or MB_ICONQUESTION or MB_TASKMODAL or MB_SETFOREGROUND cmp eax,IDYES jne @F cinvoke printf, <13,10,' USER: Testing Quit command...',13,10> stdcall SendCommandEvent, [dwEvQuit] stdcall ShowResult, eax invoke Sleep, 1000 @@: cinvoke printf, <13,10,' USER: Finishing...',13,10> .finish: invoke CloseHandle, [dwEvInit] invoke CloseHandle, [dwEvFirst] invoke CloseHandle, [dwEvSecond] invoke CloseHandle, [dwEvQuit] invoke CloseHandle, [dwEvContinue] .exit: invoke ExitProcess, 0 .ev_error: cinvoke printf, <' USER: Event #%i open error!',13,10>, eax jmp .exit ; Return EAX = 0 if ok or EAX = event number with error proc OpenEvents uses ebx xor ebx,ebx invoke OpenEvent, EVENT_MODIFY_STATE, 0, 'IPC test "Init" event' inc ebx test eax,eax jz .error mov [dwEvInit],eax invoke OpenEvent, EVENT_MODIFY_STATE, 0, 'IPC test "First" event' inc ebx test eax,eax jz .error mov [dwEvFirst],eax invoke OpenEvent, EVENT_MODIFY_STATE, 0, 'IPC test "Second" event' inc ebx test eax,eax jz .error mov [dwEvSecond],eax invoke OpenEvent, EVENT_MODIFY_STATE, 0, 'IPC test "Quit" event' inc ebx test eax,eax jz .error mov [dwEvQuit],eax invoke OpenEvent, SYNCHRONIZE, 0, 'IPC test "Continue" event' inc ebx test eax,eax jz .error mov [dwEvContinue],eax xor ebx,ebx .error: mov eax,ebx ret endp ; Returns time (CPU ticks) in EAX (< 0 = negative SignalObjectAndWait inadmissible result code) proc SendCommandEvent Handle xor eax,eax cpuid ; serialization rdtsc push eax invoke SignalObjectAndWait, [Handle], [dwEvContinue], 2000, 0 mov ecx,eax rdtsc pop edx sub eax,edx jecxz @F mov eax,ecx neg eax @@: ret endp proc ShowResult CPUTime invoke Sleep, 200 mov eax,[CPUTime] test eax,eax js .error cinvoke printf, <' USER: Time elapsed: %i CPU ticks.',13,10>, eax jmp @F .error: neg eax cinvoke printf, <' USER: Waiting is failed (SignalObjectAndWait result = %i)...',13,10>, eax @@: ret endp ;-- DATA SECTION ------------------------------------------------------------------------------------------------------- .data dwEvInit rd 1 dwEvFirst rd 1 dwEvSecond rd 1 dwEvQuit rd 1 dwEvContinue rd 1 ;-- IMPORT SECTION ----------------------------------------------------------------------------------------------------- section '.idata' import data readable library kernel32, 'kernel32.dll',\ user32, 'user32.dll',\ msvcrt, 'msvcrt.dll' import_kernel32 import_user32 all_api import msvcrt,\ printf, 'printf'
Indy_, +100500, видимо наруду шадов нравится, сами не зная чеко кодят. Есть же шаред мем и прочие фичи, даже взять rpcrt4.... 4 строчки кода
RET, ну давайте посчитаем (в случае с RtlRemoteCall)... 1. OpenFileMapping (нам же надо получить хендлы процесса и потока, а также адреса функций) 2. MapViewOfFile 3. [Nt]SuspendThread 4. NtGetContextThread (нам же надо передать функции родителя контекст, чтобы он мог сделать NtContinue) 5. RtlRemoteCall 6. ResumeThread 7. UnmapViewOfFile 8. CloseHandle А если окажется, что дочерний процесс не может использовать хендлы процесса и потока, открытые родителем, то добавится ещё 4 строки: OpenProcess OpenThread CloseHandle(pid) CloseHandle(tid) Я ничего не забыл? Ах да, надо ещё структуру данных, находящихся в shared memory описать... А вот тут можно поподробнее? Что сие такое и как его юзать?
Jin X, В вашем списке тайминг не предсказуем в 3, 4, 5 и 6. Эти все пункты сводятся к одному 5, так как это он и делает(асинхронная доставка кернел APC). > Что сие такое и как его юзать? Этот механизм слишком кривой, толстый и вам он точно не нужен. Рядом есть топик про синхронизацию RWL. Вы сформулируйте задачу, что бы можно было её решить. А так не ясно что вам нужно. Между процессами проекции отображаются аппаратно, там нет наверно смысла говорить про задержки. Тормоза происходят в интерфейсе, в синхронизациях. Если вас устраивает какой то топорный гуй механизм, тогда и любой нэйтивный сгодится походу, так как они на порядки шустрее.
Т.е. 3, 4 и 6 можно убрать, 5 сам всё сделает? Вот эта? Та тема к этой не имеет никакого отношения. Сейчас я уже понял, что смысла так делать вообще нет. Проще обойтись сопроводительной DLL-кой, которая будет подгружаться дочерним процессом и вызов будет идти напрямую. Это будет быстро и просто, как табуретка. А если родителю нужно будет иметь доступ к данным из DLL, то вполне сойдёт вариант #17. Сейчас уже мне сами варианты общения между процессами интересен Как это сделать проще, быстрее и т.д. В образовательных целях, так сказать... Например?
Какую инфу этой функцией нужно запрашивать и для чего? И где брать ProcessHandle, если FileMapping мы не используем?
Jin X, Курите матчасть, win internals, там грамотные люди всё популярно описали. Первый закон ньютона помните, что бы что то получить нужно что то отдать. Имеется ввиду время на изучение основ.