PlanetSquires Forums

Support Forums => WinFBX - Windows Framework for FreeBASIC => Topic started by: Paul Squires on January 20, 2019, 08:03:30 PM

Title: AfxExtractResourceToFile
Post by: Paul Squires on January 20, 2019, 08:03:30 PM
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.

Code: [Select]
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
Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on January 21, 2019, 11:00:01 AM
Which values are you passing in the lResourceType parameter?
Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on January 21, 2019, 11:12: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?
Title: Re: AfxExtractResourceToFile
Post by: Paul Squires on January 21, 2019, 12:11:03 PM
> 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).
Title: Re: AfxExtractResourceToFile
Post by: Paul Squires on January 21, 2019, 12:12:19 PM
Which values are you passing in the lResourceType parameter?


Any of these:
https://docs.microsoft.com/en-us/windows/desktop/menurc/resource-types
Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on January 22, 2019, 09:00:49 AM
This is my version:

Code: [Select]
' ========================================================================================
' 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.

Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on January 22, 2019, 10:06:10 AM
An additional function that returns the contents of the resource a a string.

Code: [Select]
' ========================================================================================
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:

Code: [Select]
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
Title: Re: AfxExtractResourceToFile
Post by: Paul Squires on January 22, 2019, 11:33:23 AM
Thanks Josť, the AfxExtractResourceToFile seems to work well for me. Thanks!
Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on January 22, 2019, 11:59:08 AM
I have updated the Git repository and the documentation.
Title: Re: AfxExtractResourceToFile
Post by: Paul Squires on April 03, 2019, 04:06:58 PM
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!
Title: Re: AfxExtractResourceToFile
Post by: Josť Roca on April 03, 2019, 05:56:11 PM
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.
Title: Re: AfxExtractResourceToFile
Post by: Paul Squires on April 03, 2019, 10:23:37 PM
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!