Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Зачем проверять IsWindowUnicode перед SetWindowLon


Автор: Ruzzz 9.11.2009, 08:00
Часто вижу такой код (например при написании dll):
Код
    if (IsWindowUnicode(hwndParent))
        lpWndProcOld = (WNDPROC)SetWindowLongPtrW(hwndParent,GWL_WNDPROC,(LONG)WndProc);
    else
        lpWndProcOld = (WNDPROC)SetWindowLongPtrA(hwndParent,GWL_WNDPROC,(LONG)WndProc);

Есть ли в этом смысл?
Если я компилирую dll как юникод, и будет использоваться SetWindowLongPtrW, а hwndParent допустим будет указывать на не юникод окно, то к чему это может привести? В MSDN в описании SetWindowLongPtr и SetWindowLong ничего не нашел. Учитывая что оконная функция ничем не отличается, то что будет не так?

Автор: Earnest 9.11.2009, 09:48
Скорее всего, неродная функция просто не сработает. 

Автор: Ruzzz 9.11.2009, 10:15
Сделал так:
Код
    if (!IsWindowUnicode(hwndParent))
        lpWndProcOld = (WNDPROC)SetWindowLongPtrW(hwndParent,GWL_WNDPROC,(LONG)WndProc);
    else
        lpWndProcOld = (WNDPROC)SetWindowLongPtrA(hwndParent,GWL_WNDPROC,(LONG)WndProc);

Все работает.

В чем же причина?

Автор: GremlinProg 9.11.2009, 12:21
можешь привести пример такой dll?

может там все же было что-то вроде такого:
Код

if ( ::IsWindowUnicode( hwndParent ) )
        lpWndProcOld = (WNDPROC)::SetWindowLongPtr( hwndParent, GWL_WNDPROC, (LONG)WndProcW );
    else
        lpWndProcOld = (WNDPROC)::SetWindowLongPtr( hwndParent, GWL_WNDPROC, (LONG)WndProcA );

Автор: Ruzzz 9.11.2009, 13:03
GremlinProg, мне уже подсказали что в зависимости от того какой версией SetWindowLongPtr воспользуемся, винда будет слать специфические сообщения или анси или юникодные, а ваш пример еще и показывает что иногда нужно об этом заботиться и подготавливать заранее две версии оконной процедуры. Но как я понимаю если я не «ловлю» такие сообщения, то все равно нужно предусмотреть в своей процедуре разные варианты CallWindowProc, если я вызываю ее для сообщений по умолчанию. Так ли это? Ну и какие еще могут быть ситуаци, которые требуют разных версий оконных процедкр? Мне бы примеры таких сообщений. 

То есть правильно ли я понял:
В зависимости от того оконную процедуру какого окна, юникодного или нет, мы переопределяем, я должен в своей процедуре (или одной или сделать две версии) предусмотреть:
- Вызов соответствующий CallWindowProc

В зависимости от того какую версию SetWindowLongPtr я использую, винда будет думать что я хочу принимать соотв-щие данные, а значит:
- сообщения, со строковыми данными будут «приносить» соответствующие строки (пример бы таких сообщений)
- а значит и оконная функция по умолчанию будет соот-щей версии?

Так?

Автор: Ruzzz 9.11.2009, 13:23
Нашел вот это http://www.codeproject.com/KB/winsdk/safesubclassing.aspx

Автор: GremlinProg 9.11.2009, 13:40
Цитата(Ruzzz @  9.11.2009,  15:03 Найти цитируемый пост)
в зависимости от того какой версией SetWindowLongPtr воспользуемся, винда будет слать специфические сообщения или анси или юникодные

в данном случае, особой разницы нет, какой функцией поставить процедуру окна,
2 варианта сделаны скорее для поддержки совместимости с более ранними версиями ОС:
Цитата(GetWindowLong Function @  MSDN)

GetWindowLongA is also supported to provide more consistent behavior across all Windows operating systems

разница проявится при установке разных типов процедур окна
возьмем, к примеру сообщение WM_GETTEXT:
с этим сообщением передается буфер и число символов в нем,

если для ANSI-окон поставить UNICODE-процедуру не критично,
т.к. sizeof(char) < sizeof(wchar_t), то обратное может приветси к ошибке,
а результат в обоих случаях не будет понятен отправителю сообщения

в DLL проверка IsWindowUnicode ставится потому, что саму DLL можно подгрузить как в ANSI, так и в UNICODE-проект,
вот по большей части именно поэтому API экспортируются в двух экземплярах: с постфиксами W и A
Цитата(Ruzzz @  9.11.2009,  15:03 Найти цитируемый пост)
Но как я понимаю если я не «ловлю» такие сообщения, то все равно нужно предусмотреть в своей процедуре разные варианты CallWindowProc, если я вызываю ее для сообщений по умолчанию. Так ли это?

Цитата(Ruzzz @  9.11.2009,  15:03 Найти цитируемый пост)
То есть правильно ли я понял:В зависимости от того оконную процедуру какого окна, юникодного или нет, мы переопределяем, я должен в своей процедуре (или одной или сделать две версии) предусмотреть:- Вызов соответствующий CallWindowProc

если ловишь оконные сообщения из "чужой" программы, как в случае с длл, то да - нужно учесть все,
т.к. даже DefWindowProc аналогично - существует в двух экземплярах
Цитата(Ruzzz @  9.11.2009,  15:03 Найти цитируемый пост)
В зависимости от того какую версию SetWindowLongPtr я использую, винда будет думать что я хочу принимать соотв-щие данные, а значит

винда будет думать, опираясь исключительно на то, какой функцией создано окно:
CreateWindowA, CreateWindowW, CreateWindowExA или CreateWindowExW,
т.е. по сути, что возвращает IsWindowUnicode, то и будет приниматься во внимание, остальное на совести программиста

Автор: GremlinProg 9.11.2009, 14:29
Цитата(Ruzzz @  9.11.2009,  15:23 Найти цитируемый пост)
Нашел вот это http://www.codeproject.com/KB/winsdk/safesubclassing.aspx

судя по отзывам, получилось,
видимо данные сообщения транслируются в этих двух точках: SetWindowLong и CallWindowProc

только вот что будешь делать, когда на разных типах окон будут посылаться разные сами сообщения или уведомления,
например то же TVN_GETDISPINFO, которое UNICODE окна отсылают как TVN_GETDISPINFOW(TVN_FIRST-52), а ANSI - как TVN_GETDISPINFOA(TVN_FIRST-3)?
разве что проверять в рантайме:
Код

if ( ::IsWindowUnicode( hwnd ) )
        if( code == TVN_GETDISPINFOW ){
                //...
        }
    else
        if( code == TVN_GETDISPINFOA ){
                //...
        }

не так уж и удобно

SetWindowLong и CallWindowProc не знают ничего о treeview,
подменить индекс сообщения они не смогут,
так что даже в случае из статьи, к IsWindowUnicode придется вернуться уже в теле процедуры окна,

надежнее всего будет просто задать соответствующий вариант процедуры окна

Автор: Ruzzz 9.11.2009, 14:44
GremlinProg, С сообщениями я все понял, но вот как быть с CallWindowProc и DefWindowProc. Решил что понял, но тут мне ответили на одном форуме:

На мои слова:
«in gen_tray\TRAYCTL.C line
int ret = CallWindowProc(lpOldWinampWndProc,hwnd,message,wParam,lParam);
better to replace:
if (IsWindowUnicode(hwnd))
CallWindowProcW(lpOldWinampWndProc,hwnd,message,wParam,lParam);
else
CallWindowProcA(lpOldWinampWndProc,hwnd,message,wParam,lParam);»

«Ruzzz: that's not required as the OS will do the relevant mapping based on how you subclassed to begin with. so in this case as long as a SetWindowLongPtrW(GWLP_WNNPROC) is correctly applied then it'll map things through CallWindowProcW(..) as long as nothing else has done a CallWindowProcA(..) or a SetWindowLongPtrA(GWLP_WNNPROC).»

при этом они используют:
Код

    if (!IsWindowUnicode(hwndParent))
        lpWndProcOld = (WNDPROC)SetWindowLongPtrW(hwndParent,GWL_WNDPROC,(LONG)WndProc);
    else
        lpWndProcOld = (WNDPROC)SetWindowLongPtrA(hwndParent,GWL_WNDPROC,(LONG)WndProc);

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

Автор: GremlinProg 9.11.2009, 15:13
Цитата(Ruzzz @  9.11.2009,  16:44 Найти цитируемый пост)
«Ruzzz: that's not required as the OS will do the relevant mapping based on how you subclassed to begin with. so in this case as long as a SetWindowLongPtrW(GWLP_WNNPROC) is correctly applied then it'll map things through CallWindowProcW(..) as long as nothing else has done a CallWindowProcA(..) or a SetWindowLongPtrA(GWLP_WNNPROC).»

я так понимаю, он и пишет, что мол разницы нет что вызывать: SetWindowLongPtrA или SetWindowLongPtrW,
т.к. нет необходимости транслировать строки, их там просто нет
Цитата(Ruzzz @  9.11.2009,  16:44 Найти цитируемый пост)
при этом они используют

