PlanetSquires Forums

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: AfxExtractResourceToFile  (Read 671 times)

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
AfxExtractResourceToFile
« 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
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #1 on: January 21, 2019, 11:00:01 AM »

Which values are you passing in the lResourceType parameter?

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #2 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?

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
Re: AfxExtractResourceToFile
« Reply #3 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).
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
Re: AfxExtractResourceToFile
« Reply #4 on: January 21, 2019, 12:12:19 PM »

Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #5 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.

« Last Edit: January 22, 2019, 09:05:43 AM by Josť Roca »
Logged

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #6 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

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
Re: AfxExtractResourceToFile
« Reply #7 on: January 22, 2019, 11:33:23 AM »

Thanks Josť, the AfxExtractResourceToFile seems to work well for me. Thanks!
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #8 on: January 22, 2019, 11:59:08 AM »

I have updated the Git repository and the documentation.

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
Re: AfxExtractResourceToFile
« Reply #9 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!
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Josť Roca

  • Moderator
  • Guru Member
  • *****
  • Posts: 3177
Re: AfxExtractResourceToFile
« Reply #10 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.
« Last Edit: April 04, 2019, 06:36:39 AM by Josť Roca »
Logged

Paul Squires

  • Administrator
  • Guru Member
  • *****
  • Posts: 8782
  • Windows 10
    • PlanetSquires Software
Re: AfxExtractResourceToFile
« Reply #11 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!
Logged
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer