SetWindowLong GWL_WNDPROC для другого процесса
От: VisitorS  
Дата: 04.10.07 11:22
Оценка:
Добрый день,

передо мной стоит такая задача. сделать невозможным закрытие/сворачивание/максимизирование любого окна из управляемого кода.

Системное меню окон я поборол ровно как и Alt+F4. Получилось задисэйблить кнопку "Х", но не мин/макс. с мин/макс у меня проблемы. Я решил сделать так, чтоб когда пользователь нажимает на эти кнопки ничего не происходило. Для этого я хочу использовать функцию SetWindowLong с параметром GWL_WNDPROC. Как сказано в мсдне эта функция в комбинации с этим аргументом:


Sets a new address for the window procedure.



Т.е. я устанавливаю новый адрес процедуры окна и в новой процедуре проверяю пришедшее сообщение — если оно не мин и не макс вызываем старую процедуру, а если мин или макс — ничего не делаем.

Вот код, который я использую (C#):

void Form_Load(object sender, EventArgs e)
        {
            m_Notepad = Process.Start("notepad");

            newWndProc = new WndProcDelegate(NewWndProc);
            oldWndProc = new IntPtr(GetWindowLong(m_NotepadHwnd, GWL_WNDPROC));
            SetWindowLong(m_NotepadHwnd, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProc).ToInt32());
        }

        public IntPtr NewWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == WM_SYSCOMMAND)
            {
                if (wParam.ToInt32() == SC_MINIMIZE)
                {
                    return IntPtr.Zero;
                }
...
            }
            return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
        }


Данный код пытается заменить оконную функцию блокнота. Но ничего не происходит. Если попытаться заменить оконную процедуру текущего процесса — все работает как надо.

Я так понимаю, нужно подключиться к чужому процессу и в нем менять оконную функцию.

Для этого нужно (поправьте меня если я в чем-то не прав):

1. Выделить память в чужом процессе для моей функции, которая будет заменять оконную функцию.
2. скопировать ее в это пространство.
3. создать удаленный поток (CreateRemoteThread) указав ему адрес этой же функции.

Если так, то у меня еще несколько вопросов:
а) как должна выглядеть функция, которую я собираюсь копировать в чужой процесс (которая будет менять оконную функцию)? я так понимаю, она не может быть написана на с#?
б) скопировать в чужой процесс придется не только эту функцию, но также и новую оконную функцию?
Re: SetWindowLong GWL_WNDPROC для другого процесса
От: d.4 Россия  
Дата: 04.10.07 11:59
Оценка:
Здравствуйте, VisitorS, Вы писали:

VS>Добрый день,


VS>передо мной стоит такая задача. сделать невозможным закрытие/сворачивание/максимизирование любого окна из управляемого кода.


MSDN:

Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process.


Стать частью любого другого процесса Вам помогут хуки. Почитайте тут: http://www.rsdn.ru/summary/292.xml
... << RSDN@Home 1.2.0 alpha rev. 774>>
Re[2]: SetWindowLong GWL_WNDPROC для другого процесса
От: VisitorS  
Дата: 04.10.07 14:33
Оценка:
d.4>Стать частью любого другого процесса Вам помогут хуки. Почитайте тут: http://www.rsdn.ru/summary/292.xml

Мои попытки установить хук на WH_CALLWNDPROC блокнота были обречены на провал. Что я делаю не так?

        private void SetHook()
        {
            uint processId;
            uint notepadUIThreadId = GetWindowThreadProcessId(new IntPtr(m_NotepadHwnd), out processId);
            HookProc hookProc = new HookProc(WndProcCalled);
            SetWindowsHookEx(WH_CALLWNDPROC, hookProc, Marshal.GetHINSTANCE(
                    Assembly.GetExecutingAssembly().GetModules()[0]),
                    (int)notepadUIThreadId);
        }

        private int WndProcCalled(int nCode, int wParam, IntPtr lParam)
        {
            return CallNextHookEx(m_Hook, nCode, wParam, lParam);            
        }


Спасибо.
Re[3]: SetWindowLong GWL_WNDPROC для другого процесса
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 04.10.07 18:02
Оценка:
Здравствуйте, VisitorS, Вы писали:

d.4>>Стать частью любого другого процесса Вам помогут хуки. Почитайте тут: http://www.rsdn.ru/summary/292.xml


VS>Мои попытки установить хук на WH_CALLWNDPROC блокнота были обречены на провал. Что я делаю не так?


VS>
VS>        private void SetHook()
VS>        {
VS>            uint processId;
VS>            uint notepadUIThreadId = GetWindowThreadProcessId(new IntPtr(m_NotepadHwnd), out processId);
VS>            HookProc hookProc = new HookProc(WndProcCalled);
VS>            SetWindowsHookEx(WH_CALLWNDPROC, hookProc, Marshal.GetHINSTANCE(
VS>                    Assembly.GetExecutingAssembly().GetModules()[0]),
VS>                    (int)notepadUIThreadId);
VS>        }

VS>        private int WndProcCalled(int nCode, int wParam, IntPtr lParam)
VS>        {
VS>            return CallNextHookEx(m_Hook, nCode, wParam, lParam);            
VS>        }

VS>


VS>Спасибо.


Хук для другого процесса не должен находится в exeшнике.
И вообще стоит проделать такое на С
Re[4]: SetWindowLong GWL_WNDPROC для другого процесса
От: VisitorS  
Дата: 09.10.07 20:15
Оценка:
G>Хук для другого процесса не должен находится в exeшнике.
G>И вообще стоит проделать такое на С

Значит, создал я Visual C++ win32 console application проект. В этом проекте есть файл UnmanagedHooks.cpp, а в нем следующий код:

HHOOK hMouseHook = 0;

LRESULT CALLBACK MouseHookProc (int nCode, WPARAM wParam, LPARAM lParam )
{
    return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}

extern "C" __declspec(dllexport) void SetMouseHookForThread(DWORD threadId);

void SetMouseHookForThread(DWORD threadId)
{
    if(hMouseHook != 0)
        return;
    
    HINSTANCE hIns = GetModuleHandleA(NULL);
    
    hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProc, hIns, threadId);
}


в управляемом коде (C#) вызывается метод SetMouseHookForThread():


        [DllImport("UnmanagedHooks.dll")]
        public static extern void SetMouseHookForThread(int threadId);

        private void SetHook()
        {
            SetMouseHookForThread((int)notepadUIThreadId);
        }


Хук устанавливается нормально, но, как только приходит сообщение, блокнот (в данном примере) сразу сообщает о внутренней ошибке. что я делаю не так?
Re[5]: SetWindowLong GWL_WNDPROC для другого процесса
От: StDenis Россия  
Дата: 11.10.07 06:13
Оценка:
VS>Хук устанавливается нормально, но, как только приходит сообщение, блокнот (в данном примере) сразу сообщает о внутренней ошибке.
мне кажется проблема в том, что адрес, который передается в SetWindowsHookEx актуален только в твоем процессе. поэтому когда хук возбуждается, тебя забрасывает на хрен-знает-какое-место-где-никого-нет.
решение, которое мне видится — загрузить DLL в адресное пространство процесса notepad-а и сделать так, чтобы она установила хук в контексте этого процесса [первая мысль, может не правильная, создать именованный memory-mapping file, положить туда координаты процесса за которым нужно смотреть и ждать загрузки в него, оценивая обстановку в DLL_PROCESS_ATTACH]

p.s. просто мысли вслух, ногами не бить
Re[6]: SetWindowLong GWL_WNDPROC для другого процесса
От: VisitorS  
Дата: 11.10.07 10:23
Оценка:
Здравствуйте, StDenis, Вы писали:

SD>мне кажется проблема в том, что адрес, который передается в SetWindowsHookEx актуален только в твоем процессе. поэтому когда хук возбуждается, тебя забрасывает на хрен-знает-какое-место-где-никого-нет.


Да, так и есть. В коде, который я написал выше, используется GetModuleHandle(NULL) — эта функция возвращает hinstance моего C#-exeшника. Для того, чтоб получить hinstance моей длл — UnmanagedHooks.dll — нужно GetModuleHandle(NULL) заменить на GetModuleHandle("UnmanagedHooks.dll").

Взвращаемся к теме поста. оконная функуия меняется без проблем, но только когда приходит определенное сообщение (например WH_MOUSE). Да и не хотелось бы, чтоб на сообщение WH_MOUSE висел постоянно дополнительный hookproc. нужно сделать что-то вроде такого:

        hMouseHook = SetWindowsHookEx(WH_MOUSE, ChangeWndProc, hIns, threadId);
    PostMessage(foundHwnd, WH_MOUSE, 0, 0);
    UnhookWindowsHookEx(hMouseHook);


т.е. чтоб ChangeWndProc исполнилась только один раз. Хук устанавливается без ошибок. PostMessage помещает сообщение в очередь (проверил на примере других сообщений с очевидным результатом их обработки). Но не вызывается ChangeWndProc. Такая же история и с PostThreadMessage.

"Вручную" (когда мышь над процессом threadId) все работает как надо. Вручную, это когда вызывается только этот код, а PostMessage вы делаете сами с помощью мыши:

        hMouseHook = SetWindowsHookEx(WH_MOUSE, ChangeWndProc, hIns, threadId);


есть идеи как заставить работать этот трехстрочный код?

П.С. не обращайте внимания на WH_MOUSE. это сообщение выбрано исключительно в тестовых целях.
Re[7]: SetWindowLong GWL_WNDPROC для другого процесса
От: StDenis Россия  
Дата: 11.10.07 10:40
Оценка:
так WH_MOUSE это не сообщение, и оно ессно не вызовет обработчик хука. может использовать WM_MOUSEMOVE, но я не уверен, что от этого возбудится хук.
Re[8]: SetWindowLong GWL_WNDPROC для другого процесса
От: VisitorS  
Дата: 11.10.07 10:55
Оценка:
шлем WM_LBUTTONDOWN.
такой код тоже не приносит ожидаемого результата. работает по-прежнему:

    hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProc, hIns, threadId);
    PostMessage(foundHwnd, WH_MOUSE, WM_LBUTTONDOWN, 0);
    UnhookWindowsHookEx(hMouseHook);
Re[9]: SetWindowLong GWL_WNDPROC для другого процесса
От: StDenis Россия  
Дата: 11.10.07 11:03
Оценка:
WH_MOUSE это не сообщение! поэтому код:
    PostMessage(foundHwnd, WH_MOUSE, WM_LBUTTONDOWN, 0);
выполняется и "понимается" окном как:
    PostMessage(foundHwnd, WM_SETFOCUS, WM_LBUTTONDOWN, 0);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.