Win32: UpdateLayeredWindow failing sometimes (error 317) - mfc

I have a bizarre issue here. I'm displaying a semi-transparent splash screen (a .png file) using layered windows. It works on some machines but not others. On the machines where it doesn't work, the GetLastError returns 317 (which isn't very helpful). Has anyone experienced this before? Here are my relevant functions. The incoming parameters for CreateAsPNG are WS_VISIBLE|WS_POPUP (dwStyle), 0 (dwExStyle), and the handle to a hidden tool window for the parent so no taskbar entry is created. I've verified that I can load an embedded PNG resource into CImage and that the size of the image is correct.
Thanks in advance for any help!
BOOL MySplashWnd::CreateAsPNG( DWORD dwStyle, DWORD dwExStyle, const CString& sTitle, HWND hWndParent )
{
ATL::CImage img;
CreateStreamOnResource( m_nBitmapID, img );
m_nWndWidth = img.GetWidth();
m_nWndHeight = img.GetHeight();
int nTop = 0;
int nLeft = 0;
GetTopLeft( nTop, nLeft );
dwExStyle |= WS_EX_LAYERED;
// Create the Splash Window
BOOL bRetVal = CWnd::CreateEx( dwExStyle, AfxRegisterWndClass( CS_CLASSDC ), sTitle,
dwStyle, nLeft, nTop, m_nWndWidth, m_nWndHeight, hWndParent, NULL );
//Couldn't create the window for some unknown reason...
X_ASSERT( bRetVal != FALSE );
if ( bRetVal )
{
HDC hScreenDC = ::GetDC( m_hWnd );
HDC hDC = ::CreateCompatibleDC( hScreenDC );
HBITMAP hBmp = ::CreateCompatibleBitmap( hScreenDC, m_nWndWidth, m_nWndHeight );
HBITMAP hBmpOld = ( HBITMAP ) ::SelectObject( hDC, hBmp );
img.Draw( hDC, 0, 0, m_nWndWidth, m_nWndHeight, 0, 0, m_nWndWidth, m_nWndHeight );
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptPos = { nLeft, nTop };
SIZE sizeWnd = { m_nWndWidth, m_nWndHeight };
POINT ptSource = { 0, 0 };
if ( ::UpdateLayeredWindow( m_hWnd, hScreenDC, &ptPos, &sizeWnd, hDC, &ptSource, 0, &blend, ULW_ALPHA ) )
{
}
else
{
// The last error value is 317 on some Win7 machines.
TRACE( _T( "*** Last error: %d\n" ), ::GetLastError() );
}
::SelectObject( hDC, hBmpOld );
::DeleteObject( hBmp );
::DeleteDC( hDC );
::ReleaseDC( NULL, hScreenDC );
}
return bRetVal;
}
void MySplashWnd::CreateStreamOnResource( UINT nIDRes, ATL::CImage& img )
{
HINSTANCE hInstance = ::GetMUIResourceInstance();
if ( hInstance == NULL )
{
return;
}
HRSRC hResource = ::FindResource( hInstance, MAKEINTRESOURCE( nIDRes ), "PNG" );
if ( hResource == NULL )
{
return;
}
DWORD dwResourceSize = ::SizeofResource( hInstance, hResource );
if ( dwResourceSize == 0 )
{
return;
}
HGLOBAL hImage = ::LoadResource( hInstance, hResource );
if ( hImage == NULL )
{
return;
}
LPVOID pvImageResourceData = ::LockResource( hImage );
if ( pvImageResourceData == nullptr )
{
return;
}
HGLOBAL hImageData = ::GlobalAlloc( GMEM_MOVEABLE, dwResourceSize );
if ( hImageData == NULL )
{
return;
}
LPVOID pvImageBuffer = ::GlobalLock( hImageData );
if ( pvImageBuffer != nullptr )
{
::CopyMemory( pvImageBuffer, pvImageResourceData, dwResourceSize );
::GlobalUnlock( hImageData );
IStream* pStream = nullptr;
if ( SUCCEEDED( ::CreateStreamOnHGlobal( hImageData, TRUE, &pStream ) ) )
{
img.Load( pStream );
pStream->Release();
}
::GlobalUnlock( hImageData );
}
::GlobalFree( hImageData );
} // CTTSplashWnd::CreateStreamOnResource
UPDATE: I've found that even on the same machine, sometimes UpdateLayeredWindow succeeds and other time it fails (but always with code 317 if it does fail). Another piece of info is that this splash is being run on a separate UI thread. It always works on my machine though...

I'm having this same problem and I can't find any info either on it. Using SetWindowAttributes method instead works, but I want to use the SetLayeredWindow way. I'm coming to the point where I'm going to debug through the whole windows API to find out what is happening since msdn gives jack info about this error message. The only difference is that the OpenGL layered window demo example I saw which uses this method, uses CreateDIBSection instead of CreateCompatibleBitmap which seems to work on my PC.

I've had UpdateLayeredWindow() work perfectly on a Windows 7 x86_64 machine only for it to start failing when i've disabled desktop composition. It turned out that
UpdateLayeredWindow (window_handle, NULL,
&position, &size,
buffer_hdc, &buffer_offset,
0, &blend_options, ULW_ALPHA);
works, while
UpdateLayeredWindow (window_handle, NULL,
&position, NULL,
buffer_hdc, &buffer_offset,
0, &blend_options, ULW_ALPHA);
does not work and fails with error 317.
I've been trying to skip some of the arguments because i didn't need to change window size or contents, just to move it. With desktop composition disabled, this turned out to be impossible.
Not sure how this relates to your original problem, as you're also supplying the screen DC, while i don't.

Related

Winlogon screen capturing in Windows 7/10

I need to capture a winlogon screen in WinXP/Win7/10.
For WinXP I'm using a mirror driver and a standard methodic like this:
...
extern "C" __declspec(dllexport) void SetActiveDesktop() {
if ( currentDesk != NULL )
CloseDesktop( currentDesk );
currentDesk = OpenInputDesktop( 0, FALSE, GENERIC_ALL );
BOOL ret = SetThreadDesktop( currentDesk );
int LASTeRR = GetLastError();
}
extern "C" __declspec(dllexport) HBITMAP CaptureAnImage(
int width,
int height,
int bitsPerPixel )
{
HBITMAP hbmScreen;
LPTSTR bih = NULL;
HDC hdcMemDC = NULL;
int colorDepth = GetCurrentColorDepth();
if ( bitsPerPixel > colorDepth && colorDepth > 0 )
bitsPerPixel = colorDepth;
// Checks a current HDC
if ( currHdc == NULL ) {
SetActiveDesktop();
currHdc = GetDcMirror();
}
if ( prevHdc != currHdc ) {
prevHdc = currHdc;
}
// Check an application instance handler
if ( appInstance == NULL )
appInstance = GetModuleHandle(NULL);
// Creates a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC( currHdc );
if( hdcMemDC == NULL )
{
return NULL;
}
// Defines bitmap parameters
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bitsPerPixel;
// Creates a bitmap with defined parameters
hbmScreen = CreateDIBSection( hdcMemDC, &bi, DIB_RGB_COLORS, (VOID**)&lpBitmapBits, NULL, 0 );
if ( hbmScreen == NULL ) {
return NULL;
}
// 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,
width, height,
currHdc,
0,0,
SRCCOPY))
{
SetActiveDesktop();
currHdc = GetDC(NULL);//GetDcMirror();
hdcMemDC = CreateCompatibleDC( currHdc );
// Creates a bitmap with defined parameters
hbmScreen = CreateDIBSection( hdcMemDC, &bi, DIB_RGB_COLORS, (VOID**)&lpBitmapBits, NULL, 0 );
if(!BitBlt(hdcMemDC,
0,0,
width, height,
currHdc,
0,0,
SRCCOPY ))
{
DeleteDC( hdcMemDC );
return hbmScreen;
}
}
if (DeleteDC( hdcMemDC ) == FALSE ) {
return NULL;
}
return hbmScreen;
}
And luckily it works on WinXP. But in case of win7/win10 I've a quite different situation:
SetThreadDesktop function after switching to winlogon always returns FALSE with an error 5 ( access denied )
I tried to change a strategy:
First of all program creates a list of all of existing window stations and their desktops.
After that program "polls" all of WINSTAs and HDESKs and saves screenshots on a disk.
I tried to launch this program under 3 modes:
as an Administrator
as a service with enabled desktop interaction.
at winlogon desktop using CreateProcess flags ( in this case program simply crashes )
And result was the same.
What I'm doing wrong? Should I try a Desktop Duplication API?
Thanks in advance for your responses!
As Winlogon is a secure desktop you have to run your application under LOCAL_SYSTEM account to access it.
Example: a windows service that runs under LOCAL_SYSTEM that launches an user application (that captures the screen) in a console session.
In your code there is no check for the return value of OpenInputDesktop which may be NULL with error code 5 (access denied).
Check this answer as well for more information

Expected and actual printing results do not match

INTRODUCTION AND RELEVANT INFORMATION:
I am trying to bypass another problem in my application by trying to do printing/print preview on my own.
I am trying to create a table that would look like in the picture below:
I am using C++ and WinAPI, on WindowsXP SP3. I work in MS Visual Studio 2008.
I do not have a printer, so I am testing the results by printing to MS OneNote and XPS file.
PROBLEM:
Text is obtained from database and is of variable length. Since it might not fit into the original cell, I will need to expand the cell and fit the text appropriately, like in the above image.
SIDE EFFECT:
The result of my testing code gives inconsistent results regarding font size.
In OneNote the print result seems fine :
However, in XPS it looks different :
MY EFFORTS TO SOLVE THIS TASK:
I have checked MSDN documentation to get started. So far I am able to successfully draw text and lines on a printing surface.
I have used DrawTextEx to perform word breaking ( by using flag DT_WORDBREAK ).
To obtain the size of the printing area I have used GetDeviceCaps, and to obtain printer device context I have used print property sheet.
QUESTIONS:
IMPORTANT REMARKS:
If the following questions are considered too broad please leave a comment and I will edit my post. I still believe that my mistakes are minor and can be explained in a single post.
1. Can you explain me how to adjust cells so the entire string can fit ?
Why is my font inconsistently drawn ?
As always, here are the instructions for creating SSCCE :
1) In Visual Studio, create default Win32 project.
2) in stdafx.h file comment out #define WIN32_LEAN_AND_MEAN so print property sheet can work properly.
3) In stdafx.h add the following, below #include <windows.h> line :
#include <windowsx.h>
#include <commctrl.h>
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
4) Add the following function above window procedure :
// hWnd is the window that owns the property sheet.
HRESULT DisplayPrintPropertySheet(HWND hWnd)
{
HRESULT hResult;
PRINTDLGEX pdx = {0};
LPPRINTPAGERANGE pPageRanges = NULL;
// Allocate an array of PRINTPAGERANGE structures.
pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
if (!pPageRanges)
return E_OUTOFMEMORY;
// Initialize the PRINTDLGEX structure.
pdx.lStructSize = sizeof(PRINTDLGEX);
pdx.hwndOwner = hWnd;
pdx.hDevMode = NULL;
pdx.hDevNames = NULL;
pdx.hDC = NULL;
pdx.Flags = PD_RETURNDC;
pdx.Flags2 = 0;
pdx.ExclusionFlags = 0;
pdx.nPageRanges = 0;
pdx.nMaxPageRanges = 10;
pdx.lpPageRanges = pPageRanges;
pdx.nMinPage = 1;
pdx.nMaxPage = 1000;
pdx.nCopies = 1;
pdx.hInstance = 0;
pdx.lpPrintTemplateName = NULL;
pdx.lpCallback = NULL;
pdx.nPropertyPages = 0;
pdx.lphPropertyPages = NULL;
pdx.nStartPage = START_PAGE_GENERAL;
pdx.dwResultAction = 0;
// Invoke the Print property sheet.
hResult = PrintDlgEx(&pdx);
if ( ( hResult == S_OK )
&& ( pdx.dwResultAction == PD_RESULT_PRINT ) )
{
// User clicked the Print button,
// so use the DC and other information returned in the
// PRINTDLGEX structure to print the document.
/***************** IMPORTANT INFO : ********************/
/****** I have added additional test code here *********/
/**** please refer to the edited part of this post *****/
/***************** at the very bottom !! ***************/
DOCINFO diDocInfo = {0};
diDocInfo.cbSize = sizeof( DOCINFO );
diDocInfo.lpszDocName = L"Testing printing...";
//******************** initialize testing font *****************//
HFONT font, oldFont;
long lfHeight = -MulDiv( 14, GetDeviceCaps( pdx.hDC, LOGPIXELSY), 72 );
font = CreateFont( lfHeight,
0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, 0, 0, 0,
0, 0, L"Microsoft Sans Serif" );
oldFont = SelectFont( pdx.hDC, font );
SetBkMode( pdx.hDC, TRANSPARENT );
SetTextColor( pdx.hDC, RGB( 255, 0, 0 ) );
//******************** end of initialization ******************//
if( StartDoc( pdx.hDC, &diDocInfo ) > 0 )
{
if( StartPage( pdx.hDC ) > 0 )
{
// get paper dimensions
int pageWidth, pageHeight;
pageWidth = GetDeviceCaps( pdx.hDC, HORZRES );
pageHeight = GetDeviceCaps( pdx.hDC, VERTRES );
/************ draw a testing grid ***************/
// draw vertical lines of the grid
for( int i = 0; i < pageWidth; i += pageWidth / 4 )
{
MoveToEx( pdx.hDC, i, 0, NULL );
LineTo( pdx.hDC, i, pageHeight );
}
// draw horizontal lines of the grid
for( int j = 0; j < pageHeight; j += pageWidth / 10 )
{
MoveToEx( pdx.hDC, 0, j, NULL );
LineTo( pdx.hDC, pageWidth, j );
}
/************************************************/
// test rectangle for drawing the text
RECT r;
r.left = 0;
r.top = 0;
r.right = 550;
r.bottom = 100;
// fill rectangle with light gray brush
// so we can see if text is properly drawn
FillRect( pdx.hDC, &r,
(HBRUSH)GetStockObject(LTGRAY_BRUSH) );
// draw text in test rectangle
if( 0 == DrawTextEx( pdx.hDC,
L"This is test string!",
wcslen( L"This is test string!" ),
&r,
DT_CENTER | DT_WORDBREAK | DT_NOCLIP, NULL ) )
// for now pop a message box saying something went wrong
MessageBox( hWnd, L"DrawText failed!", L"Error", MB_OK );
if( EndPage( pdx.hDC ) < 0 )
// for now pop a message box saying something went wrong
MessageBox( hWnd, L"EndDoc failed!", L"Error", MB_OK );
}
EndDoc( pdx.hDC );
SelectFont( pdx.hDC, oldFont );
DeleteFont( font );
}
}
if (pdx.hDevMode != NULL)
GlobalFree(pdx.hDevMode);
if (pdx.hDevNames != NULL)
GlobalFree(pdx.hDevNames);
if (pdx.lpPageRanges != NULL)
GlobalFree(pPageRanges);
if (pdx.hDC != NULL)
DeleteDC(pdx.hDC);
return hResult;
}
5) In WM_COMMAND handler, modify case IDM_ABOUT like this :
case IDM_ABOUT: // test our printing here
{
if( FAILED( DisplayPrintPropertySheet( hWnd ) ) )
MessageBox( hWnd,
L"Can't display print property sheet!",
L"Error", MB_OK );
}
//DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
EDITED on June, 8th 2014 :
After the block if ( ( hResult == S_OK ) && ( pdx.dwResultAction == PD_RESULT_PRINT ) ) in the submitted SSCCE I have added the following for testing purposes :
int xDpi = GetDeviceCaps( pdx.hDC, LOGPIXELSX ),
yDpi = GetDeviceCaps( pdx.hDC, LOGPIXELSY );
int mapMode = GetMapMode( pdx.hDC );
wchar_t displayDPI[50];
swprintf_s( displayDPI, 50, L" xDPI = %s , yDPI = %s", xDpi, yDpi );
MessageBox( hWnd, displayDPI, L"", MB_OK );
switch( mapMode )
{
case MM_ANISOTROPIC:
MessageBox( hWnd, L"MM_ANISOTROPIC", L"", MB_OK );
break;
case MM_HIENGLISH:
MessageBox( hWnd, L"MM_HIENGLISH", L"", MB_OK );
break;
case MM_HIMETRIC:
MessageBox( hWnd, L"MM_HIMETRIC", L"", MB_OK );
break;
case MM_ISOTROPIC:
MessageBox( hWnd, L"MM_ISOTROPIC", L"", MB_OK );
break;
case MM_LOENGLISH:
MessageBox( hWnd, L"MM_LOENGLISH", L"", MB_OK );
break;
case MM_LOMETRIC:
MessageBox( hWnd, L"MM_LOMETRIC", L"", MB_OK );
break;
case MM_TEXT:
MessageBox( hWnd, L"MM_TEXT", L"", MB_OK );
break;
case MM_TWIPS:
MessageBox( hWnd, L"MM_TWIPS", L"", MB_OK );
break;
default:
MessageBeep(0);
break;
}
In both cases mapping mode was the same ( MM_TEXT ) but for XPS I got xDPI = 600 , yDPI = 600 in the MessageBox while OneNote had xDPI = 300 , yDPI = 300.
This leads to the conclusion that comments made by member * Carey Gregory* were correct -> with the same characteristics virtual printers will reproduce the same result. This also explains why OneNote printed properly into XPS when I tested it, and why my application failed. To solve this problem I need to find DPI aware solution...
EDITED on June, 9th 2014 :
Using GDI+ to create font and draw text I was able to get consistent results ( DPI is no longer a problem ). Still, if anyone knows how to achieve the same result using only GDI I would be still interested.
The only thing left for me is to draw a proper grid so the text can fit into cells properly.
EDITED on June, 10th 2014 :
After carefully reading through this MSDN link I was able to alter the font creating code to achieve ( in my opinion ) stable results ( the actual height of the font ends up way smaller, but I could use bigger number I guess ) :
font = CreateFont(
// DPI aware, thanks to the below equation ( or so it seems... )
lfHeight / ( GetDeviceCaps( pdx.hDC, LOGPIXELSY ) / 96 ),
0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, 0, 0, 0, // remained the same
0, 0, L"Microsoft Sans Serif" ); // remained the same
Just to be safe, I will try to stick with GDI+ but will update this post with the testing results when GDI and the mentioned equation is used in case someone else stumbles upon the same problem. I just hope it will save that persons time...
The problem is simple. You are adjusting the font size (in pixels) to match the DPI of the device you're drawing to, but you're not adjusting the size of the rectangle. Since your mapping mode is MM_TEXT both are measured in pixels, and your bounding box is effectively half the size on the device with twice the resolution.
The solution is to scale the rectangle similarly to the way you scale the font size. In this case since you've determined that these coordinates are correct at 300 DPI, that's the constant we'll scale relative to.
RECT r;
r.left = 0 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;
r.top = 0 * GetDeviceCaps(pdx.hDC, LOGPIXELSY) / 300;
r.right = 550 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;
r.bottom = 100 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;
Regarding your edit of June 10, it only works because you've made the font much smaller so that it fits in both the full-size bounding box and the half-size one. I'd recommend going back to the original definition you had for font size, which is consistent with most other Windows applications.

