As I am needing code for 32 bit to do just that...
I can load a .png image from file just fine (https://masm32.com/board/index.php?msg=138003), but I need to use .png resources, to keep down the number of files for the end user.
From hutch's code in the Masm64 SDK library, he has this:
ResImageLoad proc ResID:QWORD
LOCAL hRes :QWORD
LOCAL pRes :QWORD
LOCAL lRes :QWORD
LOCAL pbmp :QWORD
LOCAL hBmp :QWORD
LOCAL npng :QWORD
mov hRes, rv(FindResource,0,ResID,RT_RCDATA) ; find the resource
mov pRes, rv(LoadResource,0,hRes) ; load the resource
mov lRes, rv(SizeofResource,0,hRes) ; get its size
mrm npng, "@@@@.@@@" ; temp file name
invoke save_file,npng,pRes,lRes ; create the temp file
invoke GdipCreateBitmapFromFile,L(npng),ADDR pbmp ; create a GDI+ bitmap
invoke GdipCreateHBITMAPFromBitmap,pbmp,ADDR hBmp,0 ; create normal bitmap handle from it
invoke GdipDisposeImage,pbmp ; remove the GDI+ bitmap
invoke DeleteFile,npng ; delete the temp file
mov rax, hBmp ; return the bitmap handle in RAX
ret
ResImageLoad endp
Edited:
While that does work.... but it only works if using BitBlt or other GDI32 functions to draw the image (which does not retain the transparency), and does not work with GdipDrawImageRectI apparently.
I mistakenly said previously that it did work.
I made a simple test for it though to find out for certain, and found it only worked as if the .png was treated as a BITMAP and is not compatible with gdiplus ... :sad:
So basically the methods that hutch used to load the .png resource are of no use to me, for my purposes.
So does anyone know how to properly load a .png resource for use with GdipDrawImageRectI???
Edited for clarity and to fix an error on my part.
I have seen some clues to use "GdipCreateBitmapFromStream" but will that retain the transparency? The "Bitmap" in the name makes me think otherwise.
I have also seen some convoluted code using that function, but it was in an .if block with seemingly unrelated code and hard to follow.
If that is the correct function needed, how do I set up the code to use it?
I am 99% clueless on gdiplus code. The little I do know, does not help me to use .png resources with transparency instead of .png files with GdipDrawImageRectI.
Least resistance:
SHCreateStreamOnModuleResourceW, it's not exported by name, it's ordinal 628 in shlwapi.dll.
Then
GdipCreateBitmapFromStream
SHCreateStream... is undocumented though, and it's already changed dlls once, so you know, use at your own risk.
The alternative is CreateStreamOnHGlobal or SHCreateMemStream and use the code in the OP to get the resource data but instead of saving it to a file, write it to the stream instead. Might need to seek the stream back to the start.
Well, that sounds complicated. Remember I am a newby to some of this stuff...
I'll try some things regarding the last alternative. I remember seeing similar code during my research... I'll be back later. :smiley:
Hi Zedd,
QuoteI have seen some clues to use "GdipCreateBitmapFromStream"
Here is an example for you :
.IF uMsg==WM_CREATE
invoke FindResource,0,100,RT_RCDATA
mov hRes,eax
invoke SizeofResource,hInstance,eax
mov RsrcSize,eax
invoke LoadResource,0,hRes
invoke LockResource,eax
mov pImage,eax
invoke GlobalAlloc,GMEM_MOVEABLE,RsrcSize
mov hMem,eax
invoke GlobalLock,eax
mov pMem,eax ; copy the image to the global memory
invoke MemCopy,pImage,eax,RsrcSize
mov eax,OFFSET StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax],1
mov GdiplusStatus,eax
invoke GdiplusStartup,ADDR token,ADDR StartupInfo,0
invoke CreateStreamOnHGlobal,hMem,TRUE,ADDR pStream
invoke GdipCreateBitmapFromStream,pStream,ADDR BmpImage
invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0
Hi Vortex.
That looks like something I can work with. :thumbsup: While I do not understand this Stream stuff, the other functions are somewhat familiar... :smiley:
Quote from: zedd151 on April 10, 2025, 05:33:25 AMThat looks like something I can work with.
Then again, maybe not.
(https://i.postimg.cc/XYYDHc80/untitled.png)
Your example displays the image as a flat
bitmap, not as an image with transparency.
Updated later:
But I fixed your code to work with png's that do have transparency, It is in post #19
Quote from: zedd151 on April 10, 2025, 04:13:53 AMI have seen some clues to use "GdipCreateBitmapFromStream" but will that retain the transparency?
Do you have an example of a transparent png file? GuiImage (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1404) uses GdipCreateBitmapFromStream
Sample of my current code that uses GdipLoadImageFromFile to load png and GdipDrawImageRectI to display it
include \masm32\include\masm32rt.inc
include \masm32\include\gdiplus.inc
includelib \masm32\lib\gdiplus.lib
DlgProc proto :dword, :dword, :dword, :dword
LoadImageFromFile proto :dword, :dword
DrawImage proto :dword, :dword, :dword, :dword
cwidth equ 240 ; desired client area width
cheight equ 240 ; desired client area height
.data
StartupInfo GdiplusStartupInput <1, 0, 0, 0>
GdiplusStatus dd 0
token dd 0
hRed dd 0
.const
redpng db "red.png", 0
.code
start proc
local hInstance:dword
invoke GetModuleHandle, NULL
mov hInstance, eax
mov eax, offset StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax], 1
mov GdiplusStatus, eax
invoke GdiplusStartup, addr token, addr StartupInfo, 0
invoke LoadImageFromFile, addr redpng, addr hRed
invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0 ;; create dialog box, from resource dilalog
invoke GdipDisposeImage, hRed
invoke GdiplusShutdown, GdiplusStatus
invoke ExitProcess, eax
start endp
DlgProc proc hWin:dword, uMsg:dword, wParam:dword, lParam:dword
local hDC:dword, ps:PAINTSTRUCT, rct:RECT, hBrush:dword, hBrush_old:dword
local mDC:dword, hBmp:dword, hBmp_old:dword, hPen:dword, hPen_old:dword
local x:dword, y:dword, wwid:dword, whgt:dword, cwid:dword, chgt:dword
.if uMsg == WM_INITDIALOG
;; resizing Client Area to exact dimensions, specified by cwidth and cheight
invoke GetWindowRect, hWin, addr rct ;; get window current dimensions.
mov eax, rct.right ;; obtain current window width by subtracting left boundary
sub eax, rct.left ;; from the right boundary
mov wwid, eax ;; store current window width
mov eax, rct.bottom ;; obtain current window height by subtracting top boundary
sub eax, rct.top ;; from the bottom boundary
mov whgt, eax ;; store current window height
invoke GetClientRect, hWin, addr rct ;; get client area current dimensions.
mov eax, rct.right ;; obtain current client width by subtracting left boundary
sub eax, rct.left ;; from the right boundary
mov cwid, eax ;; store current client area width
mov eax, rct.bottom ;; obtain current client height by subtracting top boundary
sub eax, rct.top ;; from the bottom boundary
mov chgt, eax ;; store client area height
;; calculate the difference between desired client area width and current client area width
mov eax, cwidth
sub eax, cwid
;; adjust the window width according to the difference calculated above
add wwid, eax
;; calculate the difference between desired client area height and current client area height
mov eax, cheight
sub eax, chgt
;; adjust the window height according to the difference calculated height
add whgt, eax
;; center the main window
;; obtain client area of the desktop, not including the task bar
invoke SystemParametersInfoA, SPI_GETWORKAREA, 0, addr rct, 0
;; center window width
mov eax, rct.right
sub eax, wwid
sar eax, 1
mov x, eax
;; center window height
mov eax, rct.bottom
sub eax, whgt
sar eax, 1
mov y, eax
;; implement the centering of the resized main window
invoke MoveWindow, hWin, x, y, wwid, whgt, TRUE
.elseif uMsg == WM_ERASEBKGND
mov eax, 1
ret
.elseif uMsg == WM_PAINT
invoke BeginPaint, hWin, addr ps
mov hDC, eax ; window client area DC
invoke CreateCompatibleDC, hDC
mov mDC, eax ; memory DC
invoke CreateCompatibleBitmap, hDC, ps.rcPaint.right, ps.rcPaint.bottom
mov hBmp, eax ; compatible bitmap handle
invoke SelectObject, mDC, hBmp
mov hBmp_old, eax
;; ###########################################################
;; Here I change the background color of the main window
invoke CreateSolidBrush, 00FFBF7Fh ;; a nice light blue colr
mov hBrush, eax ;; save the brush handle
;; get client area rectangle
invoke GetClientRect, hWin, addr rct
;; fill the client area rectangle with chosen color
invoke FillRect, mDC, addr rct, hBrush ; fill rectangle in memory DC
;; delete the brush, as it is no longer needed
invoke DeleteObject, hBrush
;; ###########################################################
invoke DrawImage, mDC, hRed, 80, 80
;; ###########################################################
invoke BitBlt, hDC, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, mDC, 0, 0, SRCCOPY
invoke SelectObject, mDC, hBmp_old
invoke DeleteObject, hBmp
invoke DeleteDC, mDC
invoke EndPaint, hWin, addr ps
.elseif uMsg == WM_CLOSE
invoke EndDialog, hWin, 0
.endif
xor eax, eax
ret
DlgProc endp
LoadImageFromFile proc pImgName:dword, pImage:dword
local wcBuffer[256]:byte
invoke MultiByteToWideChar, CP_ACP, 0, pImgName, -1, addr wcBuffer, 256
invoke GdipLoadImageFromFile, addr wcBuffer, pImage
mov eax, pImage
ret
LoadImageFromFile endp
DrawImage proc hDC:dword, Image:dword, x:dword, y:dword
local wid:dword, hgt:dword, pGraphics:dword
invoke GdipCreateFromHDC, hDC, addr pGraphics
invoke GdipGetImageWidth, Image, addr wid
invoke GdipGetImageHeight, Image, addr hgt
invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
invoke GdipDeleteGraphics, pGraphics
ret
DrawImage endp
end
Results: (https://i.postimg.cc/3xW6nF8D/untitled.png)
Quote from: jj2007 on April 10, 2025, 08:03:55 AMDo you have an example of a transparent png file?
right click this image and Save As... red.png
I found an example, and it works :thumbsup:
GuiParas equ "GdipCreateBitmapFromStream demo", w300, h500, b LiteBlueGreen, icon Printer ; width+height, background colour
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
GuiImage 101, fit
GuiImage 100, fit ; 100 is RCDATA ID
GuiEnd
(https://i.postimg.cc/ykBVbNrT/Transparent-Png-Demo.jpg) (https://postimg.cc/ykBVbNrT)
Quote from: jj2007 on April 10, 2025, 08:22:00 AMI found an example, and it works :thumbsup:
I downloaded it, the .asm file is empty
Better? The png file is too big for the forum, just edit the *.rc file to add your images. The order may matter.
Quote from: jj2007 on April 10, 2025, 08:28:04 AMBetter?
GuiParas equ "GdipCreateBitmapFromStream demo", w300, h500, b LiteBlueGreen, icon Printer ; width+height, background colour
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
GuiImage 101, fit
GuiImage 100, fit ; 100 is RCDATA ID
GuiEnd
Funny guy. Where is the assembly code? You know that I don't use MasmBasic.
A nice simple example with only the needed code will do, like the one I posted in #8 - nice and simple. :tongue:
without the extra baggage:
; gdiplus.GdiplusShutdown
; gdiplus.GdiplusStartup
; gdiplus.GdipCreatePen1
; gdiplus.GdipDeleteGraphics
; gdiplus.GdipCreateBitmapFromHBITMAP
; gdiplus.GdipSaveImageToFile
; gdiplus.GdipDisposeImage
; gdiplus.GdipCreateFromHDC
; gdiplus.GdipSetSmoothingMode
; gdiplus.GdipCreateBitmapFromStream
; gdiplus.GdipCreateHBITMAPFromBitmap
; gdiplus.GdipImageGetFrameDimensionsList
; gdiplus.GdipImageGetFrameCount
; gdiplus.GdipImageSelectActiveFrame
; gdiplus.GdipGetPropertyItemSize
; gdiplus.GdipGetPropertyItem
; gdiplus.GdipGetImageWidth
; gdiplus.GdipGetImageHeight
; gdiplus.GdipDrawImageRectI
; gdiplus.GdipDrawImageRectRectI
; gdiplus.GdipImageRotateFlip
; gdiplus.GdipGetImageEncodersSize
; gdiplus.GdipGetImageEncoders
; gdiplus.GdipCreatePath
; gdiplus.GdipAddPathArcI
; gdiplus.GdipClosePathFigure
; gdiplus.GdipCreateSolidFill
; gdiplus.GdipFillPath
; gdiplus.GdipDeleteBrush
; gdiplus.GdipDrawPath
; gdiplus.GdipDeletePath
; gdiplus.GdipSetPenLineJoin
; gdiplus.GdipFillRectangleI
; gdiplus.GdipFillEllipseI
; gdiplus.GdipDrawLinesI
; gdiplus.GdipDeletePen
; gdiplus.GdipGetPathWorldBounds
; gdiplus.GdipWarpPath
; gdiplus.GdipResetPath
; gdiplus.GdipAddPathRectangles
; gdiplus.GdipAddPathEllipse
; gdiplus.GdipAddPathArc
; gdiplus.GdipAddPathPie
; gdiplus.GdipAddPathBeziers
; gdiplus.GdipAddPathCurve
; gdiplus.GdipAddPathCurve2
; gdiplus.GdipAddPathClosedCurve2
; gdiplus.GdipAddPathClosedCurve
; gdiplus.GdipAddPathLine2
:rolleyes:
Dunno if this'll help, but I checked my picture-viewer program, which uses GDI+ to display images, and it certainly does work with .png files.
Here's most of the WM_PAINT handler:
INVOKE BeginPaint, hWin, ADDR ps
MOV hDC, EAX
; Get graphics "object" from DC handle:
INVOKE GdipCreateFromHDC, hDC, ADDR gdiHgraphics
; Display image at (X,Y) with dimensions (W,H):
; Many thanks to "mabdelouahab" from the MASM32 forum for this.
INVOKE GdipDrawImageRectI, gdiHgraphics, GdiHbitmap,
ImgStruct.imgX, ImgStruct.imgY, ImgStruct.imgWscaled, ImgStruct.imgHscaled
INVOKE EndPaint, hWin, ADDR ps
and I'm using this to load the file, after converting the filename to Unicode:
INVOKE GdipLoadImageFromFile, ADDR unicodeName, OFFSET GdiHbitmap
So it is possible.
Of course, I'm not loading resources: I'm loading files from disk.
But if that can be done, there must be a way to load a resource as well.
Quote from: NoCforMe on April 10, 2025, 08:58:39 AMOf course, I'm not loading resources: I'm loading files from disk.
But if that can be done, there must be a way to load a resource as well.
What you are doing, is basically what I am doing. Did you not look at my code?
We are both loading a file. I want to use .png resources.
I am certain there is a way to do it but those that do know only give cryptic clues, and not offer any actual working assembly code examples of loading a .png resource with transparency using gdiplus and displaying it with DrawImageRectI - in a simple example without all of the unnecessary baggage.
Also the Microsoft documentation leaves a lot to be desired.
I can live with my methods for now, they work. But somewhere down the line I would like to publicly offer some executables without needing to also have a bunch of .png files with it. :biggrin:
Later tonight I'll ask ChatGPT or Google Gemini for C code to do what I want, then port it to assembly (if it works). :tongue:
Or even ask for Masm compatible assembly code... ( they kinda suck with assembly though) :biggrin:
Yes, that's cheating but it might be the fastest route to success.
Quote from: zedd151 on April 10, 2025, 09:02:09 AMLater tonight I'll ask ChatGPT or Google Gemini for C code to do what I want, then port it to assembly (if it works). :tongue:
I hate to give you this advice, I really do, but two things:
1. Forget "AI". You don't want to deal with their hallucinations.
2. Have you tried searching online for (non-"AI") solutions?
I did a search for "loading .png resource" and got several hits, a few of which look promising (like this one (https://forum.codejock.com/load-pngs-from-resource-dll_topic10281.html)). Of course, a lot of them will deal with weird platforms like Wix, but some look usable in a Win32 app.
Taking another look at Vortex's example:
invoke CreateStreamOnHGlobal,hMem,TRUE,ADDR pStream
invoke GdipCreateBitmapFromStream,pStream,ADDR BmpImage
invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0
If I omit "invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0"
"BmpImage" might just be the handle I need to accomplish my goals. I believe that gdi32 needs a HBITMAP handle, where gdiplus needs an image handle. (I think I'm onto something) :biggrin:
I am away from my computer at the moment. When I get back inside I will test my theory. :biggrin:
Quotea few of which look promising (like this one (https://forum.codejock.com/load-pngs-from-resource-dll_topic10281.html))
It doesnt mention
gdiplus anywhere. So probably loading .pngs
without transparency (alpha channel). No good for my purposes.
Quote from: zedd151 on April 10, 2025, 09:29:21 AMTaking another look at Vortex's example:
invoke CreateStreamOnHGlobal,hMem,TRUE,ADDR pStream
invoke GdipCreateBitmapFromStream,pStream,ADDR BmpImage
invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0
If I omit "invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0"
"BmpImage" might just be the handle I need to accomplish my goals. I believe that gdi32 needs a HBITMAP handle, where gdiplus needs an image handle.
Success!!!
(https://i.postimg.cc/2yr9vP8n/untitled.png)
Thank you Vortex! You had the code *almost* right for working with gdiplus, I made a few adjustments. I will clean up the code and post it!
:biggrin: I got it! A big thanks to Vortex. :thumbsup: Your code was *almost* what I needed. I had tweaked it to be more suitable for using with .png images with transparency.
(https://i.postimg.cc/XJPj2cmH/untitled.png)
; Original code by Vortex
; Modified for use with png with alpha channel and gdiplus drawing by zedd151
include LoadImageFromRsrc.inc
.data
ClassName db 'ImagrClass', 0
AppName db 'LoadPNGfromRsrc', 0
.data?
hInstance dd ?
hBitmap dd ?
pNumbOfBytesRead dd ?
StartupInfo GdiplusStartupInput <?>
GdiplusStatus dd ?
token dd ?
BmpImage dd ?
hMem dd ?
pMem dd ?
pStream dd ?
fsize dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke GetCommandLine
invoke WinMain, hInstance, NULL, eax, SW_SHOWDEFAULT
invoke ExitProcess, eax
DrawImage proc hDC:dword, Image:dword, x:dword, y:dword
local wid:dword, hgt:dword, pGraphics:dword
invoke GdipCreateFromHDC, hDC, addr pGraphics
invoke GdipGetImageWidth, Image, addr wid
invoke GdipGetImageHeight, Image, addr hgt
invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
invoke GdipDeleteGraphics, pGraphics
ret
DrawImage endp
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:dword
local wc:WNDCLASSEX
local msg:MSG
local hwnd:HWND
xor eax, eax
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, offset WndProc
mov wc.cbClsExtra, eax
mov wc.cbWndExtra, eax
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+2
mov wc.lpszMenuName, eax
mov wc.lpszClassName, offset ClassName
invoke LoadIcon, eax, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
xor eax, eax
invoke CreateWindowEx, eax, addr ClassName, addr AppName, \
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, \
CW_USEDEFAULT, 200, 200, eax, eax, \
hInst, eax
mov hwnd, eax
invoke ShowWindow, hwnd, SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .if (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local ps:PAINTSTRUCT
local hdc:HDC
local hMemDC:HDC
local bm:BITMAP
local hRes:dword
local RsrcSize:dword
local pImage:dword
.IF uMsg==WM_CREATE
invoke FindResource, 0, 100, RT_RCDATA
mov hRes, eax
invoke SizeofResource, hInstance, eax
mov RsrcSize, eax
invoke LoadResource, 0, hRes
mov pImage, eax
invoke GlobalAlloc, GPTR, RsrcSize
mov hMem, eax
invoke MemCopy, pImage, eax, RsrcSize
mov eax, offset StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax], 1
mov GdiplusStatus, eax
invoke GdiplusStartup, addr token, addr StartupInfo, 0
invoke CreateStreamOnHGlobal, hMem, TRUE, addr pStream
invoke GdipCreateBitmapFromStream, pStream, addr BmpImage
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint, hWnd, addr ps
mov hdc, eax
; ====================================
invoke DrawImage, hdc, BmpImage, 50, 35 ; draws png with transparency
; ====================================
invoke EndPaint, hWnd, addr ps
.ELSEIF uMsg==WM_DESTROY
invoke DeleteObject, hBitmap
invoke GdipDisposeImage, BmpImage
invoke GdiplusShutdown, GdiplusStatus
coinvk pStream, IStream, Release
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
The code here is simplified a bit, down to the bare necessities. There may be leftover unused variables.
It may be exapnded upon, as needed.
Thanks to all that provided suggestions, no matter how cryptic. :thumbsup:
So what was it specifically you had to change to make it work with loading the image as a resource?
Enquiring minds want to know.
Quote from: NoCforMe on April 10, 2025, 11:08:34 AMSo what was it specifically you had to change to make it work with loading the image as a resource?
Enquiring minds want to know.
The image as resource loading was fine as is.
I omitted the "invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0" call and used BmpImage as the image handle. Voila. The HBITMAP was only needed for gdi32 drawing with BitBlt, et.al.
Of course I had to also add the code to draw with gdiplus as well. I removed all of the gdi32 code except for BeginPaint and EndPaint.
A double buffer probably a good idea if many images are to be drawn, or if drawn frequently. That's how I would do it. But since the code I posted is only a 'proof of concept' example, its fine as is.
Download the example. Test it, tweak it. Etc.
Now how easy it will be to use the code for multiple images, it remains to be seen. To be updated soon in a thread near you! :biggrin:
_GdipCreateImageFromResource proc ResID:QWORD
LOCAL @hImage:QWORD
LOCAL hRes:QWORD
LOCAL pRes:QWORD
LOCAL lRes:QWORD
LOCAL @IStream:QWORD
invoke FindResource,0,ResID,RT_RCDATA
mov hRes, rax
invoke LoadResource,0,hRes
mov pRes, rax ; load the resource
invoke SizeofResource,0,hRes ; get its size
mov lRes, rax
invoke pSHCreateMemStream,pRes,lRes
mov @IStream,rax
invoke FreeResource,hRes
invoke GdipLoadImageFromStream,@IStream,addr @hImage
mov rax,@IStream
mov rcx,rax
mov rax,[rax]
call QWORD PTR [rax + 16] ; @IStream params mov into rcx,invoke release,@IStream
mov rax,@hImage
ret
_GdipCreateImageFromResource endp
invoke LoadLibrary,CStr("Shlwapi.dll")
.if rax
mov hShlwapi_dll,rax
invoke GetProcAddress,hShlwapi_dll,CStr("SHCreateMemStream")
.if rax
mov pSHCreateMemStream,rax
.else
invoke MessageBox,NULL,CStr("Get SHCreateMemStream Failed"),CStr("GetProcAddress"),MB_OK
.endif
.else
invoke MessageBox,NULL,CStr("Shlwapi.dll load Failed"),CStr("LoadLibrary"),MB_OK
.endif
Thanks for your contribution six_L, I'll test it in a little while. :thumbsup:
But wait. That is 64 bit code. It will take me a little time to port it to 32 bit especially the "call qword ptr [rax+16]" to get it right, due to the differences of 64 bit and 32 bit.
Unless you have a 32 bit version of it. I don't have time for that right now. But thanks anyway.
The code itself looks more compact than the code I have been experimenting with here today though.
DELETED
Can somebody tell me what are streams and why would I want to use them to deal with bitmap images?
Quote from: NoCforMe on April 10, 2025, 01:06:27 PMCan somebody tell me what are streams and why would I want to use them to deal with bitmap images?
I haven't the foggiest clue either. :joking: I only know that it somehow works. It was much easier to load an image from a file than to load it from resources. That's for sure. :biggrin: I might follow hutchs lead, but expand on his method to be able to use gdiplus drawing. Just for simplicities sake. That gives me an idea...
DELETED
Quote from: TimoVJL on April 10, 2025, 01:38:48 PMQuote from: NoCforMe on April 10, 2025, 01:06:27 PMCan somebody tell me what are streams and why would I want to use them to deal with bitmap images?
streams are used in C++ and GDI+ is written in C++ with objects.
WTF???? That doesn't tell me anything!
What are they? What do they do?Sheesh.
QuoteIt will take me a little time to port it to 32 bit especially the "call qword ptr [rax+16]" to get it right.
i didn't test the code.
_GdipCreateImageFromResource proc ResID:DWORD
LOCAL @hImage:DWORD
LOCAL hRes:DWORD
LOCAL pRes:DWORD
LOCAL lRes:DWORD
LOCAL @IStream:DWORD
invoke FindResource,0,ResID,RT_RCDATA
mov hRes, eax
invoke LoadResource,0,hRes
mov pRes, eax ; load the resource
invoke SizeofResource,0,hRes ; get its size
mov lRes, eax
invoke pSHCreateMemStream,pRes,lRes
mov @IStream,eax
invoke FreeResource,hRes
invoke GdipLoadImageFromStream,@IStream,addr @hImage
mov eax,@IStream
push eax ;
mov eax,[eax]
call DWORD PTR [eax + 8] ; @IStream params push stack,invoke release,@IStream
sub esp,4
mov eax,@hImage
ret
_GdipCreateImageFromResource endp
Quote from: six_L on April 10, 2025, 02:53:33 PMi didn't test the code.
Thanks, six_L :thumbsup:
You didn't really have to though, I would have gotten around to it sooner or later.
I'll test this tomorrow. Its just past midnight here, I have to go to sleep now.
Quote from: zedd151 on April 10, 2025, 08:33:51 AMA nice simple example with only the needed code will do
I know, but that would be hours of work - and indeed work that I've done already and don't want to repeat. I just wanted to demonstrate that transparency can be done with streaming from resources, using GdipCreateBitmapFromStream
@NoCforMe: What is streaming? Simply doing a job from a
buffer, instead of reading from an open file. So instead of passing a file handle., you typically supply the address of a callback proc of yours to the streaming function, and when that API function calls you there, you hand over a bunch of bytes from a buffer. One often used example is the EM_STREAMIN message for RichEdit controls.
Quote from: jj2007 on April 10, 2025, 10:45:34 PM@NoCforMe: What is streaming? Simply doing a job from a buffer, instead of reading from an open file.
Thats Microsoft for ya. Why not call it what it is, Microsoft?
@jj: hours of work? For a simple no nonsense example? Surely you jest. Have you forgotten how to write masm compatible (meaning ml.exe) assembly without MasmBasic? or uasm for that matter?
:joking:
It would take
me hours, or even days (weeks, months, years - for some projects :tongue: ), but that is because gdiplus (and some other apis) and some of its functions and methods are very new to me. And the MS docs are no great help, in many cases. It takes a lot of trial and error on my part. Then posting my test code here for verification of legitimacy, usefulness, error checking and even debugging. Now all of that takes time. And cooperation and assistance of other members here obviously, which could take even more time.
Anyway after sleeping on it, I have come to the conclusion that loading images
from a file is a
hell of a lot easier and simpler than loading image
resources using gdiplus. I might just leave Connect 4 to use that method.
.... thinking further....
And in case the user deletes the image files (I could have them embedded in the executable) and write them back to file if they are 'missing'. A kind of backup plan of sorts.
The user could still use their own images to replace mine, but would have to keep the same names for them; if those are inadvertently deleted, they would be replaced with my original images automatically. I kinda like that idea.
.... more thinking ....
In fact, I would not have to supply the images files separately then, just let the program 'install' them (write them to file) upon first use.... (maybe this is not Campus material here anymore? Whaddya think?) I will be testing this method in the coming days... :biggrin:
... still more thinking ...
Not sure what AV software (or other 'security' software or settings) would think about it though. That could be an issue for some users, and scare them away from my wares. :eusa_boohoo:
DELETED
Quote from: TimoVJL on April 11, 2025, 02:59:26 AMAt campus people should be calm, not a room for Retardos :biggrin:
??? Who ???
Lemme guess... :rolleyes:
Oh, I know. :biggrin:
Who else would it be... :tongue:
Looking at a couple of files from masm32 library
bitmapfrommemory.asm
BitmapFromMemory PROC pMemory:DWORD, dwFileSize:DWORD
LOCAL hResource:DWORD, pGlobal:DWORD, pStream:DWORD
LOCAL hImage:DWORD, pPicture:DWORD, hBitmap:DWORD
;invoke CoInitialize, NULL
mov pStream, NULL
mov pPicture, NULL ; NULL pointers for later use
invoke CoTaskMemAlloc, dwFileSize ; copy picture into task memory
.if !eax
; oops! we didn't get the memory
; the last error code was set for us, and EAX is zero, so just return
ret
.endif
mov pGlobal, eax
invoke _MemCopy, pMemory, pGlobal, dwFileSize
; create a stream for the picture object's creator
invoke CreateStreamOnHGlobal, pGlobal, TRUE, ADDR pStream
or eax,eax
jz @f
invoke CoTaskMemFree,pGlobal
xor eax,eax
ret
@@:
invoke OleLoadPicture, pStream, NULL, TRUE, ADDR IID_IPicture, ADDR pPicture
or eax,eax
jz @f
mov eax, pStream
call release_pStream
xor eax,eax
ret
@@:
; now we are ready to get the hBipmap, we farm this out for reuseability
invoke BitmapFromPicture, pPicture
mov hBitmap, eax
mov eax, pStream
call release_pStream
mov eax, pPicture
call release_pPicture
mov eax, hBitmap ; hBitmap is our return value, stuff it
ret ; we're all done
BitmapFromMemory ENDP
bitmapfromresource.asm
BitmapFromResource PROC hModule: dword, ResNumber:DWORD
LOCAL hResource:DWORD, dwFileSize:DWORD, hImage:DWORD
; get a resource handle (address) and resource length from the executable
invoke FindResource, hModule, ResNumber, ADDR szImage
or eax,eax
jnz @f
invoke SetLastError, ERROR_FILE_NOT_FOUND
xor eax,eax
ret
@@:
mov hResource, eax
invoke LoadResource, hModule, eax
invoke LockResource, eax
mov hImage, eax
invoke SizeofResource, hModule, hResource
mov dwFileSize, eax
.IF dwFileSize ; we use the resource size to determine if we got a
; legit image file to open
invoke BitmapFromMemory, hImage, dwFileSize
.ELSE
invoke SetLastError, ERROR_FILE_NOT_FOUND
xor eax,eax
.ENDIF
; everything's been done for us now, just return
ret ; we're all done
BitmapFromResource ENDP
bitmapfrompicture.asm
BitmapFromPicture PROC pPicture:DWORD
LOCAL tempDC:DWORD, tempBitmap:DWORD, OldBitmap:DWORD
LOCAL dwWidth:DWORD, dwHeight:DWORD, compDC:DWORD
LOCAL hmWidth:DWORD, hmHeight:DWORD, neghmHeight:DWORD
; check we have an object
.IF pPicture == 0
; whoops, no object passed in
invoke SetLastError, ERROR_INVALID_PARAMETER
xor eax,eax
ret
.ENDIF
; get a DC to work with
invoke GetDC, NULL ; screen DC
mov compDC, eax
invoke CreateCompatibleDC, compDC
.IF !eax
; whoops, didn't get a DC
; but at least we had SetLastError called for us
invoke ReleaseDC,NULL,compDC
xor eax,eax
ret
.ENDIF
mov tempDC, eax
; read out the width and height of the IPicture object
; (IPicture)pPicture::get_Width(*hmWidth)
lea eax, hmWidth
push eax
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.get_Width
; (IPicture)pPicture::get_Height(*hmHeight)
lea eax, hmHeight
push eax
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.get_Height
; convert himetric to pixels
invoke GetDeviceCaps, compDC, LOGPIXELSX
invoke MulDiv, hmWidth, eax, HIMETRIC_INCH
mov dwWidth, eax
invoke GetDeviceCaps, compDC, LOGPIXELSY
invoke MulDiv, hmHeight, eax, HIMETRIC_INCH
mov dwHeight, eax
xor eax, eax
sub eax, hmHeight
mov neghmHeight, eax
invoke CreateCompatibleBitmap, compDC, dwWidth, dwHeight
.IF !eax
; whoops, didn't get a bitmap
; but at least we had SetLastError called for us\
; clean up the DC
invoke ReleaseDC,NULL,compDC
invoke DeleteDC, tempDC
xor eax,eax
ret
.ENDIF
mov tempBitmap, eax
invoke SelectObject, tempDC, tempBitmap
.IF !eax
; whoops, didn't select our bitmap
; but at least we had SetLastError called for us
invoke ReleaseDC,NULL,compDC
invoke DeleteDC, tempDC
invoke DeleteObject, tempBitmap
xor eax,eax
ret
.ENDIF
mov OldBitmap, eax
; ok, now we have our bitmap mounted onto our temporary DC, let's blit to it
; (IPicture)pPicture::Render(hdc, x, y, cx, cy, \
; xpos_himetric, ypos_himetric, \
; xsize_himetric, ysize_himetric, *rectBounds)
push NULL ; *rectBounds
push neghmHeight
push hmWidth
push hmHeight
push 0
push dwHeight
push dwWidth
push 0
push 0
push tempDC
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.Render
test eax, eax
.IF SIGN?
; the call failed!
push eax
; do some clean up first
invoke ReleaseDC,NULL,compDC
invoke DeleteDC, tempDC
invoke DeleteObject, tempBitmap
pop eax
; need to parse out the return fail value
.IF eax == E_FAIL
.ELSEIF eax == E_INVALIDARG
.ELSEIF eax == E_OUTOFMEMORY
.ELSEIF eax == E_POINTER
.ELSE
.ENDIF
invoke SetLastError, eax
xor eax,eax
ret
.ENDIF
; we now have the bitmap blitted, let's get it off the dc and clean up.
; we're not going to check for errors, cause we did our importaint thing
; and if these fail now, other things will fall apart anyway
invoke ReleaseDC,NULL,compDC
invoke SelectObject, tempDC, OldBitmap
invoke DeleteDC, tempDC
mov eax, tempBitmap ; the bitmap handle is the return value
ret ; we're all done
BitmapFromPicture ENDP
I will experiment with these next... :biggrin: ... leaving out anything that converts the image to HBITMAP
Will OleLoadPicture load a png as 32 bit RGBA, or 24 bit RGB??? Ima gonna find out. :cool:
I'll read up on that function in the meantime... :biggrin:
If nothing else, it will add a couple of tools into my image toolbox.
Hi Zedd,
You are welcome. I am glad if I can be helpful. You can also try my image library based on OLE technology :
https://masm32.com/board/index.php?msg=82060
Quote from: Vortex on April 11, 2025, 05:04:19 AMHi Zedd,
You are welcome. I am glad if I can be helpful. You can also try my image library based on OLE technology :
https://masm32.com/board/index.php?msg=82060
Thanks, I'll take a look at it. :thumbsup:
I have obviously been working on my game code lately. One can never have too many options for opening and displaying images.
Quote from: jj2007 on April 10, 2025, 10:45:34 PM@NoCforMe: What is streaming? Simply doing a job from a buffer, instead of reading from an open file. So instead of passing a file handle., you typically supply the address of a callback proc of yours to the streaming function, and when that API function calls you there, you hand over a bunch of bytes from a buffer. One often used example is the EM_STREAMIN message for RichEdit controls.
First of all, thank you for that straightforward explanation. Much appreciated.
A little further probing, if you don't mind:
With regard to bitmap images, does that mean that you fill your buffer with the bitmap data first? And would that be the raw data (like the file bytes), or is it another type of data, acquired from some Win32 function?
Still not sure what the advantage is here, but this sounds very similar to the way I "load" bitmaps by putting the raw .BMP data in my .data section, then using my
GetBMPhandle() code to "open" the bitmap and return a usable handle. Avoids the use of a resource file (I call it using a "fake resource file", which is just an include file full of
DB statements).
Hi Zedd,
Using SHCreateMemStream is simplifying the code. Plus, you don't need resources as you can embed any binary data into your executable with Hutch's file data assembler fda.exe. This tool converts any binary file to a MS COFF object module :
\masm32\fda logo.png logo.obj pPNG
pPNG is the label identifying the binary data.
fda creates an include file named logo.inc to define the size of the embedded data :
; -----------------------------------------------------
; Include the contents of this file in your source file
; to access the data as an OFFSET and use the equate as
; the byte count for the file data in the object module
; -----------------------------------------------------
EXTERNDEF pPNG:DWORD
ln_pPNG equ <4516>
The modified code :
include logo.inc
ApiFunc TYPEDEF PROTO :DWORD,:DWORD
.
.
.IF uMsg==WM_CREATE
.
.
invoke LoadLibrary,ADDR shlwapi
mov hDll,eax
invoke GetProcAddress,eax,ADDR func
ASSUME eax:PTR ApiFunc
invoke eax,ADDR pPNG,ln_pPNG ; ln_pPNG is defined in logo.inc
ASSUME eax:NOTHING ; created by file data assembler fda
mov pStream,eax
invoke GdipCreateBitmapFromStream,pStream,ADDR PNGimage
invoke GdipCreateHBITMAPFromBitmap,PNGimage,ADDR hBitmap,0
invoke GdipDisposeImage,PNGimage
coinvk pStream,IStream,Release
invoke FreeLibrary,hDll
Thanks Vortex, I'll look at that shortly. I'm away from the computer at the moment. :smiley:
Later:
Okay, I back inside briefly.
That looks very interesting. :thumbsup:
I will have a better look at it after I finish up with some yard work...
More Later:
Kind of neat the way that you create the object file just before assembling&linking... I was looking for a call in the source code. :joking:
Quote from: zedd151 on April 11, 2025, 06:57:29 AMThanks Vortex, I'll look at that shortly. I'm away from the computer at the moment. :smiley:
Zedd, I think we need to get you fitted with some kind of tracking device, maybe an ankle monitor. That way we can keep track of where you are at any given time.
Quote from: NoCforMe on April 11, 2025, 07:10:03 AMThat way we can keep track of where you are at any given time.
I'll never tell. :cool:
Oh wait. I did already. :toothy:
Fortunately, there wasn't a lot that needed to be done in the front yard. Just a bit of weed whacking along the western fence line. All done for today. :biggrin: anything else can wait for tomorrow.
Now I'm back on the back porch enjoying the weather, for those keeping track of my whereabouts. :tongue:
After trying several different methods of loading images other than by loading the image file, I have come to the conclusion that loading images from file is the easiest way to do it.
I will no longer seek to load image resources or any other variation at this time. I have a few projects that I would like to finish very soon, so to expedite that process, I will choose the path of least resistance and load the images from file.
Thank you to everyone that has offered the various other methods, or solutions.
I will re-explore those options at some other time in the future. :thumbsup:
DELETED
Quote from: Vortex on April 11, 2025, 06:33:35 AMUsing SHCreateMemStream is simplifying the code. Plus, you don't need resources as you can embed any binary data into your executable with Hutch's file data assembler fda.exe. This tool converts any binary file to a MS COFF object module
Hi Vortex. I did indeed test your program, and it works very well. :thumbsup: :thumbsup: (two thumbs up)
I made some adjustments to the source as usual to allow drawing .png images with true transparency.
; original code by Vortex
; modified by zedd151
include LoadPNGfromMem.inc
include logo.inc
ApiFunc TYPEDEF proto :dword, :dword
DrawImage proto :dword, :dword, :dword, :dword
.data
ClassName db 'ImageClass', 0
AppName db 'Display PNG from memory', 0
shlwapi db 'shlwapi.dll', 0
func db 'SHCreateMemStream', 0
.data?
hInstance dd ?
hBitmap dd ?
GdiPStatus dd ?
StartupInfo GdiplusStartupInput <?>
token dd ?
hDll dd ?
PNGimage dd ?
pStream dd ?
.code
start:
invoke GetModuleHandle, 0
mov hInstance, eax
invoke GetCommandLine
invoke WinMain, hInstance, 0, eax, SW_SHOWDEFAULT
invoke ExitProcess, eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:dword
local wc:WNDCLASSEX
local msg:MSG
local hwnd:HWND
xor eax, eax
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, offset WndProc
mov wc.cbClsExtra, eax
mov wc.cbWndExtra, eax
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName, 0
mov wc.lpszClassName, offset ClassName
invoke LoadIcon, eax, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, 0, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
xor eax, eax
invoke CreateWindowEx, eax, addr ClassName, addr AppName, \
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, \
CW_USEDEFAULT, 290, 90, eax, eax, \
hInst, eax
mov hwnd, eax
invoke ShowWindow, hwnd, SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.while TRUE
invoke GetMessage, addr msg, 0, 0, 0
.break .if (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.endw
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local ps:PAINTSTRUCT
local rct:RECT
local hdc:HDC
local hMemDC:HDC
local hBackBrush:HBRUSH
local hMemBmp:HBITMAP
local hMemBmp_old:HBITMAP
local bm:BITMAP
.if uMsg==WM_CREATE
mov eax, offset StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax], 1
mov GdiPStatus, eax
invoke GdiplusStartup, addr token, addr StartupInfo, 0
invoke LoadLibrary, addr shlwapi
mov hDll, eax
invoke GetProcAddress, eax, addr func
assume eax:ptr ApiFunc
invoke eax, addr pPNG, ln_pPNG ; ln_pPNG is defined in logo.inc
assume eax:nothing ; created by file data assembler fda
mov pStream, eax
invoke GdipCreateBitmapFromStream, pStream, addr PNGimage
.elseif uMsg == WM_ERASEBKGND
mov eax, 1
ret
.elseif uMsg==WM_PAINT
invoke BeginPaint, hWnd, addr ps
mov hdc, eax
invoke GetClientRect, hWnd, addr rct
invoke CreateCompatibleDC, hdc
mov hMemDC, eax
invoke CreateCompatibleBitmap, hdc, rct.right, rct.bottom
mov hMemBmp, eax
invoke SelectObject, hMemDC, hMemBmp
mov hMemBmp_old, eax
invoke CreateSolidBrush, 00FFDFBFh ;; a nice light blue colr
mov hBackBrush, eax ;; save the brush handle
invoke FillRect, hMemDC, addr rct, hBackBrush ; fill rectangle in memory DC
invoke DeleteObject, hBackBrush
invoke DrawImage, hMemDC, PNGimage, 20, 12
invoke BitBlt, hdc, 0, 0, rct.right, rct.bottom, hMemDC, 0, 0, SRCCOPY
invoke SelectObject, hMemDC, hMemBmp_old
invoke DeleteObject, hMemBmp
invoke DeleteDC, hMemDC
invoke EndPaint, hWnd, addr ps
.elseif uMsg==WM_DESTROY
invoke GdipDisposeImage, PNGimage
coinvk pStream, IStream, Release
invoke FreeLibrary, hDll
invoke DeleteObject, hBitmap
invoke GdiplusShutdown, GdiPStatus
invoke PostQuitMessage, 0
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax, eax
ret
WndProc endp
DrawImage proc hDC:dword, Image:dword, x:dword, y:dword
local wid:dword, hgt:dword, pGraphics:dword
invoke GdipCreateFromHDC, hDC, addr pGraphics
invoke GdipGetImageWidth, Image, addr wid
invoke GdipGetImageHeight, Image, addr hgt
invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
invoke GdipDeleteGraphics, pGraphics
ret
DrawImage endp
end start
I have also modified your .png image to have the background as tranparent 32 bit RGBA, replacing the white background color with transparency for demonstration here. The light blue color is the window background color, not part of the image (for clarification).
Results upon running the modified program:
(https://i.postimg.cc/521CdPhG/untitled.png)
Original image 24 bit RGB, has actual white background 00FFFFFFh:
(https://i.postimg.cc/7LtGzq58/logo.png)
It is definitely a viable contender to replace loading .png images at runtime by the end user, even though that seems to be the easiest method. Never mind what I said in my last post. I confess that I hadn't looked at your program in any great detail, before posting that. I had briefly forgotten about it, until looking through this thread... I have occasional memory leaks, I forget things often.
I will be integrating your methods into my Connect4 project! :smiley:
I'll deposit a substantial fee to your top secret account in the Cayman Islands. :biggrin:
Quote from: TimoVJL on April 11, 2025, 10:10:09 PMThis was a good topic for GDI+
Yes. More to come... there are a lot of things I want to learn how to do with gdiplus.
QuoteThis was a good topic for GDI+
This is what happens if you treat unknowns as things to attack and learn from rather than things to be scared of
Quote from: TimoVJL on April 11, 2025, 10:10:09 PMIf users abandon Windows XP, SHCreateMemStream will be best choice for streams for GDI+.
If it's loaded it with ordinal 12 rather than by name, it goes back to Win2000.
(https://img001.prntscr.com/file/img001/eEXWdGEwSru1Nl7W5KslcA.png) (https://prnt.sc/bNrTI3xbU_Wk)
It did not occur to me immediately. That is a list for SHCreateMemStream from each version and service pack of Windows, maybe not all of them though.
I must be having a senior moment. :rolleyes:
Originally this post contained something very different.
Hi adeyblue
From were did you get that information?
It can be usefull for a lot of other things!
Regards, Biterider
Quote from: Biterider on April 12, 2025, 03:35:41 PMFrom were did you get that information?
QuoteWhat are the ordinal numbers for SHCreateMemStream in various Windows versions?
ChatGPT said:
The SHCreateMemStream function in Windows is a utility function provided by Shell32.dll (or sometimes Shlwapi.dll) that creates a memory-based IStream object. The "ordinal number" of a function refers to its position in the export table of a DLL, and some functions (especially undocumented or internal ones) can be exported by ordinal only (i.e., without a name).
Here's a breakdown of SHCreateMemStream ordinal numbers across different Windows versions:
🧾 Known Ordinal Numbers for SHCreateMemStream
DLL Windows Version Ordinal Notes
shlwapi.dll Windows XP–7 12 Exported by ordinal only (not by name)
shlwapi.dll Windows 8/8.1 Deprecated Function moved or removed
shell32.dll Windows 10+ Not by ordinal Exported by name instead
DELETED
M$ Learn (https://learn.microsoft.com/en-gb/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream?redirectedfrom=MSDN): Prior to Windows Vista, this function was not included in the public Shlwapi.h file, nor was it exported by name from Shlwapi.dll. To use it on earlier systems, you must call it directly from the Shlwapi.dll file as ordinal 12.
Quote from: jj2007 on April 14, 2025, 01:05:37 AMM$ Learn (https://learn.microsoft.com/en-gb/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream?redirectedfrom=MSDN): Prior to Windows Vista, this function was not included in the public Shlwapi.h file, nor was it exported by name from Shlwapi.dll. To use it on earlier systems, you must call it directly from the Shlwapi.dll file as ordinal 12.
In other words, if you are running any OS prior to Windows Vista, maybe an OS upgrade in long overdue? Just kidding, kind of. :biggrin:
Of course a lot of users use a virtual machine for running older OS's, sometimes used for compatibility testing of programs for older Windows versions.
And a few diehards that run antique hardware that can only run older OS versions.
The export SHCreateMemStream function located at shlwapi - which is also called "SHCreateMemStreamStub" (originallly from it´s pdb) is a export helper (uses delayload library).
In Windows10 the true function SHCreateMemStream is located inside the dll shcore.
Don´t know about other windows versions, but if in other versions of Windows, the shcore.dll also contains the SHCreateMemStream function, better call them directly. For example:
call 'shcore.SHCreateMemStream' 0, 100
If on windowsXP-7, the export is also a delayload import (I don´t doubt it can be), and if on those older systems the shcore also contains the SHCreateMemStream, then, there´s no worries. Just load this function from shcore rather then shlwapi
Note: Internally, the api SHCreateMemStream is a Com object, that uses classes such as the ones from: https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/wrl/implements.h (https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/wrl/implements.h)
https://github.com/microsoft/wil/blob/master/tests/workarounds/wrl/wrl/implements.h (https://github.com/microsoft/wil/blob/master/tests/workarounds/wrl/wrl/implements.h)
such as: CanCastT and friends
DELETED
DELETED
A quick mention of issues with various API's and their functions is common during discussion of any topic.
But continuing discussion like this may be more suited in its own topic? (Especially since the discussion does not return to the main intent of the topic.)
Just asking btw, not demanding... :smiley:
I do not want any posts removed from here, but am asking politely that maybe a new topic might be more appropriate if this discussion about "shlwapi" and company is going to continue.