Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Crazy Scripting : Resource-Only DLL for Dummies - 36L / v0.7


  • Please log in to reply
71 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
[color=#AAAAAA]/*[/color]
[color=darkred]              +-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+
              |R|e|s|o|u|r|c|e|-|O|n|l|y| |D|L|L| |f|o|r| |D|u|m|m|i|e|s|!|
              +-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+[/color]

 [color=#225599]- Humble 36L wrapper to create and use DLL resources in AutoHotkey Scripting Language -[/color]

                  [color=black]By SKAN - Suresh Kumar A N  ([/color] [email protected] [color=black])[/color]
            [color=#808080]Created: 05-Sep-2010 / Last Modified: 01-Jun-2011 / Version: 0.7u[/color]

  [color=#2255AA]For usage, please refer Forum Topic :[/color] www.autohotkey.com/forum/viewtopic.php?t=62180


[color=#AAAAAA]*/[/color]

DllPackFiles( Folder, DLL, Section="Files" ) {
 IfNotExist,%DLL%, SetEnv,DLL,% DLLCreateEmpty( DLL )
 VarSetCapacity(Bin,64,Ix:=0)
 If hUPD := DllCall( "[color=darkred]BeginUpdateResource[/color]", Str,DLL, Int,0 )
  Loop, %Folder%\*.*  {
  VarSetCapacity( Bin,0 )
  FileRead, Bin, [color=red]*c[/color] %A_LoopFileLongPath%
  If nSize := VarSetCapacity(Bin)
  DllCall( "[color=darkred]UpdateResource[/color]", UInt,hUpd, Str,DllCall( "[color=darkred]CharUpper[/color]", Str,Section, Str ), Str
 ,DllCall( "[color=darkred]CharUpper[/color]", Str,A_LoopFileName, Str ), Int,0, UInt,&Bin, UInt,nSize ),Ix:=Ix+1
} Return Ix, DllCall( "[color=darkred]EndUpdateResource[/color]", UInt,hUpd, Int,0 )
}


DllCreateEmpty( F="empty.dll" ) { ; www.autohotkey.com/forum/viewtopic.php?p=381161#381161
[color=white];[/color][color=#D62A00]Creates Empty Resource-Only DLL (1024 bytes)[/color][color=#808080]  / CD:05-Sep-2010 | LM:01-Jun-2011 - [/color][color=black]by SKAN[/color]
 IfNotEqual,A_Tab, % TS:=A_NowUTC, EnvSub,TS,1970,S  
 Src := [color=#808080]"0X5A4DY3CXC0YC0X4550YC4X1014CYD4X210E00E0YD8XA07010BYE0X200YECX1000YF0X1000YF4X1"
 . "0000YF8X1000YFCX200Y100X4Y108X4Y110X2000Y114X200Y11CX4000003Y120X40000Y124X1000Y128X1"
 . "00000Y12CX1000Y134X10Y148X1000Y14CX10Y1B8X7273722EY1BCX63Y1C0X10Y1C4X1000Y1C8X200Y1CC"
 . "X200Y1DCX40000040"[/color], VarSetCapacity( Trg,1024,0 ), Numput( TS,Trg,200 ), DA:=0x40000000
 Loop, Parse, Src, XY                                                           
   Mod( A_Index,2 ) ? O := "0x" A_LoopField : NumPut( "0x" A_LoopField, Trg, O )
 If ( hF := DllCall( "[color=darkred]CreateFile[/color]", Str,F, UInt,DA, UInt,2,Int,0,UInt,2,Int,0,Int,0 ) ) > 0
   B := DllCall( "[color=darkred]_lwrite[/color]", UInt,hF,Str,Trg,UInt,1024 ),  DllCall( "[color=darkred]CloseHandle[/color]",UInt,hF )
 Loop %F%
Return B ? A_LoopFileLongPath :
}


DllRead( ByRef Var, Filename, Section, Key ) {          ; Functionality and Parameters are
 VarSetCapacity( Var,64 ), VarSetCapacity( Var,0 )      ; identical to IniRead command ;-)
 If hMod := DllCall( "[color=darkred]LoadLibrary[/color]", Str,Filename )
  If hRes := DllCall( "[color=darkred]FindResource[/color]", UInt,hMod, Str,Key, Str,Section )
   If hData := DllCall( "[color=darkred]LoadResource[/color]", UInt,hMod, UInt,hRes )
    If pData := DllCall( "[color=darkred]LockResource[/color]", UInt,hData )
 Return VarSetCapacity( Var,nSize := DllCall( "[color=darkred]SizeofResource[/color]", UInt,hMod, UInt,hRes ),32)
     , DllCall( "[color=darkred]RtlMoveMemory[/color]", UInt,&Var, UInt,pData, UInt,nSize )
     , DllCall( "[color=darkred]FreeLibrary[/color]", UInt,hMod )
Return DllCall( "[color=darkred]FreeLibrary[/color]", UInt,hMod ) >> 32
}


Usage:

Put all your files into a single folder and call DllPackFiles(), for eg:

DllPackFiles( "C:\Program Files\AutoHotkey\", "AHKFiles.DLL", "Files" )

That is it.. you may open and inspect AHKFiles.DLL in a resource editor tool such as ResHacker

Posted Image
To use the above created DLL in your scripts, you need DllRead() which is similar to ( and simpler like ) IniRead command.
For example, to load license.txt into a variable, call DllRead() like:

DllRead( Var, "AHKFiles.dll", "Files", "license.txt" )

:)

Note: To convert ANSI text to UNICODE, use: Var := StrGet( &Var, "" )


Edit: 01-June-2011: DllCreateEmpty() was updated/bug fixed. Thanks to Wicked for reporting/aiding me to fix the bug.

Wicked
  • Members
  • 504 posts
  • Last active: Nov 18 2018 02:17 AM
  • Joined: 07 Jun 2008
Posted Image

Awesome! Was looking for this a few months back but just decided to take a different path.

Thank you! Will be very useful!

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks for the fast feedback :)

Was looking for this a few months back


I will be glad to know your requirement!.

My requirement: I am trying to use DLL as a storage container for thumbnail images from IMDB for over 2000 titles ( along with other textual data ). AxC² does not support dynamic data... INI settings file is good, but hardly supports binary data.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
String Resources for Dummies

Storing and retrieving 'String Resource' from DLL can be overwhelming..
Here is a simpler way:

Create a INI file in Notepad and make sure the file has at least one blank line ( CRLF ) in the beginning ( and maybe at the end too ).

[SECTION]
Key0=The operation completed successfully
Key1=Incorrect function
Key2=The system cannot find the file specified
Key3=The system cannot find the path specified
Key4=The system cannot open the file


Save the file and include it in your DLL.. and that will be it.
To read a string into a var, use IniRead command, like:

IniRead, Var, MyResources.dll, SECTION, Key0

Can we use IniWrite too ?
There are ifs & buts, but basically, if we carefully plan the Resource layout - it is possible to use IniWrite without corrupting the DLL.

SoggyDog
  • Members
  • 803 posts
  • Last active: Mar 04 2013 06:27 AM
  • Joined: 02 May 2006
:D

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Very nice. Thx
Posted Image

Wicked
  • Members
  • 504 posts
  • Last active: Nov 18 2018 02:17 AM
  • Joined: 07 Jun 2008

Thanks for the fast feedback :)

Was looking for this a few months back


I will be glad to know your requirement!.

My requirement: I am trying to use DLL as a storage container for thumbnail images from IMDB for over 2000 titles ( along with other textual data ). AxC² does not support dynamic data... INI settings file is good, but hardly supports binary data.


Same as myself. I've got a script that requires a large amount of mainly images but also some other small resource files. This will be perfect!

Thanks again!

godsstigma
  • Members
  • 222 posts
  • Last active: May 13 2011 03:09 AM
  • Joined: 04 Nov 2008
:shock:

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

IniRead, Var, MyResources.dll, SECTION, Key0

That's an interesting idea. I presume the API used by IniRead merely reads the dll as a text/ini file (with a lot of unusable garbage before and after the ini section). Similarly, IniWrite would ignorantly write to it as if it is just a text file.

I tested the following script with Ahk2Exe64:
IniRead, Var, %A_ScriptFullPath%, SECTION, Key0 
MsgBox %Var%
/*
[SECTION]
Key0=The operation completed successfully
It worked. Ahk2Exe64 stores the plain text of the script file as a resource in the 64-bit exe file, comments and all. I suppose adding the ini file as a string resource in AutoHotkeySC.bin would also work, but you'd need to disable compression.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

Similarly, IniWrite would ignorantly write to it as if it is just a text file.


Yes! I had noticed the following:

1) It removes all nulls at the end of the file ( sees it as redundant, I guess ) and places a CRLF thereby readying the file for next IniWrite. This is bad for a resource-only DLL as there is null padding at the EOF ( almost ) always.
This can be worked around if we place an additional byte ( non-null ) at EOF before IniWrite.

2) There should be enough room for the write, or the section alignment / resource alignment is displaced. for eg. the length of following section is
18 + 8 ( 4 x CRLF ) = 26

[LASTGUIPOS]
x=0
y=0

To be able to write into X or Y, trailing spaces ( 10 or so ) should be padded, otherwise - after IniWrite - other resources following it gets mis-placed.
In this context, it is better if the INI text is placed as the last resource of the resource section. Resources are arranged by Resource Type / Alphabetical order, so a 'Named Resource' starting with 'Z' or a high ordinal number like 10000 should do it.

3) Section names should be preallocated. If IniWrite does not find a section, it creates a new section at the tail.

This is all that comes from my memory right now. I am sure there are more factors to be taken care of to prepare a PE for IniWrite.

Newbie123
  • Guests
  • Last active:
  • Joined: --
Hello, I'm a little confused as to how to use the DllRead() function to grab an image and display it in my scripts Gui. I can pack the Dll fine and I've used Resource hacker to confirm that the files are in there.

DllRead( Var, "AHK.DLL","Files","BANNER.JPG" )

Gui, Add, Picture,w600 h150, %Var% ;This does not work =(
Gui, Show 
return

DllRead( ByRef Var, Filename, Section, Key ) {          ; Functionality and Parameters are
 VarSetCapacity( Var,64 ), VarSetCapacity( Var,0 )      ; identical to IniRead command ;-)
 If hMod := DllCall( "LoadLibrary", Str,Filename )
  If hRes := DllCall( "FindResource", UInt,hMod, Str,Key, Str,Section )
   If hData := DllCall( "LoadResource", UInt,hMod, UInt,hRes )
    If pData := DllCall( "LockResource", UInt,hData )
 Return VarSetCapacity( Var,nSize := DllCall( "SizeofResource", UInt,hMod, UInt,hRes ),32)
     , DllCall( "RtlMoveMemory", UInt,&Var, UInt,pData, UInt,nSize )
     , DllCall( "FreeLibrary", UInt,hMod )
Return DllCall( "FreeLibrary", UInt,hMod ) >> 32
}

I would like to use this to function to store my Icons and retrieve them for use inside my scripts.

Any help would be very much appreciated =)

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

DllRead( Var, "AHK.DLL","Files","BANNER.JPG" )

Gui, Add, Picture,w600 h150, %Var% ;This does not work =(


Gui, Add, Picture will accept only a filename and there is no built-in command in AHK to process image resources. :(

DllRead() just loads the file as is into a memory variable.
You need to process it and obtain a gdi bitmap before it can be applied in a picture control.

Sean showed us 'how to?' using GDI+ :
How to convert Image data (JPEG/PNG/GIF) to hBITMAP ?

The above topic can a bit tough for a newbie.. but read it anyway and then try the following script:

SetWorkingDir, %A_ScriptDir%
IfNotExist,ahk.dll
 urldownloadtofile,https://ahknet.autohotkey.com/~Skan/Scripts/RoD/ahk.dll, ahk.dll

GdiPlus( "Startup" )
nSize := DllRead( Banner, "AHK.DLL","Files","BANNER.JPG" )
hBitmap := GdiPlus_hBitmapFromBuffer( Banner, nSize )
GdiPlus( "Shutdown" )

Gui, Add, Picture,x0 y0 w600 h150 hwndhPic 0x20E
SendMessage, (STM_SETIMAGE:=0x172), (IMAGE_BITMAP:=0x0), hBitmap,, ahk_id %hPic%

Gui, Show, w600 h400, ahk.dll\banner.jpg\0
OnExit, QuitScript
Return                                                 ; // end of auto-execute section //

GuiClose:
QuitScript:
 Gdi_DeleteObject( hBitmap )
 OnExit
 ExitApp
Return


DllRead( ByRef Var, Filename, Section, Key ) {          ; Functionality and Parameters are
 VarSetCapacity( Var,64 ), VarSetCapacity( Var,0 )      ; identical to IniRead command ;-)
 If hMod := DllCall( "LoadLibrary", Str,Filename )
  If hRes := DllCall( "FindResource", UInt,hMod, Str,Key, Str,Section )
   If hData := DllCall( "LoadResource", UInt,hMod, UInt,hRes )
    If pData := DllCall( "LockResource", UInt,hData )
 Return VarSetCapacity( Var,nSize := DllCall( "SizeofResource", UInt,hMod, UInt,hRes ),32)
     , DllCall( "RtlMoveMemory", UInt,&Var, UInt,pData, UInt,nSize )
     , DllCall( "FreeLibrary", UInt,hMod )
Return DllCall( "FreeLibrary", UInt,hMod ) >> 32
}

;-----------------------------------------------------------------------------------------
;                                                        GDI and GDIPLUS Wrapper Functions
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

GdiPlus( Comm="Startup" ) {
 Static pToken, hMod
 hMod := hMod ? hMod : DllCall( "LoadLibrary", Str,"gdiplus.dll" )
 If ( Comm="Startup" )
   VarSetCapacity( si,16,0 ), si := Chr(1)
 , Res := DllCall( "gdiplus\GdiplusStartup", UIntP,pToken, UInt,&si, UInt,0 )
 Else Res := DllCall( "gdiplus\GdiplusShutdown", UInt,pToken )
 , hMod := DllCall( "FreeLibrary", UInt,hMod ) >> 64
Return ! Res
}

GDIPlus_hBitmapFromBuffer( ByRef Buffer, nSize ) { ;  Last Modifed : 21-Jun-2011
; Adapted version by SKAN www.autohotkey.com/forum/viewtopic.php?p=383863#383863
; Original code by   Sean www.autohotkey.com/forum/viewtopic.php?p=147029#147029
 hData := DllCall("GlobalAlloc", UInt,2, UInt,nSize )
 pData := DllCall("GlobalLock",  UInt,hData )
 DllCall( "RtlMoveMemory", UInt,pData, UInt,&Buffer, UInt,nSize )
 DllCall( "GlobalUnlock" , UInt,hData )
 DllCall( "ole32\CreateStreamOnHGlobal", UInt,hData, Int,True, UIntP,pStream )
 DllCall( "gdiplus\GdipCreateBitmapFromStream",  UInt,pStream, UIntP,pBitmap )
 DllCall( "gdiplus\GdipCreateHBITMAPFromBitmap", UInt,pBitmap, UIntP,hBitmap, UInt
,DllCall( "ntdll\RtlUlongByteSwap",UInt
,DllCall( "GetSysColor", Int,15 ) <<8 ) | 0xFF000000 )
 DllCall( "gdiplus\GdipDisposeImage", UInt,pBitmap )
 DllCall( NumGet( NumGet(1*pStream)+8 ), UInt,pStream ) ; IStream::Release
Return hBitmap
}

Gdi_DeleteObject( hObj ) {
 Return !! DllCall( "GDI32\DeleteObject", UInt,hObj )
}



Newbie123
  • Guests
  • Last active:
  • Joined: --
Awesome! Thank you so much for this! I hated having to include a banner/picture file along with my compiled projects, now I can just stuff it in a dll with my icons! I truly appreciate that you took the time to answer my post, and you did so in a way that went above and beyond my expectations. Thank you SKAN!!!!

n-l-i-d
  • Guests
  • Last active:
  • Joined: --
You are a true master, SKAN...

:)

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks Daonlyfreez :oops:

Update: Wrapper now supports 32bit Unicode build.