private void StartProcess()
        {
            bool retValue;

            // Create startup info for new console process.
            STARTUPINFO startupInfo = new STARTUPINFO();

            startupInfo.cb          = Marshal.SizeOf(startupInfo);
            startupInfo.dwFlags     = StartFlags.STARTF_USESHOWWINDOW;
            startupInfo.wShowWindow = _consoleVisible ? WindowShowStyle.Show : WindowShowStyle.Hide;
            startupInfo.lpTitle     = this.ConsoleTitle ?? "Console";

            SECURITY_ATTRIBUTES procAttrs   = new SECURITY_ATTRIBUTES();
            SECURITY_ATTRIBUTES threadAttrs = new SECURITY_ATTRIBUTES();

            procAttrs.nLength   = Marshal.SizeOf(procAttrs);
            threadAttrs.nLength = Marshal.SizeOf(threadAttrs);

            // Set environment variables for new process.
            IntPtr pEnvironment = IntPtr.Zero;

            // Start new console process.
            retValue = WinApi.CreateProcess(null, this.CommandLine, ref procAttrs, ref threadAttrs, false,
                                            CreationFlags.CREATE_NEW_CONSOLE | CreationFlags.CREATE_SUSPENDED, pEnvironment, null,
                                            ref startupInfo, out _procInfo);
            if (!retValue)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error(),
                                         "Unable to create new console process.");
            }

            _proc = Process.GetProcessById(_procInfo.dwProcessId);

            // Create objects in shared memory.
            CreateSharedObjects(_procInfo.dwProcessId);

            // Write startup parameters to shared memory.
            unsafe
            {
                ConsoleParams *consoleParams = (ConsoleParams *)_consoleParams.Get();

                consoleParams->ConsoleMainThreadId = _procInfo.dwThreadId;
                consoleParams->ParentProcessId     = Process.GetCurrentProcess().Id;
                consoleParams->NotificationTimeout = 10;
                consoleParams->RefreshInterval     = 100;
                consoleParams->Rows          = this.ConsoleInitialWindowHeight;
                consoleParams->Columns       = this.ConsoleInitialWindowWidth;
                consoleParams->BufferRows    = this.ConsoleInitialBufferHeight;
                consoleParams->BufferColumns = this.ConsoleInitialBufferWidth;
            }
        }
        private void Dispose(bool disposing)
        {
            lock (_disposeLock)
            {
                if (!_isDisposed)
                {
                    if (disposing)
                    {
                        // Dispose managed resources.

                        // Dispose wait handles.
                        if (_procSafeWaitHandle != null)
                        {
                            _procSafeWaitHandle.Dispose();
                        }

                        // Abort monitor thread.
                        if (_monitorThread != null)
                        {
                            _monitorThread.Abort();
                        }

                        if (_consoleParams.IsAvailable)
                        {
                            // Close console window.
                            unsafe
                            {
                                ConsoleParams *consoleParams = (ConsoleParams *)_consoleParams.Get();

                                if (consoleParams->ConsoleWindowHandle != IntPtr.Zero)
                                {
                                    WinApi.SendMessage(consoleParams->ConsoleWindowHandle, WinApi.WM_CLOSE,
                                                       IntPtr.Zero, IntPtr.Zero);
                                }
                            }
                        }

                        // Dispose shared memory objects.
                        if (_consoleParams != null)
                        {
                            _consoleParams.Dispose();
                        }
                        if (_consoleScreenInfo != null)
                        {
                            _consoleScreenInfo.Dispose();
                        }
                        if (_consoleCursorInfo != null)
                        {
                            _consoleCursorInfo.Dispose();
                        }
                        if (_consoleBufferInfo != null)
                        {
                            _consoleBufferInfo.Dispose();
                        }
                        if (_consoleBuffer != null)
                        {
                            _consoleBuffer.Dispose();
                        }
                        if (_consoleCopyInfo != null)
                        {
                            _consoleCopyInfo.Dispose();
                        }
                        if (_consolePasteInfo != null)
                        {
                            _consolePasteInfo.Dispose();
                        }
                        if (_consoleMouseEvent != null)
                        {
                            _consoleMouseEvent.Dispose();
                        }
                        if (_consoleNewSizeInfo != null)
                        {
                            _consoleNewSizeInfo.Dispose();
                        }
                        if (_consoleNewScrollPos != null)
                        {
                            _consoleNewScrollPos.Dispose();
                        }

                        //// Kill console process.
                        //if (_process != null)
                        //{
                        //    _process.Kill();
                        //    _process.Dispose();
                        //}
                    }

                    // Dispose unmanaged resources.
                }

                _isDisposed = true;
            }
        }
        private void MonitorThread()
        {
            ProcessWaitHandle procWaitHandle = null;

            WaitHandle[] waitHandles;

            try
            {
                unsafe
                {
                    // Get pointers to shared memory objects.
                    ConsoleParams *consoleParams = (ConsoleParams *)_consoleParams.Get();
                    CONSOLE_SCREEN_BUFFER_INFO *consoleScreenInfo = (CONSOLE_SCREEN_BUFFER_INFO *)
                                                                    _consoleScreenInfo.Get();
                    ConsoleBufferInfo *consoleBufferInfo = (ConsoleBufferInfo *)_consoleBufferInfo.Get();

                    // Keep waiting for new events until process has exitted or thread is aborted.
                    procWaitHandle = new ProcessWaitHandle(_procSafeWaitHandle);
                    waitHandles    = new WaitHandle[] { procWaitHandle, _consoleBufferInfo.RequestEvent };

                    // Loop until console has exitted.
                    while (WaitHandle.WaitAny(waitHandles) > 0)
                    {
                        // Get current window and buffer size.
                        int columns       = consoleScreenInfo->srWindow.Right - consoleScreenInfo->srWindow.Left + 1;
                        int rows          = consoleScreenInfo->srWindow.Bottom - consoleScreenInfo->srWindow.Top + 1;
                        int bufferColumns = consoleScreenInfo->dwSize.X;
                        int bufferRows    = consoleScreenInfo->dwSize.Y;

                        // Check if window size has changed.
                        if (consoleParams->Columns != columns || consoleParams->Rows != rows)
                        {
                            consoleParams->Columns = columns;
                            consoleParams->Rows    = rows;

                            // Raise event, window has been resized.
                            if (ConsoleWindowResized != null)
                            {
                                ConsoleWindowResized(this, new EventArgs());
                            }
                        }

                        // Check if buffer size has changed.
                        if ((consoleParams->BufferColumns != 0 &&
                             consoleParams->BufferColumns != bufferColumns) ||
                            (consoleParams->BufferRows != 0 && consoleParams->BufferRows != bufferRows))
                        {
                            consoleParams->BufferColumns = bufferColumns;
                            consoleParams->BufferRows    = bufferRows;

                            // Raise event, buffer has been resized.
                            if (ConsoleBufferResized != null)
                            {
                                ConsoleBufferResized(this, new EventArgs());
                            }
                        }

                        if (consoleBufferInfo->NewDataFound || consoleBufferInfo->CursorPositionChanged)
                        {
                            // Raise event, console has sent new data.
                            if (ConsoleNewData != null)
                            {
                                ConsoleNewData(this, new EventArgs());
                            }

                            // Check if new data was found.
                            if (consoleBufferInfo->NewDataFound)
                            {
                                // Raise event, buffer data has changed.
                                if (ConsoleBufferChanged != null)
                                {
                                    ConsoleBufferChanged(this, new EventArgs());
                                }
                            }

                            // Check if cursor posistion has changed.
                            if (consoleBufferInfo->CursorPositionChanged)
                            {
                                // Raise event, cursor position has changed.
                                if (ConsoleCursorPositionChanged != null)
                                {
                                    ConsoleCursorPositionChanged(this,
                                                                 new EventArgs());
                                }
                            }
                        }
                    }
                }
            }
            catch (ThreadAbortException)
            {
            }
            finally
            {
                if (procWaitHandle != null)
                {
                    procWaitHandle.Close();
                }

                // Raise event.
                if (ConsoleClosed != null)
                {
                    ConsoleClosed(this, new EventArgs());
                }
            }
        }
        public void Initialize()
        {
            int retValue;

            // Start new console process.
            StartProcess();

            // Inject DLL into console process.
            InjectDll(this.InjectionDllFileName);

            // Resume main thread of console process.
            WinApi.ResumeThread(_procInfo.hThread);
            WinApi.CloseHandle(_procInfo.hThread);

            // Wait for DLL to set console handle.
            retValue = WinApi.WaitForSingleObject(_consoleParams.RequestEvent.SafeWaitHandle
                                                  .DangerousGetHandle(), 1000);
            if (retValue == WinApi.WAIT_FAILED)
            {
                throw new Win32Exception();
            }
            if (retValue == WinApi.WAIT_TIMEOUT)
            {
                throw new TimeoutException();
            }

            // Create wait handle for console process.
            _procSafeWaitHandle = new SafeWaitHandle(_procInfo.hProcess, false);

            // Set language of console window.
            unsafe
            {
                ConsoleParams *consoleParams = (ConsoleParams *)_consoleParams.Get();

                if (!WinApi.PostMessage(consoleParams->ConsoleWindowHandle, WinApi.WM_INPUTLANGCHANGEREQUEST,
                                        IntPtr.Zero, new IntPtr(CultureInfo.CurrentCulture.KeyboardLayoutId)))
                {
                    throw new Win32Exception();
                }
            }

            // Start thread to monitor console.
            _monitorThread      = new Thread(new ThreadStart(MonitorThread));
            _monitorThread.Name = "Console Monitor";
            _monitorThread.Start();

            // Resume monitor thread.
            unsafe
            {
                ConsoleParams *consoleParams = (ConsoleParams *)_consoleParams.Get();
                IntPtr         hHookThread   = WinApi.OpenThread(ThreadAccess.ALL_ACCESS, false,
                                                                 consoleParams->HookThreadId);

                if (WinApi.ResumeThread(hHookThread) == -1)
                {
                    throw new Win32Exception();
                }
                WinApi.CloseHandle(hHookThread);
            }

            // Raise event.
            if (ConsoleOpened != null)
            {
                ConsoleOpened(this, new EventArgs());
            }
        }