Hackers’ Corner: Hack My Exe
Vector 21:2, page 112
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 112
Introduction¶
Dyalog’s V10-feature to ‘compile’ a DWS into an EXE is really useful, as EXEs are easier to share with others (not requiring shortcuts to the runtime etc.). Even if it was possible before to share programs, it is just more fun now to write small utilities and have them available as EXE. (Now if only the accompanying DLL could be smaller – but that’s another issue). The only issue I had with Dyalog’s EXEs was that Dyalog did not give me control over the program- and version-information which shows up in the file-properties. So I started to research ways of changing that information. This article describes my findings.
When first hearing that V10.1 would include a possibility to control these, I thought this article would be redundant, but it isn’t, as you get additional benefit from features not (yet?) available in Dyalog’s tools: the tools described here allow for a fully automated build-process, so all properties can be set under program-control, and it is also possible to put additional resources into the EXE, like, for example, additional icons, so that your users have a choice.
Ingredients¶
Dyalog’s EXEs are created in the Win32 PE-Format. PE stands for “Portable Executable” and is the standard-format for programs in the Win32-World (actually also for Win64, although with some modifications). More about the format can be found on the web, one comprehensive article is [1]. The important point is that since this is a standardized format, we can use any tool to edit those structures, there is no ‘hidden APL-magic’ preventing that.
There are various tools available on the internet to edit EXE-files, one tool that proved very suitable for the job was Resource Hacker [2] which has the advantage of being available as freeware.
With Resource Hacker it is possible to edit the version-information in the file and add icons to it – interactively. The program also has a command-line mode and supports scripting, but in that mode it requires pre-compiled resource files which it then inserts into the EXE.
Compiling – again¶
So, to automate the whole process, it is necessary to create a resource-file (.RC), compile it with a resource-compiler, then use Resource Hacker to update the resource-info in the EXE with the compiled .RES-file.
Fortunately that all is not as difficult as it may sound. A resource-compiler is included with Visual Studio (Common\Tools\Bin\RC.EXE) and if you don’t have VS, you can download GoRC.EXE for free [3]. I had an issue when using GoRC to compile the resources, as finally the EXE was still showing the old version-info, yet I had no errors in the whole process. It finally turned out that the initial RC (created by using ResourceHacker’s info) had no language-information in it and so GoRC used Windows’ regional settings to determine a default-language (which, in my case, was wrong: I used German windows, but needed English language-info). Surprisingly Microsoft’s RC did not have these issues, as it seems to use a hardcoded default ENGLISH,ENGLISH_US which was exactly right in this case. Anyway, the moral of the whole story: correct (English) language-info needs to be included in the .RC
Putting it all together¶
I use a function Build to increment version numbers, save my dws, write an appropriate RC, compile the RC, compile the DWS and finally launch Resource Hacker to update the resources in the EXE.
[0] Build;this;nu;txt
[1] this←{(⌽∧\⌽⍵≠'\')/⍵}⎕WSID
[2] :If save
[3] write_rc
[4] ⎕←⎕CMD ⎕←'c:\dyalog10\dotnet\aplc.exe /v /t:nativeexe ',this,'.dws',(FileX this,'.ico')/' /icon:',this,'.ico'
[5] ⍝ ⎕←⎕CMD'"C:\Programme\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin\RC.Exe" /r /v ',this,'.rc'
[6] ⎕←⎕CMD ⎕←'c:\tools\gorc\gorc /r ',this,'.rc'
[7] :If FileX this,'.rh_script'
[8] ⎕CMD ⎕←'c:\tools\reshack\reshacker.exe -script ',this,'.rh_script'
[9] :Else
[10] ⎕CMD ⎕←'c:\tools\reshack\reshacker.exe -addoverwrite "',⎕WSID,'.exe","',⎕WSID,'.exe","',⎕WSID,'.res",versioninfo,1,1033'
[11] :EndIf
[12] nu←'c:\tools\reshack\reshacker.log'⎕NTIE 0
[13] txt←⎕NREAD nu,82,⎕NSIZE nu ⋄ ⎕NUNTIE nu
[14] {{}⎕←1↓⍵}¨{(⍵=⎕TC[2])⊂⍵}⎕TC[2],txt~⎕TC[3] ⍝ wrap every single line in output
[15] :EndIf
The function save handles version-info and the actual ⎕SAVE of the DWS, write_rc uses a template-variable, updates the version-info (simply by trying to execute anything enclosed in % and replace it with the results) in it and writes it to disk:
write_rc;txt;z;apl;file;nu
txt←my∆rc ⍝ get resource-template
txt←txt,¨⊂⎕TC[3 2]
txt←qENLIST txt
⍝ scan for any APL-Commands enclosed in '%'
z←('%'⍷txt)∧~{⍵∨0,¯1↓⍵}'%%'⍷txt
:While ∨/z
apl←1↓(1=+\z)/txt
:Trap 0 ⋄ apl←⍕⍎apl ⋄ :EndTrap ⍝ execute the APL
txt←((¯1+z⍳1)↑txt),apl,(1⍳⍨2=+\z)↓txt ⍝ repl. with res
z←('%'⍷txt)∧~{⍵∨0,¯1↓⍵}'%%'⍷txt
:EndWhile
z←'%%'⍷txt ⋄ txt←(~z)/txt ⍝ replace '%%' with simple '%'
⍝ and write the file
:Trap 0 ⍝ erase, if exists
nu←(file←⎕WSID,'.rc')⎕NTIE 0 ⋄ file ⎕NERASE nu
:EndTrap
nu←file ⎕NCREATE 0
txt ⎕NAPPEND nu
⎕NUNTIE nu
And the variable my_rc contains this:
LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US
1 VERSIONINFO
FILEVERSION %GetVersion 1%,%GetVersion 2%,%GetVersion 3%,%GetVersion 4%
PRODUCTVERSION %GetVersion 1%,%GetVersion 2%,%GetVersion 3%,%GetVersion 4%
FILEOS 0x4
FILETYPE 0x2
{
BLOCK "StringFileInfo"
{
BLOCK "040904B0"
{
VALUE "CompanyName", "Me and myself"
VALUE "FileDescription", "Great app"
VALUE "FileVersion", "%GetVersion 1%,%GetVersion 2%,%GetVersion 3%,%GetVersion 4%"
VALUE "InternalName", "GreatApp"
VALUE "LegalCopyright", "Copyright (C) 2004 Me"
VALUE "OriginalFilename", "GreatApp.EXE"
VALUE "ProductName", "GreatApp"
VALUE "ProductVersion", "%GetVersion 1%,%GetVersion 2%,%GetVersion 3%,%GetVersion 4%"
VALUE "Website", http://www.GreatSite.com
VALUE "Support", support@GreatSite.com
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409 0x04B0
}
}
As far as I know, there are no rules about the contents of the StringFileInfo-block, so you could add your own parameters there. GetVersion is a function which returns the current version-number (or selected parts of it, depending on the argument).
If you are interested in the save-function and others not shown here, or in the scripts, grab a copy of the DWS from the VectorLib [4] (or from my website [5], once it is reconstructed…)
References¶
[1] http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
[2] http://www.users.on.net/johnson/resourcehacker/
[3] http://www.godevtool.com/
[4] http://www.vectorlib.org.uk
[5] http://www.mbaas.de