Страница 1 из 1

XP и MFC-класс CToolBar

Добавлено: 14 апр 2004, 14:27
vg
Не работают нормально русские «подсказки» ToolTip для мфц-тулбара (CToolBar).
«Ненормальность» в том, что вместо русских букв в окошках тултипов появляются загадочные символы ;)), схожие то ли с греческими, то ли марсианскими. Чего надо сделать, чтобы по-русски говорил тултип?

Проект ранее был сделан в VC 7.0 (v.7.0.9466), Framework 1.0 (v.1.0.3705). Компилировалось и работало на W2k Prof (rus). Давно. Работало всё нормально.

Купил лаптоп с предустановленным XP English. Установил тот же компилер. Компильнул….В результате имеем ненормальности в поведении ToolTip, о которых сказано выше.

В части региональных настроек ОС – следующее. Сейчас установлены региональные настройки для даты, представления чисел и дат – English (United States). Не работает тултип…. Если установить региональные настройки – Russian, то всё начинает работать нормально. Здесь под региональными настройками имеется в виду значение строки в комбобоксе группы контролов “Standards and formats” на закладке “Regional Options” аплета “Regional and Language Options” в папке “Control Panel” XP. Есть причины, по которым хотелось бы оставить основные настройки ОС английскими. Кроме того, нет ВИДИМЫХ ПРИЧИН, по которым надо обязательно делать русские региональные настройки. А именно:

- Приложения, скомпилированные в BCB++ на этом же компе, работают нормально вне зависимости от региональных настроек (толтипы «говорят» по-русски, но там естественно свои vcl-толбары, а не отстой мфц M$). Поэтому нельзя сказать, что есть некие ошибки системного характера, и следует обязательно установить русские региональные настройки по умолчанию.

- Тестовые приложения, собранные в том же .NET, но в C#, и имеющие тулбар с тултипами, работают на ура. Нормально понимается рашен для толбара M$.

- Приложение M$С++, о котором речь, в котором этот несчастный толбар с неправильно работающим тултипом, в остальном прекрасно работает с русскими символами. Примечательно, что «синхронные подсказки» нормально работают в мфц – CStatusBar, который там же. Т.е. наводим мышу на кнопку тулбара – в тултипе кнопки видим абракадабру, а в статусбаре - вполне приличные русские слова. Это говорит о том, что строки из ресурсов нормально грузятся и отображаются в принципе. Поэтому и на ресурсы тоже не приходится грешить. В принципе, все контролы в этом проекте, что подгружают русские символы из ресурсов, работают нормально, вне зависимости от региональных настроек ОС.

- Сам, ToolTipCtrl API работает нормально (именно он по идее и оборачивается классами мфц). То, что с тултип-контролом shell всё в порядке говорит хотя бы тот факт, что для мфц-контролов (CEdit, CButton, …) в этом приложении, для которых подсобачен ToolTipCtrl WinAPI, мы имеем нормальные русские символы. Правда, последние грузятся из констант, а не из ресурсов (про редактирование ресурсов - позже).

