how does Windows implement clipboard format conversion? - winapi

i am implementing clipboard for my application sandbox system on Windows. I have to simulate clipboard behaviors by myself. Now all work fine except for format conversion part, i can do it for CF_TEXT/UNICODETEXT/OEMTEXT, but there are other formats that i am not familiar with like DIB. Is there any sample codes illustrating how Windows does this?

The difference between CF_DIB and other clipboard formats is that it contains memory objects of BITMAPINFO structure and bitmap bits.
The msdn document has provided a very detailed example:
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;
HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Branch depending on the clipboard format.
switch (uFormat)
{
case CF_OWNERDISPLAY:
...
case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(hwnd))
{
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;
...
If you need to save the obtained bitmap object as a file, then you must strictly follow the BITMAPINFOHEADER structure to assign values.
MSDN code sample: Storing an Image

Related

Keep window on top of taskbar on Windows 11

I found that on windows 11, even if I created windows with WS_EX_TOPMOST style, the window still get covered by taskbar if you click anywhere on the taskbar. The only exception to this is task manager. How does it achieve it?
I want a feature to draw stuff on the taskbar, or at least mimic that behavior using a transparent window. I think there used to be 2 ways to achieve that:
Get DC of taskbar and draw (which no longer work on Windows11)
Create a transparent window and keep it on top of taskbar (issues with how to keep it topmost)
I kind of get stuck now. Any help is appreciated.
UPDATE:
I just noticed that the taskbar window, shown as DesktopWindowXamlSource in Spy++ does not have the WS_EX_TOPMOST style set. I guess it's possible to cover it for some ways?
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <iostream>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
WS_EX_TOPMOST, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
And this is how the window get covered by taskbar when I click start.

the procedure deal with WM_PAINT didn't draw anything on the window with Direct2D

I am doing a program with Direct2D and I want to paint a flower on the window. However, I could only see a white window with nothing except the menu. I tested, and then I found that the code which deal with the message WM_PAINT didn't work. I added a piece of code that show a message box, but I only heard the sound, and didn't see the box.
The main code:
// other code omitted
ID2D1Factory* pFactory = nullptr;
ID2D1HwndRenderTarget* pRenderTarget = nullptr;
ID2D1SolidColorBrush* pBrush = nullptr;
IWICImagingFactory* pImagingFactory = nullptr;
IWICBitmapDecoder* pBitmapDecoder = nullptr;
IWICFormatConverter* pConverter = nullptr;
ID2D1Bitmap* pBitmap = nullptr;
IWICBitmapFrameDecode* pDecode = nullptr;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
// other messages omitted
case WM_CREATE:
{
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory);
pFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(rect.right - rect.left, rect.top - rect.bottom)),
&pRenderTarget);
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImagingFactory));
pImagingFactory->CreateDecoderFromFilename(L".\\flower.png", nullptr, GENERIC_READ,
WICDecodeMetadataCacheOnLoad, &pBitmapDecoder);
pBitmapDecoder->GetFrame(0, &pDecode);
pImagingFactory->CreateFormatConverter(&pConverter);
pConverter->Initialize(pDecode, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone,
nullptr, 0.F, WICBitmapPaletteTypeCustom);
pRenderTarget->CreateBitmapFromWicBitmap(pConverter, nullptr, &pBitmap);
}
break;
case WM_PAINT:
{
MessageBox(hWnd, L"WM_PAINT", L"flower", MB_OK | MB_ICONEXCLAMATION); // the test code
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
pRenderTarget->BeginDraw();
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Red));
if (pBitmap != nullptr)
pRenderTarget->DrawBitmap(pBitmap, D2D1::RectF(500, 500, 572, 572));
pRenderTarget->EndDraw();
EndPaint(hWnd, &ps);
}
break;
}
}

Can't draw in title bar on WM_NCPAINT?

