//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include #include "hammer.h" #include "ProcessWnd.h" #include "osver.h" // memdbgon must be the last include file in a .cpp file!!! #include #define IDC_PROCESSWND_EDIT 1 #define IDC_PROCESSWND_COPYALL 2 LPCTSTR GetErrorString(); CProcessWnd::CProcessWnd() { Font.CreatePointFont(100, "Courier New"); } CProcessWnd::~CProcessWnd() { } BEGIN_MESSAGE_MAP(CProcessWnd, CWnd) ON_BN_CLICKED(IDC_PROCESSWND_COPYALL, OnCopyAll) //{{AFX_MSG_MAP(CProcessWnd) ON_BN_CLICKED(IDC_PROCESSWND_COPYALL, OnCopyAll) ON_WM_TIMER() ON_WM_CREATE() ON_WM_SIZE() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CProcessWnd operations int CProcessWnd::Execute(LPCTSTR pszCmd, ...) { CString strBuf; va_list vl; va_start(vl, pszCmd); while(1) { char *p = va_arg(vl, char*); if(!p) break; strBuf += p; strBuf += " "; } va_end(vl); return Execute(pszCmd, (LPCTSTR)strBuf); } void CProcessWnd::Clear() { m_EditText.Empty(); Edit.SetWindowText(""); Edit.RedrawWindow(); } void CProcessWnd::Append(CString str) { m_EditText += str; if (getOSVersion() >= eWinNT) { Edit.SetWindowText(m_EditText); } else { DWORD length = m_EditText.GetLength() / sizeof(TCHAR); // Gracefully handle 64k edit control display on win9x (display last 64k of text) // Copy to clipboard will work fine, as it copies the m_EditText contents // in its entirety to the clipboard if (length >= 0x0FFFF) { LPTSTR string = m_EditText.GetBuffer(length + 1); LPTSTR offset; offset = string + length - 0x0FFFF; Edit.SetWindowText(offset); m_EditText.ReleaseBuffer(); } else { Edit.SetWindowText(m_EditText); } } Edit.LineScroll(Edit.GetLineCount()); Edit.RedrawWindow(); } int CProcessWnd::Execute(LPCTSTR pszCmd, LPCTSTR pszCmdLine) { int rval = -1; SECURITY_ATTRIBUTES saAttr; HANDLE hChildStdinRd_, hChildStdinWr, hChildStdoutRd_, hChildStdoutWr, hChildStderrWr; // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child's STDOUT. if(CreatePipe(&hChildStdoutRd_, &hChildStdoutWr, &saAttr, 0)) { if(CreatePipe(&hChildStdinRd_, &hChildStdinWr, &saAttr, 0)) { if (DuplicateHandle(GetCurrentProcess(),hChildStdoutWr, GetCurrentProcess(),&hChildStderrWr,0, TRUE,DUPLICATE_SAME_ACCESS)) { /* Now create the child process. */ STARTUPINFO si; memset(&si, 0, sizeof si); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = hChildStdinRd_; si.hStdError = hChildStderrWr; si.hStdOutput = hChildStdoutWr; PROCESS_INFORMATION pi; CString str; str.Format("%s %s", pszCmd, pszCmdLine); if (CreateProcess(NULL, (char*) LPCTSTR(str), NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) { HANDLE hProcess = pi.hProcess; #define BUFFER_SIZE 4096 // read from pipe.. char buffer[BUFFER_SIZE]; BOOL bDone = FALSE; while(1) { DWORD dwCount = 0; DWORD dwRead = 0; // read from input handle PeekNamedPipe( hChildStdoutRd_, NULL, NULL, NULL, &dwCount, NULL); if (dwCount) { dwCount = min (dwCount, (DWORD)BUFFER_SIZE - 1); ReadFile( hChildStdoutRd_, buffer, dwCount, &dwRead, NULL); } if(dwRead) { buffer[dwRead] = 0; Append(buffer); } // check process termination else if(WaitForSingleObject(hProcess, 1000) != WAIT_TIMEOUT) { if(bDone) break; bDone = TRUE; // next time we get it } } rval = 0; } else { SetForegroundWindow(); CString strTmp; strTmp.Format("* Could not execute the command:\r\n %s\r\n", str.GetBuffer()); Append(strTmp); strTmp.Format("* Windows gave the error message:\r\n \"%s\"\r\n", GetErrorString()); Append(strTmp); } CloseHandle(hChildStderrWr); } CloseHandle(hChildStdinRd_); CloseHandle(hChildStdinWr); } CloseHandle(hChildStdoutRd_); CloseHandle(hChildStdoutWr); } return rval; } ///////////////////////////////////////////////////////////////////////////// // CProcessWnd message handlers void CProcessWnd::OnTimer(UINT nIDEvent) { CWnd::OnTimer(nIDEvent); } int CProcessWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; // create big CEdit in window CRect rctClient; GetClientRect(rctClient); CRect rctEdit; rctEdit = rctClient; rctEdit.bottom = rctClient.bottom - 20; Edit.Create(WS_CHILD | WS_BORDER | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN, rctClient, this, IDC_PROCESSWND_EDIT); Edit.SetReadOnly(TRUE); Edit.SetFont(&Font); CRect rctButton; rctButton = rctClient; rctButton.top = rctClient.bottom - 20; m_btnCopyAll.Create("Copy to Clipboard", WS_CHILD | WS_VISIBLE, rctButton, this, IDC_PROCESSWND_COPYALL); m_btnCopyAll.SetButtonStyle(BS_PUSHBUTTON); return 0; } void CProcessWnd::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); // create big CEdit in window CRect rctClient; GetClientRect(rctClient); CRect rctEdit; rctEdit = rctClient; rctEdit.bottom = rctClient.bottom - 20; Edit.MoveWindow(rctEdit); CRect rctButton; rctButton = rctClient; rctButton.top = rctClient.bottom - 20; m_btnCopyAll.MoveWindow(rctButton); } //----------------------------------------------------------------------------- // Purpose: Prepare the process window for display. If it has not been created // yet, register the class and create it. //----------------------------------------------------------------------------- void CProcessWnd::GetReady(void) { if (!IsWindow(m_hWnd)) { CString strClass = AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW), HBRUSH(GetStockObject(WHITE_BRUSH))); CreateEx(0, strClass, "Compile Process Window", WS_OVERLAPPEDWINDOW, 50, 50, 600, 400, AfxGetMainWnd()->GetSafeHwnd(), HMENU(NULL)); } ShowWindow(SW_SHOW); SetActiveWindow(); Clear(); } BOOL CProcessWnd::PreTranslateMessage(MSG* pMsg) { // The edit control won't get keyboard commands from the window without this (at least in Win2k) // The right mouse context menu still will not work in w2k for some reason either, although // it is getting the CONTEXTMENU message (as seen in Spy++) ::TranslateMessage(pMsg); ::DispatchMessage(pMsg); return TRUE; } static void CopyToClipboard(const CString& text) { if (OpenClipboard(NULL)) { if (EmptyClipboard()) { HGLOBAL hglbCopy; LPTSTR tstrCopy; hglbCopy = GlobalAlloc(GMEM_DDESHARE, text.GetLength() + sizeof(TCHAR) ); if (hglbCopy != NULL) { tstrCopy = (LPTSTR) GlobalLock(hglbCopy); strcpy(tstrCopy, (LPCTSTR)text); GlobalUnlock(hglbCopy); SetClipboardData(CF_TEXT, hglbCopy); } } CloseClipboard(); } } void CProcessWnd::OnCopyAll() { // Used to call m_Edit.SetSel(0,1); m_Edit.Copy(); m_Edit.Clear() // but in win9x the clipboard will only receive at most 64k of text from the control CopyToClipboard(m_EditText); }