I need your help for a project at my job.
The main software, using 4D language, works as a MDI, it creates many windows included in the main window. The main issue is that we have a lot of windows and we need an easy way to switch from a window to another.
We decided to create a little c++ plugin which is a dll to resolve this issue.
This plugin will create a tab on the taskbar for each window opened like Windows Explorer. The creation and the deletion of tabs already works.
But the current problem is that no thumbnail is set on the Tab.
The parameter given is the ID of the window from the main software. The method called PA_GetHWND is the method given by 4D to obtain the handle of the window using the windowID.
I have already check where is the problem. The bitmap created from the window already exists and is good. For this test, i put the bitmap in the clipboard and paste it on Paint and the Bitmap was good.
Here is the code introducing the method to refresh the bitmap on the tab.
bool CManageTaskBar::UpdateWindow(long WindowID)
{
HRESULT res;
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
//Get the Handle from 4D
HWND nHandle = (HWND)(PA_GetHWND((PA_WindowRef)(WindowID)));
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(nHandle);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
// Get the client area for size calculation
RECT rcClient;
GetClientRect(nHandle, &rcClient);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow,rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(nHandle, L"BitBlt has failed", L"Failed", MB_OK);
//goto done;
}
ITaskbarList3* ptbl = NULL;
HRESULT hr = CoCreateInstance(my_CLSID_TaskbarList, NULL, CLSCTX_ALL, my_IID_ITaskbarList3, (LPVOID*)&ptbl);
BOOL fForceIconic = TRUE;
BOOL fHasIconic = TRUE;
res = DwmSetWindowAttribute(nHandle, DWMWA_FORCE_ICONIC_REPRESENTATION, &fForceIconic, sizeof(fForceIconic));
res = DwmSetWindowAttribute(nHandle, DWMWA_HAS_ICONIC_BITMAP, &fHasIconic, sizeof(fHasIconic));
if (hbmScreen)
{
res = DwmSetIconicThumbnail(nHandle, hbmScreen,0);//DWM_SIT_DISPLAYFRAME);
}
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(nHandle, hdcWindow);
return true;
}
The calls to DwmSetWindowAttribute return Invalid Handle. This handle works to get a bitmap but not to set an attribute.
And the call to DwmSetIconicThumbnail returns E_INVALIDARG maybe because the handle given is wrong.
Why cannot I set an attribute to this handle and why the call to set the thumbnail returns E_INVALIDARG ?
Thanks to everyone who will take care of my problem.
It's my first question, be friendly please :)
Related
So I'm trying to make a clone of Pong in Win32, and things WERE working, but then I did a lot of stuff with the physics, and when I tested it, the sprite bitmaps weren't even displaying any more :/
So, here is how I initialise the rendering stuff:
int InitRenderer(int showCMD)
{
context = GetDC(winHandle);
if(!context)
{
return EXIT_FAILURE;
}
ShowWindow(winHandle, showCMD);
UpdateWindow(winHandle);
CreateDoubleBuffer(&globalBuffer);
ClearWindow(globalBuffer.hdcBack, globalBuffer.scrnRect);
return EXIT_SUCCESS;
}
Here is the CreateDoubleBuffer function:
void CreateDoubleBuffer(BUFFER *buffer)
{
buffer->hwnd = winHandle;
GetClientRect(winHandle, &(buffer->scrnRect));
buffer->hdcFront = GetDC(buffer->hwnd); //get a handle to the DC and plop it into the front buffer.
buffer->hdcBack = CreateCompatibleDC(buffer->hdcFront); //get a compatible DC for the Back buffer.
buffer->hdcBitmap = CreateCompatibleDC(buffer->hdcFront); //get a compatible DC for the bitmap.
buffer->hCompBitmap = CreateCompatibleBitmap(buffer->hdcFront, buffer->scrnRect.right, buffer->scrnRect.bottom); //Create a compatible bitmap as a dummy, and store in the front buffer.
buffer->hOldBitmap = (HBITMAP)SelectObject(buffer->hdcBack, buffer->hCompBitmap);
}
the BUFFER struct, for reference, looks like this:
struct BUFFER // This is our back buffering structure
{
HWND hwnd; // This holds the current window's handle
RECT scrnRect; // This holds the client rectangle of the window
HANDLE hCompBitmap; // This holds the compatible bitmap for the backbuffer
HANDLE hOldBitmap; // This is used for storage to free when the program quits
HANDLE hOldBitmap2; // This is used as storage to swap between selected bitmaps when using selectObject()
HDC hdcFront; // This is the front buffer (The part we see)
HDC hdcBack; // This is the back buffer (the part we draw to, then flip)
HDC hdcBitmap; // This is a temp buffer to swap the bitmap back and forth from
};
So I have a Sprite class which just wraps an HBITMAP and a string for the filename, and some functions to manipulate those. When I want to draw the sprite, this function is called:
void RenderSprite(BUFFER *buffer, HBITMAP bmp, Vec2I pos, Vec2F origin)
{
buffer->hOldBitmap2 = (HBITMAP)SelectObject(buffer->hdcBitmap, bmp); //we put the bitmap into the extra HDC to hold it there.
if(!buffer->hOldBitmap2)
{
std::cout << GetLastError() << "\n";
}
BitBlt(buffer->hdcBack, pos.GetX() + (int)origin.GetX(), pos.GetY() + (int)origin.GetY(), buffer->scrnRect.right, buffer->scrnRect.bottom, buffer->hdcBitmap, 0, 0, SRCCOPY); //blit the bitmap into the backbuffer.
SelectObject(buffer->hdcBitmap, buffer->hOldBitmap2); //put the old handle to the bitmap back where it belongs.
}
And it is at the start of this function where SelectObject fails, so buffer->hOldBitmap2 is null. The error returned by GetLastError is 1400, which means invalid window handle, so I guess winHandle (a global variable, just so you know) is messed up. But I don't see how. Here is how I initialise it:
int Game::Start(HINSTANCE instance, int showCMD)
{
WNDCLASSEX winClass = {0};
winClass.cbSize = sizeof(winClass);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WndProc;
winClass.hInstance = instance;
winClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winClass.lpszClassName = _winClassName;
winClass.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(OCR_CROSS), IMAGE_CURSOR, 0, 0, LR_SHARED); //using a cross for a cursor because we're hipsters here at cow_co industries.
RegisterClassEx(&winClass);
/**
* WS_EX_CLIENTEDGE gives the client a sunken edge.
**/
winHandle = CreateWindowEx(WS_EX_CLIENTEDGE, _winClassName, "Win32 Pong", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, instance, NULL);
if(!winHandle)
{
return EXIT_FAILURE;
}
//other stuff...
And I haven't changed this since the time when it worked.
In terms of what happens on-screen, I just get a blank white window, so no Crash message or whatever, but it's just...blank.
I've looked around other questions and the solutions to those seem to be related to issues registering the window class etc. I've checked mine and I can't see anything wrong with it. I've debugged, and hOldBitmap2 is the only part of the buffer that's null. The rest's fine.
Any help you sages of the Stack could provide would be much appreciated.
I just ran into this problem.
An application cannot select a single bitmap into more than one DC at
a time. Is the bitmap already selected into another DC? – theB Aug 27
'15 at 18:09
Is close!
OK:
SelectObject( hdc_bitmap, buf_bitmap )
FAILS:
SelectObject( hdc_bitmap, buf_bitmap )
SelectObject( hdc_bitmap, buf_bitmap )
This means if your RenderSprite function tries to draw the same sprite twice in a row,
it will fail.
I'm trying to create an application in Win32 api with c++ and I want to make it FullScreen without any bar , i succeeded but i still have a problem in the background image. The image is repeated but i want it to be stretched. Have you any idea?
below part from the code :
int WINAPI WinMain (HINSTANCE cetteInstance, HINSTANCE precedenteInstance,
LPSTR lignesDeCommande, int modeDAffichage)
{
HWND fenetrePrincipale;
MSG message;
WNDCLASS classeFenetre;
instance = cetteInstance;
classeFenetre.style = 0;
classeFenetre.lpfnWndProc = procedureFenetrePrincipale;
classeFenetre.cbClsExtra = 0;
classeFenetre.cbWndExtra = 0;
classeFenetre.hInstance = NULL;
classeFenetre.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
classeFenetre.hCursor = LoadCursor(NULL, IDC_ARROW);
// classeFenetre.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
//classeFenetre.hbrBackground = CreatePatternBrush(LoadBitmap( instance, MAKEINTRESOURCE("images\Image1.bmp" ) ) );
HBITMAP hbmp = LoadBitmap(instance,MAKEINTRESOURCE(IDB_BITMAP1));
if(NULL == hbmp)
{
MessageBox(NULL,L"BitMap Loading Failed.",L"Error",MB_ICONEXCLAMATION | MB_OK);
}
else
{
HBRUSH hbr = CreatePatternBrush(hbmp);
if(NULL == hbr)
{
MessageBox(NULL,L"Brush Creation Failed.",L"Error",MB_ICONEXCLAMATION | MB_OK);
}
else
{
//StretchBlt();
HDC hdcMem = GetDC (NULL) ;
HDC wndHDC = GetDC (fenetrePrincipale) ;
StretchBlt(hdcMem, 0, 0, 800, 600, wndHDC, 0, 0, 1280, 1024, SRCCOPY);
classeFenetre.hbrBackground = hbr ;
}
}
classeFenetre.lpszMenuName = NULL;
classeFenetre.lpszClassName = L"classeF";
//fullscreen mode and delete minimize and max buttons
// On prévoit quand même le cas où ça échoue
if(!RegisterClass(&classeFenetre)) return FALSE;
//WS_OVERLAPPEDWINDOW
fenetrePrincipale = CreateWindow(L"classeF", L"Ma premiere fenetre winAPI !",WS_MAXIMIZE|WS_POPUP ,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 630,
NULL,
NULL,//LoadMenu(instance, L"ID_MENU"),
cetteInstance,
NULL);
if (!fenetrePrincipale) return FALSE;
//ShowWindow(fenetrePrincipale, modeDAffichage);
ShowWindow(fenetrePrincipale,SW_MAXIMIZE);
UpdateWindow(fenetrePrincipale);
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return message.wParam;
}
thanks
You haven't shown the exact code, but it appears that you load a bitmap, create a brush from it, and then set that brush as the brush for your window. Brushes would indeed lead to the repeating-image behavior you report. To get a stretched bitmap, you may skip any brush-related code. Instead, handle the WM_ERASEBKGND message sent to your window. In it, call StretchBlt to paint your bitmap onto the client area of your window. The HDC to paint to is given in the message's wParam argument.
Steps
1, CreateWindowEx to create the window
2, SetWindowPos to place your window on top of all windows and Fullscreen
3, On your windows's WindowProce handle WM_PAINT message
4, Load your bitmap
5, Create a memory dc using CreateCompatibleDC
6, Selet your bitmap into memory dc by calling SelectObject
7, Do the StretchBlt to your actual dc, using the prepared memory dc as the source, you should know the actual width and height of the bitmap for proper stretching
was using XP without issue for a long time. switched to 7 and trying to capture screenshots with my previously functioning code no longer works. simple concept and relatively generic code...just find the window that i call and save it as a .png. any ideas what might make this bad boy run again? can't debug with my current setup, but it makes it all the way and spits out the error message after bmp->save(...) ...couldn't save image file. edit: also a file does get created/saved, but it is blank and not written to. perhaps the bitmap encoding or GDI is screwed up?
bool CScreenShot::Snap(CString wintitle, CString file, CString& ermsg)
{
ermsg = ""; // no error message
// create screen shot bitmap
EnumWinProcStruct prm = {0, (LPSTR)(LPCTSTR)wintitle, 0};
// Find the descriptor of the window with the caption wintitle
EnumDesktopWindows(0, EnumWindowsProc, (LPARAM)&prm);
if(!prm.hwnd)
{
ermsg.Format("couldn't find window \"%s\"", wintitle);
return false;
}
// Make the window the topmost window
SetWindowPos(prm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
Sleep(300);
// Get device context for the top-level window and client rect
HDC hDC = GetDC(prm.hwnd);
RECT rc;
GetClientRect(prm.hwnd, &rc);
HDC memDC = CreateCompatibleDC(hDC);
// Set the size and color depth for the screen shot image
BITMAPINFO bmpInfo;
memset(&bmpInfo, 0, sizeof(bmpInfo));
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
bmpInfo.bmiHeader.biHeight = rc.bottom - rc.top;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight * 3;
// Create memory buffer and perform a bit-block transfer of the color data from the window to the memory
LPVOID addr;
HBITMAP memBM = CreateDIBSection(memDC, &bmpInfo, DIB_RGB_COLORS, &addr, 0, 0);
HGDIOBJ stdBM = SelectObject(memDC, memBM);
BOOL OK = BitBlt(memDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(prm.hwnd, hDC);
SetWindowPos(prm.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
{
ermsg.Format("couldn't start GDI+");
return false;
}
// Create a Bitmap object for work with images defined by pixel data from the GDI HBitmap and the GDI HPalette.
Bitmap* bmp = ::new Bitmap(memBM, DIB_RGB_COLORS);
SelectObject(memDC, stdBM);
DeleteObject(memBM);
DeleteDC(memDC);
// Find the encoder for "image/png" mime type
CLSID encoderClsid;
EncoderParameters encoderParameters;
GetEncoderClsid(L"image/png", &encoderClsid);
encoderParameters.Count = 0;
// Convert file name to Unicode (wide-char) string.
WCHAR fn[_MAX_PATH];
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, file, file.GetLength() + 1, fn, _MAX_PATH);
// Save the screen shot into the specified file using image encoder with the mime style "image/png"
if(bmp->Save(fn, &encoderClsid, &encoderParameters) != Ok)
{
ermsg.Format("couldn't save image file \"%s\"", file);
return false;
}
::delete bmp;
GdiplusShutdown(gdiplusToken);
return true;
}
The error message implies that you're trying to save the file to a folder that you don't have permission to write to. Many folders such as Program Files are now protected. Since you didn't include the path in your sample code I'm unable to determine if this is the actual problem.
Edit: Another possibility is that the Bitmap is improperly constructed which causes the Save to fail. The second parameter to the constructor is supposed to be a handle to a palette, I think DIB_RGB_COLORS would be invalid here and you should use NULL. Also there are a couple of caveats noted in the Microsoft documentation and perhaps the different OS versions react differently when you break the rules:
You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap::Bitmap object is deleted or goes out of scope.
Do not pass to the GDI+ Bitmap::Bitmap constructor a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
win7 won't accept encoderParameters.Count == 0 for some reason. Set that == 1 and you should be all set.
you probably could also just remove that parameter from Save() (overloaded)
I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.
My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.
Here's the line where I setup the new window:
hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);
For the call to RegisterClassEx, I have this set for the background:
wcex.hbrBackground = (HBRUSH)0;
Here is my handler for WM_PAINT message:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
graphics.DrawImage(*m_pBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
}
One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.
When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).
I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.
What can be done to get the window to repaint when the contents under it changes?
I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:
Displaying a Splash Screen with C++
Part 1: Creating a HBITMAP archive
Part 2: Displaying the window archive
Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = 0;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
Use the SetLayeredWindowAttributesarchive function, this allows you to set a mask color that will become transparent, thus allowing the background to show through.
You will also need to configure your window with the layered flag, e.g.:
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
After that it's fairly simple:
// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);
When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:
Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above
In my application i need to create HBITMAP objects to which I render and from where I copy the result.
I use the function "CreateDIBSection" to create these bitmaps, however this function required a DC (Device Context) as first parameter. Currently I get this by calling GetDC(hWnd) on the main windows handle (hWnd). But I would like to be able to create HBITMAPS without the requirement of having an application window, without some kind of in memory DC, Is this possible?
CreateCompatibleDC(NULL) will create you a device context that is compatible with the screen - which sounds like it would be ideal in the situation.
You can get one with CreateDC for the display:
HDC hDc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
Cleanup with DeleteDC(). It is only used to initialize the palette for bitmaps with indexed format. NULL might work if you don't use such a format, never tried it.
Then there's GDI+, #include <gdiplus.h> and the Bitmap class...
try this. it worked.
HDC hdcScreen = ::GetDC( NULL );
HDC hdcMemDC = ::CreateCompatibleDC(hdcScreen);
HBITMAP hbmScreen = ::CreateCompatibleBitmap(hdcScreen, cx, cy);
HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hdcMemDC, hbmScreen);
MyImageDraw(hdcMemDC, ...);
// The drawing image is held in hBitmap. You can save it
HBITMAP hBitmap = (HBITMAP)::SelectObject(hdcMemDC, hOldBitmap);
// save The trend image into c:\test.bmp
PBITMAPINFO pbi = CreateBitmapInfoStruct(hBitmap);
CreateBMPFile("C:\\Temp\\test.bmp", pbi, hBitmap, hdcMemDC);
//Clean up
::DeleteObject(hbmScreen);
::DeleteObject(hdcMemDC);
::ReleaseDC( NULL, hdcScreen );