I'm trying to draw something in the title bar area to represent an X since there is no WS_CAPTION, it just uses WS_EX_TOOLWINDOW | WS_EX_TOPMOST and WS_POPUP|WS_THICKFRAME. But I can't get anything to draw anywhere. I did a test below to just fill it all in red, but nothing changed. What am I doing wrong or missing?
case WM_NCACTIVATE:
case WM_NCPAINT:
{
// call default handler (I've tried it both ways, with and without DefWindowProc)
::DefWindowProc(hwnd, umsg, wparam, lparam);
HDC hdc;
if ((hdc=::GetWindowDC(hwnd))!=NULL) {
// Paint into this DC
RECT rcwin;
if (::GetWindowRect(hwnd, &rcwin)) {
HBRUSH hbrush=::CreateSolidBrush(RGB(255, 0, 0));
if (hbrush) {
rcwin.right-=rcwin.left;
rcwin.bottom-=rcwin.top;
rcwin.left=rcwin.top=0;
::FillRect(hdc, &rcwin, hbrush);
::DeleteObject(hbrush);
}
}
::ReleaseDC(hwnd, hdc);
}
return 0;
}
Based on a Link from Remy about the evil WM_NCPAINT, converted from the pascal version to the C++ version below. It works as well as the link in stackoverflow but again, only if WS_CAPTION is provided. I'm just posting here for completeness.
case WM_NCPAINT:
{
#ifndef DCX_USESTYLE
#define DCX_USESTYLE 0x00010000
#endif
HDC hdc=::GetDCEx(hwnd, 0, DCX_WINDOW|DCX_USESTYLE);
if (hdc) {
RECT rcclient;
::GetClientRect(hwnd, &rcclient);
RECT rcwin;
::GetWindowRect(hwnd, &rcwin);
POINT ptupleft;
ptupleft.x=rcwin.left;
ptupleft.y=rcwin.top;
::MapWindowPoints(0, hwnd, (LPPOINT) &rcwin, (sizeof(RECT)/sizeof(POINT)));
::OffsetRect(&rcclient, -rcwin.left, -rcwin.top);
::OffsetRect(&rcwin, -rcwin.left, -rcwin.top);
HRGN rgntemp=NULL;
if (wparam==NULLREGION || wparam==ERROR) {
::ExcludeClipRect(hdc, rcclient.left, rcclient.top, rcclient.right, rcclient.bottom);
}
else {
rgntemp=::CreateRectRgn(rcclient.left+ptupleft.x, rcclient.top+ptupleft.y, rcclient.right+ptupleft.x, rcclient.bottom+ptupleft.y);
if (::CombineRgn(rgntemp, (HRGN) wparam, rgntemp, RGN_DIFF)==NULLREGION) {
// nothing to paint
}
::OffsetRgn(rgntemp, -ptupleft.x, -ptupleft.y);
::ExtSelectClipRgn(hdc, rgntemp, RGN_AND);
}
HBRUSH hbrush = ::CreateSolidBrush(RGB(255, 0, 0));
::FillRect(hdc, &rcwin, hbrush);
::DeleteObject(hbrush);
::ReleaseDC(hwnd, hdc);
if (rgntemp!=0) {
::DeleteObject(rgntemp);
}
}
return 0;
}
Do not draw directly in WM_NCACTIVATE. If you need to trigger a repaint, you can use RedrawWindow() for that. Do all of the actual drawing in WM_PAINT/WM_NCPAINT.
When drawing in WM_NCPAINT, the documentation says to use GetDCEx() to get the HDC to draw on. The wParam is an HRGN that you can draw within. You can use GetRgnBox() to get the bounding rectangle of the HRGN, if needed.
case WM_NCPAINT: {
::DefWindowProc(hwnd, umsg, wparam, lparam);
HRGN hrgn = (HRGN)wParam;
HDC hdc = ::GetDCEx(hwnd, hrgn, DCX_WINDOW | DCX_INTERSECTRGN);
HBRUSH hbrush = ::CreateSolidBrush(RGB(255, 0, 0));
::FillRgn(hdc, hrgn, hbrush);
::DeleteObject(hbrush);
::ReleaseDC(hwnd, hdc);
return 0;
}

WinApi DrawText and TextOut cannot display unicode character U+5167

Hello Windows Programmers!
I am new to windows programming using winapi. I was reading this very nice book and I encountered a problem(displayed as a black box) when I display unicode character U+5167 (內) in the client area using DrawText and TextOut. Mysteriously, this particular unicode character is properly displayed in windows caption area. This unicode character is also displaying correctly when I display it using the MessageBox. Finally, I tried displaying other unicode characters that are relatively near to this unicode like U+5166, U+5168, U+5157 and U+5177;
Here's a link for this unicode character as defined by the standards.
http://unicode-table.com/en/#5167
Note : I am compiling this code using Unicode using Visual Studio 2010
Below is my code.
#include<windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass (&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("內Sample text 內篇 日本国 渡瀬 內篇全兦兗具 кошка"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
LPTSTR c = TEXT("內Sample text 內篇 日本国 渡瀬 內篇全兦兗具 кошка");
switch(message)
{
case WM_CREATE:
PlaySound(TEXT("shutda.wav"), NULL, SND_FILENAME | SND_ASYNC);
MessageBox(hwnd, c, c, 0);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawTextEx(hdc, c, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER, NULL);
TextOut(hdc, 100, 100, TEXT("內篇 日本国 кошка unicode"), 19);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
For non windows programmers, you can compile and run this code by copy and pasting this directly to a .cpp file. If you are using VS2010, you just need to create new "Win32 Application" project and select "Empty Project". After that, you need to add a cpp file for example "test.cpp" in the source folder of your project. Then copy and past the code
to the "test.cpp" then build and run it. You should now see my problem. :)
When a valid character is displayed as a rectangular box, that indicates that the font does not contain a glyph for the characters. In order to solve the problem you need to use a font which does have a glyph for this character.

