2011-08-18 10 views
6

¿Cuál es la mejor manera de agregar elementos a la ventana modal de otra aplicación?¿La mejor manera de agregar elementos a la ventana modal de otra aplicación?

El ejemplo simple que he escrito para esto (como una prueba de concepto) utiliza un método que sospecho que es demasiado, demasiado procesador para un proceso de fondo trivial, pero tengo problemas para encontrar una alternativa.

Por ejemplo, supongamos que es un médico que completa una ventana modal con datos de prescripción. Usted ingresa durante 30 días con 11 resurtidos y luego el paciente dice que quiere 90 días con 3 resurtidos. La aplicación original (a la que no tiene acceso) no tiene una conversión fácil. Escribí una pequeña utilidad que mira esta ventana en particular (usando un temporizador y una ventana de búsqueda) y cuando la encuentra, se hace visible y se coloca en un lugar vacío en la ventana modal de destino. Cuando se presiona el botón "30", el Rx se escribe durante 30 días con 11 repeticiones y cuando se presiona el botón "90", hace lo que cabría esperar. Si la ventana modal se mueve, los botones 30 y 90 se mueven con ella. Mientras esto funciona, me preocupan los gastos indirectos involucrados en ejecutar findwindow en un temporizador repetidamente.

1) ¿Hay una manera mejor? 2) ¿Tengo razón en estar preocupado por esto? 3) ¿se ríe de lo ineficiente que es mi kluge?

Gracias de antemano - ¡He estado muy impresionado con la gente de aquí!

+4

Lo que ocurre como una alternativa es instalar un gancho global de la TCC, mucho más propenso a errores y problemático, y no es gratuita, así que se refiere a los recursos ya sea .. Si no está de votación con demasiada rapidez, el temporizador es bueno, diría. –

+0

@Sertac: Entonces ... ¿qué consideraría una "encuesta demasiado rápida"? No noto deterioro en el rendimiento del sistema o aumento en la CPU (ambas medidas son muy rudimentarias, estoy seguro) con un intervalo de sondeo de 250 ms, pero esto simplemente "parece incorrecto" ... Cualquier cosa más lenta que eso impide que los botones sigan el formulario suavemente. –

+1

@ b-p Puede usar 1s intervalo para mirar (sondeo) para la ventana y cuando hay uno encontrado cambiar a 250ms para mantenerlo sin problemas. De esta forma, ejerce menos presión sobre el sistema cuando no hay una ventana de destino presente. – ain

Respuesta

1

La mejor manera es ... inyección de DLL.

// The DLL: 
library dll_inj; 

uses 
    ShareMem, 
    System.SysUtils, 
    System.Classes, Vcl.Dialogs, 
    windows, messages; 

{$R *.res} 

var 
    hWndMain, hDemoButton, hEdit, hNewButton, hWndEnter: THandle; 
    OldWndProc: TFarProc; 
    hBtnFont: hFont; 
    Times: integer; 
    dwThreadId: DWORD; 

function NewWndProc(hWnd: hWnd; Msg: UINT; wParam: wParam; lParam: lParam) 
    : Longint; stdcall; 
begin 
    if Times = 0 then 
    begin 
    Times := Times + 1; 
    hWndMain := FindWindowEx(0, 0, 'TForm1', 'Injection test'); 
    if hWndMain = 0 then 
     OutputDebugString('hWndMain is 0!'); 

    hNewButton := CreateWindow('button', 'NewBtn', WS_CHILD or WS_VISIBLE, 20, 
     20, 100, 24, hWndMain, 2000, GetWindowLong(hWndMain, GWL_HINSTANCE), nil); 
    if hNewButton = 0 then 
     OutputDebugString('CreateWindow failed!') 
    else 
    begin 
     hWndEnter := FindWindowEx(hWndMain, 0, 'TBitBtn', 'Enter'); 
     if hWndEnter <> 0 then 
     hBtnFont := SendMessage(hWndEnter, WM_GETFONT, 0, 0); 
     if hBtnFont <> 0 then 
     SendMessage(hNewButton, WM_SETFONT, hBtnFont, 1); 
    end; 

    hDemoButton := FindWindowEx(hWndMain, 0, 'TButton', 'Demo'); 
    if hDemoButton <> 0 then 
    begin 
     if not EnableWindow(hDemoButton, true) then 
     OutputDebugString('EnableWindow failed!'); 
    end 
    else 
     OutputDebugString('hDemoButton is 0!'); 

    hEdit := FindWindowEx(hWndMain, 0, 'TEdit', 'Serial'); 
    if hEdit = 0 then 
     OutputDebugString('hEdit is 0!') 
    else if not SetWindowText(hEdit, 'You have been hacked') then 
     OutputDebugString('SetWindowText failed!'); 
    end; 

    case Msg of 
    WM_COMMAND: 
     if (hNewButton <> 0) and (DWORD(lParam) = hNewButton) then 
     MessageBox(HWND_DESKTOP, 'You pressed a new button!', 'Yay!', MB_OK) 
     else if (hWndEnter <> 0) and (DWORD(lParam) = hWndEnter) then 
     begin 
     MessageBox(HWND_DESKTOP, 'This message is not default anymore!', 
      'Override!', MB_OK); 
     Exit(0); // Suppress default event completely 
     end; 
    end; 
    Result := CallWindowProc(OldWndProc, hWnd, Msg, wParam, lParam); 
