In a background process I want to display I short message like a balloon in the tray area. However, I do not want to add a tray icon just for that.
Hence I create a tooltip icon and want it to place near the tray.
However, sending TTM_UPDATETIPTEXT, TTM_TRACKPOSITION, TTM_TRACKACTIVATE returns 0. While I am not sure if this should be the case or not, the following code does not show the tooltip window and I don't know why:
This is the code:
// "hwnd" is the HWND to the hidden background window of my background process
HWND hTrayWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
GetWindowRect(hTrayWnd, &trayPos);
HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hWnd, NULL, NULL, NULL);
LRESULT ret;
TOOLINFO toolInfo = { 0 };
ZeroMemory(&toolInfo, sizeof(toolInfo));
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hinst = GetModuleHandle(NULL);
toolInfo.lpszText = _T("Test Test");
toolInfo.hwnd = hWnd;
ret = SendMessage(hwndTip, TTM_SETTITLE, TTI_INFO , (LPARAM)_T("Test Title"));
This call returns zero:
ret = SendMessage(hwndTip, TTM_UPDATETIPTEXT, 0 , (LPARAM)&toolInfo); // needs hinst, lpszText, hwnd, cbSize
This call returns zero too:
ret = SendMessage(hwndTip, TTM_TRACKPOSITION, 0, MAKELPARAM(trayPos.left, trayPos.top));
ZeroMemory(&toolInfo, sizeof(toolInfo));
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = hWnd;
toolInfo.uId = 1;
And this too:
ret = SendMessage(hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolInfo); // needs hwnd, uId, cbSize
And the tooltip window is never shown.
I also tried to use TTM_ADDTOOL (as suggested in a comment) ad different order of my SendMessage calls but this too returns zero and no tooltip is shown:
LRESULT ret;
TOOLINFO toolInfo = { 0 };
ZeroMemory(&toolInfo, sizeof(toolInfo));
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.lpszText = _T("Test Test");
toolInfo.hwnd = hWnd;
toolInfo.uId = 1;
toolInfo.rect.bottom = trayPos.bottom;
toolInfo.rect.left = trayPos.left;
toolInfo.rect.right = trayPos.right;
toolInfo.rect.top = trayPos.top;
//ret = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
ret = SendMessage(hwndTip, TTM_SETTITLE, TTI_INFO , (LPARAM)_T("Test Title"));
//ret = SendMessage(hwndTip, TTM_UPDATETIPTEXT, 0 , (LPARAM)&toolInfo); // needs hinst, lpszText, hwnd, uId, cbSize
//ret = SendMessage(hwndTip, TTM_TRACKPOSITION, 0, MAKELPARAM(trayPos.left, trayPos.top));
//ret = SendMessage(hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolInfo); // needs hwnd, uId, cbSize
ret = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
TTM_TRACKACTIVATE activates a 'tracking tooltip', which is created by setting TTF_TRACK in TOOLINFO::uFlags.
Related
I'm using a SwapChain created with CreateSwapChainForHwnd with DXGI_SCALING_STRETCH scaling. When the sizes of the swapchain and the window client area differ, the SwapChain's associated bitmap is correctly scaled, but its origin is moved as if the window borders were accounted in the stretch process although there sizes are constant.
Is there a way to fix or circumvent this issue ?
Thank you for the help !
(More precisely : for a client area larger that swapchain, the origin of the bitmap in shifted down, right proportionally respectively to title-bar height and left border width, the part of the bitmap thus moved outside the client area being invisible; a reverse shift is produced with client area smaller than swapchain).
Below a minimal code to reproduce this issue based on visual studio desktop app template. I put everything in InitInstance() (I know it's bad), appart from classical DX #includes and declarations. It corresponds to https://learn.microsoft.com/en-us/windows/win32/direct2d/devices-and-device-contexts except I create the swapchain with CreateSwapChainForHwnd() instead of CreateSwapChainForCoreWindow() :
The pb appears fror MDI childs, as well as for SDI windows with a menu. The way the buffer bitmap is drawn, CopyFromMemory, DrawBitmap or even D2D drawing doesn't matter, as shown in images with the two diagonals made with DrawLine().226707-stretchedout.png
Actually the white bands are flickering during resizing.
Example, starting from Visual Studios's destop app template, replace InitInstance() below and add D2D includes and declarations...
...
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
ATOM aMainClass;
ATOM aChildClass;
const TCHAR lpszMainClassName[] = TEXT("FCLAYERMAIN");
const TCHAR lpszChildClassName[] = TEXT("FCLAYERCHILD");
hInst = hInstance;
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
//prepare MDI windows (main + MDI client + one child)
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = lpszMainClassName;
wcex.hIconSm = NULL;
aMainClass = RegisterClassEx(&wcex);
wcex.lpfnWndProc = ChildWndProc;
wcex.lpszClassName = lpszChildClassName;
wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
aChildClass = RegisterClassEx(&wcex);
hWndMDI = CreateWindow((LPCTSTR)aMainClass,
szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
NULL,
NULL,
hInst,
NULL);
CLIENTCREATESTRUCT ccs;
ccs.idFirstChild = 5000;
hwndClient = CreateWindow(TEXT("mdiclient"),
NULL,
WS_CLIPCHILDREN | WS_CHILD | WS_HSCROLL | WS_VSCROLL | MDIS_ALLCHILDSTYLES,
0,
0,
0,
0,
hWndMDI,
0,
hInst,
(LPSTR)&ccs);
ShowWindow(hWndMDI, SW_SHOW);
ShowWindow(hwndClient, SW_SHOW);
//so as the initial child's client are fits the D2D swapchain (256*256px)
RECT Rect;
Rect.left = 0;
Rect.top = 0;
Rect.right = 256;
Rect.bottom = 256;
AdjustWindowRect(&Rect,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX, FALSE);
HWND hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
ShowWindow(hWnd, SW_SHOW);
//create D2D + D3D + DXGI machinery
D2D1_FACTORY_OPTIONS debugOptions;
debugOptions.debugLevel = D2D1_DEBUG_LEVEL_NONE;
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory1),
&debugOptions,
(void*)&m_D2DFactory
);
if (SUCCEEDED(hr))
{
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_pD3D11device,
NULL,
NULL
);
}
if (SUCCEEDED(hr))
{
IDXGIDevice1 dxgiDevice;
hr = m_pD3D11device->QueryInterface(__uuidof(IDXGIDevice), (void*)&dxgiDevice);
if (SUCCEEDED(hr))
{
hr = m_D2DFactory->CreateDevice(
dxgiDevice,
&m_d2dDevice
);
if (SUCCEEDED(hr))
{
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
);
}
}
IDXGIAdapter dxgiAdapter;
if (SUCCEEDED(hr))
{
hr = dxgiDevice->GetAdapter(&dxgiAdapter);
if (SUCCEEDED(hr))
{
hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&m_dxgiFactory));
SafeRelease(&dxgiAdapter);
dxgiDevice->SetMaximumFrameLatency(1);
SafeRelease(&dxgiDevice);
}
}
}
if (SUCCEEDED(hr))
{
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = 256;
swapChainDesc.Height = 256;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; //DXGI_SCALING_ASPECT_RATIO_STRETCH seems not supported
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.Flags = 0;
hr = m_dxgiFactory->CreateSwapChainForHwnd(
m_pD3D11device,
hWnd,
&swapChainDesc,
nullptr,
nullptr,
&(m_swapChain)
);
}
if (SUCCEEDED(hr))
{
hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_dxgiBackBuffer));
if (SUCCEEDED(hr))
{
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
0,
0,
NULL
);
/
//just in case dpi=0 doesn't work:
bitmapProperties.dpiX = GetDpiForWindow(hWnd);
bitmapProperties.dpiY = GetDpiForWindow(hWnd);
/
hr = m_d2dContext->CreateBitmapFromDxgiSurface(
m_dxgiBackBuffer,
&bitmapProperties,
&(m_pBitmap)
);
}
}
if (SUCCEEDED(hr))
{
D2D1_SIZE_U bitmapSize = m_pBitmap->GetPixelSize();
m_d2dContext->BeginDraw();
m_d2dContext->SetTarget(m_pBitmap);
ID2D1SolidColorBrush* m_pBlackBrush;
hr = m_d2dContext->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Red, 1.0f),
&m_pBlackBrush
);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, 0.0f),
D2D1::Point2F(bitmapSize.height, bitmapSize.width),
m_pBlackBrush,
2.0f,
NULL);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, bitmapSize.width),
D2D1::Point2F(bitmapSize.height, 0.0f),
m_pBlackBrush,
2.0f,
NULL);
}
hr = m_d2dContext->EndDraw();
hr = m_swapChain->Present(1, 0);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Analyse les sélections de menu :
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
...
Replace your AdjustWindowRect with AdjustWindowRectEx and specify WS_EX_MDICHILD extended window style.
Another thing, you’re rendering and even presenting some graphics from within InitInstance method. That’s not the right time for that, too early. Instead, you should move rendering into WM_PAINT handler of the window for which you have created the swap chain.
P.S. This sample might help.
The streching error only occurs for windows with borders and/or menu bar. A possible workaround is thus to add a borderless child window (hWndPic) to the MDI child (hWnd) itself:
//initialization :
hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_SIZEBOX|WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU ,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
hWndPic = CreateWindow(
(LPCTSTR)aPicClass,
NULL,
WS_CHILD,
0,
0,
256,
256,
hWnd,
0,
hInst,
NULL);
Then resize this picture window in response to WM_SIZE sent to the MDI child hWnd; that's all! then there is no need to repaint the picture when the window is resized and the stretch process is correctly handled by D2D, benefitting from hardware acceleration.
// windows procedures :
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_SIZE:
{
RECT Rect;
GetClientRect(hWnd,&Rect);
SetWindowPos(
hWndPic,
HWND_TOP,
0,
0,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
0
);
}
}
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK PicWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
This one is a c++/MFC project.
I need to display a position of the cursor. So I must redraw tooltip everytime when a mouse is moving.
I can draw a tooltip but a tooltip was drawn isn't cleared.
The OnShowTooltip() function will be called if mouse is moving.
void OnShowToolTip(const CPoint& ptMousePosition,CString strText)
{
UpdateData(true);
if (bToolTip)
{
unsigned int uid = 0; // for ti initialization
// CREATE A TOOLTIP WINDOW
hwndTT = CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX ,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL
);
// INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE
//ti.cbSize = sizeof(TOOLINFO);
ti.cbSize = TTTOOLINFO_V1_SIZE;
ti.uFlags = TTF_TRACK;
ti.hwnd = NULL;
ti.hinst = NULL;
ti.uId = uid;
ti.lpszText = (LPSTR)(LPCSTR) strText;
// ToolTip control will cover the whole window
ti.rect.left = 0;
ti.rect.top = 0;
ti.rect.right = 0;
ti.rect.bottom = 0;
// SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW
::SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
::SendMessage(hwndTT, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(ptMousePosition.x, ptMousePosition.y));
::SendMessage(hwndTT, TTM_TRACKACTIVATE, true, (LPARAM)(LPTOOLINFO) &ti);
bToolTip=false;
}else{
::SendMessage(hwndTT, TTM_TRACKACTIVATE, false, (LPARAM)(LPTOOLINFO) &ti);
bToolTip=true;
}
}
I have a function for handling mouse events like below
BOOL DoDragPoint2D(CPoint point,CString strPositions)
{
switch (msg.message)
{
case WM_MOUSEMOVE:
bToolTip = TRUE;
OnShowToolTip(point,strPositions);
break;
case WM_LBUTTONUP:
bToolTip = FALSE;
OnShowToolTip(point,strPositions);
}
}
Although I set bTooltip to be FALSE. But It cannot to remove tooltip.
Moreover I try to call Invalidate() function but a tooltip still display until a dubbuging is stopped.
Each time the mouse moves, a new tooltip is created.
You only need to create the tooltip once in WM_CREATE event.
Then send TTM_SETTOOLINFO message to update ti.lpszText.
ep.
case WM_CREATE:
{
unsigned int uid = 0; // for ti initialization
hwndTT = CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL
);
// INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE
//ti.cbSize = sizeof(TOOLINFO);
ti.cbSize = TTTOOLINFO_V1_SIZE;
ti.uFlags = TTF_TRACK;
ti.hwnd = NULL;
ti.hinst = NULL;
ti.uId = uid;
ti.lpszText = const_cast <wchar_t*>(L"");
// ToolTip control will cover the whole window
ti.rect.left = 0;
ti.rect.top = 0;
ti.rect.right = 0;
ti.rect.bottom = 0;
::SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
}
break;
case WM_MOUSEMOVE:
...
ti.lpszText = (LPSTR)(LPCSTR) strText;
::SendMessage(hwndTT, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
::SendMessage(hwndTT, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(ptMousePosition.x, ptMousePosition.y));
::SendMessage(hwndTT, TTM_TRACKACTIVATE, true, (LPARAM)(LPTOOLINFO) &ti);
...
I am trying to create an OpenGL window inside of already existing parent window using win32 api. However, when creating a child, the function CreateWindowEx returns NULL with an error 1407 ERROR_CANNOT_FIND_WND_CLASS. The window must be a child so there is a space left for other controls such as buttons and checkboxes...
Part of code that creates the parent window:
WNDCLASSEX wincl;
wincl.style = 0;
wincl.lpszMenuName = NULL;
wincl.lpszClassName = L"WindowClass";
wincl.lpfnWndProc = WndProc;
wincl.hInstance = hInstance;
wincl.hIconSm = LoadIcon(NULL, IDC_ICON);
wincl.hIcon = LoadIcon(NULL, IDC_ICON);
wincl.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wincl.cbWndExtra = 0;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.cbClsExtra = 0;
if(!RegisterClassEx(&wincl)){
std::cout << "Window failed to register!" << std::endl;
return false;
}
DWORD style = WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | WS_SIZEBOX;
parentHwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
L"WindowClass",
L"Title",
style,
CW_USEDEFAULT, // X
CW_USEDEFAULT, // Y
800, // Width
600, // Height
NULL,
NULL,
hInstance,
0);
if (parentHwnd == NULL){
std::cout << "Window failed to create!" << std::endl;
return false;
}
ShowWindow(parentHwnd, SW_SHOWNORMAL);
UpdateWindow(parentHwnd);
Inside the message loop, I am creating the child window:
LRESULT WndProc(HWND hwnd, UINT Msg, WPARAM wParam , LPARAM lParam){
switch (Msg){
case WM_CREATE:{
// Create child OpenGL window
childHwnd = CreateWindowEx(0,
L"OpenGL",
NULL,
WS_CLIPCHILDREN | WS_VISIBLE,
100, // X
100, // Y
400, // Width
300, // Height
hwnd, // Parent
0, // ID
NULL,
NULL);
// Prints 1407 ERROR_CANNOT_FIND_WND_CLASS
std::cout << "Last error: " << GetLastError() << std::endl;
}
default:{
return DefWindowProc(hwnd, Msg, wParam, lParam);
}
}
return 0;
}
I have looked into several tutorials (NeHe, cs.rit.edu, and many other but I can't post more than 2 links) and all of them used L"OpenGL" as class name for the second argument in the function CreateWindowEx().
What am I doing wrong? Is "OpenGL" a valid class name such as "Edit" / "Static" / "Button" ? Can I replace it with something else? I tried "Static" but it did not rendered anything.
I know there are many libraries (GLFW, SDL, ...) that handle window creation, but I need to use pure win32 API.
UPDATE:
RegisterClass() solved the problem. Here is the code that works for me:
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance; // Same hinstance used by parent
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"OpenGL";
wc.style = CS_OWNDC;
if(!RegisterClass(&wc)){
std::cout << "Failed to register window!" << std::endl;
return false;
}
childHwnd = CreateWindowEx(0, // Must be zero
L"OpenGL",
NULL,
WS_VISIBLE | WS_CHILD,
100, // X
100, // Y
400, // Width
300, // Height
parentHwnd, // Parent HWND
0, // ID
hInstance, // Same hinstance used by parent
NULL);
I use this code to create a rebar control, and introduction a band with toolbar into the rebar.
But when window is shown up, I can not see the toolbar. and when I check the height of the rebar, in this line of code: int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; I find rebar's height is only 4 pixels.
#include <windows.h>
#include <stdlib.h>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE instance;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
instance = hInstance;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"Example";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
RegisterClassEx(&wcex);
HWND hWnd = CreateWindow(L"Example", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
500, 500, NULL, NULL, hInstance, NULL);
// Initialize common controls.
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
HWND hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, 100, 50, hWnd, NULL, instance, NULL);
// create toolbar
HWND hWndToolbar = CreateWindowEx(0 , TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_TOOLTIPS,
0, 0, 0, 0, hwndRebar, (HMENU)0, instance, NULL);
HIMAGELIST hImageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, 3, 0);
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
TBBUTTON tbb[4] =
{
{0,0,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{1,1,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{2,2,TBSTATE_ENABLED,TBSTYLE_BUTTON},
};
SendMessage(hWndToolbar, (UINT) TB_ADDBUTTONS, 3, (LPARAM)&tbb);
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar , SW_SHOW);
// Initialize band info.
REBARBANDINFO rbBand = { sizeof(REBARBANDINFO) };
rbBand.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_COLORS;
rbBand.fStyle = RBBS_GRIPPERALWAYS;
// Get the height of the toolbar.
DWORD dwBtnSize = (DWORD)SendMessage(hWndToolbar, TB_GETBUTTONSIZE, 0,0);
// Set values unique to the band with the toolbar.
rbBand.clrFore = RGB(233, 233, 233);
rbBand.clrBack = RGB(200, 200, 200);
rbBand.lpText = TEXT("");
rbBand.hwndChild = hWndToolbar;
rbBand.cyChild = LOWORD(dwBtnSize);
rbBand.cyMinChild = LOWORD(dwBtnSize);
rbBand.cxMinChild = 3 * HIWORD(dwBtnSize);
// The default width is the width of the buttons.
rbBand.cx = 0;
// Add the band
SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
// show the main window
ShowWindow(hWnd, nCmdShow);
// check the rebar size
WINDOWPLACEMENT wp;
GetWindowPlacement(hwndRebar, &wp);
int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
As follows from the comments, the solution was:
REBARBANDINFO rbBand;
rbBand.cbSize = REBARBANDINFO_V3_SIZE;
// initialize the rest here
This seems to affect older versions of Windows (specifically, XP), because the original code compiles and runs fine on Windows 7.
This was mentioned in the comments to a MSDN page: http://msdn.microsoft.com/en-us/library/windows/desktop/bb774393.aspx
As was already stated, the cbSize member of the REBARBANDINFO struct needed to be set.
Note the following about the provided code as well:
The images on the toolbar buttons don't show up. Right after creating the image list you have to make the following call to load the images:
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
The TBBUTTON array is declared with a size of 4 but it only gets populated with 3 items. Technically it should be declared as:
TBBUTTON tbb[3]
This code no longer works on Windows 7 or 8.
Aren't SysAnimate32 controls supported on them ?
Is there a way to make it work as it used to work on Windows XP ?
(I am required to write more details but I have no more details to talk about :)
thanks
#include <windows.h>
#include <CommCtrl.h>
HINSTANCE hInstance;
#define IDC_MYANIMATE 9
HWND CreateAnimationControl (HWND hParent)
{
HWND hAnimation = Animate_Create( hParent, IDC_MYANIMATE, ACS_AUTOPLAY | WS_BORDER | WS_CHILD, hInstance);
Animate_Open (hAnimation, "test.avi");
ShowWindow (hAnimation, SW_SHOW);
return hAnimation;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hAnimation = NULL;
switch (uiMsg)
{
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_CREATE:
hAnimation = CreateAnimationControl (hWnd);
break;
case WM_SHOWWINDOW:
if (wParam)
{
MoveWindow (hAnimation, 0, 0, 300, 300, TRUE);
Animate_Play (hAnimation, 0, -1, -1);
}
break;
}
return DefWindowProc (hWnd, uiMsg, wParam, lParam);
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpsCmdLine, int iCmdShow)
{
WNDCLASSEX WindowClass;
HWND hWnd;
MSG uMsg;
hInstance = GetModuleHandle (NULL);
WindowClass.cbClsExtra = 0;
WindowClass.cbSize = sizeof (WNDCLASSEX);
WindowClass.cbWndExtra = 0;
WindowClass.hbrBackground = CreateSolidBrush (RGB (0, 0, 0));
WindowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
WindowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
WindowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
WindowClass.hInstance = hInstance;
WindowClass.lpfnWndProc = WindowProcedure;
WindowClass.lpszClassName = "1";
WindowClass.lpszMenuName = NULL;
WindowClass.style = 0;
if (!RegisterClassEx (&WindowClass))
{
MessageBox (NULL, "Window class registration has failed!", "Error:", MB_OK | MB_ICONERROR);
return 0;
}
hWnd = CreateWindow ("1", "Win32 Animation Testing", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, NULL);
if( ! hWnd )
{
MessageBox (NULL, "Window creation has failed!", "Error:", MB_OK | MB_ICONERROR);
return 0;
}
ShowWindow( hWnd, SW_SHOW );
UpdateWindow( hWnd );
while( GetMessage( &uMsg, NULL, 0, 0 ) > 0 )
{
TranslateMessage( &uMsg );
DispatchMessage ( &uMsg );
}
return ( int ) uMsg.wParam;
}
No, that still works just fine when I tried your code on Windows 8, compiled with VS2012. A screenshot to prove it:
Notable is that your error checking is lacking, you don't pay attention to the return value of Animate_Open(). A FALSE return indicates that it could not open the .avi file. Many possible reasons, there's no better diagnostic from that function than "could not do it".
It was difficult to find a .avi file that it could handle, the control is stone-cold old and cannot handle but the simplest ones. In particular an .avi file that also has a audio track will not open, as documented by the MSDN Library. The test one I used was a very simple one I dug out of the Visual Studio image library, Animations/filecopy_16.avi.
So basic checks, after adding the error handling, is to ensure that the file is actually present in the same directory as your EXE and that it is a very simple .avi file that at least plays back in WMP.