private async Task Connect(
            string application,
            string executable,
            Version version,
            string hive,
            CancellationToken cancel
        ) {
            Close();

            Internal.VisualStudio ide = null;
            try {
                ide = await Internal.VisualStudio.LaunchAsync(application, executable, version, hive, cancel);

                var p = Process.GetProcessById(ide.ProcessId);
                string url = string.Format(
                    "ipc://{0}/{1}",
                    Internal.VSTestHostPackage.GetChannelName(Process.GetProcessById(ide.ProcessId)),
                    TesteeTestAdapter.Url
                );

                _remote = (TesteeTestAdapter)RemotingServices.Connect(typeof(TesteeTestAdapter), url);

                _ide = ide;
                ide = null;
            } finally {
                if (ide != null) {
                    ide.Dispose();
                }
            }
        }
        private async Task Connect(
            string application,
            string executable,
            Version version,
            string hive,
            IRunContext runContext,
            ITestElement currentTest,
            CancellationToken cancel
        ) {
            var hiveOption = string.IsNullOrEmpty(hive) ? "" : (" /rootSuffix " + hive);

            if (_ide != null &&
                _remote != null &&
                application == _currentApplication &&
                executable == _currentExecutable &&
                version == _currentVersion &&
                hive == _currentHive) {
                if (runContext != null) {
                    SendMessage(
                        runContext,
                        string.Format(Resources.VSReuseMessage, application, executable, version, hiveOption),
                        currentTest
                    );
                }
                return;
            }

            Close();

            if (runContext != null) {
                SendMessage(
                    runContext,
                    string.Format(Resources.VSLaunchMessage, application, executable, version, hiveOption),
                    currentTest
                );
            }

            Internal.VisualStudio ide = null;
            try {
                ide = await Internal.VisualStudio.LaunchAsync(application, executable, version, hive, cancel);

                var p = Process.GetProcessById(ide.ProcessId);
                var url = string.Format("ipc://{0}/{1}", VSTestHostPackage.GetChannelName(p), TesteeTestAdapter.Url);

                try {
                    _remote = (TesteeTestAdapter)RemotingServices.Connect(typeof(TesteeTestAdapter), url);
                } catch (RemotingException ex) {
                    throw new InvalidOperationException(Resources.FailedToConnect, ex);
                }

                _currentApplication = application;
                _currentExecutable = executable;
                _currentVersion = version;
                _currentHive = hive;
                _ide = ide;
                ide = null;
            } finally {
                if (ide != null) {
                    ide.Dispose();
                }
            }
        }
        /// <summary>
        /// Implements initialization. This is called by ITestAdapter.Initialize
        /// (a synchronous function) which will block until this function is
        /// completed.
        /// </summary>
        /// <param name="runContext">
        /// The context for the current test run.
        /// </param>
        private async Task InitializeWorker(IRunContext runContext) {
            string application, executable, versionString, hive;
            Version version;
            string launchTimeoutInSecondsString;
            int launchTimeoutInSeconds;

            var vars = runContext.RunConfig.TestRun.RunConfiguration.TestSettingsProperties;

            // VSApplication is the registry key name like 'VisualStudio'
            vars.TryGetValue("VSApplication", out application);
            // VSExecutableName is the executable name like 'devenv'
            if (vars.TryGetValue("VSExecutable", out executable) &&
                !string.IsNullOrEmpty(executable) &&
                string.IsNullOrEmpty(Path.GetExtension(executable))) {
                executable = Path.ChangeExtension(executable, ".exe");
            }
            // VSVersion is the version like '12.0'
            if (!vars.TryGetValue("VSVersion", out versionString) ||
                !Version.TryParse(versionString, out version)) {
                version = null;
            }
            // VSHive is the optional hive like 'Exp'
            vars.TryGetValue("VSHive", out hive);

            if (!vars.TryGetValue("VSLaunchTimeoutInSeconds", out launchTimeoutInSecondsString) ||
                !int.TryParse(launchTimeoutInSecondsString, out launchTimeoutInSeconds)) {
                launchTimeoutInSeconds = 30;
            }

            if (string.IsNullOrEmpty(application) || string.IsNullOrEmpty(executable) || version == null) {
                throw new ArgumentException(string.Format(
                    Resources.MissingConfigurationValues,
                    application ?? "(null)",
                    executable ?? "(null)",
                    version != null ? version.ToString() : "(null)",
                    hive ?? "(null)"
                ));
            }
            
            if (application == "Mock") {
                _runContext = runContext;
                _remote = new TesteeTestAdapter();
                _remote.Initialize(_runContext);
                // In the mock case tester and testee are the same process, therefore
                // VSTestContext is in our process too.  So we can just set this value
                // directly here.
                VSTestContext.IsMock = true;
                _mockVs = true;
                return;
            }

            // TODO: Detect and perform first run of VS if necessary.
            // The first time a VS hive is run, the user sees a dialog allowing
            // them to select settings such as the theme. We can avoid this by
            // running devenv.exe /resetSettings <path to profile.settings>
            // first, though it is not trivial to detect when this is necessary.
            // Without having done this, all tests will time out. For now, the
            // user is responsible for running VS at least once before
            // attempting to execute tests.

            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(launchTimeoutInSeconds));
            try {
                await Connect(application, executable, version, hive, cts.Token);
            } catch (OperationCanceledException ex) {
                throw new TimeoutException(string.Format(Resources.VSLaunchTimeout, launchTimeoutInSeconds), ex);
            }

            _runContext = runContext;
            _remote.Initialize(_runContext);

            if (_runContext.RunConfig.TestRun.RunConfiguration.IsExecutedUnderDebugger) {
                // If we're debugging, tell our host VS to attach to the new VS
                // instance we just started.
                bool mixedMode = false;
                string debugMixedMode;
                if (!vars.TryGetValue("VSDebugMixedMode", out debugMixedMode) ||
                    !bool.TryParse(debugMixedMode, out mixedMode)) {
                    mixedMode = false;
                }
                TesterDebugAttacherShared.AttachDebugger(_ide.ProcessId, mixedMode);
            }
        }
        /// <summary>
        /// Implements initialization. This is called by ITestAdapter.Initialize
        /// (a synchronous function) which will block until this function is
        /// completed.
        /// </summary>
        /// <param name="runContext">
        /// The context for the current test run.
        /// </param>
        private async Task InitializeWorker(
            TestProperties vars,
            IRunContext runContext,
            ITestElement currentTest = null
        ) {
            string application, executable, versionString, hive;
            Version version;
            string launchTimeoutInSecondsString;
            int launchTimeoutInSeconds;

            // VSApplication is the registry key name like 'VisualStudio'
            application = vars[VSTestProperties.VSApplication.Key] ?? VSTestProperties.VSApplication.VisualStudio;
            // VSExecutableName is the executable name like 'devenv'
            if (!vars.TryGetValue(VSTestProperties.VSExecutable.Key, out executable)) {
                executable = VSTestProperties.VSExecutable.DevEnv;
            }
            if (!string.IsNullOrEmpty(executable) &&
                string.IsNullOrEmpty(Path.GetExtension(executable))) {
                executable = Path.ChangeExtension(executable, ".exe");
            }

            // VSVersion is the version like '12.0'
            if (!vars.TryGetValue(VSTestProperties.VSVersion.Key, out versionString) ||
                !Version.TryParse(versionString, out version)) {
                version = new Version(int.Parse(AssemblyVersionInfo.VSVersion), 0);
            }

            // VSHive is the optional hive like 'Exp'
            hive = vars[VSTestProperties.VSHive.Key];

            if (!vars.TryGetValue(VSTestProperties.VSLaunchTimeoutInSeconds.Key, out launchTimeoutInSecondsString) ||
                !int.TryParse(launchTimeoutInSecondsString, out launchTimeoutInSeconds)) {
                launchTimeoutInSeconds = 30;
            }

            if (string.IsNullOrEmpty(application) || string.IsNullOrEmpty(executable) || version == null) {
                throw new ArgumentException(string.Format(
                    Resources.MissingConfigurationValues,
                    application ?? "(null)",
                    executable ?? "(null)",
                    version != null ? version.ToString() : "(null)",
                    hive ?? "(default)"
                ));
            }

            _mockVs = (application == VSTestProperties.VSApplication.Mock);
            if (_mockVs) {
                _remote = new TesteeTestAdapter();
                _remote.Initialize(_runContext);
                // In the mock case tester and testee are the same process, therefore
                // VSTestContext is in our process too.  So we can just set this value
                // directly here.
                VSTestContext.IsMock = true;
                return;
            }


            // TODO: Detect and perform first run of VS if necessary.
            // The first time a VS hive is run, the user sees a dialog allowing
            // them to select settings such as the theme. We can avoid this by
            // running devenv.exe /resetSettings <path to profile.settings>
            // first, though it is not trivial to detect when this is necessary.
            // Without having done this, all tests will time out. For now, the
            // user is responsible for running VS at least once before
            // attempting to execute tests.

            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(launchTimeoutInSeconds));
            try {
                await Connect(application, executable, version, hive, runContext, currentTest, cts.Token);
            } catch (OperationCanceledException ex) {
                throw new TimeoutException(string.Format(Resources.VSLaunchTimeout, launchTimeoutInSeconds), ex);
            } catch (Exception ex) {
                throw new InvalidOperationException(
                    string.Format(Resources.VSFailedToLaunch, application, executable, version, hive),
                    ex
                );
            }
        }