end; 

procedure EntryPoint(Reason: integer); 
begin 
    hWndMain := FindWindowEx(0, 0, 'TForm1', 'Injection test'); 
    if hWndMain = 0 then 
    begin 
    OutputDebugString('hWndMain is 0!'); 
    Exit; 
    end; 

    OldWndProc := TFarProc(SetWindowLong(hWndMain, GWL_WNDPROC, 
    LONG(@NewWndProc))); 

    MessageBox(0, 'DLL Injected', 'OK', 0); 
end; 

begin 
    CreateThread(nil, 0, @EntryPoint, nil, 0, &dwThreadId); 

end. 

// The injector: 
program exe_inj2; 

{$APPTYPE CONSOLE} 
{$R *.res} 

uses 
    System.SysUtils, windows, TLHelp32; 

Function EnumThreadProc(wnd: HWND; Var appHwnd: HWND): LongBool; stdcall; 
Var 
    buf: array [0 .. 256] of Char; 
Begin 
    Result := LongBool(1); 
    if GetClassname(wnd, buf, sizeof(buf)) > 0 then 
    If StrComp(buf, 'TApplication') = 0 Then 
    Begin 
     appHwnd := wnd; 
     Result := False; 
    End; 
End; 

Function FindApplicationWindow(forThreadID: DWORD): HWND; 
Begin 
    Result := 0; 
    EnumThreadWindows(forThreadID, @EnumThreadProc, lparam(@Result)); 
End; 

Function ProcessIDFromAppname32(appname: String): DWORD; 
{ Take only the application filename, not full path! } 
Var 
    snapshot: THandle; 
    processEntry: TProcessEntry32; 
Begin 
    Result := 0; 
    appname := UpperCase(appname); 
    snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
    If snapshot <> 0 Then 
    try 
     processEntry.dwSize := sizeof(processEntry); 
     If Process32First(snapshot, processEntry) Then 
     Repeat 
      If Pos(appname, 
      UpperCase(ExtractFilename(StrPas(processEntry.szExeFile)))) > 0 Then 
      Begin 
      Result := processEntry.th32ProcessID; 
      Break; 
      End; { If } 
     Until not Process32Next(snapshot, processEntry); 
    finally 
     CloseHandle(snapshot); 
    End; { try } 
End; 

function InjectDLL(dwPID: DWORD; DLLPath: PWideChar): integer; 
var 
    dwThreadID: Cardinal; 
    hProc, hThread, hKernel: THandle; 
    BytesToWrite, BytesWritten: SIZE_T; 
    pRemoteBuffer, pLoadLibrary: Pointer; 
begin 
    if not FileExists(DLLPath) then 
    begin 
    MessageBox(0, PWideChar('File ' + DLLPath + ' does not exist'), 'Error', 0); 
    Exit(0); 
    end; 

    hProc := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_QUERY_INFORMATION or 
    PROCESS_VM_OPERATION or PROCESS_VM_WRITE or PROCESS_VM_READ, False, dwPID); 
    if hProc = 0 then 
    Exit(0); 
    try 
    BytesToWrite := sizeof(WideChar) * (Length(DLLPath) + 1); 
    pRemoteBuffer := VirtualAllocEx(hProc, nil, BytesToWrite, MEM_COMMIT, 
     PAGE_READWRITE); 
    if pRemoteBuffer = nil then 
     Exit(0); 
    try 
     if not WriteProcessMemory(hProc, pRemoteBuffer, DLLPath, BytesToWrite, 
     BytesWritten) then 
     Exit(0); 
     hKernel := GetModuleHandle('kernel32.dll'); 
     pLoadLibrary := GetProcAddress(hKernel, 'LoadLibraryW'); 
     hThread := CreateRemoteThread(hProc, nil, 0, pLoadLibrary, pRemoteBuffer, 
     0, dwThreadID); 
     try 
     WaitForSingleObject(hThread, INFINITE); 
     finally 
     CloseHandle(hThread); 
     end; 
    finally 
     VirtualFreeEx(hProc, pRemoteBuffer, 0, MEM_RELEASE); 
    end; 
    finally 
    CloseHandle(hProc); 
    end; 
    Exit(1); 
end; 

const 
    PROCESS_NAME = 'Default_project.exe'; 

begin 
    try 
    { TODO -oUser -cConsole Main : Insert code here } 
    WriteLn(PROCESS_NAME + ' PID: ' + 
     IntToSTr(ProcessIDFromAppname32(PROCESS_NAME))); 
    InjectDLL(ProcessIDFromAppname32(PROCESS_NAME), 'dll_inj.dll'); 
    ReadLn; 
    except 
    on E: Exception do 
     WriteLn(E.ClassName, ': ', E.Message); 
    end; 

end. 
Cuestiones relacionadas