Extending the NSIS Inetc Plugin to Add Download‑Progress Callback
The article explains how to extend NSIS’s Inetc plug‑in by adding a /callback switch, storing the callback address, pushing total size, downloaded amount and percentage onto the NSIS stack after each buffer, and invoking the user‑defined function to update a progress bar during downloads.
Background and purpose : NSIS is a widely used installer framework on Windows. The standard Inetc plug‑in can download files but does not expose the total size, current progress or downloaded amount. This article describes how to modify the plug‑in to add a callback that reports these values.
Basic steps for NSIS plug‑in development :
Download a plug‑in source package from the NSIS website and examine its directory structure.
Study the plug‑in development rules and the helper functions provided by NSIS (e.g., EXDLL_INIT , popint , pushstring ).
Understand the calling convention: the exported function must match the signature required by NSIS.
The core files are pluginapi.c/h , crt.cpp and nsis_tchar.h . They provide stack handling, character‑set conversion and the single exported entry point.
Exported function prototype (must be declared exactly as NSIS expects):
extern "C" void __declspec(dllexport) __cdecl get(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)Inside get the plug‑in parses command‑line switches (e.g., /silent , /weaksecurity , /caption , /username , /password , /callback ) and stores the callback address in a global variable g_progressCallback .
while(!popstring(url) && *url == TEXT('/')) {
if(lstrcmpi(url, TEXT("/callback")) == 0) {
g_progressCallback = popint();
TCHAR* buf12 = (TCHAR*)LocalAlloc(LPTR, 8 * sizeof(TCHAR));
wsprintf(buf12, TEXT("%d"), g_progressCallback);
}
// other switches omitted for brevity
}The download routine uses fileTransfer to read/write data. After each buffer is processed, the plug‑in pushes three values (total size, downloaded size, percentage) onto the NSIS stack and invokes the callback via ExecuteCodeSegment :
if(g_progressCallback != -1) {
static TCHAR buf[32];
wsprintf(buf, TEXT("%lu"), cnt / 1024); // downloaded KB
pushstring(buf);
wsprintf(buf, TEXT("%lu"), fs != NOT_AVAILABLE ? fs / 1024 : 0); // total KB
pushstring(buf);
wsprintf(buf, TEXT("%lu"), fs > 0 && fs != NOT_AVAILABLE ? MulDiv(100, cnt, fs) : 0); // percent
pushstring(buf);
g_pluginExtra->ExecuteCodeSegment(g_progressCallback - 1, g_hwndParent);
}In the NSIS script the user registers a callback function and calls the modified plug‑in:
Function DownLoadCallBack
Pop $0 ; percent
Pop $1 ; total size
Pop $2 ; downloaded size
Push $0
SendMessage $PROGBAR ${PBM_SETPOS} $0 0 ; update progress bar
FunctionEnd
Function Extractfunc
GetFunctionAddress $R9 DownLoadCallBack
inetc::get /SILENT /callback $R9 "https://example.com/file.7z" "D:/test/file.7z" /end
Pop $1 ; result ("ok" on success)
Push $1
FunctionEndAfter the plug‑in is rebuilt, the callback updates the installer UI with real‑time progress. The article also mentions alternative ways to pass the callback (e.g., using getuservariable or the /TRANSLATE switch) and provides additional NSIS knowledge such as reading the system version, detecting 64‑bit Windows, and common C/C++ utilities ( strlen , sizeof , wsprintf ).
Finally, the author points to the Nsis7z plug‑in for extracting the downloaded archive and encourages readers to experiment with the code to suit their own installer requirements.
37 Interactive Technology Team
37 Interactive Technology Center
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.