Форум для программистов - задавайте интересные вопросы - получайте квалифицированные ответы
Хук на движение формы
  • catchookcatchook June 2011
    написал глобальный хук на движение формы, вот код:

    library Project2;

    uses
    Windows,
    Messages,
    Sysutils,
    Dialogs;

    var
    myHook: HHook = 0;
    myFile:TextFile;
    {$R *.res}
    function MsgProc(Code: integer; wParam: Word; lParam: Longint): Longint; stdcall;
    var mas:array[0..255]of char;
    begin
    if Code<0 then begin<br /> result:=CallNextHookEx(myHook,Code,wParam,lParam);
    exit;
    end;
    if CWPRETSTRUCT(Pointer(LParam)^).message=wm_move then begin
    if IsWindowVisible(CWPRETSTRUCT(Pointer(LParam)^).hwnd) then begin
    getwindowtext(CWPRETSTRUCT(Pointer(LParam)^).hwnd,mas,sizeof(mas));
    { SetWindowLong(CWPRETSTRUCT(Pointer(LParam)^).hwnd, GWL_EXSTYLE,
    GetWindowLong(CWPRETSTRUCT(Pointer(LParam)^).hwnd, GWL_EXSTYLE) or WS_EX_LAYERED);
    SetLayeredWindowAttributes(CWPRETSTRUCT(Pointer(LParam)^).hwnd, 0, 150, LWA_ALPHA); }

    // Writeln(myFile,mas);
    ShowMessage(mas);
    end;
    end;

    result:=CallNextHookEx(myHook,Code,wParam,lParam);
    end;


    procedure setHook(Hook: boolean) export; stdcall;
    begin
    if Hook then
    begin
    // AssignFile(myFile, 'C:\Test.txt');
    // ReWrite(myFile);
    if myHook=0 then
    myHook:=SetWindowsHookEx(WH_CALLWNDPROCRET,@MsgProc,HInstance,0);
    ShowMessage('Хук установлен');
    end
    else
    begin
    // CloseFile(myFile);
    if myHook<>0 then
    UnHookWindowsHookEx(myHook);
    ShowMessage('Хук снят');
    myHook:=0;
    end;
    end;

    exports setHook name 'SetHook';
    begin
    end.


    в таком вот варианте работает все замечательно, т.е. при начале движения формы выскакивает сообщение с заголовком формы.
    Если раскомментировать эти строки

    { SetWindowLong(CWPRETSTRUCT(Pointer(LParam)^).hwnd, GWL_EXSTYLE,
    GetWindowLong(CWPRETSTRUCT(Pointer(LParam)^).hwnd, GWL_EXSTYLE) or WS_EX_LAYERED);
    SetLayeredWindowAttributes(CWPRETSTRUCT(Pointer(LParam)^).hwnd, 0, 150, LWA_ALPHA); }


    то при начале движение формы, форма становится полупрозрачной. НО если раскомментировать остальное, т.е. если я хочу что бы заголовки формы записывались в файл то получается вовсе не гуд. Поясню, запускаю хук пока двигаю свою форму все ок, ее заголовок пишется в файл, но стоит мне перейти на какую-нить чужую форму и попытаться сдвинуть ее тут же происходит "падение" проги (форму которой я двигаю).
    Как бы это не принципиально, мне важно что бы она становилось полупрозрачной и это работает, но меня заинтересовал этот вопрос, хочу разобраться в этом для самосовершенствования, из-за чего это происходит?
    Есть конечно догадки, но может они глупо звучат )) я думаю это из-за того что на выполнение Writeln нужно больше времени чем на ShowMessage
    т.е. не успевают остальные сообщения в очереди обрабатываться.
    Прошу разъяснить этот вопрос тех кто в теме )
  • shell32shell32 June 2011
    У каждого приложения на которое ставят хук по сути заново загружается dll, свой экземпляр, и только в одном у тебя эти переменные будут "рабочие".

    Цитата
    Возвращаемое значение: функция SetWindowsHookEx возвращает дескриптор установленной ловушки, именно этот дескриптор нам и надо будет сделать доступным ВСЕМ экземплярам отображаемой DLL. Как это сделать я расскажу после небольшого примера, показывающего на практике необходимость сохранять дескриптор ловушки для того, что бы суметь вызвать предыдущую ловушку в цепочке.

    Для того, что бы все экземпляры DLL, находящиеся в разных процессах, имели доступ к дескриптору ловушки, надо выделить какую-то область, доступ к которой будут иметь все "желающие". Для этого воспользуемся одним из мощнейших механизмов Windows под названием "Файлы, отображённые в память" (Memory Mapped Files). Механизм файлов, отображённых в память (MMF - Memory Mapped Files) позволяет резервировать определённую область АП системы Windows, для которой назначаются страницы физической памяти. Таким образом, с помощью MMF можно отображать в память не только файлы, но и данные, ссылаясь на них из своих программ с помощью указателей.

    пока не исправишь недочеты (а скорее всего их исправление приведет к работоспособности) смотреть смысла особого нет
  • catchookcatchook June 2011
    Только что попробовал использовать MMF результат такой же, вот посмотрите код, может я что-то не так сделал?

    library Project2;


    uses
    Windows,
    Messages,
    Sysutils,
    Dialogs;
    type
    PGlobalDLLData = ^TGlobalDLLData;
    TGlobalDLLData = packed record
    SysHook: HWND;
    MyAppWnd: HWND;
    myFile:TextFile;
    end;

    var
    GlobalData: PGlobalDLLData;
    MMFHandle: THandle;

    {$R *.res}
    function MsgProc(Code: integer; wParam: Word; lParam: Longint): Longint; stdcall;
    var mas:array[0..255]of char;
    begin
    if Code<0 then begin<br /> result:=CallNextHookEx(GlobalData^.SysHook,Code,wParam,lParam);
    exit;
    end;
    if CWPRETSTRUCT(Pointer(LParam)^).message=wm_move then begin
    GlobalData^.MyAppWnd:=CWPRETSTRUCT(Pointer(LParam)^).hwnd;
    if IsWindowVisible(GlobalData^.MyAppWnd) then begin
    getwindowtext(GlobalData^.MyAppWnd,mas,sizeof(mas));
    { SetWindowLong(GlobalData^.MyAppWnd, GWL_EXSTYLE,
    GetWindowLong(GlobalData^.MyAppWnd, GWL_EXSTYLE) or WS_EX_LAYERED);
    SetLayeredWindowAttributes(GlobalData^.MyAppWnd, 0, 150, LWA_ALPHA); }
    Write(GlobalData^.myFile,mas);
    //ShowMessage(mas);
    end;
    end;


    result:=CallNextHookEx(GlobalData^.SysHook,Code,wParam,lParam);
    end;


    procedure setHook(Hook: boolean) export; stdcall;
    begin
    if Hook then
    begin
    AssignFile(GlobalData^.myFile, 'C:\Test.txt');
    ReWrite(GlobalData^.myFile);
    if GlobalData^.SysHook=0 then
    GlobalData^.SysHook:=SetWindowsHookEx(WH_CALLWNDPROCRET,@MsgProc,HInstance,0);
    ShowMessage('Хук установлен');
    end
    else
    begin
    CloseFile(GlobalData^.myFile);
    if GlobalData^.SysHook<>0 then
    UnHookWindowsHookEx(GlobalData^.SysHook);
    ShowMessage('Хук снят');
    GlobalData^.SysHook:=0;
    end;
    end;

    procedure OpenGlobalData();
    begin
    MMFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), Pchar('KeyMMF'));

    if MMFHandle = 0 then
    begin
    MessageBox(0, 'Can''t create FileMapping', 'Message from keyhook.dll', 0);
    Exit;
    end;

    {отображаем глобальные данные на АП вызывающего процесса и получаем указатель
    на начало выделенного пространства}
    GlobalData:= MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData));
    if GlobalData = nil then
    begin
    CloseHandle(MMFHandle);
    MessageBox(0, 'Can''t make MapViewOfFile', 'Message from keyhook.dll', 0);
    Exit;
    end;

    end;

    procedure CloseGlobalData();
    begin
    UnmapViewOfFile(GlobalData);
    CloseHandle(MMFHandle);
    end;

    procedure DLLEntryPoint(dwReason: DWord); stdcall;
    begin
    case dwReason of
    DLL_PROCESS_ATTACH: OpenGlobalData;
    DLL_PROCESS_DETACH: CloseGlobalData;
    end;
    end;
    exports setHook name 'SetHook';
    begin
    DLLEntryPoint(DLL_PROCESS_ATTACH);
    end.
  • shell32shell32 June 2011
    Что-то как-то мало букавок :)
    Вот OpenFileMapping() не вижу...
    обработки ошибок нет, там можно долго ловить багу.

    что нибудь типа этого сооруди -

    type
    PGlobalDLLData = ^TGlobalDLLData;
    TGlobalDLLData = packed record
    SysHook: HWND;
    MainWnd: HWND;
    end;

    const
    MMFName: PChar = 'HAHAHA';

    var
    MMFHandle: THandle;
    MMFData: PGlobalDLLData;

    implementation

    function CreateMMF(Name: string; Size: Integer): THandle;
    begin
    Result:=CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, Size, PChar(Name));
    if Result<>0 then begin
    if GetLastError=ERROR_ALREADY_EXISTS then begin
    CloseHandle(Result);
    Result:=0;
    end;
    end;
    end;

    function OpenMMF(Name: string): THandle;
    begin
    Result:=OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(Name));
    end;

    function MapMMF(MMFHandle: THandle): Pointer;
    begin
    Result:=MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    end;

    function UnMapMMF(P:Pointer):Boolean;
    begin
    Result:=UnmapViewOfFile(P);
    end;

    function CloseMMF(MMFHandle:THandle):Boolean;
    begin
    Result:=CloseHandle(MMFHandle);
    end;

    function UnMapAndCloseMMF:Boolean;
    begin
    Result:=False;
    if(MMFData<>nil) then
    if UnMapMMF(MMFData) then begin
    MMFData:=nil;
    if CloseMMF(MMFHandle) then begin
    MMFHandle:=0;
    Result:=True;
    end;
    end;
    end;

    function GetData(var MMFD:PGlobalDLLData):boolean;
    var MMFHandle:THandle;
    begin
    result:=false;
    MMFHandle:=OpenMMF(MMFName);
    if MMFHandle<>0 then begin
    MMFData:=MapMMF(MMFHandle);
    if MMFData<>nil then begin
    MMFD:=MMFData;
    result:=true;
    end;
    end;
    end;



    и установка:

    function MsgFilterFunc(Code: Integer; wParam, lParam: Longint): Longint; stdcall;
    var MMFHandle:THandle; MMFData:PGlobalDLLData;
    begin
    Result:=0;
    MMFHandle:=OpenMMF(MMFName);
    if MMFHandle<>0 then begin

    MMFData:=MapMMF(MMFHandle);
    if MMFData<>nil then begin
    if (Code<0)or(wParam = PM_NOREMOVE) then<br /> Result:=CallNextHookEx(MMFData.SysHook, Code, wParam, lParam)
    else begin



    Result:=CallNextHookEx(MMFData.SysHook, Code, wParam, lParam);
    end;
    UnMapMMF(MMFData);
    end else begin
    //Writelog('MapMMF filed: '+SysErrorMessage(GetLastError)+' ['+GetName_DLL+']');
    end;
    CloseMMF(MMFHandle);
    end else begin
    //Writelog('OpenMMF filed: '+SysErrorMessage(GetLastError)+' ['+GetName_DLL+']');
    end;
    end;

    function SetHook(WinHandle:HWND): Boolean; stdcall;
    begin
    Result := False;
    if (MMFData = nil) and (MMFHandle = 0) then begin
    MMFHandle := CreateMMF(MMFName, SizeOf(TGlobalDLLData));
    if MMFHandle <> 0 then begin
    MMFData := MapMMF(MMFHandle);
    if MMFData <> nil then begin
    MMFData.MainWnd := WinHandle;
    MMFData.SysHook := SetWindowsHookEx(WH_KEYBOARD, @MsgFilterFunc,HInstance, 0);
    if MMFData.SysHook = 0 then begin
    //Writelog('SetHook SetWindowsHookEx filed');
    UnMapAndCloseMMF();
    end else begin
    Result := True;

    end;
    end else begin
    //Writelog('SetHook MapMMF filed: '+SysErrorMessage(GetLastError));

    CloseMMF(MMFHandle);
    MMFHandle := 0;
    end;
    end else begin
    //Writelog('SetHook CreateMMF filed: '+SysErrorMessage(GetLastError));
    end;
    end;
    end;

    function FreeHook: Boolean; stdcall;
    begin
    Result:=False;
    try
    if (MMFData<>nil) and (MMFHandle<>0) then
    if UnHookWindowsHookEx(MMFData^.SysHook) then
    Result:=UnMapAndCloseMMF;
    except
    end;
    end;
  • catchookcatchook June 2011
    shell32, код трудно читаем...
    Попытался что-то сотворить теперь ничего не происходит, не ошибки не моих действий )
    Вместо Writelog я поставил ShowMessage, при установки хука сообщения не выскакивает, но и ничего не происходит.
    Вполне вероятно что я что-то напутал, может ты попробуешь дополнить мой код (мой код попроще будет и свой ты уже знаешь, что там к чему)
    Если конечно время позволяет )))
  • shell32shell32 June 2011
    У меня код на хук клавы был, под свой подстрой и я вырезал то что должно быть внутри :)
    После

    if (Code<0)or(wParam = PM_NOREMOVE) then<br />Result:=CallNextHookEx(MMFData.SysHook, Code, wParam, lParam)
    else begin

    нужно вставить твой код. В MMFData хранится дескриптор ловушки (и у меня там хендл формы, но тебе не нужен, туда нужно myFile:TextFile; поместить) и все что тебе понадобится.

    дельфи под рукой нет, не могу проверить (((

    UPD:
    попробуй оттуда вызывать такое (т.е. вызывая assignfile() каждый раз), для теста в лог:

    procedure Writelog(filename,value: string);
    var f:textFile;
    begin
    assignfile(f, filename);
    if fileexists(filename)
    then append(f)
    else rewrite(f);
    try
    writeln(f,Value);
    finally
    Closefile(f);
    end;
    end;
Webparadox - разработка мобильных приложений под iOS и Android.