Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Подмена оконной процедуры, Как правильно завершить дело? 
V
    Опции темы
0xDEAD
Дата 8.9.2011, 10:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Добрый день.

У меня программа внедряет DLL в чужой процесс, а от туда DLL делает сабклассинг окну, таким образом:
Код

WNDPROC pOldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NewWndProc);


Все работает как надо, и когда я заканчиваю, я вызываю:
Код

SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pOldWndProc);


После чего сразу выгружаю DLL.

Проблема такая: иногда процесс вылетает, потому что пытается вызвать NewWndProc, а DLL уже выгружен.
Каким образом я могу подождать, пока все вызовы NewWndProc сделаются, чтоб только тогда выгрузить DLL?

P.S.
Я пробовал так:
Код

static LRESULT CALLBACK NewTaskbarProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    wnd_proc_call_counter++;

    // ...

    wnd_proc_call_counter--;

    return result;
}


Код

SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pOldWndProc);

while(wnd_proc_call_counter > 0)
    Sleep(10);


Все равно иногда вылетает, да и не красиво получается...

Спасибо!
PM MAIL   Вверх
BorisVorontsov
Дата 8.9.2011, 16:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Thinker
**


Профиль
Группа: Комодератор
Сообщений: 714
Регистрация: 3.11.2005
Где: Молдавия, г. Киши нёв

Репутация: 4
Всего: 10



Цитата(0xDEAD @  8.9.2011,  10:21 Найти цитируемый пост)
иногда процесс вылетает, потому что пытается вызвать NewWndProc, а DLL уже выгружен

Ты уверен, что именно в этом причина ошибки? После возврата GWL_WNDPROC все последующие сообщения, переданные в DispatchMessage, будут направлены в новую заданную оконную функцию


--------------------
[code=cpp]
const char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;
[/code]
PM MAIL ICQ GTalk   Вверх
0xDEAD
Дата 8.9.2011, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Уверен, так как программа вылетает на NewWndProc, вызванная из user32.
Программа многопоточная, может, дело в этом?
PM MAIL   Вверх
0xDEAD
Дата 8.9.2011, 23:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Да, функция и правда вызывается из нового потока.
Создается новый поток, который вызывает:
Код

PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_REMOVE);


И user32 почему-то в этом PeekMessage за одно вызывает:
Код

NewWndProc(hWnd, WM_NCHITTEST, 0, lParam);


И программа вылетает.

То есть наверно получается, что этот новый поток берет WndProc до того, как я вызываю SetWindowLongPtr, но вызывает его после того, как я выгружаю DLL.
Что делать?
PM MAIL   Вверх
GremlinProg
Дата 9.9.2011, 06:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(0xDEAD @  9.9.2011,  01:57 Найти цитируемый пост)
Что делать?

можно дождаться опустошения очереди сообщений прежде чем выгружать библиотеку ( через GetQueueStatus, например )

Это сообщение отредактировал(а) GremlinProg - 9.9.2011, 06:23


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
0xDEAD
Дата 9.9.2011, 12:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



GetQueueStatus Function
Retrieves the type of messages found in the calling thread's message queue.

Во первых, я вызываю SetWindowLongPtr с другого потока, во вторых, даже с того потока, как я могу использовать GetQueueStatus?

Код

while(GetQueueStatus(QS_ALLEVENTS))
    Sleep(10);

?

Не очень красиво и не очень надежно...
PM MAIL   Вверх
GremlinProg
Дата 9.9.2011, 12:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(0xDEAD @  9.9.2011,  14:26 Найти цитируемый пост)
Во первых, я вызываю SetWindowLongPtr с другого потока

ради бога, лишь бы в том же процессе smile
Цитата(0xDEAD @  9.9.2011,  14:26 Найти цитируемый пост)
во вторых, даже с того потока, как я могу использовать GetQueueStatus?

например: вместо явной смены процедуры окна - крутить PostMessage с каким-нибудь своим сообщением до тех пор, пока старшее слово от GetQueueStatus не обнулится, после чего - вернуть на место pOldWndProc и выгрузить длл ( последнее не стоит делать из адресного пространства выгружаемой длл smile )


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
0xDEAD
Дата 9.9.2011, 13:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Если я правильно понял, вы предлогаете сделать что-то в этом роде:

