Перейти к содержанию

Не получается отобразить ToolTip при WinApi


Antonshka

Рекомендуемые сообщения

Привет, никто не сталкивался с проблемой отображения автономного ToolTip?

 

Делаю как в книге, и как на MSDN. Приложение запускается, но подсказку не отображает.

Примитивный код С++

Спойлер

#include <windows.h>
#include <stdio.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32")

HWND hMainWnd;
HWND hwndToolTip;
HINSTANCE hInst;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateToolTip();

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	wchar_t szClassName[] = L"MyClass";
	MSG msg;
	WNDCLASSEX wc;
	hInst = hInstance;
	wc.cbSize = sizeof(wc);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = szClassName;
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	if (!RegisterClassEx(&wc)) 
	{
		MessageBoxW(NULL, L"Cannot register class", L"Error", MB_OK);
		return 0;
	}
	hMainWnd = CreateWindowW(
		szClassName, L"A Hello1 Application", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
		(HWND)NULL, (HMENU)NULL,
		(HINSTANCE)hInstance, NULL
	);
	if (!hMainWnd) 
	{
		MessageBoxW(NULL, L"Cannot create main window", L"Error", MB_OK);
		return 0;
	}
	ShowWindow(hMainWnd, nCmdShow);
	UpdateWindow(hMainWnd);
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hDC;
	PAINTSTRUCT ps;
	RECT rect;
	switch (uMsg)
	{
	case WM_CREATE:
		CreateToolTip();
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &ps);
		Rectangle(hDC, 10, 10, 100, 100);
		EndPaint(hWnd, &ps);
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}

void CreateToolTip()
{
	INITCOMMONCONTROLSEX icc;
	icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icc.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&icc);

	hwndToolTip = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		hMainWnd, NULL, hInst, NULL);

	wchar_t infoText[4] = { 't','e','s','t' };

	if (hwndToolTip)
	{
		TOOLINFO ti;
		ti.cbSize = sizeof(ti);
		ti.uFlags = TTF_CENTERTIP | TTF_SUBCLASS;
		ti.hwnd = hMainWnd;
		ti.uId = 1;
		ti.hinst = hInst;
		ti.lpszText = infoText;
		GetClientRect(hMainWnd, &ti.rect);
		SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
	}
}

 

Ссылка на MSDN

Спойлер

 

  • Зачем оно здесь? 1
Ссылка на комментарий
Поделиться на другие сайты

Забыл написать что проект на UNICODE.

 

Итак, после нескольких часов поиска решение в интернете, стало ясно что:

- элемент управления ToolTip относится к общим элементам управления (common controls). Есть еще стандартные элементы управления (user controls)

- общие элементы управления определены в файле ComCtl32.dll

- файл ComCtl32.dll находится в папке windows/sytem32

- файл ComCtl32.dll имеет разные собственные версии

- версия файла ComCtl32.dll влияет на то с какой версией структуры TOOLINFO код API, находящийся в ComCtl32.dll, может работать

- для инициализации элемента ToolTip нужно определить и заполнить структуру TOOLINFO

- существует три версии структуры TOOLINFO, отличающиеся количеством полей

         версия 1 - TTTOOLINFO_V1_SIZE   //Для ComCtl32.dll версии 4.0 и выше

         версия 2 - TTTOOLINFO_V2_SIZE   //Для ComCtl32.dll версии 4.7 и выше

         версия 3 - TTTOOLINFO_V3_SIZE   //Для ComCtl32.dll версии 6.0 и выше

- структура TOOLINFO помимо прочих своих полей, имеет поле cbSize, которое нужно заполнить, записав в него размер самой же структуры, то есть размер структуры TOOLINFO.

- поле cbSize структуры TOOLINFO используется системой для определения версии API кода, который нужно использовать для работы с ней

- заголовочный файл "commctrl.h", который необходимо подключить к проекту для того чтобы работать с общим элементам управления, основываясь на значении макроса _WIN32_WINNT, выбирает для работы с ToolTip соответствующую версию структуры TOOLINFO.

- _WIN32_WINNT - этот макрос определяет в каких версиях Windows может выполняться ваш код

- в моем проекте макрос _WIN32_WINNT имеет значение 0x0A00,  что значит Windows 10 и выше, а это значит что проект использует самую новую, самую последнюю версию структуры TOOLINFO из заголовочного файла "commctrl.h", структуру о с размеров - TTTOOLINFO_V3_SIZE

- если вы явно не укажете в своем проекте что хотите использовать ComCtl32.dll версии 6.0 и выше, то по умолчанию будет использоваться версия 5.0, а это значит что будет использоваться API код для работы со структурой версии TTTOOLINFO_V2_SIZE.

 

Таким образом, файл "commctrl.h" предоставляет нам структуру версии TTTOOLINFO_V3_SIZE, в то время как ComCtl32.dll работает с версией TTTOOLINFO_V2_SIZE.

Размер структуры TTTOOLINFO_V3_SIZE на 4 байта больше чем TTTOOLINFO_V2_SIZE. Из-за этого подсказка не отображается на экране.

 

Решение может быть одним из следующих:

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

Спойлер

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

 

- либо переопределить макрос _WIN32_WINNT, до версии Windows 2000

- либо явно заполнить поле cbSize размером структуры TOOLINFO минус 4 байта

 

Использование ComCtl32.dll версии 6.0 и выше меняет стиль отображения элементов управления. До версии 6.0 стиль элементов как в windows 95, 98. То есть до XP.

Изменено пользователем Antonshka
  • Зачем оно здесь? 1
Ссылка на комментарий
Поделиться на другие сайты

Если я правильно помню, желательно инициализировать коммон контролы до создания первого окна. Но если код выше из МСДН, возможно, это требование уже не актуально.

Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, Xipho сказал:

Если я правильно помню, желательно инициализировать коммон контролы до создания первого окна. Но если код выше из МСДН, возможно, это требование уже не актуально.

 

Инициализировать коммон контролы, то есть выполнить этот код?

Спойлер

INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&icc);

 

 

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.