Thursday, December 19, 2019

Project Explorer: Automatically Digitally Signing an EXE After Building

When a customer reports a bug, we usually fix it immediately and send them a “hot fix”, which is usually just an updated EXE. Because it doesn’t go through our usual build process, the EXE isn’t digitally signed, which can cause issues on some machines. I created a batch file to sign the EXE but most of the time forget to run it before sending the EXE to the customer. Then it occurred to me I could automate that process using Project Explorer.

I created the following program and added it to the Addins subdirectory of the folder where Project Explorer exists:

lparameters toParameter1, ;
    tuParameter2, ;
local laStack[1], ;
    lnRows, ;
    lnI, ;
    lcApp, ;
     lcDescription, ;
    lcCommand, ;
    lcFolder, ;

* If this is a registration call, tell the addin manager which method we're
* an addin for.

if pcount() = 1
    toParameter1.Method = 'AfterBuildProject'
    toParameter1.Active = .T.
    toParameter1.Name   = 'Sign EXE after building'

* This is an addin call, so if we built an EXE, sign it.

if tuParameter2 = 3
    lnRows = astackinfo(laStack)
    for lnI = 1 to lnRows
        if 'projectexplorerui.vct' $ laStack[lnI, 2]
            lcApp = fullpath('..\', laStack[lnI, 2])
        endif 'projectexplorerui.vct' $ laStack[lnI, 2]
    next lnI
    lcDescription = inputbox('Description', 'Description for EXE')
    text to lcCommand textmerge noshow pretext 1 + 2
    "SignToolPath\signtool.exe" sign /fd SHA256 /tr /td SHA256 /f CertificatePath /d "<<lcDescription>>" /p Password "<<tuParameter3>>"
    lcFolder = justpath(tuParameter3)
    loAppRun = newobject('APIAppRun', 'APIAppRun.PRG', lcApp, lcCommand, ;
        lcFolder, 'HID')
endif tuParameter2 = 3
return .T.

Substitute SignToolPath with the path to SignTool.exe, CertificatePath with the path and name of the certificate file, and Password with the password.

This program is a Project Explorer addin that’s called after a project is built successfully. This code checks whether it was an EXE (we’re not going to sign an APP) and if so, finds the location of Project Explorer so it can use the built-in APIAppRun class to call SignTool.exe to digitally sign the EXE.

Now, whenever I build an EXE in Project Explorer, it’s automatically signed so I don’t have to remember to do it.


Dan Lauer said...

I've been code signing for years, but your script raises a question for me. You're pulling from a DigiCert timeserver. Why? I have never done this. My initial certs were from GoDaddy, and I just switched to Sectigo. Is there something different about DigiCert products that you need to pull time?

Doug Hennig said...

Without the /tr parameter, the EXE won't be timestamped. Not sure if that's a problem, but I decided I wanted it.