WinAPI: Image on hover

A friend of mine is about to release an application and asked me to create a launcher for it. I found it a good excuse to finally study WinAPI and thought that a simple launcher would be easily doable even in a relatively small time window.
I was wrong.
I'm trying to create a launcher window with 5 buttons that start different things. The goal is to have transparent buttons (not done yet) that have a smaller image inside them. The image should only be displayed when the user hovers over the larger button area.
The images are in .png format. I'm using GDI+ and loading PNG files from resources with http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI.
I'm using MouseTrackEvents to keep track of the mouse and I've also subclassed a button. The problem is that I don't know how I should handle the WM_MOUSELEAVE message. I don't know how to erase the image I've drawn. If I have to save the ht_img as a variable and refer to it later, I don't know how.
Here's what I have so far. This example loads the .png from resource IDB_Website2. Displaying the image works (although it keeps being rendered over and over again currently):
WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
GDI gdi;
switch (Msg)
{
case WM_CREATE:
{
HWND hwndButton = CreateWindow(TEXT("button"), NULL,
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
80, 10, 100, 50,
hWnd, (HMENU) HT_BUTTON1, NULL, NULL);
HTButton = (WNDPROC) SetWindowLong(hwndButton, GWL_WNDPROC, (LONG) ButtonProc);
}
...
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
gdi.InitList(hInst, hdc);
EndPaint(hWnd, &ps);
break;
Buttonproc (subclassed button):
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MouseTrackEvents MouseTrack;
GDI gdi;
HDC odc = GetDC(GetParent(hWnd));
switch(Msg)
{
case WM_MOUSEMOVE:
MouseTrack.OnMouseMove(hWnd);
break;
case WM_MOUSEHOVER:
gdi.Create(IDB_Website2, _T("PNG"), hInst, odc, 62, 347, 200, 40, true);
MouseTrack.Reset(hWnd);
break;
case WM_MOUSELEAVE:
MouseTrack.Reset(hWnd);
break;
}
return CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
}
class GDI's Create graphic method:
void Create(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
Graphics grpx(hdc);
ht_img = new CGdiPlusBitmapResource();
ht_img -> Load(menuid, pType, hInst);
grpx.DrawImage(*ht_img, x, y, w, h);
delete ht_img;
}
This has been quite a challenge so far! It's been fun although a bit tear-my-hair-out inducing at times. :-) I'd be grateful for any advice on how I should proceed.
EDIT: Answering Adrian
I tried changing my Buttonproc, but the image doesn't seem to be rendered. Here's what I did:
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MouseTrackEvents MouseTrack;
GDI gdi;
HDC odc = GetDC(GetParent(hWnd));
PAINTSTRUCT ps;
int result;
switch(Msg)
{
case WM_MOUSEMOVE:
MouseTrack.OnMouseMove(hWnd);
break;
case WM_MOUSEHOVER:
hovering = true;
break;
case WM_MOUSELEAVE:
hovering = false;
MouseTrack.Reset(hWnd);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
result = CallWindowProc(HTButton, hWnd, Msg, wParam, lParam);
if (hovering == true) {
gdi.Create(IDB_Play2, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
}
EndPaint(hWnd, &ps);
return result;
}
return CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
}
You probably don't want to do the painting directly in the handling of the mouse events. You probably want to handle WM_PAINT in the button proc by calling the underlying implementation and then augmenting it based on the hover state. Then your mouse handling corresponds to flipping a state variable and invalidating the button (which will cause it to repaint).
case WM_PAINT:
// start with the standard rendering
int result = CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
// then overdraw our embellishments
if (my_state_variable == hovering) {
DrawOverlayImage();
}
return result; // don't just break here, or you'll call CallWindowProc again

Resources