SetLayeredWindowAttributes to make a window transparent is only working part of the time

I'm trying to make a window transparent so that only part of its contents are visible, I've tried using SetLayeredWindowAttributes to make this happen, this made the window transparent as I wanted, however it only works that way when part of the windows picture is outside of the visible area of my desktop. For some reason whenever the window is fully on screen it re-draws its black background (the color I use for transparency that's meant not to be seen.) Here is a video example of the problem. I'm not sure what exactly is causing this to just to be safe I'm posting the full code.
#define _WIN32_WINNT 0x501
#include "C:\Program Files\Microsoft DirectX SDK (August 2008)\Include\D3dx9core.h"
#include "C:\Documents and Settings\Death\My Documents\Downloads\DXSprite\DXSprite\resource.h"
#include <windows.h>
#include <string>
#include <stdio.h>
//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
HWND g_hWnd = NULL;
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pD3DDevice = NULL;
ID3DXSprite * g_pD3DXSprite = NULL;
LPDIRECT3DTEXTURE9 g_pTexture = NULL;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT InitializeD3D ( );
void RenderFrame ( );
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
WNDCLASSEX winClass;
MSG uMsg;
HRESULT hr;
memset(&uMsg,0,sizeof(uMsg));
winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WindowProc;
winClass.hInstance = hInstance;
winClass.hIcon = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hIconSm = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
if( !RegisterClassEx(&winClass) )
return E_FAIL;
g_hWnd = CreateWindowEx( WS_EX_LAYERED, "MY_WINDOWS_CLASS",
"Direct3D 9 - ID3DXSprite Example",
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
if( g_hWnd == NULL )
return E_FAIL;
SetLayeredWindowAttributes(g_hWnd, RGB(0x00,0x00,0x00), 0, LWA_COLORKEY});
ShowWindow( g_hWnd, nCmdShow );
//----------------------------------------------------------------
// Create the DirectX device
//----------------------------------------------------------------
if (FAILED( InitializeD3D()))
return 0;
//----------------------------------------------------------------
// CREATE THE ID3DXSprite
//----------------------------------------------------------------
// Create the ID3DXSprite interface object
hr = D3DXCreateSprite(g_pD3DDevice, &g_pD3DXSprite );
if( FAILED(hr) )
return hr;
//----------------------------------------------------------------
// LOAD THE TEXTURE FOR THE SPRITE
//----------------------------------------------------------------
// --------------------------------------------------------
// Load the texture. I decided to use the extended
// version of the texture loading function just to show what
// it would look like.
// The texture was created with Photoshop with a transparent
// background to start with. Then line cross hairs were added.
//
// Note - If you don't use a texture image that has a power of
// 2 size for the width or height then the image may not load
// properly. This image is 256x256.
//
D3DXCreateTextureFromFileEx(
g_pD3DDevice,
"C:\\Documents and Settings\\Death\\My Documents\\45handold2.tga", // Our texture image!
D3DX_DEFAULT, // width
D3DX_DEFAULT, // height
D3DX_DEFAULT, // MIP levels
0, // usage
D3DFMT_DXT1, // texture format
D3DPOOL_MANAGED, // mem pool
D3DX_DEFAULT, // filter
D3DX_DEFAULT, // MIP filter
0, // transparent color key
NULL, // image info struct
NULL, // palette
&g_pTexture); // the returned texture, if success
if ( FAILED(hr) )
return hr;
// ---------
// Main Loop
// ---------
while( uMsg.message != WM_QUIT )
{
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
}
// -------------------------
// Release directx resources
// -------------------------
if (g_pD3DXSprite)
{
g_pD3DXSprite->Release();
g_pD3DXSprite = NULL;
}
if (g_pTexture)
{
g_pTexture->Release();
g_pTexture = NULL;
}
if (g_pD3DDevice)
{
g_pD3DDevice->Release();
g_pD3DDevice = NULL;
}
UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );
return (int)uMsg.wParam;
}
//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam )
{
switch( msg )
{
case WM_KEYDOWN:
{
switch( wParam )
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
}
break;
case WM_CLOSE:
{
PostQuitMessage(0);
}
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
default:
{
RenderFrame();
return DefWindowProc( hWnd, msg, wParam, lParam );
}
break;
}
return 0;
}
//-----------------------------------------------------------------------------
// Name: InitializeD3D()
// Desc: Create DirectX interface objects
// Initialize the view matrix.
// Setup render states that will not need changing throughout
// the life of the application.
//-----------------------------------------------------------------------------
HRESULT InitializeD3D( )
{
HRESULT hr;
// Create a direct 3D interface object
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
if( g_pD3D == NULL )
{
// TO DO: Respond to failure of Direct3DCreate9
return E_FAIL;
}
D3DDISPLAYMODE d3ddm;
if( FAILED( hr = g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
{
// TO DO: Respond to failure of GetAdapterDisplayMode
return hr;
}
//
if( FAILED( hr = g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
{
if( hr == D3DERR_NOTAVAILABLE )
// POTENTIAL PROBLEM: We need at least a 16-bit z-buffer!
return hr;
}
//
// Do we support hardware vertex processing? If so, use it.
// If not, downgrade to software.
//
D3DCAPS9 d3dCaps;
if( FAILED( hr = g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, &d3dCaps ) ) )
{
// TO DO: Respond to failure of GetDeviceCaps
return hr;
}
DWORD dwBehaviorFlags = 0;
if( d3dCaps.VertexProcessingCaps != 0 )
dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//
// Everything checks out - create a simple, windowed device.
//
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// Attempt to create a HAL device, end app on failure just to keep things
// simple. In other words we are not trying to create a REF device if the
// HAL fails.
if( FAILED( hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
dwBehaviorFlags, &d3dpp, &g_pD3DDevice ) ) )
{
// char blah[100];
// snprintf (blah, 1000, "%d", hr);
//MessageBox (NULL,blah,NULL,NULL);
}
// If we get here everything worked!
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: RenderFrame()
// Desc: Draw the image to the framebuffer.
//-----------------------------------------------------------------------------
void RenderFrame( )
{
if (!g_pD3DDevice && !g_pD3DXSprite && !g_pTexture)
return;
// Clear the frame & depth buffer ready for drawing (Black color)
g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
g_pD3DDevice->BeginScene();
{
//-------------------------
// Render the sprite
//
D3DXVECTOR3 vecPos = D3DXVECTOR3(0,0,0);
if (g_pD3DXSprite && g_pTexture)
{
g_pD3DXSprite->Begin( D3DXSPRITE_ALPHABLEND );
g_pD3DXSprite->Draw(g_pTexture, NULL, NULL, &vecPos, 0xffffffff);
g_pD3DXSprite->End();
}
}
g_pD3DDevice->EndScene();
// Frame buffer to Front buffer
g_pD3DDevice->Present( NULL, NULL, NULL, NULL );
}
The problem you're running into is basically that there used to be two entirely separate rendering stacks in Windows: GDI and Direct3D. They didn't really talk to one another at all, so standard windowing and GDI APIs don't really know anything about Direct3D. When Vista came along they unified the two driver stacks, but the GDI code (generally speaking) still knows nothing about Direct3D, even if it internally uses some Direct3D behind the scenes (for desktop composition).
In short, SetLayeredWindowAttributes as well as UpdateLayeredWindow cannot and do not know about your Direct3D swapchain. If you had tried this back on Windows XP or 2000 I expect you would have gotten some really funky visual results. There are some really good reasons for this, I should add. For example, in the GDI world, using UpdateLayeredWindow to set a bitmap with per-pixel alpha actually results in places with an alpha value of zero being treated as not part of the window. In other words, clicks pass through to the window underneath. In order to implement this with Direct3D, the system would have to read back the Direct3D texture from GPU to CPU memory, which is quite expensive, and then perform the hit test.
One solution, of course, is to use GDI to render the window, including the color key. I'm assuming you ruled that out for performance reasons.
I'm not entirely sure of the visual results you are expecting, but if you want to render arbitrary alpha-blended content in a window with full hardware acceleration and no window border, you can create a borderless window (e.g. just WS_POPUP for its window style) and call DwmExtendFrameIntoClientArea passing -1 for all the margins. Then, create your swap-chain with the D3DFMT_A8R8G8B8 pixel format or the DXGI equivalent for Direct3D 10/11 (which is the native format DWM uses to render windows) and render into it. You now have a window that contains only your alpha-blended content superimposed on the desktop. There is an older article on CodeProject about this very topic.

Getting HBITMAP from layered window - incorrect data

I created a layered window (with WS_EX_LAYERED), size of about 400X300 px.
When drawing the window (using UpdateLayeredWindow) everything works great.
The problem is that I'm unable to get the HBITMAP of the window after drawing it.
When trying to get the HBITMAP through the window's HDC, I get an empty (black) bitmap, the size of my entire desktop (1920X1080 px insted of 400X300 px).
Does anybody know if it is even possible to get the HDC\HBITMAP of a layered window?
Code samples
Here's the code of how I draw the layered window (again, works great):
void MyLayeredWindow::DrawLayered() const
{
RECT rcWindow;
GetWindowRect(rcWindow);
int nWidth = abs(rcWindow.right - rcWindow.left);
int nHeight = abs(rcWindow.bottom - rcWindow.top);
// Create 32Bit bitmap to apply transparency
// (have to set negative height because if not the (0,0) point would be the bottom left instead of top left)
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = nWidth;
BitmapInfo.bmiHeader.biHeight = -nHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
// Copy view buffer to a temp DC and bitmap
HDC hDcTemp = ::CreateCompatibleDC(NULL);
assert(hDcTemp);
HBITMAP hBitmapTemp = ::CreateDIBSection(hDcTemp, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
assert(hBitmapTemp && hBitmapTemp!=(HBITMAP)ERROR_INVALID_PARAMETER)
::SelectObject(hDcTemp, hBitmapTemp);
// Darwing the window's conent here
// ....
// ....
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 190;
blend.AlphaFormat = AC_SRC_ALPHA;
SIZE sizeWnd = {0};
sizeWnd.cx = nWidth;
sizeWnd.cy = nHeight;
POINT ptPos = {0};
ptPos.x = rcWindow.left;
ptPos.y = rcWindow.top;
POINT ptSrc = {0,0};
::UpdateLayeredWindow(m_hWnd, NULL, &ptPos, &sizeWnd, hDcTemp, &ptSrc, 0, &blend, ULW_ALPHA);
if(hDcTemp)
::DeleteDC(hDcTemp);
if(hBitmapTemp)
::DeleteObject(hBitmapTemp);
}
Here's the code of how I capture the window's bitmap and save it to a file:
(NOTICE: It works of "normal" windows, such as the Calculator)
bool MyLayeredWindow::SaveBitmapFile(__in const HWND& hWnd, __in const wstring& sFileName)
{
HDC hDC = ::GetDC(hWnd);
// get bitmap of DC
HBITMAP hBmp = (HBITMAP)::GetCurrentObject( hDC, OBJ_BITMAP );
// get info of bitmap
BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );
// init info size
ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}
// copy header
PBITMAPINFO pstBmpInfo = NULL;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest = (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc = (PBYTE)&stBmpInfo;
ULONG iSizeTmp = sizeof( BITMAPINFOHEADER );
while( iSizeTmp-- )
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
// create file
HANDLE hFile = CreateFile( sFileName.c_str(), GENERIC_WRITE, 0 , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL );
// init bmp file header
BITMAPFILEHEADER stBmpFileHder = {0};
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + iBmpInfoSize + pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;
// write header to file
DWORD dRet;
WriteFile( hFile, (LPCVOID)&stBmpFileHder, sizeof(BITMAPFILEHEADER), &dRet, NULL );
// allocate size for rest of bmp (body)
PBYTE pBits = (PBYTE)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, stBmpInfo.bmiHeader.biSizeImage );
// get bmp bits
HBITMAP hBmpOld;
HBITMAP hTmpBmp = CreateCompatibleBitmap( hDC, pstBmpInfo->bmiHeader.biWidth, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( hDC, hTmpBmp );
GetDIBits( hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );
// write bmp info
WriteFile( hFile, (LPCVOID)pstBmpInfo, iBmpInfoSize, &dRet, NULL );
// write bmp bits
WriteFile( hFile, (LPCVOID)pBits, pstBmpInfo->bmiHeader.biSizeImage, &dRet, NULL );
// release handles and free memory
SelectObject( hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
ReleaseDC( hWnd, hDC );
return true;
}
Thanks!
Since I didn't get any better answer, I simply called a "Draw" function that paints my layered window, onto a temporary HDC.
Meaning I don't copy existing bitmap, but create an identical one, using the same drawing function.
I'd still love to get a better answer for this question.

array[byte] to HBITMAP or CBitmap

I have an array of bytes (which I read through a stream directly from a .bmp and then store as a BLOB in a database) which I want to display as icons in a CImageList. Therefore I want to somehow load my data into an HBITMAP or CBitmap. I have done it like this up to now, reading from a file:
hPic = (HBITMAP)LoadImage(NULL, strPath, IMAGE_BITMAP, dwWidth, dwHeight, LR_LOADFROMFILE | LR_VGACOLOR);
...
CBitmap bitmap;
bitmap.Attach(hPicRet);
But obviously, that only works for files, but not for byte-arrays. How can I get the same result, but reading from an array of byte?
Edit:
Note that my array does not contain just the colour information, but rather the complete file as it is written on disk, including all headers and meta-data. It seems to me that discarding all that information is a bad idea.
Assuming you have the information loaded into a BYTE array named bytes....
BITMAPFILEHEADER* bmfh;
bmfh = (BITMAPFILEHEADER*)bytes;
BITMAPINFOHEADER* bmih;
bmih = (BITMAPINFOHEADER*)(bytes + sizeof(BITMAPFILEHEADER));
BITMAPINFO* bmi;
bmi = (BITMAPINFO*)bmih;
void* bits;
bits = (void*)(bytes + bmfh->bfOffBits);
HDC hdc = ::GetDC(NULL);
HBITMAP hbmp = CreateDIBitmap(hdc, bmih, CBM_INIT, bits, bmi, DIB_RGB_COLORS) ;
::ReleaseDC(NULL, hdc);
It's a little messy and could use a hefty dose of error checking, but the basic idea is sound.
Following sample could help you.
BITMAPINFO bmInfo;
BITMAPINFOHEADER &bmInfohdr = (BITMAPINFOHEADER)bmInfo.bmiHeader;
bmInfohdr.biSize = 40 + 255; //I think it's not of use
bmInfohdr.biWidth = x;
bmInfohdr.biHeight = y;
bmInfohdr.biPlanes=1;
bmInfohdr.biBitCount=8;
bmInfohdr.biCompression=0;
bmInfohdr.biSizeImage=0;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;
// should I allocate memory further than the
// bmColors[1]?? anyway the compiler gives an
// error for type mismatch!
//bmInfo.bmiColors = (RGBQUAD *)
malloc(sizeof(RGBQUAD) * 256);
// here I define the 256 graylevel palette
for (int i=0; i<256; i++)
{
bmInfo.bmiColors[i].rgbRed = i;
bmInfo.bmiColors[i].rgbGreen = i;
bmInfo.bmiColors[i].rgbBlue = i;
}
BYTE *matrix;
matrix = (BYTE*)malloc(size*sizeof(BYTE));
// here I put the BYTE values of the pixels
CDC *pdcDest = this->GetDC();
HBITMAP hBmp = CreateDIBitmap( pdcDest->m_hDC,
&bmInfohdr,
CBM_INIT,
matrix,
&bmInfo,
DIB_RGB_COLORS);
m_bmpBitmap.Attach( hBmp );
Something like this worked for me:
int bitmap[WX*WY]; // truecolor bitmap data
BITMAPINFO bm = { sizeof(BITMAPINFOHEADER), WX, WY, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
HBITMAP bmp = CreateDIBSection( GetDC(win), &bm, DIB_RGB_COLORS, (void**)&bitmap, 0,0 );
(This is specifically configured for 32-bit colors, but you can specify any kind).
Ok, here's a complete example: http://nishi.dreamhosters.com/u/so_bmp_v0.zip
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"user32.lib")
char buf[1<<22];
int main( int argc, char **argv ) {
FILE* f = fopen( "winnt.bmp", "rb" ); if( f==0 ) return 1;
fread( buf, 1,sizeof(buf), f );
fclose(f);
BITMAPFILEHEADER& bfh = (BITMAPFILEHEADER&)buf[0];
BITMAPINFO& bi = (BITMAPINFO&)buf[sizeof(BITMAPFILEHEADER)];
BITMAPINFOHEADER& bih = bi.bmiHeader;
char* bitmap = &buf[bfh.bfOffBits];
int WX=1024, WY=512; // window's width/height
int SX=bih.biWidth, SY=bih.biHeight;
HWND win = CreateWindow( "STATIC", "Bitmap test", 0x90C0, 0,0, WX,WY, 0,0, GetModuleHandle(0), 0 );
MSG msg;
PAINTSTRUCT ps;
HDC DC = GetDC(win); // window's DC
HBITMAP dib = CreateDIBitmap( DC, &bih, CBM_INIT, bitmap, &bi, DIB_RGB_COLORS );
HDC dibDC = CreateCompatibleDC( DC ); SelectObject( dibDC, dib );
ShowWindow( win, SW_SHOWNOACTIVATE );
SetFocus( win );
while( GetMessage(&msg,win,0,0) ) {
int m = msg.message;
if( m==WM_PAINT ) {
DC = BeginPaint( win, &ps );
StretchBlt( DC, 0,0,WX,WY, dibDC,0,0,SX,SY, SRCCOPY );
EndPaint( win, &ps );
} else if( (m==WM_KEYDOWN) || (m==WM_SYSKEYDOWN) ) {
break;
} else {
DispatchMessage(&msg);
}
}
return 0;
}