- В принципе, сам ToolBarCtrl WinAPI с апишеым тулбаром работает нормально (именно его по идее должен оборачивать CToolBar MFC). Проверено. Т.е. если в приложении заменить CToolBar на свою реализацию обёртки ToolBarCtrl shell, то всё работает прекрасно (что, увы, и пришлось пока сделать). Русские символы нормально отображаются. И это не зависит от того, откуда и как мы грузим строки подсказок в окошке тултипа – или из констант, по ходу пьесы, или из ресурсов. При любой региональной настройке ОС тултипы тулбара WinAPI работают нормально. Т.е. как бы и для CToolBar MFC должно всё работать. Это потому, что где-то попадалось, что CToolBar MFC оборачивает ToolTipCtrl SHELL. Если работает контрол SHELL, то должна корректно работать и его обёртка (вряд ли кто будет сомневаться в корректности программистов M$). Ан нет. Не работает ;(.

Дополнительно, для пользы следствия могу сообщить следующее. Проект использует Multi-byte Character Set (_MBCS, что по умолчанию для проектов MFC ). Командная строка такова:
/Od /D "WIN32" /D "_WINDOWS" /D "_DEBUG" /D "" /D "_AFXDLL" /Gm /EHsc /RTC1 /MDd /Zc:wchar_t /Yu"stdafx.h" /Fp"Debug/Gkm.pch" /Fo"Debug/" /Fd"Debug/vc70.pdb" /W3 /nologo /c /Wp64 /ZI /TP

Строки в ресурсах расположены «в русской секции» (cp 1251).

Вот, что может наводить на грустные мысли…. Для нормального ввода в файл ресурсов русских строк невозможно использовать IDE. Там, в диалоговых окошках для ввода строк и в редакторе ресурсов после ввода появляются аброказябры вместо русского. Т.е. пока вводишь строку – печатаются русские символы, как только нажимаешь ENTER для завершения ввода - строка принимает невообразимый вид. Поэтому, приходится «руками» редактировать rc-файл ресурсов.

ПС.
Не думаю, что указанный гимор c тулбаром связан с контролами COMCTRL32.DLL XP. Если убить в проекте манифест, то имеем теже яйца только с видом W2k. Те же проблемы остаются. Надо бы найти способ, как использовать именно тулбар мфц, а не WinAPI. Апишный толбар, конечно, нормально работает. Но там может в перспективе добавится другой гимор, если он встраивается в проект с каркасом мфц, вместо CToolBar (не в проект с контролами MFC, а с каркасом MFC).

Как это делать?
Гурам спасибо в самом конце и пиво.

Re: XP и MFC-класс CToolBar

Добавлено: 15 апр 2004, 08:53
Woozy
[quote="vg"][/quote]А ты попробуй изобразить тот же toolbar используя только API. Будет легче понять, что происходит, возможно. Код покажешь здесь, если что. Я сам только API использовал напрямую для toolbars. Правда способов более одного, конечно...

Добавлено: 15 апр 2004, 12:59
vg
2Папа Карло,

Тьфу-ты, 2 Woozy, конечно, (извини, что перепутал с Папо-Карло. Просто ты также как он внимательно прочитал мой постинг выше. Хотя он их обычно и не читает, наверное :D ).
Я ж постил, что приходится к сожалению использовать сейчас API ToolBarCtrl. "К сожалению" - потому, что с MFC те глюки, о которых я постил для MFC-CToolBar. См. выше это и далее по тексту:
.....- В принципе, сам ToolBarCtrl WinAPI с апишеым тулбаром работает нормально (именно его по идее должен оборачивать CToolBar MFC). Проверено. Т.е. если в приложении заменить CToolBar на свою реализацию обёртки ToolBarCtrl shell, то всё работает прекрасно (что, увы, и пришлось пока сделать). Русские символы нормально отображаются. И это не зависит от того, откуда и как мы грузим строки подсказок в окошке тултипа – или из констант, по ходу пьесы, или из ресурсов.....
Здесь ToolBarCtrl shell - и есть ToolBarCtrl WinAPI :lol: . И работет это, как часы в отличие от MFC. Ну, а код каков? Ну, а каков он может быть для контролов API? Конечно примитивным, как 3 рубля. Хотя, может ты знаешь другие навороты? :lol: Я сделал и использую в своих проектах приблизительно такой wrapper (что б не было очень нудно, я кое-что почикал здесь и для удобства обсуждения всё вонзил всё в хедер + how to use):

Код: Выделить всё

/////////////////////////////////////////////////////////////////////////////
//	CCustomToolBarEx class
//
//
//	TBBUTTON tbButtons[] = 	{
//		{ 0, ID_FILE_NEW,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0 },
//		{ 1, ID_FILE_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 2, ID_FILE_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0},
//		{ 3, ID_EDIT_CUT,   TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 4, ID_EDIT_COPY,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 5, ID_EDIT_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0},
//		{ 6, ID_FILE_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
//		{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0},
//		{ 7, ID_HELP_ABOUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}
//	};
//
//	CCustomToolBarEx ToolBar;
//	if (!ToolBar.Create(this->m_hWnd, ID_TOOLBAR))
//	{
//		TRACE0("Failed to create toolbar\n");
//		return -1;		// fail to create
//	}
//
//	ToolBar.SetReBarSizeStyles();		// if needed
//
//	ToolBar.SetBitmap(IDB_TOOLBAR_BITMAP, 8);
//
//	SET BUTTONS FROM ARRAY
//	ToolBar.SetButtons(tbButtons, 11);	
//	Tb.AddTool( ID_FILE_NEW, "Новый файл");
//	Tb.AddTool( ID_FILE_OPEN, "Открыть файл");
//
//	OR
//	Tb.AddButton(ID_FILE_NEW,0 , "Создать");
//	Tb.AddButton(ID_FILE_OPEN,1, "Открыть");
//	Tb.AddButton(ID_FILE_SAVE,2 , "Сохранить");
//	Tb.TextToToolTip();
//
//	OR
//	Tb.AddButton(ID_FILE_NEW,0 , "Создать", "Новый файл");
//	Tb.AddButton(ID_FILE_OPEN,1, "Открыть", "Открыть файл" );
//	Tb.AddButton(ID_FILE_SAVE,2 , "Сохранить", "Сохранить файл");
//
//	Tb.EnableButton(ID_FILE_SAVE, FALSE);
//
class CCustomToolBarEx
{
protected:
	HINSTANCE		m_hInst;
	BOOL			m_bUseReBar;
	HWND			m_hWndToolTip;
public:
	HWND			m_hWndToolBar;
public:
	CCustomToolBarEx	( void )
	{
			m_hWndToolBar = m_hWndToolTip = NULL;
			m_bUseReBar = FALSE;
	}
	virtual			~CCustomToolBarEx( void ) {};

	#pragma warning(disable: 4312)
	HWND APIENTRY	Create( HWND hWndParent, UINT ID_ToolBar)
	{
			if ( m_hWndToolBar )	return m_hWndToolBar;
			m_hInst = (HINSTANCE)GetModuleHandle(NULL);
			InitCommonControls();
			m_hWndToolBar = CreateWindowEx(
							0,
							TOOLBARCLASSNAME,
							NULL,
								CCS_TOP |  WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS |
								TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_ADJUSTABLE ,
							0, 0, 0, 0,
							hWndParent, ( HMENU ) ID_ToolBar, 0, 0 );
			if ( m_hWndToolBar )
				SendMessage(m_hWndToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
			return m_hWndToolBar;
	}
	#pragma warning(default : 4312)

	BOOL APIENTRY	SetBitmap	( UINT ID_BITMAP, UINT n_Images )
	{
			TBADDBITMAP	tbab;
			tbab.hInst = m_hInst;
			tbab.nID   = ID_BITMAP;
			return (BOOL) SendMessage(m_hWndToolBar, TB_ADDBITMAP, (WPARAM) n_Images, (LPARAM)&tbab);
	}
 
	BOOL APIENTRY	SetButtonSize	( int cx, int cy )
	{		// The size can be set only before adding any buttons to the toolbar.
			BOOL rc = (BOOL) SendMessage(m_hWndToolBar, TB_SETBUTTONSIZE, 0, (LPARAM) MAKELONG(cx, cy));
			SendMessage(m_hWndToolBar, TB_AUTOSIZE, 0, 0);
			return rc ; 
	}
	
	BOOL APIENTRY	AddTool( UINT idBtn, LPCTSTR pszText )
	{
			if (!m_hWndToolTip && !(m_hWndToolTip = (HWND) SendMessage( m_hWndToolBar,TB_GETTOOLTIPS, 0, 0 )))
				return FALSE;

			RECT		rct;
			if ( !SendMessage(m_hWndToolBar, TB_GETRECT, idBtn, (LPARAM) &rct) ) return FALSE;
			
			TOOLINFO	ti;
			memset( &ti, 0, sizeof( TOOLINFO ) );

			ti.cbSize	= sizeof( TOOLINFO );
			ti.uFlags	= TTF_SUBCLASS;
			ti.hwnd		= m_hWndToolBar;
			ti.uId		= (UINT_PTR) m_hWndToolBar;
			
			ti.hinst	= m_hInst;
			ti.lpszText = LPSTR (pszText);
			memcpy(&ti.rect, &rct, sizeof(RECT));

			return  (BOOL) SendMessage( m_hWndToolTip,TTM_ADDTOOL,0,(LPARAM)&ti );
	}
	#pragma warning(disable: 4311)
	BOOL APIENTRY	AddButton	( UINT idBtn, int iBitmap, LPCTSTR pszText, LPCTSTR pszToolText = NULL)
	{
			TBBUTTON tb; 
			tb.iBitmap	= iBitmap;	// zerro-based index 
			tb.idCommand= idBtn;
			tb.fsState	= TBSTATE_ENABLED; 
			tb.fsStyle	= TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE; 
			tb.dwData	= 0; 
			tb.iString	=  (int) pszText;
			if( !SendMessage(m_hWndToolBar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)(LPTBBUTTON)&tb) )
				return FALSE;
			if( pszToolText )
				AddTool( idBtn, pszToolText );

			SendMessage(m_hWndToolBar, TB_AUTOSIZE, 0, 0);
			return TRUE;
	}

	BOOL APIENTRY	AddSeparator( void )
	{
			TBBUTTON tb; 
			tb.iBitmap	= 0;
			tb.idCommand= 0;
			tb.fsState	= TBSTATE_ENABLED; 
			tb.fsStyle	= TBSTYLE_SEP; 
			tb.dwData	= 0; 
			tb.iString	=  0;
			if( !SendMessage(m_hWndToolBar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)(LPTBBUTTON)&tb) )
				return FALSE;
			SendMessage(m_hWndToolBar, TB_AUTOSIZE, 0, 0);
			return TRUE; 
	}
	#pragma warning(default: 4311)
	BOOL APIENTRY	SetButtons	( LPTBBUTTON pButtons, UINT n_Btns )
	{
			BOOL rc = (BOOL) SendMessage( m_hWndToolBar, TB_ADDBUTTONS, (WPARAM)n_Btns, (LPARAM)(LPTBBUTTON)pButtons);
			SendMessage( m_hWndToolBar, TB_AUTOSIZE, 0, 0 );
			return rc; 
	}

	BOOL APIENTRY	ShowWindow	( int nCmdShow  )
	{
			return ::ShowWindow( m_hWndToolBar, nCmdShow ); 
	}

	LONG APIENTRY	SetStyle	( LONG dwStyle, BOOL bAddMode = FALSE )
	{
			LONG wStyle = GetWindowLong(m_hWndToolBar,GWL_STYLE);
			wStyle = ( bAddMode )? wStyle | dwStyle : dwStyle;
			return SetWindowLong(m_hWndToolBar,GWL_STYLE, wStyle); 
	}

	LONG APIENTRY	GetStyle	( void )
	{
			return GetWindowLong(m_hWndToolBar,GWL_STYLE);
	}

	LONG APIENTRY	SetReBarSizeStyles( void )
	{
			m_bUseReBar = TRUE;
			return SetStyle( CCS_NOPARENTALIGN | CCS_NORESIZE , TRUE);
	}
	#pragma warning(disable: 4267)
	BOOL APIENTRY	SetText	( UINT ID_BTN, LPCTSTR pszText)
	{
			TBBUTTONINFO tbi;
			tbi.dwMask = TBIF_TEXT;
			tbi.cbSize = sizeof (TBBUTTONINFO);
			tbi.pszText = (LPSTR) pszText;
			tbi.cchText = strlen( (LPSTR) pszText );
		
			BOOL rc = (BOOL) SendMessage(m_hWndToolBar, TB_SETBUTTONINFO,
										ID_BTN, (LPARAM) (LPTBBUTTONINFO)&tbi);
			SendMessage( m_hWndToolBar, TB_AUTOSIZE, 0, 0 );
			return rc;
	}
	#pragma warning(default: 4267)
	BOOL APIENTRY	TextToToolTip	( void )
	{
			return (BOOL) SendMessage( m_hWndToolBar, TB_SETMAXTEXTROWS, 0, 0);
	}
	
	BOOL APIENTRY	OnSize( void )
	{
			return ( m_bUseReBar )? TRUE : (BOOL) SendMessage( m_hWndToolBar, WM_SIZE, 0,0);
	}

	BOOL APIENTRY	EnableButton( UINT idBtn, BOOL bEnable)
	{
			return (BOOL) SendMessage(m_hWndToolBar, TB_ENABLEBUTTON, (WPARAM) idBtn, (LPARAM) MAKELONG(bEnable, 0));
	}
};
Пускай тебя не смущают #прагмы, что выше относительно ворнингов. Это из-за идотизма M$. Думаю, что даже ты бы не смог бы это обойти. Лично мне здесь никакие cast-ы не помогли в приведении типов. Если у тебя выйдет красИвее, напиши. Я всегда за чистоту кода. Обеими руками :(

