
Compiler output is identical before & after this step git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/branches/wizou@6036 212acab6-be3b-0410-9dea-997c60f758d6
494 lines
15 KiB
C
494 lines
15 KiB
C
/*
|
|
Copyright (c) 2002 Robert Rainwater <rrainwater@yahoo.com>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Unicode support by Jim Park -- 08/24/2007
|
|
|
|
*/
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
#include <winnt.h>
|
|
#include <nsis/pluginapi.h> // nsis plugin
|
|
|
|
#ifndef true
|
|
#define true TRUE
|
|
#endif
|
|
#ifndef false
|
|
#define false FALSE
|
|
#endif
|
|
#define LOOPTIMEOUT 100
|
|
|
|
HWND g_hwndParent;
|
|
HWND g_hwndList;
|
|
|
|
void ExecScript(BOOL log);
|
|
void LogMessage(const TCHAR *pStr, BOOL bOEM);
|
|
TCHAR *my_strstr(TCHAR *a, TCHAR *b);
|
|
unsigned int my_atoi(TCHAR *s);
|
|
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
|
|
|
|
void __declspec(dllexport) Exec(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
|
|
g_hwndParent=hwndParent;
|
|
EXDLL_INIT();
|
|
{
|
|
ExecScript(0);
|
|
}
|
|
}
|
|
|
|
void __declspec(dllexport) ExecToLog(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
|
|
g_hwndParent=hwndParent;
|
|
EXDLL_INIT();
|
|
{
|
|
ExecScript(1);
|
|
}
|
|
}
|
|
|
|
void __declspec(dllexport) ExecToStack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop) {
|
|
g_hwndParent=hwndParent;
|
|
EXDLL_INIT();
|
|
{
|
|
ExecScript(2);
|
|
}
|
|
}
|
|
|
|
HINSTANCE g_hInst;
|
|
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) {
|
|
g_hInst = hInst;
|
|
return TRUE;
|
|
}
|
|
|
|
#define TAB_REPLACE _T(" ")
|
|
#define TAB_REPLACE_SIZE (sizeof(TAB_REPLACE)-1)
|
|
|
|
// Turn a pair of chars into a word
|
|
// Turn four chars into a dword
|
|
#ifdef __BIG_ENDIAN__ // Not very likely, but, still...
|
|
#define CHAR2_TO_WORD(a,b) (((WORD)(b))|((a)<<8))
|
|
#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(c,d))|(CHAR2_TO_WORD(a,b)<<16))
|
|
#else
|
|
#define CHAR2_TO_WORD(a,b) (((WORD)(a))|((b)<<8))
|
|
#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(a,b))|(CHAR2_TO_WORD(c,d)<<16))
|
|
#endif
|
|
|
|
BOOL IsWOW64() {
|
|
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
|
|
BOOL wow64;
|
|
LPFN_ISWOW64PROCESS fnIsWow64Process;
|
|
|
|
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
|
|
GetModuleHandle(_T("kernel32")), "IsWow64Process");
|
|
|
|
if (fnIsWow64Process != NULL) {
|
|
if (fnIsWow64Process(GetCurrentProcess(), &wow64)) {
|
|
return wow64;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void ExecScript(int log) {
|
|
TCHAR szRet[128] = _T("");
|
|
TCHAR meDLLPath[MAX_PATH];
|
|
TCHAR *executor;
|
|
TCHAR *g_exec;
|
|
TCHAR *pExec;
|
|
unsigned int g_to;
|
|
BOOL bOEM;
|
|
|
|
if (!IsWOW64()) {
|
|
TCHAR* p;
|
|
int nComSpecSize;
|
|
|
|
nComSpecSize = GetModuleFileName(g_hInst, meDLLPath, MAX_PATH) + 2; // 2 chars for quotes
|
|
g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR)*(g_stringsize+nComSpecSize+2)); // 1 for space, 1 for null
|
|
p = meDLLPath + nComSpecSize - 2; // point p at null char of meDLLPath
|
|
*g_exec = _T('"');
|
|
executor = g_exec + 1;
|
|
|
|
// Look for the last '\' in path.
|
|
do
|
|
{
|
|
if (*p == _T('\\'))
|
|
break;
|
|
p = CharPrev(meDLLPath, p);
|
|
}
|
|
while (p > meDLLPath);
|
|
if (p == meDLLPath)
|
|
{
|
|
// bad path
|
|
pushstring(_T("error"));
|
|
GlobalFree(g_exec);
|
|
return;
|
|
}
|
|
|
|
*p = 0;
|
|
GetTempFileName(meDLLPath, _T("ns"), 0, executor); // executor = new temp file name in module path.
|
|
*p = _T('\\');
|
|
if (CopyFile(meDLLPath, executor, FALSE)) // copy current DLL to temp file in module path.
|
|
{
|
|
HANDLE hFile, hMapping;
|
|
LPBYTE pMapView;
|
|
PIMAGE_NT_HEADERS pNTHeaders;
|
|
hFile = CreateFile(executor, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0, 0);
|
|
hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
|
|
pMapView = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
|
|
if (pMapView)
|
|
{
|
|
pNTHeaders = (PIMAGE_NT_HEADERS)(pMapView + ((PIMAGE_DOS_HEADER)pMapView)->e_lfanew);
|
|
// Turning the copied DLL into a stripped down executable.
|
|
pNTHeaders->FileHeader.Characteristics = IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_LOCAL_SYMS_STRIPPED |
|
|
IMAGE_FILE_LINE_NUMS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE;
|
|
// Windows character-mode user interface (CUI) subsystem.
|
|
pNTHeaders->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
|
pNTHeaders->OptionalHeader.AddressOfEntryPoint = (DWORD)_tWinMain - (DWORD)g_hInst;
|
|
UnmapViewOfFile(pMapView);
|
|
}
|
|
CloseHandle(hMapping);
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
lstrcat(g_exec, _T("\""));
|
|
|
|
// add space
|
|
pExec = g_exec + lstrlen(g_exec);
|
|
*pExec = _T(' ');
|
|
pExec++;
|
|
} else {
|
|
executor = NULL;
|
|
g_exec = (TCHAR *)GlobalAlloc(GPTR, sizeof(TCHAR)*(g_stringsize+1)); // 1 for NULL
|
|
pExec = g_exec;
|
|
}
|
|
|
|
g_to = 0; // default is no timeout
|
|
bOEM = FALSE; // default is no OEM->ANSI conversion
|
|
|
|
g_hwndList = NULL;
|
|
|
|
// g_hwndParent = the caller, usually NSIS installer.
|
|
if (g_hwndParent) // The window class name for dialog boxes is "#32770"
|
|
g_hwndList = FindWindowEx(FindWindowEx(g_hwndParent,NULL,_T("#32770"),NULL),NULL,_T("SysListView32"),NULL);
|
|
|
|
// g_exec is the complete command to run: It has the copy of this DLL turned
|
|
// into an executable right now.
|
|
|
|
params:
|
|
// Get the command I need to run from the NSIS stack.
|
|
popstring(pExec);
|
|
if (my_strstr(pExec, _T("/TIMEOUT=")) == pExec) {
|
|
TCHAR *szTimeout = pExec + 9;
|
|
g_to = my_atoi(szTimeout);
|
|
*pExec = 0;
|
|
goto params;
|
|
}
|
|
if (!lstrcmpi(pExec, _T("/OEM"))) {
|
|
bOEM = TRUE;
|
|
*pExec = 0;
|
|
goto params;
|
|
}
|
|
|
|
if (!pExec[0])
|
|
{
|
|
pushstring(_T("error"));
|
|
*(pExec-2) = _T('\0'); // skip space and quote
|
|
if (executor) DeleteFile(executor);
|
|
GlobalFree(g_exec);
|
|
return;
|
|
}
|
|
|
|
// Got all the params off the stack.
|
|
|
|
{
|
|
STARTUPINFO si={sizeof(si),};
|
|
SECURITY_ATTRIBUTES sa={sizeof(sa),};
|
|
SECURITY_DESCRIPTOR sd={0,};
|
|
PROCESS_INFORMATION pi={0,};
|
|
OSVERSIONINFO osv={sizeof(osv)};
|
|
HANDLE newstdout=0,read_stdout=0;
|
|
HANDLE newstdin=0,read_stdin=0;
|
|
DWORD dwRead = 1;
|
|
DWORD dwExit = 0;
|
|
DWORD dwWait = WAIT_TIMEOUT;
|
|
DWORD dwLastOutput;
|
|
static TCHAR szBuf[1024];
|
|
HGLOBAL hUnusedBuf = NULL;
|
|
TCHAR *szUnusedBuf = 0;
|
|
|
|
if (log) {
|
|
hUnusedBuf = GlobalAlloc(GHND, log & 2 ? (g_stringsize*sizeof(TCHAR)) : sizeof(szBuf)*4);
|
|
if (!hUnusedBuf) {
|
|
lstrcpy(szRet, _T("error"));
|
|
goto done;
|
|
}
|
|
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf);
|
|
}
|
|
|
|
GetVersionEx(&osv); // Get OS info
|
|
if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
|
|
SetSecurityDescriptorDacl(&sd,true,NULL,false);
|
|
sa.lpSecurityDescriptor = &sd;
|
|
}
|
|
else
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = true;
|
|
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) {
|
|
lstrcpy(szRet, _T("error"));
|
|
goto done;
|
|
}
|
|
if (!CreatePipe(&read_stdin,&newstdin,&sa,0)) {
|
|
lstrcpy(szRet, _T("error"));
|
|
goto done;
|
|
}
|
|
|
|
GetStartupInfo(&si);
|
|
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.hStdInput = newstdin;
|
|
si.hStdOutput = newstdout;
|
|
si.hStdError = newstdout;
|
|
if (!CreateProcess(NULL,g_exec,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
|
|
lstrcpy(szRet, _T("error"));
|
|
goto done;
|
|
}
|
|
|
|
dwLastOutput = GetTickCount();
|
|
|
|
// Now I'm talking with an executable copy of myself.
|
|
while (dwWait != WAIT_OBJECT_0 || dwRead) {
|
|
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL);
|
|
if (dwRead) {
|
|
dwLastOutput = GetTickCount();
|
|
ReadFile(read_stdout, szBuf, sizeof(szBuf)-1, &dwRead, NULL);
|
|
szBuf[dwRead] = 0;
|
|
if (log) {
|
|
TCHAR *p, *p2;
|
|
SIZE_T iReqLen = lstrlen(szBuf) + lstrlen(szUnusedBuf);
|
|
if (GlobalSize(hUnusedBuf) < iReqLen && (iReqLen < g_stringsize || !(log & 2))) {
|
|
GlobalUnlock(hUnusedBuf);
|
|
hUnusedBuf = GlobalReAlloc(hUnusedBuf, iReqLen+sizeof(szBuf), GHND);
|
|
if (!hUnusedBuf) {
|
|
lstrcpy(szRet, _T("error"));
|
|
break;
|
|
}
|
|
szUnusedBuf = (TCHAR *)GlobalLock(hUnusedBuf);
|
|
}
|
|
p = szUnusedBuf; // get the old left overs
|
|
if (iReqLen < g_stringsize || !(log & 2)) lstrcat(p, szBuf);
|
|
else {
|
|
lstrcpyn(p + lstrlen(p), szBuf, g_stringsize - lstrlen(p));
|
|
}
|
|
|
|
if (!(log & 2)) {
|
|
while ((p = my_strstr(p, _T("\t")))) {
|
|
if ((int)(p - szUnusedBuf) > (int)(GlobalSize(hUnusedBuf) - TAB_REPLACE_SIZE - 1))
|
|
{
|
|
*p++ = _T(' ');
|
|
}
|
|
else
|
|
{
|
|
int len = lstrlen(p);
|
|
TCHAR *c_out=(TCHAR*)p+TAB_REPLACE_SIZE+len;
|
|
TCHAR *c_in=(TCHAR *)p+len;
|
|
while (len-- > 0) {
|
|
*c_out--=*c_in--;
|
|
}
|
|
|
|
lstrcpy(p, TAB_REPLACE);
|
|
p += TAB_REPLACE_SIZE;
|
|
*p = _T(' ');
|
|
}
|
|
}
|
|
|
|
p = szUnusedBuf; // get the old left overs
|
|
for (p2 = p; *p2;) {
|
|
if (*p2 == _T('\r')) {
|
|
*p2++ = 0;
|
|
continue;
|
|
}
|
|
if (*p2 == _T('\n')) {
|
|
*p2 = 0;
|
|
while (!*p && p != p2) p++;
|
|
LogMessage(p, bOEM);
|
|
p = ++p2;
|
|
continue;
|
|
}
|
|
p2 = CharNext(p2);
|
|
}
|
|
|
|
// If data was taken out from the unused buffer, move p contents to the start of szUnusedBuf
|
|
if (p != szUnusedBuf) {
|
|
TCHAR *p2 = szUnusedBuf;
|
|
while (*p) *p2++ = *p++;
|
|
*p2 = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (g_to && GetTickCount() > dwLastOutput+g_to) {
|
|
TerminateProcess(pi.hProcess, -1);
|
|
lstrcpy(szRet, _T("timeout"));
|
|
}
|
|
else Sleep(LOOPTIMEOUT);
|
|
}
|
|
|
|
dwWait = WaitForSingleObject(pi.hProcess, 0);
|
|
GetExitCodeProcess(pi.hProcess, &dwExit);
|
|
PeekNamedPipe(read_stdout, 0, 0, 0, &dwRead, NULL);
|
|
}
|
|
done:
|
|
if (log & 2) pushstring(szUnusedBuf);
|
|
if (log & 1 && *szUnusedBuf) LogMessage(szUnusedBuf, bOEM);
|
|
if ( dwExit == STATUS_ILLEGAL_INSTRUCTION )
|
|
lstrcpy(szRet, _T("error"));
|
|
if (!szRet[0]) wsprintf(szRet,_T("%d"),dwExit);
|
|
pushstring(szRet);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(newstdout);
|
|
CloseHandle(read_stdout);
|
|
CloseHandle(newstdin);
|
|
CloseHandle(read_stdin);
|
|
*(pExec-2) = _T('\0'); // skip space and quote
|
|
if (executor) DeleteFile(executor);
|
|
GlobalFree(g_exec);
|
|
if (log) {
|
|
GlobalUnlock(hUnusedBuf);
|
|
GlobalFree(hUnusedBuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tim Kosse's LogMessage
|
|
void LogMessage(const TCHAR *pStr, BOOL bOEM) {
|
|
LVITEM item={0};
|
|
int nItemCount;
|
|
if (!g_hwndList) return;
|
|
//if (!lstrlen(pStr)) return;
|
|
if (bOEM == TRUE) OemToCharBuff(pStr, (TCHAR *)pStr, lstrlen(pStr));
|
|
nItemCount=SendMessage(g_hwndList, LVM_GETITEMCOUNT, 0, 0);
|
|
item.mask=LVIF_TEXT;
|
|
item.pszText=(TCHAR *)pStr;
|
|
item.cchTextMax=0;
|
|
item.iItem=nItemCount;
|
|
ListView_InsertItem(g_hwndList, &item);
|
|
ListView_EnsureVisible(g_hwndList, item.iItem, 0);
|
|
}
|
|
|
|
TCHAR *my_strstr(TCHAR *a, TCHAR *b)
|
|
{
|
|
int l = lstrlen(b);
|
|
while (lstrlen(a) >= l)
|
|
{
|
|
TCHAR c = a[l];
|
|
a[l] = 0;
|
|
if (!lstrcmpi(a, b))
|
|
{
|
|
a[l] = c;
|
|
return a;
|
|
}
|
|
a[l] = c;
|
|
a = CharNext(a);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int my_atoi(TCHAR *s) {
|
|
unsigned int v=0;
|
|
if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) {
|
|
s+=2;
|
|
for (;;) {
|
|
int c=*s++;
|
|
if (c >= _T('0') && c <= _T('9')) c-=_T('0');
|
|
else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10;
|
|
else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10;
|
|
else break;
|
|
v<<=4;
|
|
v+=c;
|
|
}
|
|
}
|
|
else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) {
|
|
s++;
|
|
for (;;) {
|
|
int c=*s++;
|
|
if (c >= _T('0') && c <= _T('7')) c-=_T('0');
|
|
else break;
|
|
v<<=3;
|
|
v+=c;
|
|
}
|
|
}
|
|
else {
|
|
for (;;) {
|
|
int c=*s++ - _T('0');
|
|
if (c < 0 || c > 9) break;
|
|
v*=10;
|
|
v+=c;
|
|
}
|
|
}
|
|
return (int)v;
|
|
}
|
|
|
|
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
|
|
{
|
|
DWORD Ret;
|
|
STARTUPINFO si = {0};
|
|
PROCESS_INFORMATION pi = {0};
|
|
TCHAR command_line[1024];
|
|
TCHAR seekchar=_T(' ');
|
|
TCHAR *cmdline;
|
|
|
|
si.cb = sizeof(si);
|
|
// Make child process use this app's standard files. Not needed because the handles
|
|
// we created when executing this process were inheritable.
|
|
//si.dwFlags = STARTF_USESTDHANDLES;
|
|
//si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
|
|
//si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
//si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
|
|
lstrcpyn(command_line, GetCommandLine(), 1024);
|
|
|
|
cmdline = command_line;
|
|
if (*cmdline == _T('\"')) seekchar = *cmdline++;
|
|
|
|
while (*cmdline && *cmdline != seekchar) cmdline=CharNext(cmdline);
|
|
cmdline=CharNext(cmdline);
|
|
// skip any spaces before the arguments
|
|
while (*cmdline && *cmdline == _T(' ')) cmdline++;
|
|
|
|
Ret = CreateProcess (NULL, cmdline,
|
|
NULL, NULL,
|
|
TRUE, 0,
|
|
NULL, NULL,
|
|
&si, &pi
|
|
);
|
|
|
|
if (Ret)
|
|
{
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
GetExitCodeProcess(pi.hProcess, &Ret);
|
|
CloseHandle (pi.hProcess);
|
|
CloseHandle (pi.hThread);
|
|
ExitProcess(Ret);
|
|
}
|
|
else
|
|
{
|
|
ExitProcess(STATUS_ILLEGAL_INSTRUCTION);
|
|
}
|
|
|
|
return 0; // dummy
|
|
}
|