| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043 | using System;using System.Collections.Generic;using System.Diagnostics;using System.Globalization;using System.IO;using System.Threading;using Microsoft.Win32;using Microsoft.Win32.SafeHandles;using System.Runtime.InteropServices;using System.Reflection;using System.Security.Principal;using System.Security.AccessControl;namespace WinSCP{    internal class ExeSessionProcess : IDisposable    {        public event OutputDataReceivedEventHandler OutputDataReceived;        public bool HasExited { get { return _process.HasExited; } }        public int ExitCode { get { return _process.ExitCode; } }        public static ExeSessionProcess CreateForSession(Session session)        {            return new ExeSessionProcess(session, true, null);        }        public static ExeSessionProcess CreateForConsole(Session session, string additionalArguments)        {            return new ExeSessionProcess(session, false, additionalArguments);        }        private ExeSessionProcess(Session session, bool useXmlLog, string additionalArguments)        {            _session = session;            _logger = session.Logger;            _incompleteLine = string.Empty;            using (_logger.CreateCallstack())            {                string executablePath = GetExecutablePath();                _logger.WriteLine("EXE executable path resolved to {0}", executablePath);                string assemblyFilePath = _logger.GetAssemblyFilePath();                FileVersionInfo assemblyVersion = null;                if (assemblyFilePath != null)                {                    assemblyVersion = FileVersionInfo.GetVersionInfo(assemblyFilePath);                }                CheckVersion(executablePath, assemblyVersion);                string configSwitch;                if (_session.DefaultConfigurationInternal)                {                    configSwitch = "/ini=nul ";                }                else                {                    if (!string.IsNullOrEmpty(_session.IniFilePathInternal))                    {                        configSwitch = string.Format(CultureInfo.InvariantCulture, "/ini=\"{0}\" ", _session.IniFilePathInternal);                    }                    else                    {                        configSwitch = "";                    }                }                string logSwitch = null;                if (!string.IsNullOrEmpty(_session.SessionLogPath))                {                    logSwitch = string.Format(CultureInfo.InvariantCulture, "/log=\"{0}\" ", LogPathEscape(_session.SessionLogPath));                }                string xmlLogSwitch;                if (useXmlLog)                {                    xmlLogSwitch = string.Format(CultureInfo.InvariantCulture, "/xmllog=\"{0}\" /xmlgroups /xmllogrequired ", LogPathEscape(_session.XmlLogPath));                }                else                {                    xmlLogSwitch = "";                }                string logLevelSwitch = null;                if (_session.DebugLogLevel > 0)                {                    logLevelSwitch = string.Format(CultureInfo.InvariantCulture, "/loglevel={0} ", _session.DebugLogLevel);                }                string assemblyVersionStr =                    (assemblyVersion == null) ? "unk" :                    string.Format(CultureInfo.InvariantCulture, "{0}.{1}.{2} ", assemblyVersion.ProductMajorPart, assemblyVersion.ProductMinorPart, assemblyVersion.ProductBuildPart);                string assemblyVersionSwitch =                    string.Format(CultureInfo.InvariantCulture, "/dotnet={0} ", assemblyVersionStr);                string arguments =                    xmlLogSwitch + "/nointeractiveinput " + assemblyVersionSwitch +                    configSwitch + logSwitch + logLevelSwitch + _session.AdditionalExecutableArguments;                Tools.AddRawParameters(ref arguments, _session.RawConfiguration, "/rawconfig", false);                if (!string.IsNullOrEmpty(additionalArguments))                {                    arguments += " " + additionalArguments;                }                _process = new Process();                _process.StartInfo.FileName = executablePath;                _process.StartInfo.WorkingDirectory = Path.GetDirectoryName(executablePath);                _process.StartInfo.Arguments = arguments;                _process.StartInfo.UseShellExecute = false;                _process.Exited += ProcessExited;            }        }        private static string LogPathEscape(string path)        {            return Tools.ArgumentEscape(path).Replace("!", "!!");        }        public void Abort()        {            using (_logger.CreateCallstack())            {                lock (_lock)                {                    if ((_process != null) && !_process.HasExited)                    {                        _process.Kill();                    }                }            }        }        public void Start()        {            using (_logger.CreateCallstack())            {                InitializeConsole();                InitializeChild();            }        }        private void InitializeChild()        {            using (_logger.CreateCallstack())            {                // The /console is redundant for CreateForConsole                _process.StartInfo.Arguments += string.Format(CultureInfo.InvariantCulture, " /console /consoleinstance={0}", _instanceName);#if !NETSTANDARD                // When running under IIS in "impersonated" mode, the process starts, but does not do anything.                // Supposedly it "displayes" some invisible error message when starting and hangs.                // Running it "as the user" helps, eventhough it already runs as the user.                // These's probably some difference between "run as" and impersonations                if (!string.IsNullOrEmpty(_session.ExecutableProcessUserName))                {                    _logger.WriteLine("Will run process as {0}", _session.ExecutableProcessUserName);                    _process.StartInfo.UserName = _session.ExecutableProcessUserName;                    _process.StartInfo.Password = _session.ExecutableProcessPassword;                    // One of the hints for resolving C0000142 error (see below)                    // was setting this property, so that an environment is correctly loaded,                    // so DLLs can be found and loaded.                    _process.StartInfo.LoadUserProfile = true;                    // Without granting both window station and desktop access permissions,                    // WinSCP process aborts with C0000142 (DLL Initialization Failed) error,                    // when "running as user"                    _logger.WriteLine("Granting access to window station");                    try                    {                        IntPtr windowStation = UnsafeNativeMethods.GetProcessWindowStation();                        GrantAccess(windowStation, (int)WindowStationRights.AllAccess);                    }                    catch (Exception e)                    {                        throw _logger.WriteException(new SessionLocalException(_session, "Error granting access to window station", e));                    }                    _logger.WriteLine("Granting access to desktop");                    try                    {                        IntPtr desktop = UnsafeNativeMethods.GetThreadDesktop(UnsafeNativeMethods.GetCurrentThreadId());                        GrantAccess(desktop, (int)DesktopRights.AllAccess);                    }                    catch (Exception e)                    {                        throw _logger.WriteException(new SessionLocalException(_session, "Error granting access to desktop", e));                    }                }#endif                _logger.WriteLine("Starting \"{0}\" {1}", _process.StartInfo.FileName, _process.StartInfo.Arguments);                _process.Start();                _logger.WriteLine("Started process {0}", _process.Id);                _thread = new Thread(ProcessEvents)                {                    IsBackground = true                };                _thread.Start();            }        }        // Handles returned by GetProcessWindowStation and GetThreadDesktop should not be closed        internal class NoopSafeHandle : SafeHandle        {            public NoopSafeHandle(IntPtr handle) :                base(handle, false)            {            }            public override bool IsInvalid            {                get { return false; }            }            protected override bool ReleaseHandle()            {                return true;            }        }#if !NETSTANDARD        private void GrantAccess(IntPtr handle, int accessMask)        {            using (SafeHandle safeHandle = new NoopSafeHandle(handle))            {                GenericSecurity security =                    new GenericSecurity(false, ResourceType.WindowObject, safeHandle, AccessControlSections.Access);                security.AddAccessRule(                    new GenericAccessRule(new NTAccount(_session.ExecutableProcessUserName), accessMask, AccessControlType.Allow));                security.Persist(safeHandle, AccessControlSections.Access);            }        }#endif        private void ProcessExited(object sender, EventArgs e)        {            _logger.WriteLine("Process {0} exited with exit code {1}", _process.Id, _process.ExitCode);        }        private bool AbortedOrExited()        {            if (_abort)            {                _logger.WriteLine("Aborted");                return true;            }            else if (_process.HasExited)            {                _logger.WriteLine("Exited");                return true;            }            else            {                return false;            }        }        private void ProcessEvents()        {            using (_logger.CreateCallstack())            {                while (!AbortedOrExited())                {                    _logger.WriteLineLevel(1, "Waiting for request event");                    // Keep in sync with a delay in SessionLogReader.DoRead                    if (_requestEvent.WaitOne(100, false))                    {                        _logger.WriteLineLevel(1, "Got request event");                        ProcessEvent();                    }                    if (_logger.LogLevel >= 1)                    {                        _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "2nd generation collection count: {0}", GC.CollectionCount(2)));                        _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Total memory allocated: {0}", GC.GetTotalMemory(false)));                    }                }            }        }        private void ProcessEvent()        {            using (_logger.CreateCallstack())            {                using (ConsoleCommStruct commStruct = AcquireCommStruct())                {                    switch (commStruct.Event)                    {                        case ConsoleEvent.Print:                            ProcessPrintEvent(commStruct.PrintEvent);                            break;                        case ConsoleEvent.Input:                            ProcessInputEvent(commStruct.InputEvent);                            break;                        case ConsoleEvent.Choice:                            ProcessChoiceEvent(commStruct.ChoiceEvent);                            break;                        case ConsoleEvent.Title:                            ProcessTitleEvent(commStruct.TitleEvent);                            break;                        case ConsoleEvent.Init:                            ProcessInitEvent(commStruct.InitEvent);                            break;                        case ConsoleEvent.Progress:                            ProcessProgressEvent(commStruct.ProgressEvent);                            break;                        default:                            throw _logger.WriteException(new NotImplementedException());                    }                }                _responseEvent.Set();            }        }        private void ProcessChoiceEvent(ConsoleChoiceEventStruct e)        {            using (_logger.CreateCallstack())            {                _logger.WriteLine(                    "Options: [{0}], Timer: [{1}], Timeouting: [{2}], Timeouted: [{3}], Break: [{4}]",                    e.Options, e.Timer, e.Timeouting, e.Timeouted, e.Break);                QueryReceivedEventArgs args = new QueryReceivedEventArgs                {                    Message = e.Message                };                _session.ProcessChoice(args);                if (args.SelectedAction == QueryReceivedEventArgs.Action.None)                {                    if (e.Timeouting)                    {                        Thread.Sleep((int)e.Timer);                        e.Result = e.Timeouted;                    }                    else                    {                        e.Result = e.Break;                    }                }                else if (args.SelectedAction == QueryReceivedEventArgs.Action.Continue)                {                    if (e.Timeouting)                    {                        Thread.Sleep((int)e.Timer);                        e.Result = e.Timeouted;                    }                    else                    {                        e.Result = e.Continue;                    }                }                else if (args.SelectedAction == QueryReceivedEventArgs.Action.Abort)                {                    e.Result = e.Break;                }                _logger.WriteLine("Options Result: [{0}]", e.Result);            }        }        private void ProcessTitleEvent(ConsoleTitleEventStruct e)        {            using (_logger.CreateCallstack())            {                _logger.WriteLine("Not-supported title event [{0}]", e.Title);            }        }        private void ProcessInputEvent(ConsoleInputEventStruct e)        {            using (_logger.CreateCallstack())            {                while (!AbortedOrExited())                {                    lock (_input)                    {                        if (_input.Count > 0)                        {                            e.Str = _input[0];                            e.Result = true;                            _input.RemoveAt(0);                            Print(false, false, _log[0] + "\n");                            _log.RemoveAt(0);                            return;                        }                    }                    _inputEvent.WaitOne(100, false);                }            }        }        private void Print(bool fromBeginning, bool error, string message)        {            if (fromBeginning && ((message.Length == 0) || (message[0] != '\n')))            {                _lastFromBeginning = message;                _logger.WriteLine("Buffered from-beginning message [{0}]", _lastFromBeginning);                OutputDataReceived?.Invoke(this, null);            }            else            {                if (!string.IsNullOrEmpty(_lastFromBeginning))                {                    AddToOutput(_lastFromBeginning, false);                    _lastFromBeginning = null;                }                if (fromBeginning && (message.Length > 0) && (message[0] == '\n'))                {                    AddToOutput("\n", false);                    _lastFromBeginning = message.Substring(1);                    _logger.WriteLine("Buffered from-beginning message [{0}]", _lastFromBeginning);                }                else                {                    AddToOutput(message, error);                }            }        }        private void AddToOutput(string message, bool error)        {            string[] lines = (_incompleteLine + message).Split(new[] { '\n' });            _incompleteLine = lines[lines.Length - 1];            for (int i = 0; i < lines.Length - 1; ++i)            {                OutputDataReceived?.Invoke(this, new OutputDataReceivedEventArgs(lines[i], error));            }        }        private void ProcessPrintEvent(ConsolePrintEventStruct e)        {            _logger.WriteLineLevel(1, string.Format(CultureInfo.CurrentCulture, "Print: {0}", e.Message));            Print(e.FromBeginning, e.Error, e.Message);        }        private void ProcessInitEvent(ConsoleInitEventStruct e)        {            using (_logger.CreateCallstack())            {                e.InputType = 3; // pipe                e.OutputType = 3; // pipe                e.WantsProgress = _session.WantsProgress;            }        }        private void ProcessProgressEvent(ConsoleProgressEventStruct e)        {            using (_logger.CreateCallstack())            {                _logger.WriteLine(                    "File Name [{0}] - Directory [{1}] - Overall Progress [{2}] - File Progress [{3}] - CPS [{4}]",                    e.FileName, e.Directory, e.OverallProgress, e.FileProgress, e.CPS);                if (!_cancel)                {                    FileTransferProgressEventArgs args = new FileTransferProgressEventArgs();                    switch (e.Operation)                    {                        case ConsoleProgressEventStruct.ProgressOperation.Copy:                            args.Operation = ProgressOperation.Transfer;                            break;                        default:                            throw _logger.WriteException(new ArgumentOutOfRangeException("Unknown progress operation", (Exception)null));                    }                    switch (e.Side)                    {                        case ConsoleProgressEventStruct.ProgressSide.Local:                            args.Side = ProgressSide.Local;                            break;                        case ConsoleProgressEventStruct.ProgressSide.Remote:                            args.Side = ProgressSide.Remote;                            break;                        default:                            throw _logger.WriteException(new ArgumentOutOfRangeException("Unknown progress side", (Exception)null));                    }                    args.FileName = e.FileName;                    args.Directory = e.Directory;                    args.OverallProgress = ((double)e.OverallProgress) / 100;                    args.FileProgress = ((double)e.FileProgress) / 100;                    args.CPS = (int)e.CPS;                    args.Cancel = false;                    _session.ProcessProgress(args);                }                if (_cancel)                {                    e.Cancel = true;                }            }        }        private void InitializeConsole()        {            using (_logger.CreateCallstack())            {                int attempts = 0;                Random random = new Random();                int process = Process.GetCurrentProcess().Id;                do                {                    if (attempts > MaxAttempts)                    {                        throw _logger.WriteException(new SessionLocalException(_session, "Cannot find unique name for event object."));                    }                    int instanceNumber = random.Next(1000);                    _instanceName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}_{2}", process, GetHashCode(), instanceNumber);                    _logger.WriteLine("Trying event {0}", _instanceName);                    if (!TryCreateEvent(ConsoleEventRequest + _instanceName, out _requestEvent))                    {                        _logger.WriteLine("Event {0} is not unique", _instanceName);                        _requestEvent.Close();                        _requestEvent = null;                    }                    else                    {                        _logger.WriteLine("Event {0} is unique", _instanceName);                        _responseEvent = CreateEvent(ConsoleEventResponse + _instanceName);                        _cancelEvent = CreateEvent(ConsoleEventCancel + _instanceName);                        string fileMappingName = ConsoleMapping + _instanceName;                        _fileMapping = CreateFileMapping(fileMappingName);                        if (Marshal.GetLastWin32Error() == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)                        {                            throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "File mapping {0} already exists", fileMappingName)));                        }                        if (_fileMapping.IsInvalid)                        {                            throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "Cannot create file mapping {0}", fileMappingName)));                        }                    }                    ++attempts;                }                while (_requestEvent == null);                using (ConsoleCommStruct commStruct = AcquireCommStruct())                {                    commStruct.InitHeader();                }                if (_session.GuardProcessWithJobInternal)                {                    string jobName = ConsoleJob + _instanceName;                    _job = new Job(_logger, jobName);                }            }        }        private SafeFileHandle CreateFileMapping(string fileMappingName)        {            unsafe            {                IntPtr securityAttributesPtr = IntPtr.Zero;#if !NETSTANDARD                // We use the EventWaitHandleSecurity only to generate the descriptor binary form                // that does not differ for object types, so we abuse the existing "event handle" implementation,                // not to have to create the file mapping SecurityAttributes via P/Invoke.                // .NET 4 supports MemoryMappedFile and MemoryMappedFileSecurity natively already                EventWaitHandleSecurity security = CreateSecurity((EventWaitHandleRights)FileMappingRights.AllAccess);                if (security != null)                {                    SecurityAttributes securityAttributes = new SecurityAttributes();                    securityAttributes.nLength = (uint)Marshal.SizeOf(securityAttributes);                    byte[] descriptorBinaryForm = security.GetSecurityDescriptorBinaryForm();                    byte * buffer = stackalloc byte[descriptorBinaryForm.Length];                    for (int i = 0; i < descriptorBinaryForm.Length; i++)                    {                        buffer[i] = descriptorBinaryForm[i];                    }                    securityAttributes.lpSecurityDescriptor = (IntPtr)buffer;                    int length = Marshal.SizeOf(typeof(SecurityAttributes));                    securityAttributesPtr = Marshal.AllocHGlobal(length);                    Marshal.StructureToPtr(securityAttributes, securityAttributesPtr, false);                }#endif                return                    UnsafeNativeMethods.CreateFileMapping(                        new SafeFileHandle(new IntPtr(-1), true), securityAttributesPtr, FileMapProtection.PageReadWrite, 0,                        ConsoleCommStruct.Size, fileMappingName);            }        }        private ConsoleCommStruct AcquireCommStruct()        {            return new ConsoleCommStruct(_session, _fileMapping);        }        private bool TryCreateEvent(string name, out EventWaitHandle ev)        {            _logger.WriteLine("Creating event {0}", name);            string securityDesc;#if !NETSTANDARD            EventWaitHandleSecurity security = CreateSecurity(EventWaitHandleRights.FullControl);            ev = new EventWaitHandle(false, EventResetMode.AutoReset, name, out bool createdNew, security);            securityDesc = (security != null ? security.GetSecurityDescriptorSddlForm(AccessControlSections.All) : "none");#else            ev = new EventWaitHandle(false, EventResetMode.AutoReset, name, out bool createdNew);            securityDesc = "not impl";#endif            _logger.WriteLine(                "Created event {0} with handle {1} with security {2}, new {3}",                name, ev.SafeWaitHandle.DangerousGetHandle(), securityDesc, createdNew);            return createdNew;        }#if !NETSTANDARD        private EventWaitHandleSecurity CreateSecurity(EventWaitHandleRights eventRights)        {            EventWaitHandleSecurity security = null;            // When "running as user", we have to grant the target user permissions to the objects (events and file mapping) explicitly            if (!string.IsNullOrEmpty(_session.ExecutableProcessUserName))            {                security = new EventWaitHandleSecurity();                IdentityReference si;                try                {                    si = new NTAccount(_session.ExecutableProcessUserName);                }                catch (Exception e)                {                    throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.CurrentCulture, "Error resolving account {0}", _session.ExecutableProcessUserName), e));                }                EventWaitHandleAccessRule rule =                    new EventWaitHandleAccessRule(                        si, eventRights, AccessControlType.Allow);                security.AddAccessRule(rule);            }            return security;        }#endif        private EventWaitHandle CreateEvent(string name)        {            if (!TryCreateEvent(name, out EventWaitHandle ev))            {                throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "Event {0} already exists", name)));            }            return ev;        }        private void TestEventClosed(string name)        {            if (_session.TestHandlesClosedInternal)            {                _logger.WriteLine("Testing that event {0} is closed", name);                if (TryCreateEvent(name, out EventWaitHandle ev))                {                    ev.Close();                }                else                {                    _logger.WriteLine("Exception: Event {0} was not closed yet", name);                }            }        }        private void AddInput(string str, string log)        {            Type structType = typeof(ConsoleInputEventStruct);            FieldInfo strField = structType.GetField("Str");            object[] attributes = strField.GetCustomAttributes(typeof(MarshalAsAttribute), false);            if (attributes.Length != 1)            {                throw _logger.WriteException(new InvalidOperationException("MarshalAs attribute not found for ConsoleInputEventStruct.Str"));            }            MarshalAsAttribute marshalAsAttribute = (MarshalAsAttribute)attributes[0];            if (marshalAsAttribute.SizeConst <= str.Length)            {                throw _logger.WriteException(                    new SessionLocalException(                        _session,                        string.Format(CultureInfo.CurrentCulture, "Input [{0}] is too long ({1} limit)", str, marshalAsAttribute.SizeConst)));            }            lock (_input)            {                _input.Add(str);                _log.Add(log);                _inputEvent.Set();            }        }        public void ExecuteCommand(string command, string log)        {            using (_logger.CreateCallstack())            {                _cancel = false;                AddInput(command, log);            }        }        public void Close()        {            using (_logger.CreateCallstack())            {                int timeout;                #if DEBUG                // in debug build, we expect the winscp.exe to run in tracing mode, being very slow                timeout = 10000;                #else                timeout = 2000;                #endif                _logger.WriteLine("Waiting for process to exit ({0} ms)", timeout);                if (!_process.WaitForExit(timeout))                {                    _logger.WriteLine("Killing process");                    _process.Kill();                }            }        }        public void Dispose()        {            using (_logger.CreateCallstack())            {                lock (_lock)                {                    if (_session.TestHandlesClosedInternal)                    {                        _logger.WriteLine("Will test that handles are closed");                    }                    _abort = true;                    if (_thread != null)                    {                        _thread.Join();                        _thread = null;                    }                    if (_process != null)                    {                        _process.Dispose();                        _process = null;                    }                    if (_requestEvent != null)                    {                        _requestEvent.Close();                        TestEventClosed(ConsoleEventRequest + _instanceName);                    }                    if (_responseEvent != null)                    {                        _responseEvent.Close();                        TestEventClosed(ConsoleEventResponse + _instanceName);                    }                    if (_cancelEvent != null)                    {                        _cancelEvent.Close();                        TestEventClosed(ConsoleEventCancel + _instanceName);                    }                    if (_fileMapping != null)                    {                        _fileMapping.Dispose();                        _fileMapping = null;                        if (_session.TestHandlesClosedInternal)                        {                            _logger.WriteLine("Testing that file mapping is closed");                            string fileMappingName = ConsoleMapping + _instanceName;                            SafeFileHandle fileMapping = CreateFileMapping(fileMappingName);                            if (Marshal.GetLastWin32Error() == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)                            {                                _logger.WriteLine("Exception: File mapping {0} was not closed yet", fileMappingName);                            }                            if (!fileMapping.IsInvalid)                            {                                fileMapping.Dispose();                            }                        }                    }                    if (_inputEvent != null)                    {                        _inputEvent.Close();                        _inputEvent = null;                    }                    if (_job != null)                    {                        _job.Dispose();                        _job = null;                    }                }            }        }        private string GetExecutablePath()        {            using (_logger.CreateCallstack())            {                string executablePath;                if (!string.IsNullOrEmpty(_session.ExecutablePath))                {                    executablePath = _session.ExecutablePath;                    if (!File.Exists(executablePath))                    {                        throw _logger.WriteException(new SessionLocalException(_session, string.Format(CultureInfo.CurrentCulture, "{0} does not exists.", executablePath)));                    }                }                else                {                    if (!TryFindExecutableInPath(GetAssemblyPath(), out executablePath) &&#if !NETSTANDARD                        !TryFindExecutableInPath(GetInstallationPath(RegistryHive.CurrentUser), out executablePath) &&                        !TryFindExecutableInPath(GetInstallationPath(RegistryHive.LocalMachine), out executablePath) &&#endif                        !TryFindExecutableInPath(GetDefaultInstallationPath(), out executablePath))                    {                        throw _logger.WriteException(                            new SessionLocalException(_session,                                string.Format(CultureInfo.CurrentCulture,                                    "The {0} executable was not found at location of the assembly ({1}), nor in an installation path. You may use Session.ExecutablePath property to explicitly set path to {0}.",                                    ExeExecutableFileName, GetAssemblyPath())));                    }                }                return executablePath;            }        }        private static string GetDefaultInstallationPath()        {            string programFiles;            if (IntPtr.Size == 8)            {                programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);            }            else            {                programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);            }            return Path.Combine(programFiles, "WinSCP");        }#if !NETSTANDARD        private static string GetInstallationPath(RegistryHive hive)        {            RegistryKey baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Registry32);            RegistryKey key = baseKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\winscp3_is1");            string result = (key != null) ? (string)key.GetValue("Inno Setup: App Path") : null;            return result;        }#endif        private bool TryFindExecutableInPath(string path, out string result)        {            if (string.IsNullOrEmpty(path))            {                result = null;            }            else            {                string executablePath = Path.Combine(path, ExeExecutableFileName);                if (File.Exists(executablePath))                {                    result = executablePath;                    _logger.WriteLine("Executable found in {0}", executablePath);                }                else                {                    result = null;                    _logger.WriteLine("Executable not found in {0}", executablePath);                }            }            return (result != null);        }        private string GetAssemblyPath()        {            string codeBasePath = _logger.GetAssemblyFilePath();            string path = null;            if (!string.IsNullOrEmpty(codeBasePath))            {                path = Path.GetDirectoryName(codeBasePath);                Debug.Assert(path != null);            }            return path;        }        private void CheckVersion(string exePath, FileVersionInfo assemblyVersion)        {            using (_logger.CreateCallstack())            {                if (assemblyVersion == null)                {                    _logger.WriteLine("Assembly version not known, cannot check version");                }                else if (assemblyVersion.ProductVersion == AssemblyConstants.UndefinedProductVersion)                {                    _logger.WriteLine("Undefined assembly version, cannot check version");                }                else                {                    FileVersionInfo version = FileVersionInfo.GetVersionInfo(exePath);                    _logger.WriteLine("Version of {0} is {1}, product {2} version is {3}", exePath, version.FileVersion, version.ProductName, version.ProductVersion);                    if (_session.DisableVersionCheckInternal)                    {                        _logger.WriteLine("Version check disabled (not recommended)");                    }                    else if (assemblyVersion.ProductVersion != version.ProductVersion)                    {                        throw _logger.WriteException(                            new SessionLocalException(                                _session, string.Format(CultureInfo.CurrentCulture,                                    "The version of {0} ({1}) does not match version of this assembly {2} ({3}).",                                    exePath, version.ProductVersion, _logger.GetAssemblyFilePath(), assemblyVersion.ProductVersion)));                    }                }            }        }        public void WriteStatus()        {            string executablePath = GetExecutablePath();            _logger.WriteLine("{0} - exists [{1}]", executablePath, File.Exists(executablePath));        }        public void RequestCallstack()        {            using (_logger.CreateCallstack())            {                lock (_lock)                {                    if (_process == null)                    {                        _logger.WriteLine("Process is closed already");                    }                    else                    {                        try                        {                            string eventName = string.Format(CultureInfo.InvariantCulture, "WinSCPCallstack{0}", _process.Id);                            using (EventWaitHandle ev = EventWaitHandle.OpenExisting(eventName))                            {                                _logger.WriteLine("Setting event {0}", eventName);                                ev.Set();                                string callstackFileName = string.Format(CultureInfo.InvariantCulture, "{0}.txt", eventName);                                string callstackPath = Path.Combine(Path.GetTempPath(), callstackFileName);                                int timeout = 2000;                                while (!File.Exists(callstackPath))                                {                                    if (timeout < 0)                                    {                                        string message = string.Format(CultureInfo.CurrentCulture, "Timeout waiting for callstack file {0} to be created ", callstackPath);                                        throw new TimeoutException(message);                                    }                                    int step = 50;                                    timeout -= 50;                                    Thread.Sleep(step);                                }                                _logger.WriteLine("Callstack file {0} has been created", callstackPath);                                // allow writting to be finished                                Thread.Sleep(100);                                _logger.WriteLine(File.ReadAllText(callstackPath));                                File.Delete(callstackPath);                            }                        }                        catch (Exception e)                        {                            _logger.WriteException(e);                        }                    }                }            }        }        public void Cancel()        {            _cancel = true;        }        private const int MaxAttempts = 10;        private const string ConsoleMapping = "WinSCPConsoleMapping";        private const string ConsoleEventRequest = "WinSCPConsoleEventRequest";        private const string ConsoleEventResponse = "WinSCPConsoleEventResponse";        private const string ConsoleEventCancel = "WinSCPConsoleEventCancel";        private const string ConsoleJob = "WinSCPConsoleJob";        private const string ExeExecutableFileName = "winscp.exe";        private Process _process;        private readonly object _lock = new object();        private readonly Logger _logger;        private readonly Session _session;        private EventWaitHandle _requestEvent;        private EventWaitHandle _responseEvent;        private EventWaitHandle _cancelEvent;        private SafeFileHandle _fileMapping;        private string _instanceName;        private Thread _thread;        private bool _abort;        private string _lastFromBeginning;        private string _incompleteLine;        private readonly List<string> _input = new List<string>();        private readonly List<string> _log = new List<string>();        private AutoResetEvent _inputEvent = new AutoResetEvent(false);        private Job _job;        private bool _cancel;    }}
 |