Код

PostMessage(hSubclassedWnd, UMW_UNSUBCLASS, 0, 0);
while(pOldWndProc)
    Sleep(10);


Код

static LRESULT CALLBACK NewTaskbarProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // ...

    switch(uMsg)
    {
    // ...
    case UMW_UNSUBCLASS:
        SetTimer(hWnd, 0x1337, 10, NULL);
        break;

    case WM_TIMER:
        if(wParam = 0x1337)
        {
            if(HIWORD(GetQueueStatus(QS_ALLINPUT) == 0)
            {
                SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pOldWndProc);
                pOldWndProc = NULL;
                KillTimer(hWnd, 0x1337);
            }
        }
        break;
    // ...
    }

    return result;
}



Вопрос: если процесс довольно активно посылает сообщения, не может получится так, что GetQueueStatus всегда будет что-то возвращать?
Процесс, которого я мучаю: експлорер smile
PM MAIL   Вверх
GremlinProg
Дата 10.9.2011, 20:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



не совсем так,
таймер я бы не советовал, т.к. QS_ALLINPUT включает QS_TIMER, который реагирует на WM_TIMER,
крутить - в смысле, крутить через саму очередь:
Код

...
case UMW_UNSUBCLASS:
  if( HIWORD( ::GetQueueStatus( QS_ALLINPUT ) ) != 0 ){
    ::PostMessage( hSubclassedWnd, UMW_UNSUBCLASS, 0, 0 );
  }else{
    ::SetWindowLongPtr ...
  }
  break;
...

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


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
0xDEAD
Дата 11.9.2011, 14:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Цитата(0xDEAD @  9.9.2011,  13:35 Найти цитируемый пост)
Вопрос: если процесс довольно активно посылает сообщения, не может получится так, что GetQueueStatus всегда будет что-то возвращать?

Тут я вроде был прав, с вашим методом программа заходит в бесконечный цикл, а GetQueueStatus(QS_ALLINPUT) никогда не обнуляется.

Я попробовал немного по другому:

Код

    SendMessage(hWnd, UMW_UNSUBCLASS, 0, 0);
    while(pOldWndProc)
    {
        Sleep(10);
        SendMessage(hWnd, UMW_UNSUBCLASS, 0, 0);
    }


Код

    case UMW_UNSUBCLASS:
        if(HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0)
        {
            SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pOldWndProc);
            pOldWndProc = NULL;
        }
        break;


почти всегда работает, но иногда получается так, что HIWORD(GetQueueStatus(QS_ALLINPUT)) не перестает возвращать 0x30 (QS_PAINT|QS_TIMER), и получается бесконечный цикл.

С QS_TIMER-ом все ясно, QS_PAINT тоже вроде как-то особенно посылается. Наверно я их просто не буду проверять...

Это сообщение отредактировал(а) 0xDEAD - 11.9.2011, 15:25
PM MAIL   Вверх
0xDEAD
Дата 11.9.2011, 15:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 20.9.2009

Репутация: 3
Всего: 3



Сделал так:

Код

    SendMessage(hWnd, UMW_UNSUBCLASS, 0, 0);
    while(pOldWndProc)
    {
        Sleep(10);
        SendMessage(hWnd, UMW_UNSUBCLASS, 0, 0);
    }


Код

    case UMW_UNSUBCLASS:
        if(HIWORD(GetQueueStatus(QS_INPUT | QS_POSTMESSAGE | QS_HOTKEY | QS_SENDMESSAGE)) == 0)
        {
            SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pOldWndProc);
            pOldWndProc= NULL;
        }
        break;


Погонял немного, не вылетает и не зависает.
Вроде проблема решена, спасибо за помощь smile
PM MAIL   Вверх
GremlinProg
Дата 12.9.2011, 06:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(0xDEAD @  11.9.2011,  16:03 Найти цитируемый пост)
с вашим методом программа заходит в бесконечный цикл, а GetQueueStatus(QS_ALLINPUT) никогда не обнуляется.

логично, пока стоит какой-нибудь таймер или видно какое-либо окно потока,
на это и был намек smile


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1146 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.