Mercurial > ecos-v2_0-branch
comparison host/tools/Utils/win32/SubProcess.cpp @ 76:435cced73e2f ecos-v1_3_1-release
eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
| author | jlarmour |
|---|---|
| date | Tue, 28 Mar 2000 14:10:45 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 75:41bf073c0c32 | 76:435cced73e2f |
|---|---|
| 1 //####COPYRIGHTBEGIN#### | |
| 2 // | |
| 3 // ---------------------------------------------------------------------------- | |
| 4 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 5 // | |
| 6 // This program is part of the eCos host tools. | |
| 7 // | |
| 8 // This program is free software; you can redistribute it and/or modify it | |
| 9 // under the terms of the GNU General Public License as published by the Free | |
| 10 // Software Foundation; either version 2 of the License, or (at your option) | |
| 11 // any later version. | |
| 12 // | |
| 13 // This program is distributed in the hope that it will be useful, but WITHOUT | |
| 14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 15 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 16 // more details. | |
| 17 // | |
| 18 // You should have received a copy of the GNU General Public License along with | |
| 19 // this program; if not, write to the Free Software Foundation, Inc., | |
| 20 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| 21 // | |
| 22 // ---------------------------------------------------------------------------- | |
| 23 // | |
| 24 //####COPYRIGHTEND#### | |
| 25 //=========================================================================== | |
| 26 //=========================================================================== | |
| 27 //#####DESCRIPTIONBEGIN#### | |
| 28 // | |
| 29 // Author(s): sdf | |
| 30 // Contact(s): sdf | |
| 31 // Date: 1998/08/11 | |
| 32 // Version: 0.01 | |
| 33 // Purpose: | |
| 34 // Description: This is the implementation of the class which allows for spawning subprocesses | |
| 35 // | |
| 36 // Requires: | |
| 37 // Provides: | |
| 38 // See also: | |
| 39 // Known bugs: | |
| 40 // Usage: | |
| 41 // | |
| 42 //####DESCRIPTIONEND#### | |
| 43 // | |
| 44 //=========================================================================== | |
| 45 #include "subprocess.h" | |
| 46 | |
| 47 const UINT CSubprocess::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF; | |
| 48 | |
| 49 CSubprocess::CSubprocess(bool bVerbose): | |
| 50 m_dwParentThreadId(0), | |
| 51 m_pstrOutput(0), | |
| 52 m_idProcess(0), | |
| 53 m_bThreadRunning(false), | |
| 54 m_bVerbose(bVerbose), | |
| 55 m_dwExitCode(0xffffffff), | |
| 56 m_bAutoDelete(false), | |
| 57 m_hThread(0), | |
| 58 m_pfnLogfunc(0), | |
| 59 m_pLogparam(0), | |
| 60 m_hwndParent(0) | |
| 61 { | |
| 62 } | |
| 63 | |
| 64 // Run (non-blocking and discarding output) | |
| 65 /* static */ unsigned int CSubprocess::Run(LPCTSTR pszCmd,LPCTSTR pszDir) | |
| 66 { | |
| 67 CSubprocess *psp=new CSubprocess(false); | |
| 68 psp->m_bAutoDelete=true; | |
| 69 String strOutput; | |
| 70 return psp->Run(strOutput,pszCmd,pszDir); | |
| 71 } | |
| 72 | |
| 73 unsigned int CSubprocess::Run(LogFunc *pfnLog,void * pLogparam, LPCTSTR pszCmd,LPCTSTR pszDir) | |
| 74 { | |
| 75 unsigned int rc; | |
| 76 if(m_bThreadRunning){ | |
| 77 rc=0; | |
| 78 } else { | |
| 79 m_pfnLogfunc=pfnLog; | |
| 80 m_pLogparam=pLogparam; | |
| 81 m_strCmd=pszCmd; | |
| 82 m_strDir=pszDir; | |
| 83 rc=CreateProcess(); | |
| 84 if(rc){ | |
| 85 ThreadFunc(); | |
| 86 } | |
| 87 } | |
| 88 return rc; | |
| 89 } | |
| 90 | |
| 91 #ifdef _WIN32 | |
| 92 // Non-blocking: WM_SUBPROCESS messages are posted to HWND supplied as argument | |
| 93 unsigned int CSubprocess::Run(HWND hwndParent,LPCTSTR pszCmd,LPCTSTR pszDir) | |
| 94 { | |
| 95 unsigned int rc; | |
| 96 if(m_bThreadRunning){ | |
| 97 rc=0; | |
| 98 } else { | |
| 99 m_hwndParent=hwndParent; | |
| 100 m_strCmd=pszCmd; | |
| 101 m_strDir=pszDir; | |
| 102 rc=CreateProcess(); | |
| 103 if(rc){ | |
| 104 DWORD dwID; | |
| 105 m_bThreadRunning=true; | |
| 106 m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID); | |
| 107 } | |
| 108 } | |
| 109 return rc; | |
| 110 } | |
| 111 | |
| 112 // Non-blocking: WM_SUBPROCESS messages are posted to thread supplied as argument | |
| 113 unsigned int CSubprocess::Run(DWORD dwParentThreadId,LPCTSTR pszCmd,LPCTSTR pszDir) | |
| 114 { | |
| 115 unsigned int rc; | |
| 116 if(m_bThreadRunning){ | |
| 117 rc=0; | |
| 118 } else { | |
| 119 m_dwParentThreadId=dwParentThreadId; | |
| 120 m_strCmd=pszCmd; | |
| 121 m_strDir=pszDir; | |
| 122 rc=CreateProcess(); | |
| 123 if(rc){ | |
| 124 DWORD dwID; | |
| 125 m_bThreadRunning=true; | |
| 126 m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID); | |
| 127 } | |
| 128 } | |
| 129 return rc; | |
| 130 } | |
| 131 #endif | |
| 132 | |
| 133 // Blocking: output is placed in string supplied as argument | |
| 134 unsigned int CSubprocess::Run(String &strOutput,LPCTSTR pszCmd,LPCTSTR pszDir) | |
| 135 { | |
| 136 strOutput=_T(""); | |
| 137 m_strCmd=pszCmd; | |
| 138 m_strDir=pszDir; | |
| 139 m_pstrOutput=&strOutput; | |
| 140 unsigned int rc=CreateProcess(); | |
| 141 if(rc){ | |
| 142 ThreadFunc(); | |
| 143 } | |
| 144 return rc; | |
| 145 } | |
| 146 | |
| 147 CSubprocess::~CSubprocess() | |
| 148 { | |
| 149 Kill(); | |
| 150 | |
| 151 if(m_hThread){ // running non-blocking and thread started (but possibly already finished) | |
| 152 if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){ | |
| 153 // In general this is bad news, but we can't allow the thread to live on if | |
| 154 // the class object is about to be deleted. | |
| 155 if(m_bVerbose){ | |
| 156 Output(_T("*** CSubprocess: Forcibly terminating thread\n")); | |
| 157 } | |
| 158 ::TerminateThread(m_hThread,0); | |
| 159 } | |
| 160 ::CloseHandle(m_hThread); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 #ifdef _WIN32 | |
| 165 unsigned int CSubprocess::CreateProcess() | |
| 166 { | |
| 167 | |
| 168 STARTUPINFO si; // For CreateProcess call | |
| 169 HANDLE hrPipe,hwPipe,hwPipe2; | |
| 170 // Create the anonymous pipe | |
| 171 | |
| 172 SECURITY_ATTRIBUTES saPipe; // Security for anonymous pipe | |
| 173 saPipe.nLength = sizeof(SECURITY_ATTRIBUTES); | |
| 174 saPipe.lpSecurityDescriptor = NULL; | |
| 175 saPipe.bInheritHandle = true; | |
| 176 | |
| 177 ::CreatePipe(&m_hrPipe,&hwPipe,&saPipe,80); | |
| 178 | |
| 179 // In most cases you can get away with using the same anonymous | |
| 180 // pipe write handle for both the child's standard output and | |
| 181 // standard error, but this may cause problems if the child app | |
| 182 // explicitly closes one of its standard output or error handles. If | |
| 183 // that happens, the anonymous pipe will close, since the child's | |
| 184 // standard output and error handles are really the same handle. The | |
| 185 // child won't be able to write to the other write handle since the | |
| 186 // pipe is now gone, and parent reads from the pipe will return | |
| 187 // ERROR_BROKEN_PIPE and child output will be lost. To solve this | |
| 188 // problem, simply duplicate the write end of the pipe to create | |
| 189 // another distinct, separate handle to the write end of the pipe. | |
| 190 // One pipe write handle will serve as standard out, the other as | |
| 191 // standard error. Now *both* write handles must be closed before the | |
| 192 // write end of the pipe actually closes. | |
| 193 | |
| 194 ::DuplicateHandle(::GetCurrentProcess(), // Source process | |
| 195 hwPipe, // Handle to duplicate | |
| 196 ::GetCurrentProcess(), // Destination process | |
| 197 &hwPipe2, // New handle, used as stderr by child | |
| 198 0, // New access flags - ignored since DUPLICATE_SAME_ACCESS | |
| 199 true, // It's inheritable | |
| 200 DUPLICATE_SAME_ACCESS); | |
| 201 | |
| 202 ::CreatePipe(&hrPipe,&m_hwPipe,&saPipe,80); | |
| 203 | |
| 204 memset(&si, 0, sizeof(si)); | |
| 205 si.cb = sizeof(si); | |
| 206 | |
| 207 si.hStdOutput = hwPipe; | |
| 208 si.hStdError = hwPipe2; | |
| 209 si.hStdInput = hrPipe; | |
| 210 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |
| 211 si.wShowWindow = SW_SHOW; | |
| 212 | |
| 213 LPCTSTR pszDir=0; | |
| 214 if(m_strDir.GetLength()){ | |
| 215 pszDir=m_strDir; // NULL = start in current | |
| 216 } | |
| 217 | |
| 218 PROCESS_INFORMATION pi; | |
| 219 unsigned int rc=(::CreateProcess(NULL, // Application name | |
| 220 m_strCmd.GetBuffer(m_strCmd.GetLength()), // Full command line for child | |
| 221 NULL, // Process security descriptor | |
| 222 NULL, // Thread security descriptor | |
| 223 true, // Inherit handles? Also use if STARTF_USESTDHANDLES | |
| 224 // Creation flags of | |
| 225 // CREATE_NEW_PROCESS_GROUP so we can use | |
| 226 // GenerateConsoleCtrlEvent to | |
| 227 // terminate the child | |
| 228 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP, | |
| 229 NULL, // Inherited environment address | |
| 230 pszDir,&si,&pi) ? pi.dwProcessId : 0); | |
| 231 | |
| 232 m_strCmd.ReleaseBuffer(); | |
| 233 String strMsg; | |
| 234 | |
| 235 if(rc){ | |
| 236 m_idProcess=pi.dwProcessId; | |
| 237 m_hProcess=pi.hProcess; | |
| 238 strMsg.Format(_T("*** Process %d created \"%s\"\n"),m_idProcess,(LPCTSTR)m_strCmd); | |
| 239 m_dwExitCode=STILL_ACTIVE; | |
| 240 } else { | |
| 241 strMsg.Format(_T("*** Failed to create process \"%s\"\n"),(LPCTSTR)m_strCmd); | |
| 242 m_dwExitCode=GetLastError(); | |
| 243 } | |
| 244 | |
| 245 if(m_bVerbose){ | |
| 246 Output(strMsg); | |
| 247 } | |
| 248 | |
| 249 ::CloseHandle(hrPipe); | |
| 250 ::CloseHandle(hwPipe); | |
| 251 ::CloseHandle(hwPipe2); | |
| 252 ::CloseHandle(pi.hThread); | |
| 253 | |
| 254 return rc; | |
| 255 | |
| 256 } | |
| 257 #else // UNIX | |
| 258 unsigned int CSubprocess::CreateProcess() | |
| 259 { | |
| 260 int pipe_ends_w[2]; | |
| 261 if (pipe(pipe_ends_w) < 0 ) { | |
| 262 Log(_T("Failed to create pipe_ends_w - %s\n"),strerror(errno)); | |
| 263 } else { | |
| 264 int pipe_ends_r[2]; | |
| 265 if (pipe(pipe_ends_r) < 0 ) { | |
| 266 Log(_T("Failed to create pipe_ends_r - %s\n"),strerror(errno)); | |
| 267 } else { | |
| 268 int pid=fork(); | |
| 269 | |
| 270 switch (pid) { | |
| 271 // Fork failed | |
| 272 case -1: | |
| 273 Log(_T("Failed to create gdb process - %s\n"),strerror(errno)); | |
| 274 pid=0; | |
| 275 break; | |
| 276 case 0: | |
| 277 // Process is created (we're the child) | |
| 278 // No point in outputting except via output streams | |
| 279 | |
| 280 // Input to child process | |
| 281 if (dup2(pipe_ends_w[0], 0) < 0) { | |
| 282 _ftprintf(stderr,(_T("dup2 error\n")); | |
| 283 exit(1); | |
| 284 } | |
| 285 | |
| 286 // Output from process | |
| 287 if (dup2(pipe_ends_r[1], 2) < 0) { | |
| 288 _ftprintf(stderr,(_T("dup2 error\n")); | |
| 289 exit(2); | |
| 290 } | |
| 291 if (dup2(pipe_ends_r[1], 1) < 0) { | |
| 292 _ftprintf(stderr,(_T("dup2 error\n")); | |
| 293 exit(3); | |
| 294 } | |
| 295 setvbuf(stdout,0,_IONBF,0); | |
| 296 setvbuf(stderr,0,_IONBF,0); | |
| 297 if(m_bVerbose){ | |
| 298 strMsg.Format(_T("*** Process %d created \"%s\"\n"),pid,(LPCTSTR)m_strCmd); | |
| 299 } | |
| 300 | |
| 301 StringArray ar; | |
| 302 int argc=m_strCmd.Chop(ar,_TCHAR(' '),true); | |
| 303 TCHAR **argv=new TCHAR *[1+argc]; | |
| 304 int i; | |
| 305 for(i=0;i<argc;i++){ | |
| 306 argv[i]=ar[i]; | |
| 307 } | |
| 308 argv[i]=0; | |
| 309 _texecvp(argv[0], argv); | |
| 310 delete [] argv; | |
| 311 _ftprintf(stderr,(_T("exec error - %s\n"),strerror(errno)); | |
| 312 | |
| 313 exit(4); | |
| 314 | |
| 315 default: | |
| 316 // Process is created (we're the parent) | |
| 317 | |
| 318 TRACE(_T("Forked to create gdb process %s - pid is <%d>\n"), pszCmdline, pid); | |
| 319 if (fcntl(pipe_ends_r[0], F_SETFL, O_NONBLOCK) <0) { | |
| 320 Log(_T("Couldn't set pipe non-blocking - %s\n"),strerror(errno)); | |
| 321 } else { | |
| 322 m_rPipeHandle=(void *)pipe_ends_r[0]; | |
| 323 m_wPipeHandle=(void *)pipe_ends_w[1]; | |
| 324 } | |
| 325 break; | |
| 326 } | |
| 327 close (pipe_ends_r[0]); | |
| 328 close (pipe_ends_r[1]); | |
| 329 } | |
| 330 close (pipe_ends_w[0]); | |
| 331 close (pipe_ends_w[1]); | |
| 332 } | |
| 333 return pid; | |
| 334 } | |
| 335 #endif | |
| 336 | |
| 337 DWORD CSubprocess::ThreadFunc() | |
| 338 | |
| 339 { | |
| 340 | |
| 341 DWORD dwAvail; | |
| 342 | |
| 343 while (::PeekNamedPipe(m_hrPipe, NULL, 0, 0, &dwAvail, NULL)){ | |
| 344 if(dwAvail){ | |
| 345 DWORD dwRead; | |
| 346 char *buf=new char[dwAvail+1]; | |
| 347 if(!::ReadFile(m_hrPipe, buf, dwAvail, &dwRead, NULL)){ | |
| 348 delete [] buf; | |
| 349 break; | |
| 350 } | |
| 351 buf[dwRead]='\0'; | |
| 352 Output(String::CStrToUnicodeStr(buf)); | |
| 353 delete [] buf; | |
| 354 } else { | |
| 355 DWORD dw; | |
| 356 ::GetExitCodeProcess(m_hProcess, &dw); | |
| 357 | |
| 358 if(STILL_ACTIVE!=dw){ | |
| 359 break; | |
| 360 } | |
| 361 | |
| 362 Sleep(250); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 ::GetExitCodeProcess(m_hProcess, &m_dwExitCode); | |
| 367 String strMsg; | |
| 368 if(m_bVerbose){ | |
| 369 if(PROCESS_KILL_EXIT_CODE==m_dwExitCode){ | |
| 370 strMsg.Format(_T("\n*** Process %d killed\n"),m_idProcess); | |
| 371 } else { | |
| 372 strMsg.Format(_T("\n*** Process %d terminated (rc=%d)\n"),m_idProcess,m_dwExitCode); | |
| 373 } | |
| 374 Output(strMsg); | |
| 375 } | |
| 376 | |
| 377 if(m_dwParentThreadId || m_hwndParent){ | |
| 378 Post(0); | |
| 379 } | |
| 380 m_idProcess=0; | |
| 381 | |
| 382 ::CloseHandle(m_hrPipe); | |
| 383 ::CloseHandle(m_hwPipe); | |
| 384 ::CloseHandle(m_hProcess); | |
| 385 m_bThreadRunning=false; | |
| 386 DWORD rc=m_dwExitCode; // protect against auto-delete | |
| 387 | |
| 388 if(m_bAutoDelete){ | |
| 389 delete this; | |
| 390 } | |
| 391 | |
| 392 return rc; | |
| 393 | |
| 394 } | |
| 395 | |
| 396 bool CSubprocess::Kill() | |
| 397 { | |
| 398 if(0!=m_idProcess){ | |
| 399 //TRACE(_T("*** Killing process %d(ctrl/c)\n"),m_idProcess); | |
| 400 //::GenerateConsoleCtrlEvent( CTRL_C_EVENT, m_idProcess); | |
| 401 //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){ | |
| 402 //TRACE(_T("*** Killing process %d(ctrl/break)\n"),m_idProcess); | |
| 403 //::GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, m_idProcess); | |
| 404 //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){ | |
| 405 if(m_bVerbose){ | |
| 406 String strMsg; | |
| 407 strMsg.Format(_T("*** Killing process %d (TerminateProcess)\n"),m_idProcess); | |
| 408 Output(strMsg); | |
| 409 } | |
| 410 ::TerminateProcess(m_hProcess,PROCESS_KILL_EXIT_CODE); | |
| 411 //} | |
| 412 //} | |
| 413 } | |
| 414 return 0==m_idProcess; | |
| 415 } | |
| 416 | |
| 417 void CSubprocess::Output (LPCTSTR psz) | |
| 418 { | |
| 419 if(m_dwParentThreadId || m_hwndParent){ | |
| 420 TCHAR *pszCopy=new TCHAR[1+_tcslen(psz)]; | |
| 421 _tcscpy(pszCopy,psz); | |
| 422 Post((LPARAM)pszCopy); | |
| 423 } else if (m_pfnLogfunc) { | |
| 424 m_pfnLogfunc(m_pLogparam,psz); | |
| 425 } else { | |
| 426 assert(NULL!=m_pstrOutput); | |
| 427 (*m_pstrOutput)+=psz; | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 | |
| 432 void CSubprocess::Post(LPARAM lParam) | |
| 433 { | |
| 434 if(m_hwndParent){ | |
| 435 ::PostMessage(m_hwndParent,WM_SUBPROCESS,(WPARAM)this,lParam); | |
| 436 } else { | |
| 437 assert(m_dwParentThreadId); | |
| 438 ::PostThreadMessage(m_dwParentThreadId,WM_SUBPROCESS,(WPARAM)m_idProcess,lParam); | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 void CSubprocess::CygKill() | |
| 443 { | |
| 444 | |
| 445 // Map win32 pids to cygwin format | |
| 446 PtrArray map; | |
| 447 | |
| 448 int nKillCount; | |
| 449 int nGid=-1; | |
| 450 do { | |
| 451 nKillCount=0; | |
| 452 // Execute a "ps -l" to find out what's out there in Cygwin land | |
| 453 CSubprocess sp; | |
| 454 String strOutput; | |
| 455 sp.Run(strOutput,_T("ps -l")); | |
| 456 if(0==strOutput.GetLength()){ | |
| 457 if(m_bVerbose){ | |
| 458 Output(_T("*** Warning: could not run ps to effect a Cygkill()\n")); | |
| 459 } | |
| 460 } else { | |
| 461 // Walk through the lines by line and extract what we need | |
| 462 for(LPCTSTR psz=-1+(LPCTSTR)strOutput;psz;psz=_tcschr(psz,_TCHAR('\n'))){ | |
| 463 psz++; // move over the '\n' | |
| 464 CygProcessInfo cpi; | |
| 465 if(0==_tcsstr(psz,_T("ps.exe")) && 4==_stscanf(2+psz,_T("%d %d %d %d"),&cpi.nPid,&cpi.nPpid,&cpi.nPgid,&cpi.nWinpid) && cpi.nWinpid){ | |
| 466 for(unsigned int i=0;i<map.size();i++){ | |
| 467 if(((CygProcessInfo *)map[i])->nWinpid==cpi.nWinpid){ | |
| 468 break; | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 if(i==map.size()){ | |
| 473 // No existing match - add: | |
| 474 map.push_back(new CygProcessInfo(cpi)); | |
| 475 if((int)m_idProcess==cpi.nWinpid){ | |
| 476 nGid=cpi.nPgid; | |
| 477 } | |
| 478 } | |
| 479 } | |
| 480 } | |
| 481 | |
| 482 // Kill everything in the same group as our process | |
| 483 if(-1!=nGid){ | |
| 484 for (unsigned int i=0;i<map.size();i++){ | |
| 485 CygProcessInfo *pcpi=(CygProcessInfo *)map[i]; | |
| 486 if(pcpi->nPgid==nGid){ | |
| 487 // Check that the process exists | |
| 488 HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid); | |
| 489 if(hProcess){ | |
| 490 CloseHandle(hProcess); | |
| 491 String strCmd,strOut; | |
| 492 strCmd.Format(_T("kill %d"),pcpi->nPid); | |
| 493 pcpi->nPgid=-pcpi->nPgid; // prevent this being done more than once | |
| 494 CSubprocess::Run(strCmd); | |
| 495 nKillCount++; | |
| 496 } | |
| 497 } | |
| 498 } | |
| 499 } | |
| 500 | |
| 501 } | |
| 502 } while (nKillCount>0); | |
| 503 | |
| 504 // Now we've sent a kill to every process. Go back and check they are really dead (use windows API this time) | |
| 505 for(unsigned int i=0;i<map.size();i++){ | |
| 506 CygProcessInfo *pcpi=(CygProcessInfo *)map[i]; | |
| 507 if(abs(pcpi->nPgid)==nGid){ | |
| 508 HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid); | |
| 509 if(hProcess){ | |
| 510 ::TerminateProcess(hProcess,0x7fffffff); | |
| 511 CloseHandle(hProcess); | |
| 512 } | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 for(i=0;i<map.size();i++){ | |
| 517 delete (CygProcessInfo *)map[i]; | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 void CSubprocess::Send(LPCTSTR str) | |
| 522 { | |
| 523 int nLength=_tcslen(str); | |
| 524 char *psz=new char[1+nLength]; | |
| 525 #ifdef _UNICODE | |
| 526 WideCharToMultiByte(CP_ACP, 0, str, -1, psz, 1+nLength, NULL, NULL); | |
| 527 #else | |
| 528 strcpy(psz,str); | |
| 529 #endif | |
| 530 | |
| 531 DWORD dwWritten; | |
| 532 ::WriteFile(m_hwPipe,psz,nLength,&dwWritten,0); | |
| 533 delete [] psz; | |
| 534 } | |
| 535 | |
| 536 | |
| 537 | |
| 538 void CSubprocess::CloseInput() | |
| 539 { | |
| 540 ::CloseHandle(m_hwPipe); | |
| 541 } | |
| 542 |
