-
Jun 10th, 2019, 04:44 AM
#1
Thread Starter
Fanatic Member
Create and load Cursor from byte array.
Hi all,
Below is a byte dump of .cur file .
I want to store those dump bytes in an array and from the bytes, re-create the cursor and get its handle so I can pass the handle to the SetCursor API.
I know I can get the cursor handle if I use the LoadCursorfromFile API but I don't want to use a file .. I want to load the cursor from the byte array in memory.
Regards.
Cursor file dump.
Code:
00 00 02 00 01 00 20 20 10 00 0c 00 01 00 e8 02
00 00 16 00 00 00 28 00 00 00 20 00 00 00 40 00
00 00 01 00 04 00 00 00 00 00 80 02 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00
00 00 80 00 80 00 80 80 00 00 80 80 80 00 c0 c0
c0 00 00 00 ff 00 00 ff 00 00 00 ff ff 00 ff 00
00 00 ff 00 ff 00 ff ff 00 00 ff ff ff 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 0f ff ff f8 00 00 00 00 00 00 00 00
00 00 00 00 ff ff ff ff 80 00 00 00 00 00 00 00
00 00 00 0f ff ff ff ff 80 00 00 00 00 00 00 00
00 00 00 ff ff ff ff ff f8 00 00 00 00 00 00 00
00 00 00 ff ff ff ff ff f8 00 00 00 00 00 00 00
00 00 0f ff ff ff ff ff f8 00 00 00 00 00 00 00
00 00 0f ff ff ff ff ff ff 80 00 00 00 00 00 00
00 00 ff ff ff ff ff ff ff 80 00 00 00 00 00 00
00 00 ff 88 ff ff ff ff ff 80 00 00 00 00 00 00
00 0f f8 00 ff ff ff ff ff 80 00 00 00 00 00 00
00 0f 80 00 ff ff ff ff 7f 80 00 00 00 00 00 00
00 f8 00 00 ff 7f f7 f8 0f 80 00 00 00 00 00 00
00 00 00 00 f8 0f 80 f8 0f 80 00 00 00 00 00 00
00 00 00 00 f8 0f 80 f8 00 00 00 00 00 00 00 00
00 00 00 00 f8 0f 80 f8 00 00 00 00 00 00 00 00
00 00 00 00 f8 0f 80 00 00 00 00 00 00 00 00 00
00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff f8
07 ff ff f0 07 ff ff e0 03 ff ff c0 03 ff ff 80
01 ff ff 80 01 ff ff 00 01 ff ff 00 00 ff fe 00
00 ff fe 00 00 ff fc 00 00 ff fc 20 00 ff f8 60
00 ff fc e0 00 ff ff e0 01 ff ff e0 07 ff ff e0
0f ff ff e0 7f ff ff e1 ff ff ff e1 ff ff ff e1
ff ff ff e1 ff ff ff f3 ff ff ff ff ff ff
-
Jun 10th, 2019, 06:51 AM
#2
Re: Create and load Cursor from byte array.
-
Jun 10th, 2019, 02:46 PM
#3
Fanatic Member
Re: Create and load Cursor from byte array.
Sorry to interrupt this thread but is it possible to create a mouse pointer image from any bitmap I have and at the size of the bitmap.
-
Jun 10th, 2019, 04:35 PM
#4
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by The trick
Thanks The trick.
I never knew about this handy CreateIconFromResourceEx API function.
However, this seems to works only for PNG files not for .ICO or .CUR files
Also, as opposed to what the documentation says, it doesn't seem to return a propper handle to the cursor or icon which you can then pass to the SetCursor API
Below is a code that uses the CreateIconFromResourceEx API to create a handle from a PNG file (hand.png) which I downloaded from the internet.
The LoadPNGtoICO function sucessfully returns a IPicture object and it also can be passed to the DrawIcon API to draw the icon onto the DC BUT the following doesn't work :
I need to use the SetCursor API in the Form's mouse move event dynamically.
This code works fine for creating a IPicture Object and for using the DrawIcon API BUT doesn't work work for the SetCursor API
Code:
Option Explicit
Private Type uPicDesc
Size As Long
Type As Long
hPic As Long
hPal As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
Private Declare Function CreateIconFromResourceEx Lib "user32.dll" (ByRef presbits As Any, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "OleAut32.dll" (lpPictDesc As Any, riid As Any, ByVal fPictureOwnsHandle As Long, ipic As IPicture) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
Private Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Long) As Long
Private Declare Function DrawIcon Lib "user32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal hIcon As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Const vbPicTypeIcon As Long = 3
Private hIcon As Long, hCopy As Long
Private Sub Command1_Click()
Dim tPicture As StdPicture
Dim imgData() As Byte, fnr As Integer, lSize As Long
Dim hdc As Long
fnr = FreeFile()
On Error Resume Next
Open app.Path & "\hand.png" For Binary As #fnr
If Err Then
MsgBox Err.Description
Exit Sub
End If
On Error GoTo 0
lSize = LOF(fnr)
If lSize = 0& Then
MsgBox "File does not exist", vbExclamation + vbOKOnly
Close #fnr
Exit Sub
End If
ReDim imgData(0 To lSize - 1&)
Get #fnr, 1, imgData()
Close #fnr
Set tPicture = LoadPNGtoICO(imgData())
Set Me.Picture = tPicture
If tPicture Is Nothing Then
MsgBox "Failed to load the file. May not be valid PNG format or pre-Vista operating system.", vbExclamation + vbOKOnly
Else
'DrawIcon ALSO WORKS !!!
DrawIcon Me.hdc, 0, 0, hIcon
End If
DestroyIcon hIcon
DestroyIcon hCopy
End Sub
Function LoadPNGtoICO(pngData() As Byte) As IPicture
Const IMAGE_CURSOR = 2
Const IMAGE_ICON = 1
Const LR_COPYRETURNORG = &H4
Dim IID_IDispatch As GUID, uPicinfo As uPicDesc
hIcon = CreateIconFromResourceEx(pngData(0), UBound(pngData) + 1&, 1&, &H30000, 0&, 0&, 0&)
hCopy = CopyImage(hIcon, IMAGE_CURSOR, 0, 0, LR_COPYRETURNORG)
If hCopy Then
With uPicinfo
.Size = Len(uPicinfo)
.Type = vbPicTypeIcon
.hPic = hCopy
.hPal = 0
End With
With IID_IDispatch
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
Call OleCreatePictureIndirect(uPicinfo, IID_IDispatch, True, LoadPNGtoICO)
End If
End Function
Regards.
Last edited by JAAFAR; Jun 10th, 2019 at 04:41 PM.
-
Jun 10th, 2019, 06:49 PM
#5
Re: Create and load Cursor from byte array.
CreateIconFromResourceEx works for icons & cursors also, but for cursors, you need to prefix the bits with 4 bytes: Cursor HotSpot
1st 2 bytes is HotSpotX
2nd 2 bytes is HotSpotY
remaining bytes is the pixel data in proper format, i.e., BitmapInfoHeader followed by mask/and bits
-
Jun 10th, 2019, 07:16 PM
#6
Re: Create and load Cursor from byte array.
Originally Posted by Code Dummy
Sorry to interrupt this thread but is it possible to create a mouse pointer image from any bitmap I have and at the size of the bitmap.
An old MS KB article said:
Cursor sizes
Although cursors can, in theory, be any size, the system imposes a standard size that is exposed by means of the SM_CXCURSOR and SM_CYCURSOR values. These metrics are read-only. On standard, low-DPI systems, these metrics are set to 32x32 pixels (32 bytes/row). When the system loads cursors by means of the standard LoadCursor function, the cursor is stretched to this dimension.
Most system cursors appear smaller than 32x32 and do not use some of the space around the cursor. The cursors in the extra-large schemes use more of the available 32x32 area. Windows XP does not include any system cursors that are larger than 32x32. (If larger cursors were included, they would be stretched down to 32x32 when the standard APIs load the cursors.)
For high-DPI systems, Windows XP has adjusted the SM_CXCURSOR and SM_CYCURSOR values to be 64x64 pixels. This size adjustment is to prevent the mouse pointer from virtually disappearing because it is too small to be effectively used. Although the other aspects of the system scale with DPI, the mouse pointer does not scale. Microsoft does not try to enforce a DPI-independent size for the mouse pointer.
The system also provides the SetSystemCursor API function that you can use to change the system cursor for specific categories. You can use this function to set a cursor of any size. However, you must call the function programmatically, and you can only use it to set a cursor for a specific category. You cannot use it to make all cursors on the system the same size.
For cursor sets that you want to use on high-DPI systems, Microsoft recommends that you author them with both 32x32 and 64x64 candidates. The system will select the appropriate candidate during loading, depending on the DPI of the system.
That is supposed to be Q307213 but I can't find it online.
-
Jun 11th, 2019, 11:35 AM
#7
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
CreateIconFromResourceEx works for icons & cursors also, but for cursors, you need to prefix the bits with 4 bytes: Cursor HotSpot
1st 2 bytes is HotSpotX
2nd 2 bytes is HotSpotY
remaining bytes is the pixel data in proper format, i.e., BitmapInfoHeader followed by mask/and bits
This is new territory to me.
Is the CreateDIBSection API the right way to create the bitmap ?
And if so, I don't know how to determine the correct Width and Height in the bitmap header based on the hex dump .
Regards.
-
Jun 11th, 2019, 12:38 PM
#8
Re: Create and load Cursor from byte array.
Originally Posted by JAAFAR
This is new territory to me.
Is the CreateDIBSection API the right way to create the bitmap ?
And if so, I don't know how to determine the correct Width and Height in the bitmap header based on the hex dump .
Regards.
You can assume the dimensions are square, but that wouldn't be fool-proof. Based on that idea, if you have a hex dump of 256 pixels, then your icon/cursor is 16x16: Sqr(256). And if a cursor, you can assume the hotspot is dead center or top left, but that would not be a good assumption. A lot of assumptions....
Regarding CreateDibSection... no, the CreateIconFromResourceEx just requires a byte array. But you'll need to know how to write all the info to that byte array. That requires knowledge of the binary format of a cursor/icon
-
Jun 14th, 2019, 07:04 AM
#9
Re: Create and load Cursor from byte array.
Originally Posted by JAAFAR
This is new territory to me.
I was curious and looked at your dump closer. That dump is a complete cursor resource. Sending it to the API just needs a little tweak...
Explaining your hex dump
1st 6 bytes is the ICONDIR. In that structure, is the number of images in the entire file
next 16 bytes is the ICONDIRENTRY. One of these for each image in the file
after the last ICONDIRENTRY are the icon/cursor data of each image, stacked
Code:
-------- the 1st 22 bytes below are a standard icon/cursor header for 1 image
if more than 1 image, bytes 7-22 are repeated with different values
/// ICONDIR
00 00 02 00 << header meaning Cursor, last two would be 01 00 if icon
01 00 << nr images in this dump
/// ICONDIRENTRY #1
20 << image width = 32, note: 0 = 256+
20 << image height =32, note: 0 = 256+
10 << image color count = 16
00 << reserved
0c 00 << cursor hotspotX (12) or planes for icons
01 00 << cursor hotspotY (1) or bitcount for icons
e8 02 00 00 << size of resource = 744
16 00 00 00 << offset in dump where icon/cursor starts = 22
------- BitmapInfoHeader starts here, followed by color table (if any) and then pixel data
28 00 00 00 << 40 size of bitmapinfoheader structure
20 00 00 00 << 32 width of image
40 00 00 00 << 64 height of image (icon/cursor are always doubled)
....
Based on this information, you can send the dump to the API after tweaking just 4 bytes:
- I'll assume the entire dump is in a byte array: bData(0 to SizeOf(dump) - 1)
After parsing the the above info, we know:
- BitmapInfoHeader starts at byte 22 in the array, per parsing at ICONDIRENTRY# * 16 + 18
- Size of the resource is 744, per parsing at ICONDIRENTRY# * 16 + 14
- hotspotX & hotspotY are at bytes 10-13 in the array ICONDIRENTRY# * 16 + 10
Since this is a cursor, copy the hotspot to the 4 bytes immediately preceding the BitmapInfoHeader.
- preserve the original bytes if needed and restore after array sent to the API
For i = 0 to 3: bData(18 + i) = bData(10 + i): Next
Now send the array to the API ByRef as: bData(18). Size of data passed is 744 + 4 (including the hotspot's 4 bytes)
Edited & FYI:
If resource is a PNG, then the entire PNG file begins where the BitmapInfoHeader is expected and the 1st 4 bytes = 1196314761 (PNG signature). Also, if the image size is zero, I typically parse the BitmapInfoHeader/PNG header to determine actual size. And last, the color count member of the ICONDIRENTRY is not always filled in nor trustworthy. If it is important to you, it should be parsed from the BitmapInfoHeader/PNG header
Last edited by LaVolpe; Jun 14th, 2019 at 09:14 AM.
-
Jun 14th, 2019, 11:17 PM
#10
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
I was curious and looked at your dump closer. That dump is a complete cursor resource. Sending it to the API just needs a little tweak...
Thank you for investigating this further.
With the help of a bitmap/cursor editor, I created a simple (32x32 pix) bitmap (16 colors) (file size 630 bytes ) ... This is just a small Red drag cursor.
I then loaded the bitmap on a picture object that I have embedeed on an excel worksheet .
I then copied the picture image to the clipborad using :
Step1-
Code:
Pic.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
OpenClipboard 0
hPtr = GetClipboardData(CF_BITMAP)
hBmp = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
The above sucessfully returns a valid memory handle to the bitmap in the hBmp variable.
Then I fill-in the BitInfo.bmiHeader with the corresponding values and then I make a call to the GetDIBits API as follows:
Step2-
Code:
GetDIBits hdc, hBmp , 0, BitInfo.bmiHeader.biHeight, Bits(1, 1, 1), BitInfo, DIB_RGB_COLORS
SetDIBitsToDevice hdc, 0, 0, BitInfo.bmiHeader.biWidth, BitInfo.bmiHeader.biHeight, _
0, 0, 0, BitInfo.bmiHeader.biHeight, Bits(1, 1, 1), BitInfo, DIB_RGB_COLORS
Step3-
Finally, I set up the necessary data for the icon structure as well as the icon masks and call CreateIconIndirect
The above steps work sucessfully and get me a valid cursor handle that I pass to the SetCursor API and display the correct cursor image as expected.
What I want:
I want to store the bits of the bitmap that I placed in the clipboard (in step1) in an array and create the bitmap from the bits in that array ... I want to skip the need of copying the bitmap to the clipboard and getting the bitmap handle from there.
I used the CreateBitmap API and passed the bitmap bits to it (I got the bitmap bits from the above call to GetDIBits) but I keep getting a black 32x32 icon
I also tried CreateDIBSection/SetDIBits to create the memory bitmap from the stored bits but without success.
Does the GetDIBits API retrieve the bitmap pixel data alone or the bitmap header data + the pixel data ? I think that's where lies my confusion.
I hope I have explained the situation clearly.
Regards.
Last edited by JAAFAR; Jun 15th, 2019 at 12:05 AM.
-
Jun 15th, 2019, 09:11 AM
#11
Re: Create and load Cursor from byte array.
If you just need a copy of the bitmap, why not use CopyImage and be done with it?
Regarding your original idea of using the data from a valid cursor file, far less of a headache. If the cursor file has just 1 image in it, this is all that is needed...
1. Extract just the bytes you need for your cursor from that file & save the bytes with your Excel project. This is done one time, outside of your project.
Code:
Dim bData() As Byte, lHotSpot As Integer
Dim hCur As Long, fNr As Integer
fNr = FreeFile()
Open "C:\...\myCursor.cur" For Binary As #fNr ' << change name to valid file
ReDim bData(1 To LOF(fNr) - 18) ' need entire file less 18 bytes
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, lHotSpot
bData(1) = lHotSpot And &HFF
bData(2) = (lHotSpot And &HFFFF&) \ &H100
Get #fNr, 13, lHotSpot
bData(3) = lHotSpot And &HFF
bData(4) = (lHotSpot And &HFFFF&) \ &H100
Close #fNr
2. Now bData() has the bytes you need, write them to a temp file and load them into your Excel project
3. Whenever you need the cursor, get the bytes, and assuming one-bound array...
Code:
hCur = CreateIconFromResourceEx(bData(1), UBound(bData), 0&, &H30000, Cx, Cy, 0&)
FYI: the 5th byte (1-bound) of the file will tell you how many images are inside the file
with the API:
3rd param is 0 for cursor, 1 for icon
And Cx,Cy can be zero unless you want to change the size of the cursor
Last edited by LaVolpe; Jun 15th, 2019 at 10:40 AM.
-
Jun 15th, 2019, 10:53 AM
#12
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
If you just need a copy of the bitmap, why not use CopyImage and be done with it?
Regarding your original idea of using the data from a valid cursor file, far less of a headache. If the cursor file has just 1 image in it, this is all that is needed...
1. Extract just the bytes you need for your cursor from that file & save the bytes with your Excel project. This is done one time, outside of your project.
Code:
Dim bData() As Byte, lHotSpot As Integer
Dim hCur As Long, fNr As Integer
fNr = FreeFile()
Open "C:\...\myCursor.cur" For Binary As #fNr ' << change name to valid file
ReDim bData(1 To LOF(fNr) - 18) ' need entire file less 18 bytes
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, lHotSpot
bData(1) = lHotSpot And &HFF
bData(2) = (lHotSpot And &HFFFF&) \ &H100
Get #fNr, 13, lHotSpot
bData(3) = lHotSpot And &HFF
bData(4) = (lHotSpot And &HFFFF&) \ &H100
Close #fNr
2. Now bData() has the bytes you need, write them to a temp file and load them into your Excel project
3. Whenever you need the cursor, get the bytes, and assuming one-bound array...
Code:
hCur = CreateIconFromResourceEx(bData(1), UBound(bData), 0&, &H30000, Cx, Cy, 0&)
FYI: the 5th byte (1-bound) of the file will tell you how many images are inside the file
with the API:
3rd param is 0 for cursor, 1 for icon
And Cx,Cy can be zero unless you want to change the size of the cursor
That sounds great... I never knew about this handy CreateIconFromResourceEx API function !
I'll give this a try and will post back what the result I get.
Thank you very much for your guidance.
-
Jun 15th, 2019, 02:13 PM
#13
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Ok - I seem to have managed to make this work for x32 and x64 bit.
workbook test
This the code I used in the excel userform module
Code:
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Function SetCursor Lib "user32" (ByVal hCursor As LongPtr) As LongPtr
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long
Private Declare PtrSafe Function CreateIconFromResourceEx Lib "user32.dll" (ByRef presbits As Any, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Private hCur As LongPtr
#Else
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function SetCursor Lib "user32" (ByVal hCursor As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function CreateIconFromResourceEx Lib "user32.dll" (ByRef presbits As Any, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Private hCur As Long
#End If
Private Sub UserForm_Initialize()
hCur = GetCurHandle
If hCur = 0 Then Debug.Print "failed to get a valid cursor handle."
End Sub
Private Sub UserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If hCur Then SetCursor hCur
End Sub
Private Sub UserForm_Terminate()
DeleteObject hCur
End Sub
#If Win64 Then
Private Function GetCurHandle() As LongPtr
Const PTR_LEN = 6
#Else
Private Function GetCurHandle() As Long
Const PTR_LEN = 4
#End If
Dim iByteCnt As Integer, iPos As Integer
Dim vArTemp1() As Variant, vArTemp2() As Variant, vArTemp3() As Variant, vArTemp4() As Variant
vArTemp1 = Array(0, 0, 0, 0, 40, 0, 0, 0, 32, 0, 0, 0, 64, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 128, _
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, _
0, 128, 0, 0, 0, 128, 128, 0, 128, 0, 0, 0, 128, 0, 128, 0, 128, 128, 0, 0, _
192, 192, 192, 0, 128, 128, 128, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 255, _
0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 144, 144, 144, _
144, 144, 144, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, _
0, 0, 144, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0)
vArTemp2 = Array(0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 9, _
0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 144, 144, 144, 144, 144, 144, _
0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 144, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, _
0, 9, 153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 153, 153, 0, 0, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 153, 144, 153, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, 153, _
144, 0, 0, 0, 0, 0, 0, 0, 0)
iPos = UBound(vArTemp1)
ReDim Preserve vArTemp1(UBound(vArTemp1) + UBound(vArTemp2) + 1)
CopyMemory ByVal VarPtr(vArTemp1(iPos + 1)), ByVal VarPtr(vArTemp2(0)), PTR_LEN * 4 * (UBound(vArTemp2) + 1)
vArTemp3 = Array(0, 0, 0, 0, 153, 153, 153, 153, 153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, _
153, 153, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, 153, 153, 144, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 153, 153, 153, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, _
153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 153, 153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 153, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _
255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 170, 191, 255, 245, 85, 127, _
255, 239, 255, 191, 255, 247, 255, 127, 255, 239, 255, 191, 255, 247, 255, 127)
iPos = UBound(vArTemp1)
ReDim Preserve vArTemp1(UBound(vArTemp1) + UBound(vArTemp3) + 1)
CopyMemory ByVal VarPtr(vArTemp1(iPos + 1)), ByVal VarPtr(vArTemp3(0)), PTR_LEN * 4 * (UBound(vArTemp3) + 1)
vArTemp4 = Array(255, 239, 255, 191, 255, 247, 255, 127, 255, 239, 255, 191, 255, 245, 85, 127, _
255, 234, 170, 191, 254, 127, 255, 255, 252, 63, 255, 255, 252, 63, 255, 255, _
248, 127, 255, 255, 120, 127, 255, 255, 48, 255, 255, 255, 16, 255, 255, 255, _
1, 255, 255, 255, 0, 31, 255, 255, 0, 63, 255, 255, 0, 127, 255, 255, 0, 255, 255, _
255, 1, 255, 255, 255, 3, 255, 255, 255, 7, 255, 255, 255, 15, 255, 255, 255, _
31, 255, 255, 255, 63, 255, 255, 255, 127, 255, 255, 255)
iPos = UBound(vArTemp1)
ReDim Preserve vArTemp1(UBound(vArTemp1) + UBound(vArTemp4) + 1)
CopyMemory ByVal VarPtr(vArTemp1(iPos + 1)), ByVal VarPtr(vArTemp4(0)), PTR_LEN * 4 * (UBound(vArTemp4) + 1)
ReDim bytes(LBound(vArTemp1) To UBound(vArTemp1)) As Byte
For iByteCnt = LBound(vArTemp1) To UBound(vArTemp1)
bytes(iByteCnt) = CByte(vArTemp1(iByteCnt))
Next
GetCurHandle = CreateIconFromResourceEx(bytes(0), UBound(bytes) + 1, 0&, &H30000, 0, 0, 0&)
End Function
Thanks very much LaVolpe for your guidance and patience .. I wouldn't have been able to solve this without your help - I really appreciate it.
Regards.
Last edited by JAAFAR; Jun 15th, 2019 at 03:01 PM.
-
Jun 15th, 2019, 02:17 PM
#14
Re: Create and load Cursor from byte array.
Glad you got it to work. Keep in mind that if you want to play with other cursor files, you need to ensure the file only has 1 image. Otherwise, that sample code to extract the needed bytes will fail. If that does apply, you can either parse the file and extract bytes or use some other app that allows you to extract individual cursors from files that have multiple cursors.
Happy to assist
Edited: Oh hey, are you sure about your API call? Bytes(0) & UBound(bytes)? Should it be UBound(bytes)+1 or bytes(1) instead?
Last edited by LaVolpe; Jun 15th, 2019 at 02:20 PM.
-
Jun 15th, 2019, 02:31 PM
#15
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
Glad you got it to work. Keep in mind that if you want to play with other cursor files, you need to ensure the file only has 1 image. Otherwise, that sample code to extract the needed bytes will fail. If that does apply, you can either parse the file and extract bytes or use some other app that allows you to extract individual cursors from files that have multiple cursors.
Happy to assist
Edited: Oh hey, are you sure about your API call? Bytes(0) & UBound(bytes)? Should it be UBound(bytes)+1 or bytes(1) instead?
Yes I would normally use a file that has 1 image for the moment until I get a grasp of the memory layout internals of Cursors, Icons and bitmaps which I am still trying properly understand.
As for Bytes(0) vs Bytes(1), passing Byes(1) to the CreateIconFromResourceEx API returns 0
Regards.
-
Jun 15th, 2019, 02:34 PM
#16
Re: Create and load Cursor from byte array.
Regarding my question, this is your code statement
GetCurHandle = CreateIconFromResourceEx(bytes(0), UBound(bytes), 0&, &H30000, 0, 0, 0&)
I think the bolded part should be: UBound(bytes)+1
or at least: UBound(bytes) - LBound(bytes) + 1
unless you purposely resized your array 1 byte greater than the number of bytes needed for the cursor
-
Jun 15th, 2019, 02:55 PM
#17
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
Regarding my question, this is your code statement
GetCurHandle = CreateIconFromResourceEx(bytes(0), UBound(bytes), 0&, &H30000, 0, 0, 0&)
I think the bolded part should be: UBound(bytes)+1
or at least: UBound(bytes) - LBound(bytes) + 1
unless you purposely resized your array 1 byte greater than the number of bytes needed for the cursor
Yes If I understand you correctly ,you are right. It should be UBound(bytes)+1 to include all the bytes as I am using the default Optiion Base 0.
However, the cursor is still created without UBound(bytes)+1
-
Jun 15th, 2019, 02:57 PM
#18
Re: Create and load Cursor from byte array.
The API may just assume the missing byte is zero? Better to be safe than sorry though.
-
Jun 15th, 2019, 03:08 PM
#19
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
The API may just assume the missing byte is zero? Better to be safe than sorry though.
Yes- That's what I thought as well. However, the last byte happens to be 255 not 0 unless the API starts in reverse order beginning from the last element in the array.
Anyway, I have edited the code in post #13 with the correct byte array size..
Thank you very much.
Last edited by JAAFAR; Jun 15th, 2019 at 03:16 PM.
-
Jun 15th, 2019, 03:17 PM
#20
Re: Create and load Cursor from byte array.
Oh, here is one minor optimization...
You can use a LONG array vs a byte array. The way bitmap/icon/cursor data is formatted (on DWord boundaries and BitmapInfo always being a multiple of 4), the number of bytes needs should always be a multiple of 4.
That would reduce the number of lines in your Array() statements and your loop count when transferring from the variant array. However, be sure to include the & suffix, if you go that route, for each Array() entry, i.e., 0& not 0. Otherwise, the entry will be treated as Integer vs Long for values < &H10000
Of course, you would have to tweak other parts of your code, i.e., nr of bytes sent to the API will be UBound(longs)*4+4
Last edited by LaVolpe; Jun 15th, 2019 at 03:29 PM.
Reason: clariifed when values treated as Integer vs Long
-
Jun 15th, 2019, 03:25 PM
#21
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
Oh, here is one minor optimization...
You can use a LONG array vs a byte array. The way bitmap/icon/cursor data is formatted (on DWord boundaries and BitmapInfo always being a multiple of 4), the number of bytes needs should always be a multiple of 4.
That would reduce the number of lines in your Array() statements and your loop count when transferring from the variant array. However, be sure to include the & suffix, if you go that route, for each Array() entry, i.e., 0& not 0. Otherwise, the entry will be treated as Integer vs Long. It makes a difference for values between &H8000 to &H8FFF
Of course, you would have to tweak other parts of your code, i.e., nr of bytes sent to the API will be UBound(longs)*4+4
I'll look into that when I feel more confortable with handling memory parts of image files - But first, I will need to roll up my sleeves and do some studying
Regards.
-
Jun 15th, 2019, 03:52 PM
#22
Re: Create and load Cursor from byte array.
I was thinking something like the following. This sample will create a text file you can copy & paste right into your Excel module. Rename the array from "longs" to whatever you want. Using something like the following will negate needing to create those variant arrays and transferring them to another array.
Code:
Dim bData() As Long, n As Integer, x As Long
Dim sText As String, fNr As Integer
fNr = FreeFile()
Open "C:\...\myCursor.cur" For Binary As #fNr <<< change to valid cursor file
Get #fNr, 5, n
If n <> 1 Then
Close #fNr
Stop ' more than 1 file in array
End If
Get #fNr, 15, x
If (x Mod 4) <> 0 Then
Close #fNr
Stop ' image size is expected to be multiple of 4
End If
ReDim bData(1 To x \ 4 + 1) ' image size + hotspot
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, bData(1) ' get hotspot into 1st element
Close #fNr
fNr = FreeFile()
Open "C:\...\formattedArray.txt" For Output As #fNr ' <<< change folder
n = 1
Print #fNr, "ReDim longs(0 To " & CStr(UBound(bData) - 1) & ") As Long"
For x = 1 To UBound(bData)
Print #fNr, "longs(" & CStr(x - 1) & ") = " & CStr(bData(x));
If n = 30 Then
Print #fNr, vbCrLf;
n = 1
Else
Print #fNr, ": ";
n = n + 1
End If
Next
Close #fNr
Then your API call looks like:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
I use something like the above when I create thunks and want to hardcode the array elements inside a module/class
Last edited by LaVolpe; Jun 16th, 2019 at 06:32 AM.
-
Jun 15th, 2019, 04:43 PM
#23
Re: Create and load Cursor from byte array.
I thought there was an issue that cursors must be created from locked memory that stays resident as long as the cursor handle is valid. Isn't that why they are supposed to be created from a resource?
I can't find it, but I seem to remember an old MS KB article about display driver crashes that could lead to a Blue Screen crash in both Win9x and WinNT. But maybe that's obsolete and drivers now protect themselves?
-
Jun 15th, 2019, 04:47 PM
#24
Re: Create and load Cursor from byte array.
@dilettante. If so, I'd assume that all MS APIs that create cursors take care of that, i.e., LoadImage (which is recommended over LoadCursor) and others.
-
Jun 15th, 2019, 04:51 PM
#25
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
I was thinking something like the following. This sample will create a text file you can copy & paste right into your Excel module. Rename the array from "longs" to whatever you want. Using something like the following will negate needing to create those variant arrays and transferring them to another array.
Code:
Dim bData() As Long, n As Integer, x As Long
Dim fNr As Integer
fNr = FreeFile()
Open "C:\...\myCursor.cur" For Binary As #fnr ' <<< change to valid cursor file
If ((LOF(fNr) - 6) Mod 4) <> 0 Then
Close #fNr
Stop ' file size is expected to be multiple of 4
End If
Get #fNr, 5, n
If n <> 1 Then
Close #fNr
Stop ' more than 1 file in array
End If
ReDim bData(1 To (LOF(fNr) - 18) \ 4) ' need entire file less 18 bytes
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, bData(1) ' get hotspot into 1st element
Close #fNr
fNr = FreeFile()
Open "C:\...\formattedArray.txt" For Output As #fNr ' <<< change to desired folder
n = 0
Print #fNr, "ReDim longs(0 To " & CStr(UBound(bData) - 1) & ")"
For x = 1 To UBound(bData)
Print #fNr, "longs(" & CStr(x - 1) & ") = " & CStr(bData(x));
If n = 29 Then
Print #fNr, vbCrLf;
n = 0
Else
Print #fNr, ": ";
n = n + 1
End If
Next
Close #fNr
Then your API call looks like:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
I use something like the above when I create thunks and want to hardcode the array elements inside a module/class
Thanks LaVolpe,
That is cool but unless I am missing something, doesn't that defeat my purpose as there will be a need for a text file along the project ?
I might just as well have the cur file and use the LoadCursorFromFile API if that's the case.
Regards.
-
Jun 15th, 2019, 04:58 PM
#26
Re: Create and load Cursor from byte array.
No, if you run it (recently updated it), then load the text file into NotePad, you can simply copy it all and paste it into your module. That should make your module more compact, i.e.,....
Code:
Public Function GetCurHandle () As LongLong ???
... add pasted code here -- removes need for all that Array() and CopyMemory stuff
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
End Function
Of course, I am assuming you needed the arrays to split the 744 bytes into managable arrays, combined them, then rewrote them into a byte array. If I misunderstood your routine, then ignore my suggestion.
-
Jun 15th, 2019, 05:03 PM
#27
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
No, if you run it (recently updated it), then load the text file into NotePad, you can simply copy it all and paste it into your module. That should make your module more compact, i.e.,....
Code:
Public Function GetCurHandle () As LongLong ???
... add pasted code here -- removes need for all that Array() and CopyMemory stuff
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
End Function
Of course, I am assuming you needed the arrays to split the 744 bytes into managable arrays, combined them, then rewrote them into a byte array. If I misunderstood your routine, then ignore my suggestion.
Yes your assumption is correct.
I haven't tried your code yet but I will do later on as I am logging off now .
I will post back with the result I get later on.
Regards.
-
Jun 15th, 2019, 10:16 PM
#28
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Hi LaVolpe,
I followed your instructions and this is the output I got from the text file :
Code:
Private Function GetCurHandle() As LongPtr
ReDim longs(0 To 186)
longs(0) = 0: longs(1) = 40: longs(2) = 32: longs(3) = 64: longs(4) = 262145: longs(5) = 0: longs(6) = 640: longs(7) = 0: longs(8) = 0: longs(9) = 16: longs(10) = 0: longs(11) = 0: longs(12) = 8388608: longs(13) = 32768: longs(14) = 8421376: longs(15) = 128: longs(16) = 8388736: longs(17) = 32896: longs(18) = 12632256: longs(19) = 8421504: longs(20) = 16711680: longs(21) = 65280: longs(22) = 16776960: longs(23) = 255: longs(24) = 16711935: longs(25) = 65535: longs(26) = 16777215: longs(27) = 0: longs(28) = 0: longs(29) = 0
longs(30) = 0: longs(31) = 0: longs(32) = 0: longs(33) = 0: longs(34) = 0: longs(35) = 0: longs(36) = 151587072: longs(37) = 151587081: longs(38) = 9: longs(39) = 0: longs(40) = -1869611008: longs(41) = -1869574000: longs(42) = 144: longs(43) = 0: longs(44) = 2304: longs(45) = 0: longs(46) = 9: longs(47) = 0: longs(48) = 9437184: longs(49) = 0: longs(50) = 144: longs(51) = 0: longs(52) = 2304: longs(53) = 0: longs(54) = 9: longs(55) = 0: longs(56) = 9437184: longs(57) = 0: longs(58) = 144: longs(59) = 0
longs(60) = 2304: longs(61) = 0: longs(62) = 9: longs(63) = 0: longs(64) = 9437184: longs(65) = 0: longs(66) = 144: longs(67) = 0: longs(68) = 2304: longs(69) = 0: longs(70) = 9: longs(71) = 0: longs(72) = -1869611008: longs(73) = -1869574000: longs(74) = 144: longs(75) = 0: longs(76) = 151587072: longs(77) = 151587081: longs(78) = 9: longs(79) = 150994944: longs(80) = 144: longs(81) = 0: longs(82) = 0: longs(83) = -1728053248: longs(84) = 153: longs(85) = 0: longs(86) = 0: longs(87) = -1728053248: longs(88) = 153: longs(89) = 0
longs(90) = 0: longs(91) = -1727463424: longs(92) = 144: longs(93) = 0: longs(94) = 0: longs(95) = -1727463280: longs(96) = 144: longs(97) = 0: longs(98) = 0: longs(99) = -1718026087: longs(100) = 0: longs(101) = 0: longs(102) = 0: longs(103) = -1717989223: longs(104) = 0: longs(105) = 0: longs(106) = 0: longs(107) = -1868981863: longs(108) = 0: longs(109) = 0: longs(110) = 0: longs(111) = -1717986919: longs(112) = 37017: longs(113) = 0: longs(114) = 0: longs(115) = -1717986919: longs(116) = 153: longs(117) = 0: longs(118) = 0: longs(119) = -1717986919
longs(120) = 144: longs(121) = 0: longs(122) = 0: longs(123) = -1717986919: longs(124) = 0: longs(125) = 0: longs(126) = 0: longs(127) = -1868981863: longs(128) = 0: longs(129) = 0: longs(130) = 0: longs(131) = 10066329: longs(132) = 0: longs(133) = 0: longs(134) = 0: longs(135) = 9476505: longs(136) = 0: longs(137) = 0: longs(138) = 0: longs(139) = 39321: longs(140) = 0: longs(141) = 0: longs(142) = 0: longs(143) = 37017: longs(144) = 0: longs(145) = 0: longs(146) = 0: longs(147) = 153: longs(148) = 0: longs(149) = 0
longs(150) = 0: longs(151) = 144: longs(152) = 0: longs(153) = 0: longs(154) = 0: longs(155) = -1: longs(156) = -1: longs(157) = -1079317761: longs(158) = 2136339967: longs(159) = -1073745921: longs(160) = 2147481599: longs(161) = -1073745921: longs(162) = 2147481599: longs(163) = -1073745921: longs(164) = 2147481599: longs(165) = -1073745921: longs(166) = 2136339967: longs(167) = -1079317761: longs(168) = -32770: longs(169) = -49156: longs(170) = -49156: longs(171) = -32776: longs(172) = -32904: longs(173) = -208: longs(174) = -240: longs(175) = -255: longs(176) = -57600: longs(177) = -49408: longs(178) = -33024: longs(179) = -256
longs(180) = -255: longs(181) = -253: longs(182) = -249: longs(183) = -241: longs(184) = -225: longs(185) = -193: longs(186) = -129:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
Debug.Print GetCurHandle
End Function
Unfortunately, GetCurHandle now returns a null pointer .. I doubled checked to ensure I copied the entire longs output but nothing.
EDIT:
I made it work by defining the longs array as Long instead of Variant.
So I just edited your code as follows:
Code:
Print #fNr, "ReDim longs(0 To " & CStr(UBound(bData) - 1) & ") As Long"
I now just need to understand your code that extracts the bytes from the cursor file
Regards.
Last edited by JAAFAR; Jun 15th, 2019 at 10:33 PM.
-
Jun 16th, 2019, 05:58 AM
#29
Re: Create and load Cursor from byte array.
You got it right, I got it wrong. Should be ReDim longs(0 To 186) As Long
VB is treating it as Variant otherwise -- sorry about that & fixed it
Edited: Regarding the code. Pretty simple. Reference post #9 for icon/cursor structure & offsets.
2 sanity checks made to begin with:
-- ensure only 1 image in the source file (code requires changes otherwise). Always at 5th byte in file
-- get image/resource size and verify a multiple of 4. Size always begins at 15th byte
Next is simply to read the number of bytes/4 into longs. We start reading 4 bytes before the actual data location because we need room for the cursor hotspot in the 1st element. The data starts at 23rd byte, so we read from the 19th byte. After that we then read the hotspot into the 1st Long, overwriting it, and that hotspot begins at 11th byte.
As far as printing the array? Easy really. Using a counter to restrict 30 statements per line, but other than that, just formatting the text as we would if we were manually typing it out. Not 100% perfect, may have a trailing colon at the end of a line. Tweak to perfection if you want.
Any specific questions? Just ask.
Here's one more optimization. It won't print out a statement if the Long value is zero. Since VB/VBA initializes arrays with zero values, no need to explicitly set them to zero in a statement. Potential of significantly reducing number of statements is high. Example with a simple test cursor, 187 statements before tweak, 111 after (40% reduction)
Code:
Dim bData() As Long, n As Integer, x As Long
Dim lValue As Long, bLinePrinted As Boolean, fNr As Integer
fNr = FreeFile()
Open "C:\...\myCursor.cur" For Binary As #fNr ' <<< change to valid cursor file
Get #fNr, 5, n
If n <> 1 Then
Close #fNr
Stop ' more than 1 file in array
End If
Get #fNr, 15, x
If (x Mod 4) <> 0 Then
Close #fNr
Stop ' image size is expected to be multiple of 4
End If
ReDim bData(1 To x \ 4 + 1) ' image size + hotspot
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, bData(1) ' get hotspot into 1st element
Close #fNr
fNr = FreeFile()
Open "C:\...\formattedArray.txt" For Output As #fNr ' <<< change folder/filename
n = 1
Print #fNr, "ReDim longs(0 To " & CStr(UBound(bData) - 1) & ") As Long"
For x = 1 To UBound(bData)
lValue = bData(x)
If lValue Then Print #fNr, "longs(" & CStr(x - 1) & ") = " & CStr(lValue);
If n = 30 Then
If bLinePrinted Then Print #fNr, vbCrLf;
n = 1: bLinePrinted = False
Else
If lValue Then Print #fNr, ": "; Else bLinePrinted = True
n = n + 1
End If
Next
Close #fNr
Last edited by LaVolpe; Jun 16th, 2019 at 09:03 AM.
-
Jun 16th, 2019, 09:08 AM
#30
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
LaVolpe,
That's brilliant ! I now I think I understand- You already had the byte extraction code well commented but having no idea about the internal memory layout of the cursor file, I didn't pay enough attention.... Thanks a lot for the detailed explanation.
A cool side effect is this trick of formatting the code in a text file and later pasting it in the project ... I am sure this trick will come in handy for me in future projects.
Couple of questions if you don't mind:
1- For the sake of experimentation and better understanding, I want to define the array bData() As Byte instead of As Long (obviously this would output larger code) then resize the array accordingly as follows :
Code:
ReDim bData(1 To x + 1) '\ 4 + 1) ' image size + hotspot
Get #fNr, 19, bData() ' start at 19th byte in file
Get #fNr, 11, bData(1) ' get hotspot into 1st element
Close #fNr
Finally API call :
Code:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) + 1, 0&, &H30000, 0, 0, 0&)
I get a null pointer - Can you spot what I am doing wrong ?
2- As you said "BitmapInfo always being a multiple of 4" I would be interested to know how a bytes extraction code would look like for 1 image .ICO files and .BMP files taking into account their differences like hotspot etc ..
-
Jun 16th, 2019, 09:14 AM
#31
Re: Create and load Cursor from byte array.
The code in post #11 uses a byte array. But to summarize
1. The array size is wrong it needs to include 4 bytes for the hotspot, so: x + 4, not x + 1
2. You are only reading 1 byte of the hotspot, you need to read 4 bytes
-- just add this afterwards: Get #fNr, , bData(2): Get #fNr, , bData(3): Get #fNr, , bData(4)
For ico files, you don't adjust for hotspot, there is no additional 4 bytes to read. The bitmap info header for icon and cursor are pretty much identical. Bitmaps don't double the .bmiHeight member of the structure, as icons/cursors do, and bitmaps can use structures other than 40 bytes, versions 2,3,4 of that structure, also a more obscure pre-Win95 version. Icons/cursors always use the 40 byte v1 structure. There are a few more differences, but those are the big ones.
Oh, the 3rd parameter in CreateIconFromResourceEx is 1 for icons.
Last edited by LaVolpe; Jun 16th, 2019 at 09:34 AM.
-
Jun 16th, 2019, 09:36 AM
#32
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
LaVolpe,
Brilliant ! I got it working with the bytes array with the adjustement you suggested and I can now better visualize the cursor memory layout .. now makes sense to me
Since the memory structure of ICO files are similar (minus hotspot), I'll will experiment with one in the same fashion and see what I get.
This was a good lesson and I learnt a lot so thank you very much for all your help.
Regards.
-
Jun 16th, 2019, 09:38 AM
#33
Re: Create and load Cursor from byte array.
You're welcome. Just remember to not adjust for hotspot. In other words, resize array correctly and begin reading from 23rd byte of the file
FYI: CreateIconFromResourceEx is used to extract information in a format that resource files use. Inside our res files for vbResTypeIcon & vbResTypeCursor, you now know how the information is actually stored for each individual image.
-
Jun 16th, 2019, 10:03 AM
#34
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
You're welcome. Just remember to not adjust for hotspot. In other words, resize array correctly and begin reading from 23rd byte of the file
FYI: CreateIconFromResourceEx is used to extract information in a format that resource files use. Inside our res files for vbResTypeIcon & vbResTypeCursor, you now know how the information is actually stored for each individual image.
Yes the 23rd byte is where the actual image data starts for both .CUR and .ICO - ie: no need now to reserve 4 bytes before for the hotspot byte.
Yes I would use the value of the vbResTypeIcon vb6 constant as per CreateIconFromResourceEx documentation.
BTW, I never used res files as there is no such option in VBA.
Regards.
-
Jun 16th, 2019, 10:57 AM
#35
Re: Create and load Cursor from byte array.
One more FYI, just for grins.
Any cursor can be converted to an icon -- use 1 in CreateIconFromResourceEx and don't adjust for the cursor hotspot
Any icon can be converted to a cursor -- use 0 in CreateIconFromResourceEx and fake a hotspot, maybe dead center of the icon, requires parsing the icon dimensions. Or set hotspot at 0,0 to not parse dimensions, but 0,0 may be transparent in the icon.
-
Jun 16th, 2019, 11:10 PM
#36
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
One more FYI, just for grins.
Any cursor can be converted to an icon -- use 1 in CreateIconFromResourceEx and don't adjust for the cursor hotspot
Any icon can be converted to a cursor -- use 0 in CreateIconFromResourceEx and fake a hotspot, maybe dead center of the icon, requires parsing the icon dimensions. Or set hotspot at 0,0 to not parse dimensions, but 0,0 may be transparent in the icon.
Thanks LaVolpe,
1- I have just converted a 32x32 True color ICO file to a CUR based on your suggestion and apparently it works as expected.
This is how I set up the Long array (image data + hotspot)
Code:
ReDim bData(1 To x \ 4 + 1) ' image size + hotspot
Get #fNr, 19, bData() ' start at 19th byte in file
bData(1) = (16 * &H10000) Or (16 And &HFFFF&) ' Hotspot at the center of the 32x32 cursor
Or bData(1) =0 for hotspot at top left of the cursor.
Then API call :
Code:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4 + 4, 0&, &H30000, 0, 0, 0&)
2- As for converting a CUR to an ICO, I haven't tested it but I guess this how I should build the Long array :
Code:
ReDim bData(1 To x \ 4) ' image size
Get #fNr, 23, bData() ' start at 23th byte in file
and then API call :
Code:
GetCurHandle = CreateIconFromResourceEx(longs(0), UBound(longs) * 4, 1&, &H30000, 0, 0, 0&)
I hope this is correct.
Regards.
-
Jun 17th, 2019, 07:24 AM
#37
Re: Create and load Cursor from byte array.
For #2 above... Yes. You can always test it by rendering it somewhere, using DrawIconEx (icons or cursors). Not mentioned in previous posts, be sure you destroy the created icon/cursor at some point.
Edited: To be clear on some comments in earlier posts regarding cx,cy. Here is what MSDN says when those value are zero, for the width: The desired width, in pixels, of the icon or cursor. If this parameter is zero, the function uses the SM_CXICON or SM_CXCURSOR system metric value to set the width.
That would lead you to believe that passing zero does not always use actual size. However, the APIs flags parameter can override that... Flag LR_DEFAULTSIZE uses the width or height specified by the system metric values for cursors or icons, if the cxDesired or cyDesired values are set to zero. If this flag is not specified and cxDesired and cyDesired are set to zero, the function uses the actual resource size.
So when I said you could pass 0 for cx,cy and actual size would be used, I was correct. But I didn't explain why I was correct -- we are not passing LR_DEFAULTSIZE in the flags parameter.
Last edited by LaVolpe; Jun 17th, 2019 at 07:43 AM.
-
Jun 17th, 2019, 01:37 PM
#38
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
For #2 above... Yes. You can always test it by rendering it somewhere, using DrawIconEx (icons or cursors). Not mentioned in previous posts, be sure you destroy the created icon/cursor at some point.
Thanks .
Yes I use DeleteObject upon unloading the form.
Now that you mention the flag parameter, this is waht the MSDN doc says about the LR_SHARED flag:
"When you use this flag, the system will destroy the resource when it is no longer needed."
Does that mean I could set this flag and not worry about destroying the cursor when done ?
Also while we are on the subject, what do you think could happen if we have a cursor/icon resource handle and for some reason (like a project crash), the vb poject goes out of scope therefore we have no chance to release the resource with DeleteObject ? Does Windows release a resource when the vbaproject crashes while debugging ?
As a side note I, I have been printing the formatted text to the immediate window. It is slower but more concise IMHO.
Code:
n = 1
Debug.Print "ReDim longs(0 To " & CStr(UBound(bData) - 1) & ") As Long"
For x = 1 To UBound(bData)
Debug.Print "longs(" & CStr(x - 1) & ") = " & CStr(bData(x));
If n = 30 Then
Debug.Print vbCrLf;
n = 1
Else
Debug.Print ": ";
n = n + 1
End If
Next
-
Jun 17th, 2019, 02:04 PM
#39
Re: Create and load Cursor from byte array.
DeleteObject isn't technically correct. Per MSDN, use DestroyCursor for cursors and DestroyIcon for icons.
As for LR_SHARED, that is intended for loading from an application's resources (i.e., compiled res file). Consider the way you are loading the cursor is same as loading from a file. If so, then this applies: Do not use LR_SHARED for icons or cursors that have non-standard sizes, that may change after loading, or that are loaded from a file.
As far as crashes go or applications completely closing/unloading... all memory related to that process is released back to the system.
-
Jun 17th, 2019, 02:21 PM
#40
Thread Starter
Fanatic Member
Re: Create and load Cursor from byte array.
Originally Posted by LaVolpe
DeleteObject isn't technically correct. Per MSDN, use DestroyCursor for cursors and DestroyIcon for icons.
As for LR_SHARED, that is intended for loading from an application's resources (i.e., compiled res file). Consider the way you are loading the cursor is same as loading from a file. If so, then this applies: Do not use LR_SHARED for icons or cursors that have non-standard sizes, that may change after loading, or that are loaded from a file.
As far as crashes go or applications completely closing/unloading... all memory related to that process is released back to the system.
Thank you. DestroyIcon is the propper API ... That said, I read somewhere that DeleObject can just as well be used for destroying resources such as DCs,Brushes etc - Is that correct ?
What I really meant by a vbproject crash was just the project going out of scope ie:- loosing the resource handle and therfore not being able to call the DestoyIcon\DeleteObject on it.
Regards.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|