Добавлено: 19 апр 2004, 14:04
Woozy
CreateWindowEx(W), SendMessage(W), etc.

Ты уверен, что в данном случае VC++ генерит вызов с W?

Добавлено: 20 апр 2004, 14:06
vg
2Woozy,
CreateWindowEx(W).
Ты уверен, что в данном случае VC++ генерит вызов с W?
Нет, это окна не Unicode. Построены окна при помощи CreateWindowExA. Проект собран с опцией Multi-byte Character Set (_MBCS). Эта опция по умолчанию установлена для проектов MFC, что вообще очень даже и не плохо, на мой взгляд, см. ниже. И никак не должно сказываться на возможности отображения национальных символов в данном случае.

Тул- и статусбары MFC создаются там, где создаются практически всегда в CDocument/CView проектах MFC (int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)).
В силу того, что проект собран, как MBCS, а не UNICODE, то функция API ::IsWindowUnicode (HWND hWnd) возвращает FALSE для окна главной рамки и контролов.
Что же касается тулбара WinAPI (класс его обёртки см. постингом выше), то для него ::IsWindowUnicode возвращает TRUE. Как было сказано, UNICODE-окно этого тулбара API корректно отображает русские символы в тултипе, когда проект _MBCS (не UNICODE). Этот тулбар создаётся там же, где и бары MFC, т.е в CMainFrame::OnCreate.

