/* Copyright (c) 2002 Robert Rainwater 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 #include #include #include // 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 }