Hi José,
I couldn't find a function in WinFBX that extracts a resource to a file so I stole some of your other code and put together the code below. Maybe you'll find it useful and include it in the library at some point.
I needed such a function because as you've noted in your CImageCtx control some image types can't be loaded from a resource. I created a PictureBox control for the WinFBE visual designer and it extracts the image to a temp file and uses the LoadImageFromFile function that works well loading the different file types.
function AfxExtractResourceToFile( byval hInstance as HINSTANCE, _
byref wszResourceName as wstring, _
byref wszFileName as wstring, _
byval lResourceType as LPWSTR _
) as boolean
' Default that the function is successful
dim AS LONG lResult = true
' // Find the resource and lock it
DIM hResource AS HRSRC = FindResourceW(hInstance, wszResourceName, CAST(LPCWSTR, lResourceType))
IF hResource = NULL THEN RETURN false
DIM imageSize AS DWORD = SizeofResource(hInstance, hResource)
IF imageSize = 0 THEN RETURN false
DIM pResourceData AS LPVOID = LockResource(LoadResource(hInstance, hResource))
IF pResourceData = NULL THEN RETURN false
' // Allocate memory to hold the image
DIM hGlobal AS HGLOBAL = GlobalAlloc(GMEM_MOVEABLE, imageSize)
IF hGlobal THEN
' // Lock the memory
DIM pGlobalBuffer AS LPVOID = GlobalLock(hGlobal)
IF pGlobalBuffer THEN
' // Copy the image from the resource file to global memory
memcpy pGlobalBuffer, pResourceData, imageSize
AfxDeleteFile( wszFileName )
dim as long f = freefile
if open( wszFileName, for binary, as #f ) = 0 then
dim pBuffer as byte ptr = pGlobalBuffer
put #f, , pBuffer[0], imagesize
close #f
else
return true
end if
' // Unlock the memory
GlobalUnlock pGlobalBuffer
END IF
' // Free the memory
GlobalFree hGlobal
END IF
function = lResult
end function
Which values are you passing in the lResourceType parameter?
> I needed such a function because as you've noted in your CImageCtx control some image types can't be loaded from a resource.
But the note is for the LoadBitmapFromResource method. Have you tried with LoadImageFromResource?
Quote from: José Roca on January 21, 2019, 11:42:42 AM
> I needed such a function because as you've noted in your CImageCtx control some image types can't be loaded from a resource.
But the note is for the LoadBitmapFromResource method. Have you tried with LoadImageFromResource?
Hi José, yes I did try LoadImageFromResource but it failed for JPEG (at least, it did fail for the image jpg image file that I used for testin).
Quote from: José Roca on January 21, 2019, 11:30:01 AM
Which values are you passing in the lResourceType parameter?
Any of these:
https://docs.microsoft.com/en-us/windows/desktop/menurc/resource-types
This is my version:
' ========================================================================================
' Saves a resource to a disk file.
' Parameters:
' - 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.
' - wszResourceName = [in] Name of the resource. If the resource is an image that uses an integral
' identifier, wszResourceName 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 in format .png, .jpg, .gif, .tiff.
' - wszFileName = Path of the file where to save the extracted resource.
' - pResourceType = Type of the resource, e.g. RT_RCDATA. For a list of predefined resource types see:
' https://docs.microsoft.com/en-us/windows/desktop/menurc/resource-types
' Return Value:
' Returns TRUE on success or FALSE on failure.
' Example:
' AfxExtractResourceToFile(NULL, "IDI_ARROW_RIGHT", "IDI_ARROW_RIGHT.png", RT_RCDATA)
' where IDI_ARROW_RIGHT is the identifier in the resource file for
' IDI_ARROW_RIGHT RCDATA ".\Resources\arrow_right_64.png"
' Example:
' AfxExtractResourceToFile(NULL, "#111", "VEGA_PAZ_01.jpg", RT_RCDATA)
' where "#111" is the identifier in the resource file for
' 111 RCDATA ".\Resources\VEGA_PAZ_01.jpg"
' ========================================================================================
PRIVATE FUNCTION AfxExtractResourceToFile (BYVAL hInstance AS HINSTANCE, BYREF wszResourceName AS WSTRING, _
BYREF wszFileName AS WSTRING, BYVAL pResourceType AS LPWSTR) AS BOOLEAN
' // Find the resource
DIM hRes AS HRSRC
IF LEFT(wszResourceName, 1) = "#" THEN
DIM wID AS WORD = VAL(MID(wszResourceName, 2))
DIM dwID AS DWORD = MAKELONG(wID, 0)
hRes = FindResourceW(hInstance, MAKEINTRESOURCEW(dwID), pResourceType)
ELSE
hRes = FindResourceW(hInstance, wszResourceName, pResourceType)
END IF
IF hRes = NULL THEN RETURN FALSE
' // Retrieve the resource size
DIM ResSize AS DWORD = SizeofResource(hInstance, hRes)
IF ResSize = 0 THEN RETURN FALSE
' // Lock the resource
DIM pResData AS LPVOID = LockResource(LoadResource(hInstance, hRes))
IF pResData = NULL THEN RETURN FALSE
' // Write the resource data to a file
DIM hFile AS HANDLE = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)
IF hFile = INVALID_HANDLE_VALUE THEN RETURN FALSE
DIM dwBytesWritten AS DWORD
DIM bSuccess AS BOOLEAN = WriteFile(hFile, pResData, ResSize, @dwBytesWritten, NULL)
CloseHandle(hFile)
RETURN bSuccess
END FUNCTION
' ========================================================================================
I'm using CreateFileW instad of FB's OPEN because OPEN does not accept unicode file names. Notice also that the resource data can be saved directly without copying it first to global memory.
An additional function that returns the contents of the resource a a string.
' ========================================================================================
PRIVATE FUNCTION AfxExtractResource (BYVAL hInstance AS HINSTANCE, _
BYREF wszResourceName AS WSTRING, BYVAL pResourceType AS LPWSTR) AS STRING
' // Find the resource
DIM hRes AS HRSRC
IF LEFT(wszResourceName, 1) = "#" THEN
DIM wID AS WORD = VAL(MID(wszResourceName, 2))
DIM dwID AS DWORD = MAKELONG(wID, 0)
hRes = FindResourceW(hInstance, MAKEINTRESOURCEW(dwID), pResourceType)
ELSE
hRes = FindResourceW(hInstance, wszResourceName, pResourceType)
END IF
IF hRes = NULL THEN RETURN ""
' // Retrieve the resource size
DIM ResSize AS DWORD = SizeofResource(hInstance, hRes)
IF ResSize = 0 THEN RETURN ""
' // Lock the resource
DIM pResData AS LPVOID = LockResource(LoadResource(hInstance, hRes))
IF pResData = NULL THEN RETURN ""
DIM strResData AS STRING = SPACE(ResSize)
memcpy STRPTR(strResData), pResData, ResSize
RETURN strResData
END FUNCTION
' ========================================================================================
Usage example:
DIM strResData AS STRING = AfxExtractResource(NULL, "#111", RT_RCDATA)
' // Write the resource data to a file
DIM hFile AS HANDLE = CreateFileW("PazVega.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)
IF hFile THEN
DIM dwBytesWritten AS DWORD
DIM bSuccess AS BOOLEAN = WriteFile(hFile, STRPTR(strResData), LEN(strResData), @dwBytesWritten, NULL)
CloseHandle(hFile)
print bSuccess
END IF
Thanks José, the AfxExtractResourceToFile seems to work well for me. Thanks!
I have updated the Git repository and the documentation.
Maybe I spoke too soon. It seems to work for the RT_RCDATA types but not for RT_ICON or RT_BITMAP.
I have create a small test project (see attached).
Could you try it on your system to see if you get the same results as I do. Extraction of icons fails (I tried two different icons). Bitmap extracts to the file but it produces a file format that will not load. The PNG (using RCDATA) seems to be okay.
Thanks!
Icons and bitmaps aren't stored in resource files as raw data. Therefore, the data must be interpreted.
Regarding icons, read this Raymond Chen's article:
https://devblogs.microsoft.com/oldnewthing/?p=7083
Regarding bitmaps, apparently are stored as DIBs.
Maybe a solution could be to load the icon or bitmap using LoadImage.
Thanks José, I will rework the logic of my visual designer to store both the image resource name and its type and then modify the image control to call a specific routine for each of the types (image, bitmap, icon).
Thanks!