Кстати, ещё прикол - почему окно тулбара, который сделан "руками" (WinAPI) - UNICODE :shock: , когда как CWnd-объекты являются неюникод. Опция-то _MBCS касается всего проекта и не маскируется нигде. По идее и WinAPI-тулбар должен быть неюникод. Ты не поверишь, окно тулбара фактически создаётся CreateWindowExA, но после этого ::IsWindowUnicode возвращает TRUE Что происходит?
...SendMessage(W), etc.

Ты уверен, что в данном случае VC++ генерит вызов с W?
Не критично (кроме снижения производительности), т.к. ANSII строки преобразуются автоматически MSLU к wchar_t при передаче в UNICODE-окно:
....The system does automatic two-way translation (Unicode to ANSI) for window messages. For example, if an ANSI window message is sent to a window that uses the Unicode character set, the system translates that message into a Unicode message before calling the window procedure. The system calls IsWindowUnicode to determine whether to translate the message....

Важно другое. Передать-то буфер c "automatic two-way translation" мы передадим, но вот вопрос - а будет ли это буфер содержать корректные 16-битные последовательности UNICODE, а не муссор? :lol:
В общем случае это будет мусор. Практически уверен.

ПОЧЕМУ НЕ ОБЯЗАТЕЛЬНО ИСПОЛЬЗОВАТЬ UNICODE В ПРОЕКТАХ VISUAL STUDIO:

