CWindow release candidate 3.
Added support for subclassing using SetWindowSubclass.
For the uIdSubclass parameter you can use any positive value except &hFFFFFFFF. CWindow uses the reserved default value of &hFFFFFFF to know if it has to use the old way of subclassing or the new way with SetWindowSubclass.
For more information about subclassing controls see:
https://msdn.microsoft.com/en-us/library/bb773183%28VS.85%29.aspx
The following example demonstrates how to subclass a button using the new support for SetWindowSubclass.
' ########################################################################################
' Microsoft Windows
' File: CW_ButtonSubclass2.fbtpl
' Contents: CWindow with a subclassed button
' Subclasses the control using SetWindowSubclass.
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#define unicode
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
USING Afx.CWindowClass
CONST IDC_BUTTON = 1001
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(""), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DIM pWindow AS CWindow PTR
SELECT CASE uMsg
CASE WM_CREATE
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE WM_SIZE
IF wParam <> SIZE_MINIMIZED THEN
' // Resize the buttons
pWindow = CAST(CWindow PTR, GetPropW(hwnd, "CWINDOWPTR"))
pWindow->MoveWindow GetDlgItem(hwnd, IDC_BUTTON), pWindow->ClientWidth - 120, pWindow->ClientHeight - 50, 75, 23, CTRUE
END IF
CASE WM_DESTROY
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Processes messages for the subclassed Button window.
' ========================================================================================
FUNCTION Button_SubclassProc ( _
BYVAL hwnd AS HWND, _ ' // Control window handle
BYVAL uMsg AS UINT, _ ' // Type of message
BYVAL wParam AS WPARAM, _ ' // First message parameter
BYVAL lParam AS LPARAM, _ ' // Second message parameter
BYVAL uIdSubclass AS UINT_PTR, _ ' // The subclass ID
BYVAL dwRefData AS DWORD_PTR _ ' // Pointer to reference data
) AS LRESULT
SELECT CASE uMsg
CASE WM_GETDLGCODE
' // All keyboard input
FUNCTION = DLGC_WANTALLKEYS
EXIT FUNCTION
CASE WM_LBUTTONDOWN
MessageBoxW(GetParent(hwnd), "Click", "FreeBasic", MB_OK)
EXIT FUNCTION
CASE WM_KEYDOWN
SELECT CASE LOWORD(wParam)
CASE VK_ESCAPE
SendMessageW(GetParent(hwnd), WM_CLOSE, 0, 0)
EXIT FUNCTION
END SELECT
CASE WM_DESTROY
' // REQUIRED: Remove control subclassing
RemoveWindowSubclass hwnd, @Button_SubclassProc, uIdSubclass
END SELECT
FUNCTION = DefSubclassProc(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a subclassed button", @WndProc)
pWindow.SetClientSize(500, 320)
pWindow.Center
' // Add a subclassed button without position or size (it will be resized in the WM_SIZE message).
pWindow.AddControl("Button", pWindow.hWindow, IDC_BUTTON, "Click me", , , , , , , , _
CAST(WNDPROC, @Button_SubclassProc), IDC_BUTTON, CAST(DWORD_PTR, @pWindow))
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
Two of the added benefits are that you can use the same subclass procedure for several controls and identify them thanks to the uIdSubclass (for example passing the ID of the control), and that you can pass a pointer to reference data.
SetWindowSubclass, GetWindowSubclass, RemoveWindowSubclass and DefSubclassProc were made available for the first time in ComCtl32.dll version 6; therefore, can only be used with Windows XP or superior.
ComCtl32.dll version 6 is Unicode only. The common controls supported by ComCtl32.dll version 6 should not be subclassed (or superclassed) with ANSI window procedures.
Next version will include a little class to create tooltips.
' ########################################################################################
' CToolTip class
' Creates a tooltip control
' ########################################################################################
TYPE CTooltip
Private:
m_hTooltip AS HWND ' // Tooltip handle
m_hwndCtrl AS HWND ' // Handle of the control
m_cbSize AS LONG ' // Size of the TOOLINFO structure
m_bBalloon AS LONG ' // Balloon
m_bCentered AS LONG ' // Centered
Public:
DECLARE CONSTRUCTOR (BYVAL hwndCtrl AS HWND, BYREF wszText AS CONST WSTRING = "", BYVAL bBalloon AS LONG = 0, BYVAL bCentered AS LONG = 0)
DECLARE DESTRUCTOR
DECLARE PROPERTY hTooltip () AS HWND
DECLARE PROPERTY TipText (BYREF wszText AS CONST WSTRING)
DECLARE SUB SetTitle (BYVAL hIcon AS HANDLE, BYREF wszTitle AS CONST WSTRING)
END TYPE
' ========================================================================================
' ========================================================================================
' CTooltip constructor
' Usage example: DIM pTooltip AS CTooltip = CTooltip(hButton, "This is a tooltip", CTRUE, CTRUE)
' ========================================================================================
CONSTRUCTOR CTooltip (BYVAL hwndCtrl AS HWND, BYREF wszText AS CONST WSTRING = "", BYVAL bBalloon AS LONG = 0, BYVAL bCentered AS LONG = 0)
' // Creates the tooltip control
DIM dwStyle AS DWORD = WS_POPUP OR TTS_ALWAYSTIP
IF bBalloon THEN dwStyle = dwStyle OR TTS_BALLOON
IF .IsWindow(hwndCtrl) THEN
m_hTooltip = .CreateWindowExW(0, "tooltips_class32", "", dwStyle, 0, 0, 0, 0, NULL, NULL, NULL, NULL)
IF m_hTooltip THEN
END IF
END IF
' // Registers the window with the tooltip control
' // 32-bit: The size of the TOOLINFO structure is of 48 bytes in
' version 6 of comctl32.dll, and of 44 bytes in lower versions.
' // 64-bit: The size of the TOOLINFO structure is of 72 bytes in
' version 6 of comctl32.dll, and of 64 bytes in lower versions.
DIM tti AS TOOLINFOW
#ifdef __FB_64BIT__
IF AfxComCtlVersion < 600 THEN m_cbSize = 64 ELSE m_cbSize = 72
#else
IF AfxComCtlVersion < 600 THEN m_cbSize = 44 ELSE m_cbSize = 48
#endif
tti.cbSize = m_cbSize
tti.uFlags = TTF_IDISHWND OR TTF_SUBCLASS
IF bCentered THEN tti.uFlags = tti.uFlags OR TTF_CENTERTIP
m_hwndCtrl = hwndCtrl
tti.hwnd = .GetParent(m_hwndCtrl)
tti.hinst = .GetModuleHandleW(NULL)
' // The length of the string must not exceed of 80 characters, including the terminating null
DIM wszTooltipText AS WSTRING * 80
wszTooltipText = wszText
tti.lpszText = @wszTooltipText
tti.uId = CAST(UINT_PTR, m_hwndCtrl)
.SendMessageW m_hTooltip, TTM_ADDTOOLW, 0, CAST(LPARAM, @tti)
m_bBalloon = bBalloon
m_bCentered = bCentered
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
' CTooltip destructor
' ========================================================================================
DESTRUCTOR CTooltip
' // Removes the tool from the tooltip control.
DIM tti AS TOOLINFOW
tti.cbSize = m_cbSize
tti.hwnd = .GetParent(m_hwndCtrl)
tti.uId = CAST(UINT_PTR, m_hwndCtrl)
.SendMessageW(m_hTooltip, TTM_DELTOOLW, 0, CAST(LPARAM, @tti))
END DESTRUCTOR
' ========================================================================================
' ========================================================================================
' Returns the handle of the tooltip control
' ========================================================================================
PROPERTY CTooltip.hTooltip () AS HWND
PROPERTY = m_hTooltip
END PROPERTY
' ========================================================================================
' ========================================================================================
' Sets/replaces the text of a tooltip control
' ========================================================================================
PROPERTY CTooltip.TipText (BYREF wszText AS CONST WSTRING)
' // The length of the text must not exceed of 80 characters, including the terminating null.
DIM wszTooltipText AS WSTRING * 80
wszTooltipText = wszText
DIM tti AS TOOLINFOW
tti.cbSize = m_cbSize
tti.hwnd = .GetParent(m_hwndCtrl)
tti.uId = CAST(UINT_PTR, m_hwndCtrl)
' // Retrieve the tooltip information
.SendMessageW(m_hTooltip, TTM_GETTOOLINFOW, 0, CAST(LPARAM, @tti))
IF .SendMessageW(m_hTooltip, TTM_GETTOOLINFOW, 0, CAST(LPARAM, @tti)) THEN
tti.lpszText = @wszTooltipText
.SendMessageW(m_hTooltip, TTM_SETTOOLINFOW, 0, CAST(LPARAM, @tti))
END IF
END PROPERTY
' ========================================================================================
' ========================================================================================
' Set icon and/or title
' Usage example: pTooltip.SetTitle CAST(HANDLE, TTI_INFO), "Tooltip title"
' ========================================================================================
SUB CTooltip.SetTitle (BYVAL hIcon AS HANDLE, BYREF wszTitle AS CONST WSTRING)
.SendMessageW m_hTooltip, TTM_SETTITLEW, CAST(WPARAM, hIcon), CAST(LPARAM, @wszTitle)
END SUB
' ========================================================================================
This part is very important:
#ifdef __FB_64BIT__
IF AfxComCtlVersion < 600 THEN m_cbSize = 64 ELSE m_cbSize = 72
#else
IF AfxComCtlVersion < 600 THEN m_cbSize = 44 ELSE m_cbSize = 48
#endif
because the size of the TOOLINFO structure should be different depending on the version of the CommCtrl library being used (5.xx without a manifest, 6.xx with a manifest).
Usage is very simple:
' // Add a button
DIM hButton AS HWND = pWindow.AddControl("Button", pWindow.hWindow, IDCANCEL, "&Close", 350, 250, 75, 23)
' // Add a tooltip to the button
DIM pTooltip AS CTooltip = CTooltip(hButton, "This is a button with a tooltip", TRUE, TRUE)
' // Optional: Set the icon and title
pTooltip.SetTitle CAST(HANDLE, TTI_INFO), "Tooltip title"
' // To change the text, use:
' pTooltip.TipText = "This is a modified text"
Will add a few methods more, like one to set the max width.
Other options less often used can be set with SendMessageW <pToolTip>.hTooltip, <message>, <wparam>, <lparam>.
I have written a function to allow to use .png icons in toolbars, etc. Have had some problems until I realized that BYTE is signed in FreeBasic and had to use UBYTE.
Because for some reason GDI+ makes gray icons darker that it should, I have added the option to specify a dimming percentage and gray conversion.
A manifest must be used to preserve the alpha channel.
Will write another one to load it from resources.
' ========================================================================================
' Loads an image from a file using GDI+, converts it to an icon and returns the icon handle.
' Parameter:
' - wszFileName = [in] Path of the image to load and convert.
' - dimPercent = Percent of dimming (1-99)
' - bGrayScale = TRUE or FALSE. Convert to gray scale.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdiplusCreateHICONFromFile (BYREF wszFileName AS WSTRING, _
BYVAL dimPercent AS LONG = 0, BYVAL bGrayScale AS LONG = FALSE) AS HICON
DIM hStatus AS GpStatus, token AS ULONG_PTR
DIM pImage AS GpImage PTR, hIcon AS HICON
DIM ImageWidth AS LONG, ImageHeight AS LONG, x AS LONG, y AS LONG
DIM pixColor AS GDIP_BGRA, iColor AS LONG, rFactor AS SINGLE
' // Initialize Gdiplus
token = AfxGdiplusInit
' // Load the image from file
GdipLoadImageFromFile(wszFileName, @pImage)
IF pImage = NULL THEN EXIT FUNCTION
' // Get the image width and height
GdipGetImageWidth(pImage, @ImageWidth)
GdipGetImageHeight(pImage, @ImageHeight)
' // Dim or/and gray the image
IF dimPercent > 0 AND dimPercent < 100 THEN rFactor = dimPercent / 100
IF rFactor <> 0 OR bGrayScale <> 0 THEN
FOR y = 0 TO ImageWidth - 1
FOR x = 0 TO ImageHeight - 1
' // Get the pixel color
GdipBitmapGetPixel(CAST(GpBitmap PTR, pImage), x, y, @pixColor.color)
IF dimPercent > 0 THEN
pixColor.red = (255 - pixColor.red) * rFactor + pixColor.red
pixColor.green = (255 - pixColor.green) * rFactor + pixColor.green
pixColor.blue = (255 - pixColor.blue) * rFactor + pixColor.blue
END IF
IF bGrayScale THEN
' Note: The sum of the percentages for the three colors should add tp up 1
iColor = 0.299 * pixColor.red + 0.587 * pixColor.green + 0.114 * pixColor.blue
pixColor.Color = GDIP_BGRA (iColor, iColor, iColor, pixColor.alpha)
ELSE
pixColor.color = GDIP_ARGB(pixColor.alpha, pixColor.red, pixColor.green, pixColor.Blue)
END IF
' // Set the modified pixel color
GdipBitmapSetPixel(CAST(GpBitmap PTR, pImage), x, y, pixColor.color)
NEXT
NEXT
END IF
' // Crete icon from image
hStatus = GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hIcon)
' // Free the image
IF pImage THEN GdipDisposeImage pImage
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hIcon
END FUNCTION
' ========================================================================================
What about 256-bit icons? We are ready for upcoming very high resolution monitors :)
A good thing is that we don't need to have icons of different sizes for different DPI settings. We can have only one and use something like...
DIM cx, cy AS LONG
cx = 16 : cy = 16
IF pWindow.IsProcessDPIAware THEN
IF pWindow.DPI => 120 AND pWindow.DPI < 144 THEN
cx = 20 : cy = 20
ELSEIF pWindow.DPI => 144 AND pWindow.DPI < 192 THEN
cx = 24 : cy = 24
ELSEIF pWindow.DPI => 192 THEN
cx = 32 : cy = 32
END IF
END IF
hImageList = ImageList_Create(cx, cx, ILC_COLOR32 OR ILC_MASK, 4, 0)
IF hImageList THEN
ImageList_ReplaceIcon(hImageList, -1, AfxGdiplusCreateHICONFromFile(ExePath & "\arrow_left_64.png"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdiplusCreateHICONFromFile(ExePath & "\arrow_right_64.png", 60, -1))
ImageList_ReplaceIcon(hImageList, -1, AfxGdiplusCreateHICONFromFile(ExePath & "\home_64.png"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdiplusCreateHICONFromFile(ExePath & "\save_64.png", 60, -1))
END IF
[code]
This new function can be used to create an icon from an image stored in a buffer. Can be used with gray images because it does not darken the image as GdipLoadImageFromFile does.
' ========================================================================================
' Converts an image stored in a buffer into an icon and returns the icon handle.
' Parameters:
' - pBuffer = [in] Pointer to the buffer
' - bufferSize = Size of the buffer
' Return Value:
' If the function succeeds, the return value is the handle of the created icon.
' If the function fails, the return value is NULL.
' Usage example:
' DIM wszFileName AS WSTRING * MAX_PATH
' wszFileName = ExePath & "\arrow_left_256.png"
' DIM bufferSize AS SIZE_T_
' DIM nFile AS LONG
' nFile = FREEFILE
' OPEN wszFileName FOR BINARY AS nFile
' IF ERR THEN EXIT FUNCTION
' bufferSize = LOF(nFile)
' DIM pBuffer AS UBYTE PTR
' pBuffer = CAllocate(1, bufferSize)
' GET #nFile, , *pBuffer, bufferSize
' CLOSE nFile
' IF pBuffer THEN
' ImageList_ReplaceIcon(hImageList, -1, AfxGdiplusCreateHICONFromRawData(pBuffer, ImageSize))
' DeAllocate(pBuffer)
' END IF
' ========================================================================================
FUNCTION AfxGdiplusCreateHICONFromBuffer (BYVAL pBuffer AS ANY PTR, BYVAL bufferSize AS SIZE_T_) AS HICON
DIM hStatus AS GpStatus, token AS ULONG_PTR
DIM pImage AS GpImage PTR, hIcon AS HICON
DIM ImageWidth AS LONG, ImageHeight AS LONG, x AS LONG, y AS LONG
DIM pixColor AS GDIP_BGRA, iColor AS LONG, rFactor AS SINGLE
DIM pImageStream AS IStream PTR, hGlobal AS HGLOBAL, pGlobalBuffer AS LPVOID
' // Initialize Gdiplus
token = AfxGdiplusInit
IF token = NULL THEN EXIT FUNCTION
' // Allocate memory to hold the image
hGlobal = GlobalAlloc(GMEM_MOVEABLE, bufferSize)
IF hGlobal THEN
' // Lock the memory
pGlobalBuffer = GlobalLock(hGlobal)
IF pGlobalBuffer THEN
' // Copy the image from the binary string file to global memory
CopyMemory(pGlobalBuffer, pBuffer, bufferSize)
' // Create an stream in global memory
IF CreateStreamOnHGlobal(hGlobal, FALSE, @pImageStream) = S_OK THEN
IF pImageStream THEN
' // Create a bitmap from the data contained in the stream
hStatus = GdipCreateBitmapFromStream(pImageStream, @pImage)
' // Crete icon from image
hStatus = GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hIcon)
' // Free the image
IF pImage THEN GdipDisposeImage pImage
pImageStream->lpVtbl->Release(pImageStream)
END IF
END IF
' // Unlock the memory
GlobalUnlock pGlobalBuffer
END IF
' // Free the memory
GlobalFree hGlobal
END IF
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hIcon
END FUNCTION
' ========================================================================================
As the above function has not problem with gray images, I will rename the first one as AfxGdiplusCreateHICONFromFileEx and implemented this new one.
' ========================================================================================
' Loads an image from a file, converts it to an icon and returns the icon handle.
' Parameters:
' - wszFileName = [in] Path of the image to load and convert.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdiplusCreateHICONFromFile (BYREF wszFileName AS WSTRING) AS HICON
DIM fd AS WIN32_FIND_DATAW
DIM hFind AS HANDLE
' // Check for the existence of the file
IF LEN(wszFileName) = 0 THEN EXIT FUNCTION
hFind = FindFirstFileW(@wszFileName, @fd)
IF hFind = INVALID_HANDLE_VALUE THEN EXIT FUNCTION
FindClose hFind
' // Make sure that is not a directory or a temporary file
IF (fd.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY OR _
(fd.dwFileAttributes AND FILE_ATTRIBUTE_TEMPORARY) = FILE_ATTRIBUTE_TEMPORARY THEN
EXIT FUNCTION
END IF
' // Open the file and store its contents into a buffer
DIM nFile AS LONG, bufferSize AS SIZE_T_
nFile = FREEFILE
OPEN wszFileName FOR BINARY AS nFile
IF ERR THEN EXIT FUNCTION
bufferSize = LOF(nFile)
DIM pBuffer AS UBYTE PTR
pBuffer = CAllocate(1, bufferSize)
GET #nFile, , *pBuffer, bufferSize
CLOSE nFile
IF pBuffer THEN
FUNCTION = AfxGdiplusCreateHICONFromBuffer(pBuffer, bufferSize)
DeAllocate(pBuffer)
END IF
END FUNCTION
' ========================================================================================
Jose,
I am doing some experimenting and noticed this:
Quote
' ########################################################################################
' File: CWindow.inc
' Version: 1.0
' Release candidate 2
' Contents: A wrapper class to create a SDK main window and add controls to it.
' Operating system: Microsoft Windows
' Compiler: FreeBasic 32/64-bit, Unicode.
' Freeware. Use at your own risk.
' Copyright (c) 2016 Jose Roca. All Rights Reserved.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' #######################################################################################
Did you inadvertently upload rc2 or not update the header info.
James
Forgot to update the number. Sorry.
BTW is "dimming" the appropriate word for what I'm doing, i.e. reducing the saturation of color?
I'm going to shorten a little the name of the functions, e.g. AfxGdipIconFromFile instead of AfxGdiplusCreateHICONFromFile.
Hi,
> BTW is "dimming" the appropirate word for what I'm doing, i.e. reducing the saturation of color?
So why not 'desaturate' then?
Cheers
Barry
Don't know. I have used dim because of this thread: How to Create Dim Background Images? ( http://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/54850-how-to-create-dim-background-images?t=53493 ), from which I have taken the idea. Just wanted to know if dimPercent = Percent of dimming (1-99) makes sense to English speaking people or not.
And this is the function to load .png icons from resources.
' ========================================================================================
' Loads an image from a resource, converts it to an icon using GDI+ and returns the icon handle.
' Parameter:
' - hInstance = [in] A handle to the module whose portable executable file or an accompanying
' MUI file contains the resource. If this parameter is NULL, the function searches
' the module used to create the current process.
' - wszImage = [in] Name of the image in the resource file (.RES). If the image resource uses
' an integral identifier, wszImage should begin with a number symbol (#)
' followed by the identifier in an ASCII format, e.g., "#998". Otherwise,
' use the text identifier name for the image. Only images embedded as raw data
' (type RCDATA) are valid. These must be icons in format .png, .jpg, .gif, .tiff.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdipIconFromRes (BYVAL hInstance AS HINSTANCE, BYREF wszImage AS WSTRING) AS HICON
DIM hStatus AS GpStatus, token AS ULONG_PTR
DIM hRes AS HRSRC, wID AS WORD, dwID AS DWORD, imageSize AS DWORD
DIM pImage AS GpImage PTR, hIcon AS HICON
DIM pResData AS HRSRC
DIM pImageStream AS IStream PTR, hGlobal AS HGLOBAL, pGlobalBuffer AS LPVOID
' // Initialize Gdiplus
token = AfxGdipInit
IF token = NULL THEN EXIT FUNCTION
' // Find the resource
IF LEFT(wszImage, 1) = "#" THEN
wID = VAL(MID(wszImage, 2))
dwID = MAKELONG(wID, 0)
hRes = FindResourceW(hInstance, MAKEINTRESOURCEW(dwID), CAST(LPCWSTR, RT_RCDATA))
ELSE
hRes = FindResourceW(hInstance, wszImage, CAST(LPCWSTR, RT_RCDATA))
END IF
IF hRes THEN
' // Retrieve the size of the image
imageSize = SizeofResource(hInstance, hRes)
IF imageSize THEN
' // Load the resource and get a pointer to the resource data.
' // Note: LockResource does not actually lock memory; it is just used
' // to obtain a pointer to the memory containing the resource data.
pResData = LockResource(LoadResource(hInstance, hRes))
IF pResData THEN
' // Allocate memory to hold the image
hGlobal = GlobalAlloc(GMEM_MOVEABLE, imageSize)
IF hGlobal THEN
' // Lock the memory
pGlobalBuffer = GlobalLock(hGlobal)
IF pGlobalBuffer THEN
' // Copy the image from the binary string file to global memory
CopyMemory(pGlobalBuffer, pResData, imageSize)
' // Create an stream in global memory
IF CreateStreamOnHGlobal(hGlobal, FALSE, @pImageStream) = S_OK THEN
IF pImageStream THEN
' // Create a bitmap from the data contained in the stream
hStatus = GdipCreateBitmapFromStream(pImageStream, @pImage)
' // Crete icon from image
hStatus = GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hIcon)
' // Free the image
IF pImage THEN GdipDisposeImage pImage
pImageStream->lpVtbl->Release(pImageStream)
END IF
END IF
' // Unlock the memory
GlobalUnlock pGlobalBuffer
END IF
' // Free the memory
GlobalFree hGlobal
END IF
END IF
END IF
END IF
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hIcon
END FUNCTION
' ========================================================================================
In the resource file, we need to use the RCDATA statement, e.g.
//=============================================================================
// Manifest
//=============================================================================
1 24 "WThemes.xml"
//=============================================================================
// Toolbar icons
//=============================================================================
// Toolbar, normal
IDI_ARROW_LEFT_48 RCDATA "arrow_left_48.png"
IDI_ARROW_RIGHT_48 RCDATA "arrow_right_48.png"
IDI_HOME_48 RCDATA "home_48.png"
IDI_SAVE_48 RCDATA "save_48.png"
// Toolbar, disabled
IDI_ARROW_LEFT_48_D RCDATA "arrow_left_48_d.png"
IDI_ARROW_RIGHT_48_D RCDATA "arrow_right_48_d.png"
IDI_HOME_48_D RCDATA "home_48_d.png"
IDI_SAVE_48_D RCDATA "save_48_d.png"
Usage example:
' // Add a tooolbar
DIM hToolBar AS HWND = pWindow.AddControl("Toolbar", pWindow.hWindow, IDC_TOOLBAR)
' // MOdule instance handle
DIM hInst AS HINSTANCE = GetModuleHandle(NULL)
' // Create an image list for the toolbar
DIM hImageList AS HIMAGELIST
hImageList = ImageList_Create(48, 48, ILC_COLOR32 OR ILC_MASK, 4, 0)
IF hImageList THEN
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_ARROW_LEFT_48"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_ARROW_RIGHT_48"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_HOME_48"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_SAVE_48"))
END IF
SendMessageW hToolBar, TB_SETIMAGELIST, 0, CAST(LPARAM, hImageList)
' // Add buttons to the toolbar
AfxToolbarAddButtonW hToolBar, 0, IDM_LEFTARROW
AfxToolbarAddButtonW hToolBar, 1, IDM_RIGHTARROW
AfxToolbarAddButtonW hToolBar, 2, IDM_HOME
AfxToolbarAddButtonW hToolBar, 3, IDM_SAVEFILE
' // Size the toolbar
SendMessageW hToolBar, TB_AUTOSIZE, 0, 0
I have modified all the functions to create an icon or a bitmap. It defaults to icon, but there are two optional parameters imageType (IMAGE_ICON or IMAGE_BITMAP) and clrBackground )the background color of the bitmap). The names of the functions now begin with AfxImage instead of AfxIcon.
This is the new include file so far:
' ########################################################################################
' Microsoft Windows
' File: AfxGdiplus.bas
' Content: Gdi Plus wrapper functions
' Compiler: Free Basic
' Freeware. Use at your own risk.
' Copyright (c) 2016 Jose Roca. All Rights Reserved.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#pragma once
#include once "windows.bi"
#ifdef __FB_64BIT__
#inclib "gdiplus"
#include "win/gdiplus-c.bi"
#else
#include once "win/gdiplus.bi"
using gdiplus
#endif
NAMESPACE Afx.Gdiplus
UNION GDIP_BGRA
color AS DWORD
TYPE
blue AS UBYTE
green AS UBYTE
red AS UBYTE
alpha AS UBYTE
END TYPE
END UNION
' ========================================================================================
' Returns an ARGB color value initialized with the specified values for the alpha, red,
' green, and blue components.
' ========================================================================================
FUNCTION GDIP_ARGB (BYVAL a AS UBYTE, BYVAL r AS UBYTE, BYVAL g AS UBYTE, BYVAL b AS UBYTE) AS DWORD
DIM clr AS GDIP_BGRA
clr.alpha = a : clr.red = r : clr.green = g : clr.blue = b
FUNCTION = clr.color
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns an XRGB color value initialized with the specified values for the red, green,
' and blue components.
' ========================================================================================
FUNCTION GDIP_XRGB (BYVAL r AS UBYTE, BYVAL g AS UBYTE, BYVAL b AS UBYTE) AS DWORD
FUNCTION = GDIP_ARGB(&HFF, r, g, b)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns a BGRA color value initialized with the specified values for the blue, green,
' red and alpha components.
' ========================================================================================
FUNCTION GDIP_BGRA (BYVAL b AS UBYTE, BYVAL g AS UBYTE, BYVAL r AS UBYTE, BYVAL a AS UBYTE) AS DWORD
DIM clr AS GDIP_BGRA
clr.blue = b : clr.green = g : clr.red = r : clr.alpha = a
FUNCTION = clr.color
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns an RGBA color value initialized with the specified values for the red, green,
' blue and alpha components.
' ========================================================================================
FUNCTION GDIP_RGBA (BYVAL r AS UBYTE, BYVAL g AS UBYTE, BYVAL b AS UBYTE, BYVAL a AS UBYTE) AS DWORD
FUNCTION = GDIP_ARGB(a, r, g, b)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns the version of Gdiplus.dll, e.g. 601 for version 6.01.
' ========================================================================================
FUNCTION AfxGdipDllVersion () AS LONG
DIM pvsffi AS VS_FIXEDFILEINFO PTR
DIM pVerInfo AS HANDLE, dwHandle AS DWORD, dwVersion AS DWORD
DIM cbLen AS DWORD, wMajor AS WORD, wMinor AS WORD
cbLen = GetFileVersionInfoSizeW("GDIPLUS.DLL", @dwHandle)
IF cbLen = 0 THEN RETURN NULL
pVerInfo = HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, cbLen)
IF pVerInfo = NULL THEN RETURN NULL
IF GetFileVersionInfoW("GDIPLUS.DLL", dwHandle, cbLen, pVerInfo) THEN
IF VerQueryValueW(pVerInfo, "\", @pvsffi, @cbLen) THEN
wMajor = HIWORD(pvsffi->dwFileVersionMS)
wMinor = LOWORD(pvsffi->dwFileVersionMS)
END IF
END IF
HeapFree GetProcessHeap, 0, pVerInfo
FUNCTION = (wMajor + wMinor / 100) * 100
END FUNCTION
' ========================================================================================
' ========================================================================================
' Initilizes GDI+
' Returns a token. Pass the token to GdiplusShutdown when you have finished using GDI+.
' ========================================================================================
FUNCTION AfxGdipInit (BYVAL version AS UINT32 = 1) AS ULONG_PTR
DIM hStatus AS GpStatus, token AS ULONG_PTR, StartupInput AS GdiplusStartupInput
StartupInput.GdiplusVersion = version
hStatus = GdiplusStartup(@token, @StartupInput, NULL)
FUNCTION = token
END FUNCTION
' ========================================================================================
' ========================================================================================
' Loads an image from a file using GDI+, converts it to an icon or bitmap and returns the handle.
' Parameters:
' - wszFileName = [in] Path of the image to load and convert.
' - dimPercent = Percent of dimming (1-99)
' - bGrayScale = TRUE or FALSE. Convert to gray scale.
' - imageType = IMAGE_ICON or IMAGE_BITMAP.
' - clrBackground = [in] The background color. This parameter is ignored if the image type
' is IMAGE_ICON or the bitmap is totally opaque.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon or bitmap.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdipImageFromFileEx (BYREF wszFileName AS WSTRING, _
BYVAL dimPercent AS LONG = 0, BYVAL bGrayScale AS LONG = FALSE, _
BYVAL imageType AS LONG = IMAGE_ICON, BYVAL clrBackground AS ARGB = 0) AS HANDLE
DIM token AS ULONG_PTR, pImage AS GpImage PTR, hImage AS HANDLE
DIM ImageWidth AS LONG, ImageHeight AS LONG, x AS LONG, y AS LONG
DIM pixColor AS GDIP_BGRA, iColor AS LONG, rFactor AS SINGLE
' // Initialize Gdiplus
token = AfxGdipInit
IF token = NULL THEN EXIT FUNCTION
' // Load the image from file
GdipLoadImageFromFile(wszFileName, @pImage)
IF pImage = NULL THEN EXIT FUNCTION
' // Get the image width and height
GdipGetImageWidth(pImage, @ImageWidth)
GdipGetImageHeight(pImage, @ImageHeight)
' // Dim or/and gray the image
IF dimPercent > 0 AND dimPercent < 100 THEN rFactor = dimPercent / 100
IF rFactor <> 0 OR bGrayScale <> 0 THEN
FOR y = 0 TO ImageWidth - 1
FOR x = 0 TO ImageHeight - 1
' // Get the pixel color
GdipBitmapGetPixel(CAST(GpBitmap PTR, pImage), x, y, @pixColor.color)
IF dimPercent > 0 THEN
pixColor.red = (255 - pixColor.red) * rFactor + pixColor.red
pixColor.green = (255 - pixColor.green) * rFactor + pixColor.green
pixColor.blue = (255 - pixColor.blue) * rFactor + pixColor.blue
END IF
IF bGrayScale THEN
' Note: The sum of the percentages for the three colors should add tp up 1
iColor = 0.299 * pixColor.red + 0.587 * pixColor.green + 0.114 * pixColor.blue
pixColor.Color = GDIP_BGRA (iColor, iColor, iColor, pixColor.alpha)
ELSE
pixColor.color = GDIP_ARGB(pixColor.alpha, pixColor.red, pixColor.green, pixColor.Blue)
END IF
' // Set the modified pixel color
GdipBitmapSetPixel(CAST(GpBitmap PTR, pImage), x, y, pixColor.color)
NEXT
NEXT
END IF
' // Crete icon from image
IF imageType = IMAGE_ICON THEN
GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hImage)
ELSE
GdipCreateHBITMAPFromBitmap(CAST(GpBitmap PTR, pImage), @hImage, clrBackground)
END IF
' // Free the image
IF pImage THEN GdipDisposeImage pImage
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hImage
END FUNCTION
' ========================================================================================
' ========================================================================================
' Converts an image stored in a buffer into an icon or bitmap and returns the handle.
' Parameters:
' - pBuffer = [in] Pointer to the buffer
' - bufferSize = Size of the buffer
' - imageType = IMAGE_ICON or IMAGE_BITMAP.
' - clrBackground = [in] The background color. This parameter is ignored if the image type
' is IMAGE_ICON or the bitmap is totally opaque.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon or bitmap.
' If the function fails, the return value is NULL.
' Usage example:
' DIM wszFileName AS WSTRING * MAX_PATH
' wszFileName = ExePath & "\arrow_left_256.png"
' DIM bufferSize AS SIZE_T_
' DIM nFile AS LONG
' nFile = FREEFILE
' OPEN wszFileName FOR BINARY AS nFile
' IF ERR THEN EXIT FUNCTION
' bufferSize = LOF(nFile)
' DIM pBuffer AS UBYTE PTR
' pBuffer = CAllocate(1, bufferSize)
' GET #nFile, , *pBuffer, bufferSize
' CLOSE nFile
' IF pBuffer THEN
' ImageList_ReplaceIcon(hImageList, -1, AfxGdipImageFromBuffer(pBuffer, ImageSize))
' DeAllocate(pBuffer)
' END IF
' ========================================================================================
FUNCTION AfxGdipImageFromBuffer (BYVAL pBuffer AS ANY PTR, BYVAL bufferSize AS SIZE_T_, _
BYVAL imageType AS LONG = IMAGE_ICON, BYVAL clrBackground AS ARGB = 0) AS HANDLE
DIM token AS ULONG_PTR, pImage AS GpImage PTR, hImage AS HANDLE
DIM ImageWidth AS LONG, ImageHeight AS LONG, x AS LONG, y AS LONG
DIM pixColor AS GDIP_BGRA, iColor AS LONG, rFactor AS SINGLE
DIM pImageStream AS IStream PTR, hGlobal AS HGLOBAL, pGlobalBuffer AS LPVOID
' // Initialize Gdiplus
token = AfxGdipInit
IF token = NULL THEN EXIT FUNCTION
' // Allocate memory to hold the image
hGlobal = GlobalAlloc(GMEM_MOVEABLE, bufferSize)
IF hGlobal THEN
' // Lock the memory
pGlobalBuffer = GlobalLock(hGlobal)
IF pGlobalBuffer THEN
' // Copy the image from the binary string file to global memory
CopyMemory(pGlobalBuffer, pBuffer, bufferSize)
' // Create an stream in global memory
IF CreateStreamOnHGlobal(hGlobal, FALSE, @pImageStream) = S_OK THEN
IF pImageStream THEN
' // Create a bitmap from the data contained in the stream
GdipCreateBitmapFromStream(pImageStream, CAST(GpBitmap PTR PTR, @pImage))
' // Crete icon from image
IF imageType = IMAGE_ICON THEN
GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hImage)
ELSE
GdipCreateHBITMAPFromBitmap(CAST(GpBitmap PTR, pImage), @hImage, clrBackground)
END IF
' // Free the image
IF pImage THEN GdipDisposeImage pImage
pImageStream->lpVtbl->Release(pImageStream)
END IF
END IF
' // Unlock the memory
GlobalUnlock pGlobalBuffer
END IF
' // Free the memory
GlobalFree hGlobal
END IF
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hImage
END FUNCTION
' ========================================================================================
' ========================================================================================
' Loads an image from a file, converts it to an icon or bitmap and returns the handle.
' Parameters:
' - wszFileName = [in] Path of the image to load and convert.
' - imageType = IMAGE_ICON or IMAGE_BITMAP.
' - clrBackground = [in] The background color. This parameter is ignored if the image type
' is IMAGE_ICON or the bitmap is totally opaque.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon or bitmap.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdipImageFromFile (BYREF wszFileName AS WSTRING, _
BYVAL imageType AS LONG = IMAGE_ICON, BYVAL clrBackground AS ARGB = 0) AS HICON
DIM fd AS WIN32_FIND_DATAW
DIM hFind AS HANDLE
' // Check for the existence of the file
IF LEN(wszFileName) = 0 THEN EXIT FUNCTION
hFind = FindFirstFileW(@wszFileName, @fd)
IF hFind = INVALID_HANDLE_VALUE THEN EXIT FUNCTION
FindClose hFind
' // Make sure that is not a directory or a temporary file
IF (fd.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY OR _
(fd.dwFileAttributes AND FILE_ATTRIBUTE_TEMPORARY) = FILE_ATTRIBUTE_TEMPORARY THEN
EXIT FUNCTION
END IF
' // Open the file and store its contents into a buffer
DIM nFile AS LONG, bufferSize AS SIZE_T_
nFile = FREEFILE
OPEN wszFileName FOR BINARY AS nFile
IF ERR THEN EXIT FUNCTION
bufferSize = LOF(nFile)
DIM pBuffer AS UBYTE PTR
pBuffer = CAllocate(1, bufferSize)
GET #nFile, , *pBuffer, bufferSize
CLOSE nFile
IF pBuffer THEN
FUNCTION = AfxGdipImageFromBuffer(pBuffer, bufferSize, imageType, clrBackground)
DeAllocate(pBuffer)
END IF
END FUNCTION
' ========================================================================================
' ========================================================================================
' Loads an image from a resource, converts it to an icon or bitmap and returns the handle.
' Parameter:
' - hInstance = [in] A handle to the module whose portable executable file or an accompanying
' MUI file contains the resource. If this parameter is NULL, the function searches
' the module used to create the current process.
' - wszImage = [in] Name of the image in the resource file (.RES). If the image resource uses
' an integral identifier, wszImage should begin with a number symbol (#)
' followed by the identifier in an ASCII format, e.g., "#998". Otherwise,
' use the text identifier name for the image. Only images embedded as raw data
' (type RCDATA) are valid. These must be icons in format .png, .jpg, .gif, .tiff.
' - imageType = IMAGE_ICON or IMAGE_BITMAP.
' - clrBackground = [in] The background color. This parameter is ignored if the image type
' is IMAGE_ICON or the bitmap is totally opaque.
' Return Value:
' If the function succeeds, the return value is the handle of the created icon or bitmap.
' If the function fails, the return value is NULL.
' ========================================================================================
FUNCTION AfxGdipImageFromRes (BYVAL hInstance AS HINSTANCE, BYREF wszImage AS WSTRING, _
BYVAL imageType AS LONG = IMAGE_ICON, BYVAL clrBackground AS ARGB = 0) AS HANDLE
DIM token AS ULONG_PTR, pImage AS GpImage PTR, hImage AS HANDLE
DIM hRes AS HRSRC, pResData AS HRSRC, wID AS WORD, dwID AS DWORD, imageSize AS DWORD
DIM pImageStream AS IStream PTR, hGlobal AS HGLOBAL, pGlobalBuffer AS LPVOID
' // Initialize Gdiplus
token = AfxGdipInit
IF token = NULL THEN EXIT FUNCTION
' // Find the resource
IF LEFT(wszImage, 1) = "#" THEN
wID = VAL(MID(wszImage, 2))
dwID = MAKELONG(wID, 0)
hRes = FindResourceW(hInstance, MAKEINTRESOURCEW(dwID), CAST(LPCWSTR, RT_RCDATA))
ELSE
hRes = FindResourceW(hInstance, wszImage, CAST(LPCWSTR, RT_RCDATA))
END IF
IF hRes THEN
' // Retrieve the size of the image
imageSize = SizeofResource(hInstance, hRes)
IF imageSize THEN
' // Load the resource and get a pointer to the resource data.
' // Note: LockResource does not actually lock memory; it is just used
' // to obtain a pointer to the memory containing the resource data.
pResData = LockResource(LoadResource(hInstance, hRes))
IF pResData THEN
' // Allocate memory to hold the image
hGlobal = GlobalAlloc(GMEM_MOVEABLE, imageSize)
IF hGlobal THEN
' // Lock the memory
pGlobalBuffer = GlobalLock(hGlobal)
IF pGlobalBuffer THEN
' // Copy the image from the binary string file to global memory
CopyMemory(pGlobalBuffer, pResData, imageSize)
' // Create an stream in global memory
IF CreateStreamOnHGlobal(hGlobal, FALSE, @pImageStream) = S_OK THEN
IF pImageStream THEN
' // Create a bitmap from the data contained in the stream
GdipCreateBitmapFromStream(pImageStream, CAST(GpBitmap PTR PTR, @pImage))
' // Crete icon from image
IF imageType = IMAGE_ICON THEN
GdipCreateHICONFromBitmap(CAST(GpBitmap PTR, pImage), @hImage)
ELSE
GdipCreateHBITMAPFromBitmap(CAST(GpBitmap PTR, pImage), @hImage, clrBackground)
END IF
' // Free the image
IF pImage THEN GdipDisposeImage pImage
pImageStream->lpVtbl->Release(pImageStream)
END IF
END IF
' // Unlock the memory
GlobalUnlock pGlobalBuffer
END IF
' // Free the memory
GlobalFree hGlobal
END IF
END IF
END IF
END IF
' // Shut down Gdiplus
GdiplusShutdown token
' // Return the handle of the icon
FUNCTION = hImage
END FUNCTION
' ========================================================================================
END NAMESPACE
Hi Jose, can you post an attachment with the "WThemes.xml" file and the png's that you are using?
This attachment includes the testing code that I have used for toolbars. Requires version RC05.