это уже я не понял )
какой смысл для юникод-окна вызывать ANSI версию, а для ANSI-окна - юникод версию, да еще опираться при этом на родительское окно?
разве что в целях эксперимента, чтобы показать, что мол процедура окна у них "симметрична"
Цитата(Ruzzz @  9.11.2009,  16:44 Найти цитируемый пост)
может они предполагают что компилироваться будет только Юникод-версия? или я не пойму.

ну да, может быть,
винамп, по крайней мере, последних версий - в юникоде

Автор: Ruzzz 9.11.2009, 15:27
GremlinProg, аааа ) я ошибся, без ! smile
Винамп или сам плохо поддерживает юникод или некоторые модули которые идут с ним, по крайней мере в VirtualBox в XP поменял язык для Ansi приложений и получил кое-где крякозябры в русифицированном винампе, ну да не про это smile

У них в SDK есть строки:
Код

    if (IsWindowUnicode(hwndParent))
        lpWndProcOld = (WNDPROC)SetWindowLongPtrW(hwndParent,GWL_WNDPROC,(LONG)WndProc);
    else
        lpWndProcOld = (WNDPROC)SetWindowLongPtrA(hwndParent,GWL_WNDPROC,(LONG)WndProc);


При этом говорят что нет смысла в этом:
Код

    if (IsWindowUnicode(hwnd))
        CallWindowProcW(lpOldWinampWndProc,hwnd,message,wParam,lParam);
    else
        CallWindowProcA(lpOldWinampWndProc,hwnd,message,wParam,lParam);


Не понимаю почему? Только если они предполагают что этот модуль будет компилироваться как юникод? тогда еще понимаю. То есть будет явно подставлена CallWindowProcW, ну тогда нафига они верхний IsWindowUnicode не уберут? не пойму.

Автор: GremlinProg 9.11.2009, 16:34
в принципе, если локаль фиксирована, т.е. четко выставлена при старте в какой-нибудь cp1252,
то при такого рода неявных трансляциях сообщений вполне могут проскальзывать и кракозябры,
тут же физически производится перевод из MBS в WCS и обратно,

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

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

#define MY_UNICODE_WNDPROC
#include "my-wndproc.h"
#undef MY_UNICODE_WNDPROC
#include "my-wndproc.h"

естественно со всеми вытекающими,
т.е. пишем один транс-код, но ориентированный на MY_UNICODE_WNDPROC, а не _UNICODE
Цитата(Ruzzz @  9.11.2009,  17:27 Найти цитируемый пост)
Не понимаю почему? Только если они предполагают что этот модуль будет компилироваться как юникод? тогда еще понимаю. То есть будет явно подставлена CallWindowProcW, ну тогда нафига они верхний IsWindowUnicode не уберут? не пойму.

тут может быть такой вариант:
у них при сабклассинге используется одна кодировка, к примеру - юникод, а в процедуре по-умолчанию - ANSI,
когда они подставляли процедуру, не учли или просто не захотели пользоваться вот этой трансляцией
(может как раз из-за проблем с интернационализацией),
в итоге и получили первый уровень процедуры - юникод, а второй - ANSI,
а вместо системного перевода WCS-сообщения в MBS, используют прямой вызов ANSI процедуры,

т.е. сделали себе проблему на ровном месте,
но это просто предположение, в их SDK я не лазил

Автор: Earnest 10.11.2009, 08:18
Цитата(GremlinProg @  9.11.2009,  15:29 Найти цитируемый пост)
только вот что будешь делать, когда на разных типах окон будут посылаться разные сами сообщения или уведомления,
например то же TVN_GETDISPINFO, которое UNICODE окна отсылают как TVN_GETDISPINFOW(TVN_FIRST-52), а ANSI - как TVN_GETDISPINFOA(TVN_FIRST-3)?
разве что проверять в рантайме:

Чисто добавление: в данном случае нужно проверять в обработчике код уведомления, а не Юникод. Так обычно и делают, хотя код получается громоздкий. Например, собственный (встроенный) тултип у тривью или листвью всегда Юникодный, даже если само окно не-Юникод. Помниться, как-то помучалась, пока дошло... 

Автор: Ruzzz 10.11.2009, 14:57
Цитата(Earnest @ 10.11.2009,  08:18)
проверять в обработчике код уведомления, а не Юникод

не совсем понял :(

Автор: Ruzzz 10.11.2009, 15:28
Вот еще интересная ссылка http://msdn.microsoft.com/en-us/library/ms997565.aspx

Автор: Earnest 10.11.2009, 18:31
Цитата(Ruzzz @  10.11.2009,  15:57 Найти цитируемый пост)
не совсем понял :( 

Ну, в обработчике W_NOTIFY нужно проверять не IsUnicodeWindow, а код уведомления
(скажем TTN_NEEDTEXTA или TTN_NEEDTEXTW), т.к. не-Юникод окно вполне может получить Юникод сообщения от своих детей.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)