VS поддерживает два способа работы с «широкими» 16-битными строками символов – UNICODE и альтернативный (и гораздо более простой) DBCS. Вот что пишет Майкрософт:
….Multibyte character sets (MBCS) are an alternative to Unicode for supporting character sets, like Japanese and Chinese, that cannot be represented in a single byte. If you are programming for an international market, consider using either Unicode or MBCS, or enabling your program so you can build it for either by changing a switch.
The most common MBCS implementation is double-byte character sets (DBCS). Visual C++ in general, and MFC in particular, is fully enabled for DBCS.
For samples, see the MFC source code files.
For platforms used in markets whose languages use large character sets, the best alternative to Unicode is MBCS. MFC supports MBCS by using "internationalizable" data types and C run-time functions. You should do the same in your code.
Under MBCS, characters are encoded in either one or two bytes. In two-byte characters, the first, or "lead-byte," signals that both it and the following byte are to be interpreted as one character. The first byte comes from a range of codes reserved for use as lead bytes. Which ranges of bytes can be lead bytes depends on the code page in use. For example, Japanese code page 932 uses the range 0x81 through 0x9F as lead bytes, but Korean code page 949 uses a different range…..

Это действительно так. Практически всё работает нормально для проектов MBCS, кроме того самого MFC-CToolBar–а. Все контролы, окна их тултипов и т.п. работают нормально с русскими символами, в том числе и WinAPI ToolBarCtrl, если проект MBCS (не UNICODE). Таким образом, для того, чтобы корректно отображать национальные символы, вовсе не обязательно использовать UNICODE. Здесь только несколько вышибает из колеи:

1) позиция Майкрософт, когда она делает максимум в своих библиотеках для поддержки MBCS, но настоятельно использует использовать кодировку и техники работы с UNICODE.

2) то, что Майкрософт прав в целом, когда говорит о том, что их "альтернативная" (и не спорю, очень удобная) технология работы с национальными символами работает в целом, и не работает в некоторых маленьких случаях. Действительно, подумаешь, какая мелочь в моём-проекте всё работает, кроме всего лишь одного контрола :lol:

ПС. Предлагаю обсудить недостатки проекта и пробелы в моих знаниях (вернее полное отсутствие непробелов) в части построения не UNICODE проектов. Т.е. при помощи 16-битных строк кодировки MBCS. Здесь есть все возможности построения проектов, которые могут быть локализованы и без UNICODE в VS. Предлагаю снести обсуждение, сравнение MBCS и UNICODE в другой топик, тем более, что это лучше обсуждать на примере plain-WinAPI проектов. Там не вмешивается за кулисами MFC, чтобы чем нибудь помочь, и что-нибудь "улучшить" по-мимо воли программера. :lol: