This commit is contained in:
Steve Dower 2015-07-29 16:06:48 -07:00
Родитель d8d284b0b4
Коммит 3f18aaa011
8 изменённых файлов: 1425 добавлений и 1383 удалений

42
.hgeol Normal file
Просмотреть файл

@ -0,0 +1,42 @@
[patterns]
# Non human-editable files are binary
**.dsp = BIN
**.dsw = BIN
**.mk = BIN
**.sln = BIN
**.vcproj = BIN
**.vsprops = BIN
**.aif = BIN
**.aifc = BIN
**.aiff = BIN
**.au = BIN
**.bmp = BIN
**.db = BIN
**.exe = BIN
**.icns = BIN
**.gif = BIN
**.ico = BIN
**.info = BIN
**.jpg = BIN
**.pck = BIN
**.png = BIN
**.psd = BIN
**.tar = BIN
**.wav = BIN
**.whl = BIN
**.xar = BIN
**.zip = BIN
# Windows batch files work best with CRLF, there can be subtle problems with LF
**.bat = CRLF
# All other files (which presumably are human-editable) are "native".
# This must be the last rule!
** = native
[repository]
native = LF

396
VSTestHost/Resources.Designer.cs сгенерированный
Просмотреть файл

@ -1,203 +1,203 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudioTools.VSTestHost.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Unable to restart Visual Studio because there is no active test run..
/// </summary>
internal static string CannotReconnectNoRunContext {
get {
return ResourceManager.GetString("CannotReconnectNoRunContext", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to attach debugger to Visual Studio process..
/// </summary>
internal static string FailedToAttach {
get {
return ResourceManager.GetString("FailedToAttach", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to connect to Visual Studio instance. Ensure VSTestHost is installed..
/// </summary>
internal static string FailedToConnect {
get {
return ResourceManager.GetString("FailedToConnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to resume test run..
/// </summary>
internal static string FailedToResume {
get {
return ResourceManager.GetString("FailedToResume", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to determine bounds for screen capture..
/// </summary>
internal static string InvalidScreenBounds {
get {
return ResourceManager.GetString("InvalidScreenBounds", resourceCulture);
}
}
/// <summary>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudioTools.VSTestHost.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Unable to restart Visual Studio because there is no active test run..
/// </summary>
internal static string CannotReconnectNoRunContext {
get {
return ResourceManager.GetString("CannotReconnectNoRunContext", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to attach debugger to Visual Studio process..
/// </summary>
internal static string FailedToAttach {
get {
return ResourceManager.GetString("FailedToAttach", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to connect to Visual Studio instance. Ensure VSTestHost is installed..
/// </summary>
internal static string FailedToConnect {
get {
return ResourceManager.GetString("FailedToConnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to resume test run..
/// </summary>
internal static string FailedToResume {
get {
return ResourceManager.GetString("FailedToResume", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to determine bounds for screen capture..
/// </summary>
internal static string InvalidScreenBounds {
get {
return ResourceManager.GetString("InvalidScreenBounds", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Some required configuration values were missing.
/// VSApplication: &apos;{0}&apos;
/// VSExecutable: &apos;{1}&apos;
/// VSVersion: &apos;{2}&apos;
/// VSHive: &apos;{3}&apos;.
/// </summary>
internal static string MissingConfigurationValues {
get {
return ResourceManager.GetString("MissingConfigurationValues", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Visual Studio is no longer running..
/// </summary>
internal static string NoClient {
get {
return ResourceManager.GetString("NoClient", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to There is no active test run..
/// </summary>
internal static string NoRunContext {
get {
return ResourceManager.GetString("NoRunContext", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No service provider is available in the current context..
/// </summary>
internal static string NoServiceProvider {
get {
return ResourceManager.GetString("NoServiceProvider", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to communicate with Visual Studio process during {0}: {1}.
/// </summary>
internal static string RemotingError {
get {
return ResourceManager.GetString("RemotingError", resourceCulture);
}
}
/// <summary>
/// VSHive: &apos;{3}&apos;.
/// </summary>
internal static string MissingConfigurationValues {
get {
return ResourceManager.GetString("MissingConfigurationValues", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Visual Studio is no longer running..
/// </summary>
internal static string NoClient {
get {
return ResourceManager.GetString("NoClient", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to There is no active test run..
/// </summary>
internal static string NoRunContext {
get {
return ResourceManager.GetString("NoRunContext", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No service provider is available in the current context..
/// </summary>
internal static string NoServiceProvider {
get {
return ResourceManager.GetString("NoServiceProvider", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to communicate with Visual Studio process during {0}: {1}.
/// </summary>
internal static string RemotingError {
get {
return ResourceManager.GetString("RemotingError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to communicate with Visual Studio process during {0}: {1}
///{2}.
/// </summary>
internal static string RemotingErrorDebug {
get {
return ResourceManager.GetString("RemotingErrorDebug", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Retrying call into Visual Studio process from {0}..
/// </summary>
internal static string RetryRemoteCall {
get {
return ResourceManager.GetString("RetryRemoteCall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attempted to set service provider multiple time..
/// </summary>
internal static string ServiceProviderAlreadySet {
get {
return ResourceManager.GetString("ServiceProviderAlreadySet", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to launch {1} ({0}, {2}{3}).
/// </summary>
internal static string VSFailedToLaunch {
get {
return ResourceManager.GetString("VSFailedToLaunch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Visual Studio failed to start within {0} seconds..
/// </summary>
internal static string VSLaunchTimeout {
get {
return ResourceManager.GetString("VSLaunchTimeout", resourceCulture);
}
}
}
}
///{2}.
/// </summary>
internal static string RemotingErrorDebug {
get {
return ResourceManager.GetString("RemotingErrorDebug", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Retrying call into Visual Studio process from {0}..
/// </summary>
internal static string RetryRemoteCall {
get {
return ResourceManager.GetString("RetryRemoteCall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attempted to set service provider multiple time..
/// </summary>
internal static string ServiceProviderAlreadySet {
get {
return ResourceManager.GetString("ServiceProviderAlreadySet", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to launch {1} ({0}, {2}{3}).
/// </summary>
internal static string VSFailedToLaunch {
get {
return ResourceManager.GetString("VSFailedToLaunch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Visual Studio failed to start within {0} seconds..
/// </summary>
internal static string VSLaunchTimeout {
get {
return ResourceManager.GetString("VSLaunchTimeout", resourceCulture);
}
}
}
}

Просмотреть файл

@ -1,171 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CannotReconnectNoRunContext" xml:space="preserve">
<value>Unable to restart Visual Studio because there is no active test run.</value>
</data>
<data name="FailedToAttach" xml:space="preserve">
<value>Unable to attach debugger to Visual Studio process.</value>
</data>
<data name="FailedToConnect" xml:space="preserve">
<value>Failed to connect to Visual Studio instance. Ensure VSTestHost is installed.</value>
</data>
<data name="FailedToResume" xml:space="preserve">
<value>Unable to resume test run.</value>
</data>
<data name="InvalidScreenBounds" xml:space="preserve">
<value>Unable to determine bounds for screen capture.</value>
</data>
<data name="MissingConfigurationValues" xml:space="preserve">
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CannotReconnectNoRunContext" xml:space="preserve">
<value>Unable to restart Visual Studio because there is no active test run.</value>
</data>
<data name="FailedToAttach" xml:space="preserve">
<value>Unable to attach debugger to Visual Studio process.</value>
</data>
<data name="FailedToConnect" xml:space="preserve">
<value>Failed to connect to Visual Studio instance. Ensure VSTestHost is installed.</value>
</data>
<data name="FailedToResume" xml:space="preserve">
<value>Unable to resume test run.</value>
</data>
<data name="InvalidScreenBounds" xml:space="preserve">
<value>Unable to determine bounds for screen capture.</value>
</data>
<data name="MissingConfigurationValues" xml:space="preserve">
<value>Some required configuration values were missing.
VSApplication: '{0}'
VSExecutable: '{1}'
VSVersion: '{2}'
VSHive: '{3}'</value>
</data>
<data name="NoClient" xml:space="preserve">
<value>Visual Studio is no longer running.</value>
</data>
<data name="NoRunContext" xml:space="preserve">
<value>There is no active test run.</value>
</data>
<data name="NoServiceProvider" xml:space="preserve">
<value>No service provider is available in the current context.</value>
</data>
<data name="RemotingError" xml:space="preserve">
<value>Failed to communicate with Visual Studio process during {0}: {1}</value>
</data>
<data name="RemotingErrorDebug" xml:space="preserve">
VSHive: '{3}'</value>
</data>
<data name="NoClient" xml:space="preserve">
<value>Visual Studio is no longer running.</value>
</data>
<data name="NoRunContext" xml:space="preserve">
<value>There is no active test run.</value>
</data>
<data name="NoServiceProvider" xml:space="preserve">
<value>No service provider is available in the current context.</value>
</data>
<data name="RemotingError" xml:space="preserve">
<value>Failed to communicate with Visual Studio process during {0}: {1}</value>
</data>
<data name="RemotingErrorDebug" xml:space="preserve">
<value>Failed to communicate with Visual Studio process during {0}: {1}
{2}</value>
</data>
<data name="RetryRemoteCall" xml:space="preserve">
<value>Retrying call into Visual Studio process from {0}.</value>
</data>
<data name="ServiceProviderAlreadySet" xml:space="preserve">
<value>Attempted to set service provider multiple time.</value>
</data>
<data name="VSFailedToLaunch" xml:space="preserve">
<value>Failed to launch {1} ({0}, {2}{3})</value>
<comment>application, executable, version, hive</comment>
</data>
<data name="VSLaunchTimeout" xml:space="preserve">
<value>Visual Studio failed to start within {0} seconds.</value>
</data>
{2}</value>
</data>
<data name="RetryRemoteCall" xml:space="preserve">
<value>Retrying call into Visual Studio process from {0}.</value>
</data>
<data name="ServiceProviderAlreadySet" xml:space="preserve">
<value>Attempted to set service provider multiple time.</value>
</data>
<data name="VSFailedToLaunch" xml:space="preserve">
<value>Failed to launch {1} ({0}, {2}{3})</value>
<comment>application, executable, version, hive</comment>
</data>
<data name="VSLaunchTimeout" xml:space="preserve">
<value>Visual Studio failed to start within {0} seconds.</value>
</data>
</root>

Просмотреть файл

@ -1,55 +1,55 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.Common;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
class TestProperties {
private readonly ITestElement _testElement;
private readonly Dictionary<string, string> _testSettings;
public TestProperties(ITestElement testElement, TestRunConfiguration runConfig) {
_testElement = testElement;
if (runConfig != null) {
_testSettings = runConfig.TestSettingsProperties;
}
}
public string this[string key] {
get {
string value;
TryGetValue(key, out value);
return value;
}
}
public bool TryGetValue(string key, out string value) {
value = null;
if (_testElement != null && _testElement.Properties.ContainsKey(key)) {
try {
value = (string)_testElement.Properties[key];
return true;
} catch (KeyNotFoundException) {
} catch (InvalidCastException) {
}
}
if (_testSettings != null) {
return _testSettings.TryGetValue(key, out value);
}
return false;
}
}
}
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.Common;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
class TestProperties {
private readonly ITestElement _testElement;
private readonly Dictionary<string, string> _testSettings;
public TestProperties(ITestElement testElement, TestRunConfiguration runConfig) {
_testElement = testElement;
if (runConfig != null) {
_testSettings = runConfig.TestSettingsProperties;
}
}
public string this[string key] {
get {
string value;
TryGetValue(key, out value);
return value;
}
}
public bool TryGetValue(string key, out string value) {
value = null;
if (_testElement != null && _testElement.Properties.ContainsKey(key)) {
try {
value = (string)_testElement.Properties[key];
return true;
} catch (KeyNotFoundException) {
} catch (InvalidCastException) {
}
}
if (_testSettings != null) {
return _testSettings.TryGetValue(key, out value);
}
return false;
}
}
}

Просмотреть файл

@ -1,214 +1,214 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Remoting;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
/// <summary>
/// Acts as a communication proxy between VS instances.
///
/// This class is instantiated in the client VS.
/// </summary>
class TesteeTestAdapter : MarshalByRefObject, ITestAdapter, IDisposable {
private IRunContext _runContext;
private readonly Dictionary<string, ITestAdapter> _adapters = new Dictionary<string, ITestAdapter>();
public const string Url = "vstest";
public TesteeTestAdapter() { }
public void Dispose() {
Dispose(true);
}
protected void Dispose(bool disposing) {
if (disposing) {
RemotingServices.Disconnect(this);
}
}
public bool IsInitialized {
get {
return _runContext != null;
}
}
private IEnumerable<ITestAdapter> GetAdapters() {
lock (_adapters) {
return _adapters.Values.ToList();
}
}
private ITestAdapter GetAdapter(string adapterName) {
ITestAdapter adapter;
lock (_adapters) {
if (_adapters.TryGetValue(adapterName, out adapter)) {
return adapter;
}
}
adapter = (ITestAdapter)Activator.CreateInstance(Type.GetType(adapterName));
if (_runContext != null) {
adapter.Initialize(_runContext);
}
lock (_adapters) {
try {
_adapters.Add(adapterName, adapter);
} catch (ArgumentException) {
adapter.Cleanup();
adapter = _adapters[adapterName];
}
}
return adapter;
}
public void Initialize(IRunContext runContext) {
_runContext = runContext;
foreach (var adapter in GetAdapters()) {
adapter.Initialize(runContext);
}
}
public void PreTestRunFinished(IRunContext runContext) {
foreach (var adapter in GetAdapters()) {
adapter.PreTestRunFinished(runContext);
}
}
public void ReceiveMessage(object message) {
foreach (var adapter in GetAdapters()) {
adapter.ReceiveMessage(message);
}
}
public void AbortTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.AbortTestRun();
}
}
public void Cleanup() {
_runContext = null;
foreach (var adapter in GetAdapters()) {
adapter.Cleanup();
}
_adapters.Clear();
}
public void PauseTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.PauseTestRun();
}
}
public void ResumeTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.ResumeTestRun();
}
}
private bool TryGetProperty(
ITestElement testElement,
IRunContext runContext,
string propertyName,
out string value
) {
value = null;
var runProps = runContext.RunConfig.TestRun.RunConfiguration.TestSettingsProperties;
if (testElement.Properties.ContainsKey(propertyName)) {
value = testElement.Properties[propertyName] as string;
return value != null;
}
return runProps.TryGetValue(propertyName, out value);
}
public void Run(ITestElement testElement, ITestContext testContext) {
var testAdapter = GetAdapter(testElement.Adapter);
using (var screenRecorder = StartSceenRecorder(testElement, _runContext)) {
try {
testAdapter.Run(testElement, testContext);
} catch (Exception ex) {
var message = new TextTestResultMessage(
_runContext.RunConfig.TestRun.Id,
testElement,
ex.ToString()
);
testContext.ResultSink.AddResult(message);
}
if (screenRecorder != null && !string.IsNullOrEmpty(screenRecorder.Failure)) {
var message = new TextTestResultMessage(
_runContext.RunConfig.TestRun.Id,
testElement,
screenRecorder.Failure
);
testContext.ResultSink.AddResult(message);
}
}
}
private ScreenRecorder StartSceenRecorder(ITestElement testElement, IRunContext runContext) {
string screenCapture;
if (!TryGetProperty(testElement, _runContext, VSTestProperties.ScreenCapture.Key, out screenCapture) ||
string.IsNullOrEmpty(screenCapture)) {
return null;
}
try {
if (!Path.IsPathRooted(screenCapture)) {
screenCapture = Path.Combine(
_runContext.RunConfig.TestRun.RunConfiguration.RunDeploymentOutDirectory,
screenCapture
);
}
} catch (ArgumentException) {
return null;
}
screenCapture = screenCapture.Replace("$id$", testElement.HumanReadableId);
screenCapture = screenCapture.Replace("$date$", DateTime.Today.ToShortDateString());
var screenRecorder = new ScreenRecorder(screenCapture);
string intervalString;
int interval;
if (TryGetProperty(testElement, runContext, VSTestProperties.ScreenCapture.IntervalKey, out intervalString) &&
int.TryParse(intervalString, out interval)) {
screenRecorder.Interval = TimeSpan.FromMilliseconds(interval);
} else {
screenRecorder.Interval = TimeSpan.FromSeconds(1);
}
return screenRecorder;
}
public void StopTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.StopTestRun();
}
}
}
}
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Remoting;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
/// <summary>
/// Acts as a communication proxy between VS instances.
///
/// This class is instantiated in the client VS.
/// </summary>
class TesteeTestAdapter : MarshalByRefObject, ITestAdapter, IDisposable {
private IRunContext _runContext;
private readonly Dictionary<string, ITestAdapter> _adapters = new Dictionary<string, ITestAdapter>();
public const string Url = "vstest";
public TesteeTestAdapter() { }
public void Dispose() {
Dispose(true);
}
protected void Dispose(bool disposing) {
if (disposing) {
RemotingServices.Disconnect(this);
}
}
public bool IsInitialized {
get {
return _runContext != null;
}
}
private IEnumerable<ITestAdapter> GetAdapters() {
lock (_adapters) {
return _adapters.Values.ToList();
}
}
private ITestAdapter GetAdapter(string adapterName) {
ITestAdapter adapter;
lock (_adapters) {
if (_adapters.TryGetValue(adapterName, out adapter)) {
return adapter;
}
}
adapter = (ITestAdapter)Activator.CreateInstance(Type.GetType(adapterName));
if (_runContext != null) {
adapter.Initialize(_runContext);
}
lock (_adapters) {
try {
_adapters.Add(adapterName, adapter);
} catch (ArgumentException) {
adapter.Cleanup();
adapter = _adapters[adapterName];
}
}
return adapter;
}
public void Initialize(IRunContext runContext) {
_runContext = runContext;
foreach (var adapter in GetAdapters()) {
adapter.Initialize(runContext);
}
}
public void PreTestRunFinished(IRunContext runContext) {
foreach (var adapter in GetAdapters()) {
adapter.PreTestRunFinished(runContext);
}
}
public void ReceiveMessage(object message) {
foreach (var adapter in GetAdapters()) {
adapter.ReceiveMessage(message);
}
}
public void AbortTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.AbortTestRun();
}
}
public void Cleanup() {
_runContext = null;
foreach (var adapter in GetAdapters()) {
adapter.Cleanup();
}
_adapters.Clear();
}
public void PauseTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.PauseTestRun();
}
}
public void ResumeTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.ResumeTestRun();
}
}
private bool TryGetProperty(
ITestElement testElement,
IRunContext runContext,
string propertyName,
out string value
) {
value = null;
var runProps = runContext.RunConfig.TestRun.RunConfiguration.TestSettingsProperties;
if (testElement.Properties.ContainsKey(propertyName)) {
value = testElement.Properties[propertyName] as string;
return value != null;
}
return runProps.TryGetValue(propertyName, out value);
}
public void Run(ITestElement testElement, ITestContext testContext) {
var testAdapter = GetAdapter(testElement.Adapter);
using (var screenRecorder = StartSceenRecorder(testElement, _runContext)) {
try {
testAdapter.Run(testElement, testContext);
} catch (Exception ex) {
var message = new TextTestResultMessage(
_runContext.RunConfig.TestRun.Id,
testElement,
ex.ToString()
);
testContext.ResultSink.AddResult(message);
}
if (screenRecorder != null && !string.IsNullOrEmpty(screenRecorder.Failure)) {
var message = new TextTestResultMessage(
_runContext.RunConfig.TestRun.Id,
testElement,
screenRecorder.Failure
);
testContext.ResultSink.AddResult(message);
}
}
}
private ScreenRecorder StartSceenRecorder(ITestElement testElement, IRunContext runContext) {
string screenCapture;
if (!TryGetProperty(testElement, _runContext, VSTestProperties.ScreenCapture.Key, out screenCapture) ||
string.IsNullOrEmpty(screenCapture)) {
return null;
}
try {
if (!Path.IsPathRooted(screenCapture)) {
screenCapture = Path.Combine(
_runContext.RunConfig.TestRun.RunConfiguration.RunDeploymentOutDirectory,
screenCapture
);
}
} catch (ArgumentException) {
return null;
}
screenCapture = screenCapture.Replace("$id$", testElement.HumanReadableId);
screenCapture = screenCapture.Replace("$date$", DateTime.Today.ToShortDateString());
var screenRecorder = new ScreenRecorder(screenCapture);
string intervalString;
int interval;
if (TryGetProperty(testElement, runContext, VSTestProperties.ScreenCapture.IntervalKey, out intervalString) &&
int.TryParse(intervalString, out interval)) {
screenRecorder.Interval = TimeSpan.FromMilliseconds(interval);
} else {
screenRecorder.Interval = TimeSpan.FromSeconds(1);
}
return screenRecorder;
}
public void StopTestRun() {
foreach (var adapter in GetAdapters()) {
adapter.StopTestRun();
}
}
}
}

Просмотреть файл

@ -1,424 +1,424 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
using Microsoft.VisualStudioTools.VSTestHost.Internal;
namespace Microsoft.VisualStudioTools.VSTestHost {
/// <summary>
/// Executes tests attributed with HostType("VSTestHost").
///
/// This class is instantiated by the EXECUTION ENGINE and communicates with
/// the TESTEE via <see cref="TesteeTestAdapter"/> over IPC.
/// </summary>
class TesterTestAdapter : ITestAdapter {
private Internal.VisualStudio _ide;
private string _currentApplication, _currentExecutable, _currentHive;
private Version _currentVersion;
private Guid _runId;
private IRunContext _runContext;
private TesteeTestAdapter _remote;
private bool _mockVs;
public TesterTestAdapter() { }
private async Task Connect(
string application,
string executable,
Version version,
string hive,
CancellationToken cancel
) {
if (_ide != null &&
_remote != null &&
application == _currentApplication &&
executable == _currentExecutable &&
version == _currentVersion &&
hive == _currentHive) {
return;
}
Close();
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>
/// Closes VS and clears our state. Close may be called multiple times
/// safely, and may be safely followed by another call to Connect with
/// the same or different parameters.
/// </summary>
private void Close() {
var ide = Interlocked.Exchange(ref _ide, null);
var remote = Interlocked.Exchange(ref _remote, null);
var disposableRemote = remote as IDisposable;
if (disposableRemote != null) {
try {
disposableRemote.Dispose();
} catch (RemotingException) {
}
}
if (ide != null) {
try {
// Try to close VS gracefully, otherwise Dispose() will just
// go in and kill it
ide.DTE.Quit();
} catch (Exception ex) {
Trace.TraceError(ex.ToString());
}
ide.Dispose();
}
}
private bool IsClientAlive() {
var ide = _ide;
var remote = _remote;
if (ide == null || remote == null) {
return false;
}
try {
if (remote.IsInitialized) {
return true;
}
} catch (RemotingException) {
}
return false;
}
/// <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, ITestElement testElement) {
string application, executable, versionString, hive;
Version version;
string launchTimeoutInSecondsString;
int launchTimeoutInSeconds;
var vars = new TestProperties(testElement, _runContext.RunConfig.TestRun.RunConfiguration);
// 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] ?? VSTestProperties.VSHive.Exp;
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 ?? "(null)"
));
}
_mockVs = (application == VSTestProperties.VSApplication.Mock);
if (_mockVs) {
_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;
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);
} catch (Exception ex) {
throw new InvalidOperationException(
string.Format(Resources.VSFailedToLaunch, application, executable, version, hive),
ex
);
}
}
private static TestRunTextResultMessage GetFailure(Exception ex, Guid runId) {
var res = new TestRunTextResultMessage(runId, ex.Message);
#if DEBUG
if (ex.InnerException != null) {
res.SystemException = ex.InnerException;
}
#endif
return res;
}
private bool InitializeForTest(ITestElement testElement, IRunContext runContext) {
var runId = runContext.RunConfig.TestRun.Id;
TestRunTextResultMessage failure = null;
try {
InitializeWorker(runContext, testElement).GetAwaiter().GetResult();
_remote.Initialize(_runContext);
AttachDebuggerIfNeeded(runContext, _ide);
} catch (ArgumentException ex) {
failure = GetFailure(ex, runId);
} catch (TimeoutException ex) {
failure = GetFailure(ex, runId);
} catch (InvalidOperationException ex) {
failure = GetFailure(ex, runId);
} catch (Exception ex) {
failure = new TestRunTextResultMessage(
runId,
string.Format("{0}: {1}{2}{3}", ex.GetType().Name, ex.Message, Environment.NewLine, ex)
);
failure.SystemException = ex;
}
if (failure != null) {
runContext.ResultSink.AddResult(failure);
runContext.StopTestRun();
return false;
}
return true;
}
private void AttachDebuggerIfNeeded(IRunContext runContext, Internal.VisualStudio ide) {
var config = runContext.RunConfig.TestRun.RunConfiguration;
if (config.IsExecutedUnderDebugger && ide != null) {
// If we're debugging, tell our host VS to attach to the new VS
// instance we just started.
bool mixedMode = false;
string debugMixedMode;
if (!config.TestSettingsProperties.TryGetValue(
VSTestProperties.VSDebugMixedMode.Key,
out debugMixedMode) ||
!bool.TryParse(debugMixedMode, out mixedMode)
) {
mixedMode = false;
}
TesterDebugAttacherShared.AttachDebugger(ide.ProcessId, mixedMode);
}
}
private void SendMessage(IRunContext runContext, string message, ITestElement currentTest = null) {
if (runContext == null) {
return;
}
var runId = runContext.RunConfig.TestRun.Id;
TestMessage msg;
if (currentTest == null) {
msg = new TestRunTextResultMessage(runId, message);
} else {
msg = new TextTestResultMessage(runId, currentTest, message);
}
runContext.ResultSink.AddResult(msg);
}
private bool RemoteCall(
Action<TesteeTestAdapter> action,
int retries = 2,
bool restartVS = true,
ITestElement currentTest = null,
[CallerMemberName] string caller = null
) {
var runContext = _runContext;
if (runContext == null) {
throw new InvalidOperationException(Resources.NoRunContext);
}
if (currentTest != null) {
InitializeForTest(currentTest, runContext);
}
bool firstAttempt = true;
while (retries-- > 0) {
if (!_mockVs && !firstAttempt) {
// Send a message announcing that we are retrying the call
SendMessage(
runContext,
string.Format(
Resources.RetryRemoteCall,
currentTest != null ? currentTest.HumanReadableId : caller
),
currentTest
);
}
firstAttempt = false;
if (!IsClientAlive()) {
Close();
if (restartVS && currentTest != null) {
SendMessage(runContext, "Restarting VS", currentTest);
InitializeForTest(currentTest, runContext);
} else {
SendMessage(runContext, Resources.NoClient, currentTest);
return false;
}
}
var remote = _remote;
if (remote == null) {
return false;
}
try {
action(remote);
return true;
} catch (RemotingException ex) {
#if DEBUG
var msg = string.Format(Resources.RemotingErrorDebug, caller, ex.Message, ex.ToString());
#else
var msg = string.Format(Resources.RemotingError, caller, ex.Message);
#endif
SendMessage(runContext, msg, currentTest);
// Close _remote and let EnsureClient bring it back if
// requested by the caller
var disposableRemote = Interlocked.Exchange(ref _remote, null) as IDisposable;
if (disposableRemote != null) {
try {
disposableRemote.Dispose();
} catch (RemotingException) {
}
}
}
}
throw new InvalidOperationException(Resources.NoClient);
}
#region ITestAdapter members
public void Initialize(IRunContext runContext) {
_runContext = runContext;
_runId = runContext.RunConfig.TestRun.Id;
}
public void Cleanup() {
RemoteCall(r => r.Cleanup());
Close();
}
public void PreTestRunFinished(IRunContext runContext) {
RemoteCall(r => r.PreTestRunFinished(runContext));
}
public void ReceiveMessage(object message) {
RemoteCall(r => r.ReceiveMessage(message));
}
public void AbortTestRun() {
RemoteCall(r => r.AbortTestRun(), restartVS: false);
}
public void PauseTestRun() {
RemoteCall(r => r.PauseTestRun(), restartVS: false);
}
public void ResumeTestRun() {
if (!RemoteCall(r => r.ResumeTestRun())) {
throw new InvalidOperationException(string.Format(Resources.FailedToResume));
}
}
public void Run(ITestElement testElement, ITestContext testContext) {
if (!RemoteCall(r => r.Run(testElement, testContext), currentTest: testElement)) {
testContext.ResultSink.AddResult(new TestResult(".", _runId, testElement) {
Outcome = TestOutcome.NotRunnable
});
}
}
public void StopTestRun() {
RemoteCall(r => r.StopTestRun(), restartVS: false);
}
#endregion
}
}
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
using Microsoft.VisualStudioTools.VSTestHost.Internal;
namespace Microsoft.VisualStudioTools.VSTestHost {
/// <summary>
/// Executes tests attributed with HostType("VSTestHost").
///
/// This class is instantiated by the EXECUTION ENGINE and communicates with
/// the TESTEE via <see cref="TesteeTestAdapter"/> over IPC.
/// </summary>
class TesterTestAdapter : ITestAdapter {
private Internal.VisualStudio _ide;
private string _currentApplication, _currentExecutable, _currentHive;
private Version _currentVersion;
private Guid _runId;
private IRunContext _runContext;
private TesteeTestAdapter _remote;
private bool _mockVs;
public TesterTestAdapter() { }
private async Task Connect(
string application,
string executable,
Version version,
string hive,
CancellationToken cancel
) {
if (_ide != null &&
_remote != null &&
application == _currentApplication &&
executable == _currentExecutable &&
version == _currentVersion &&
hive == _currentHive) {
return;
}
Close();
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>
/// Closes VS and clears our state. Close may be called multiple times
/// safely, and may be safely followed by another call to Connect with
/// the same or different parameters.
/// </summary>
private void Close() {
var ide = Interlocked.Exchange(ref _ide, null);
var remote = Interlocked.Exchange(ref _remote, null);
var disposableRemote = remote as IDisposable;
if (disposableRemote != null) {
try {
disposableRemote.Dispose();
} catch (RemotingException) {
}
}
if (ide != null) {
try {
// Try to close VS gracefully, otherwise Dispose() will just
// go in and kill it
ide.DTE.Quit();
} catch (Exception ex) {
Trace.TraceError(ex.ToString());
}
ide.Dispose();
}
}
private bool IsClientAlive() {
var ide = _ide;
var remote = _remote;
if (ide == null || remote == null) {
return false;
}
try {
if (remote.IsInitialized) {
return true;
}
} catch (RemotingException) {
}
return false;
}
/// <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, ITestElement testElement) {
string application, executable, versionString, hive;
Version version;
string launchTimeoutInSecondsString;
int launchTimeoutInSeconds;
var vars = new TestProperties(testElement, _runContext.RunConfig.TestRun.RunConfiguration);
// 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] ?? VSTestProperties.VSHive.Exp;
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 ?? "(null)"
));
}
_mockVs = (application == VSTestProperties.VSApplication.Mock);
if (_mockVs) {
_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;
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);
} catch (Exception ex) {
throw new InvalidOperationException(
string.Format(Resources.VSFailedToLaunch, application, executable, version, hive),
ex
);
}
}
private static TestRunTextResultMessage GetFailure(Exception ex, Guid runId) {
var res = new TestRunTextResultMessage(runId, ex.Message);
#if DEBUG
if (ex.InnerException != null) {
res.SystemException = ex.InnerException;
}
#endif
return res;
}
private bool InitializeForTest(ITestElement testElement, IRunContext runContext) {
var runId = runContext.RunConfig.TestRun.Id;
TestRunTextResultMessage failure = null;
try {
InitializeWorker(runContext, testElement).GetAwaiter().GetResult();
_remote.Initialize(_runContext);
AttachDebuggerIfNeeded(runContext, _ide);
} catch (ArgumentException ex) {
failure = GetFailure(ex, runId);
} catch (TimeoutException ex) {
failure = GetFailure(ex, runId);
} catch (InvalidOperationException ex) {
failure = GetFailure(ex, runId);
} catch (Exception ex) {
failure = new TestRunTextResultMessage(
runId,
string.Format("{0}: {1}{2}{3}", ex.GetType().Name, ex.Message, Environment.NewLine, ex)
);
failure.SystemException = ex;
}
if (failure != null) {
runContext.ResultSink.AddResult(failure);
runContext.StopTestRun();
return false;
}
return true;
}
private void AttachDebuggerIfNeeded(IRunContext runContext, Internal.VisualStudio ide) {
var config = runContext.RunConfig.TestRun.RunConfiguration;
if (config.IsExecutedUnderDebugger && ide != null) {
// If we're debugging, tell our host VS to attach to the new VS
// instance we just started.
bool mixedMode = false;
string debugMixedMode;
if (!config.TestSettingsProperties.TryGetValue(
VSTestProperties.VSDebugMixedMode.Key,
out debugMixedMode) ||
!bool.TryParse(debugMixedMode, out mixedMode)
) {
mixedMode = false;
}
TesterDebugAttacherShared.AttachDebugger(ide.ProcessId, mixedMode);
}
}
private void SendMessage(IRunContext runContext, string message, ITestElement currentTest = null) {
if (runContext == null) {
return;
}
var runId = runContext.RunConfig.TestRun.Id;
TestMessage msg;
if (currentTest == null) {
msg = new TestRunTextResultMessage(runId, message);
} else {
msg = new TextTestResultMessage(runId, currentTest, message);
}
runContext.ResultSink.AddResult(msg);
}
private bool RemoteCall(
Action<TesteeTestAdapter> action,
int retries = 2,
bool restartVS = true,
ITestElement currentTest = null,
[CallerMemberName] string caller = null
) {
var runContext = _runContext;
if (runContext == null) {
throw new InvalidOperationException(Resources.NoRunContext);
}
if (currentTest != null) {
InitializeForTest(currentTest, runContext);
}
bool firstAttempt = true;
while (retries-- > 0) {
if (!_mockVs && !firstAttempt) {
// Send a message announcing that we are retrying the call
SendMessage(
runContext,
string.Format(
Resources.RetryRemoteCall,
currentTest != null ? currentTest.HumanReadableId : caller
),
currentTest
);
}
firstAttempt = false;
if (!IsClientAlive()) {
Close();
if (restartVS && currentTest != null) {
SendMessage(runContext, "Restarting VS", currentTest);
InitializeForTest(currentTest, runContext);
} else {
SendMessage(runContext, Resources.NoClient, currentTest);
return false;
}
}
var remote = _remote;
if (remote == null) {
return false;
}
try {
action(remote);
return true;
} catch (RemotingException ex) {
#if DEBUG
var msg = string.Format(Resources.RemotingErrorDebug, caller, ex.Message, ex.ToString());
#else
var msg = string.Format(Resources.RemotingError, caller, ex.Message);
#endif
SendMessage(runContext, msg, currentTest);
// Close _remote and let EnsureClient bring it back if
// requested by the caller
var disposableRemote = Interlocked.Exchange(ref _remote, null) as IDisposable;
if (disposableRemote != null) {
try {
disposableRemote.Dispose();
} catch (RemotingException) {
}
}
}
}
throw new InvalidOperationException(Resources.NoClient);
}
#region ITestAdapter members
public void Initialize(IRunContext runContext) {
_runContext = runContext;
_runId = runContext.RunConfig.TestRun.Id;
}
public void Cleanup() {
RemoteCall(r => r.Cleanup());
Close();
}
public void PreTestRunFinished(IRunContext runContext) {
RemoteCall(r => r.PreTestRunFinished(runContext));
}
public void ReceiveMessage(object message) {
RemoteCall(r => r.ReceiveMessage(message));
}
public void AbortTestRun() {
RemoteCall(r => r.AbortTestRun(), restartVS: false);
}
public void PauseTestRun() {
RemoteCall(r => r.PauseTestRun(), restartVS: false);
}
public void ResumeTestRun() {
if (!RemoteCall(r => r.ResumeTestRun())) {
throw new InvalidOperationException(string.Format(Resources.FailedToResume));
}
}
public void Run(ITestElement testElement, ITestContext testContext) {
if (!RemoteCall(r => r.Run(testElement, testContext), currentTest: testElement)) {
testContext.ResultSink.AddResult(new TestResult(".", _runId, testElement) {
Outcome = TestOutcome.NotRunnable
});
}
}
public void StopTestRun() {
RemoteCall(r => r.StopTestRun(), restartVS: false);
}
#endregion
}
}

Просмотреть файл

@ -1,54 +1,54 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
namespace Microsoft.VisualStudioTools.VSTestHost {
public static class VSTestProperties {
public static class ScreenCapture {
public const string Key = "ScreenCapture";
public const string IntervalKey = "ScreenCaptureInterval";
}
public static class VSApplication {
public const string Key = "VSApplication";
public const string VisualStudio = "VisualStudio";
public const string WDExpress = "WDExpress";
public const string VWDExpress = "VWDExpress";
public const string Mock = "Mock";
}
public static class VSExecutable {
public const string Key = "VSExecutable";
public const string DevEnv = "devenv";
public const string WDExpress = "wdexpress";
public const string VWDExpress = "vwdexpress";
}
public static class VSVersion {
public const string Key = "VSVersion";
}
public static class VSHive {
public const string Key = "VSHive";
public const string Exp = "Exp";
}
public static class VSLaunchTimeoutInSeconds {
public const string Key = "VSLaunchTimeoutInSeconds";
}
public static class VSDebugMixedMode {
public const string Key = "VSDebugMixedMode";
}
}
}
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
namespace Microsoft.VisualStudioTools.VSTestHost {
public static class VSTestProperties {
public static class ScreenCapture {
public const string Key = "ScreenCapture";
public const string IntervalKey = "ScreenCaptureInterval";
}
public static class VSApplication {
public const string Key = "VSApplication";
public const string VisualStudio = "VisualStudio";
public const string WDExpress = "WDExpress";
public const string VWDExpress = "VWDExpress";
public const string Mock = "Mock";
}
public static class VSExecutable {
public const string Key = "VSExecutable";
public const string DevEnv = "devenv";
public const string WDExpress = "wdexpress";
public const string VWDExpress = "vwdexpress";
}
public static class VSVersion {
public const string Key = "VSVersion";
}
public static class VSHive {
public const string Key = "VSHive";
public const string Exp = "Exp";
}
public static class VSLaunchTimeoutInSeconds {
public const string Key = "VSLaunchTimeoutInSeconds";
}
public static class VSDebugMixedMode {
public const string Key = "VSDebugMixedMode";
}
}
}

Просмотреть файл

@ -1,273 +1,273 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.Win32;
using Process = System.Diagnostics.Process;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
class VisualStudio : IDisposable {
private readonly Version _version;
private readonly int _processId;
private readonly bool _killOnDispose;
private DTE _dte;
public VisualStudio(int processId, Version version, bool killOnDispose = false) {
_processId = processId;
_version = version;
_killOnDispose = killOnDispose;
}
protected virtual void Dispose(bool disposing) {
MessageFilter.Revoke();
if (_killOnDispose) {
try {
Process.GetProcessById(_processId).Kill();
} catch (ArgumentException) {
} catch (InvalidOperationException) {
} catch (Win32Exception) {
}
}
}
~VisualStudio() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public int ProcessId {
get { return _processId; }
}
public static async Task<VisualStudio> LaunchAsync(
string application,
string executable,
Version version,
string hive,
CancellationToken cancel
) {
var installDir = GetInstallDir(application, version, hive);
if (!Directory.Exists(installDir)) {
throw new DirectoryNotFoundException("Cannot find Visual Studio install path");
}
var devenv = Path.Combine(installDir, executable);
if (!File.Exists(devenv)) {
throw new FileNotFoundException("Cannot find Visual Studio executable");
}
var psi = new ProcessStartInfo(devenv);
if (!string.IsNullOrEmpty(hive)) {
psi.Arguments = "/rootSuffix " + hive;
}
cancel.ThrowIfCancellationRequested();
var process = Process.Start(psi);
await Task.Run(() => process.WaitForInputIdle(), cancel);
var vs = new VisualStudio(process.Id, version, true);
try {
while (true) {
cancel.ThrowIfCancellationRequested();
if (vs.DTE != null) {
return Interlocked.Exchange(ref vs, null);
}
await Task.Delay(1000, cancel);
}
} finally {
if (vs != null) {
vs.Dispose();
}
}
}
private static string GetInstallDir(string application, Version version, string hive) {
using (var root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var key = root.OpenSubKey("Software\\Microsoft\\" + application)) {
if (key == null) {
return null;
}
string installDir;
using (var subkey = key.OpenSubKey(version.ToString())) {
if (subkey == null) {
return null;
}
installDir = subkey.GetValue("InstallDir") as string;
}
if (!Directory.Exists(installDir)) {
return null;
}
return installDir;
}
}
public DTE DTE {
get {
if (_dte == null) {
_dte = GetDTE(_processId, _version);
}
return _dte;
}
}
public T GetService<T>(Type type = null) where T : class {
var sp = ((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)DTE);
return new ServiceProvider(sp).GetService(type ?? typeof(T)) as T;
}
// Source from
// http://blogs.msdn.com/b/kirillosenkov/archive/2011/08/10/how-to-get-dte-from-visual-studio-process-id.aspx
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
private static DTE GetDTE(int processId, Version version) {
MessageFilter.Register();
var process = Process.GetProcessById(processId);
string progIdName = "VisualStudio";
switch (process.MainModule.ModuleName.ToLowerInvariant()) {
case "wdexpress.exe":
progIdName = "WDExpress";
break;
case "vwdexpress.exe":
progIdName = "VWDExpress";
break;
}
string progId = string.Format("!{0}.DTE.{1}:{2}", progIdName, version, processId);
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
uint numberFetched = 0;
while (enumMonikers.Next(1, moniker, out numberFetched) == 0) {
IMoniker runningObjectMoniker = moniker[0];
string name = null;
try {
if (runningObjectMoniker != null) {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
} catch (UnauthorizedAccessException) {
// Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal)) {
rot.GetObject(runningObjectMoniker, out runningObject);
break;
}
}
return (DTE)runningObject;
}
public class MessageFilter : IOleMessageFilter {
// Start the filter.
public static void Register() {
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke() {
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
const int SERVERCALL_ISHANDLED = 0;
const int SERVERCALL_RETRYLATER = 2;
const int PENDINGMSG_WAITDEFPROCESS = 2;
private MessageFilter() { }
// IOleMessageFilter functions.
// Handle incoming thread requests.
int IOleMessageFilter.HandleInComingCall(int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo) {
return SERVERCALL_ISHANDLED;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) {
if (dwRejectType == SERVERCALL_RETRYLATER && dwTickCount < 10000) {
// Retry the thread call after 250ms
return 250;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType) {
return PENDINGMSG_WAITDEFPROCESS;
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
[ComImport]
[Guid("00000016-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter {
[PreserveSig]
int HandleInComingCall(int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
}
}
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.Win32;
using Process = System.Diagnostics.Process;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudioTools.VSTestHost.Internal {
class VisualStudio : IDisposable {
private readonly Version _version;
private readonly int _processId;
private readonly bool _killOnDispose;
private DTE _dte;
public VisualStudio(int processId, Version version, bool killOnDispose = false) {
_processId = processId;
_version = version;
_killOnDispose = killOnDispose;
}
protected virtual void Dispose(bool disposing) {
MessageFilter.Revoke();
if (_killOnDispose) {
try {
Process.GetProcessById(_processId).Kill();
} catch (ArgumentException) {
} catch (InvalidOperationException) {
} catch (Win32Exception) {
}
}
}
~VisualStudio() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public int ProcessId {
get { return _processId; }
}
public static async Task<VisualStudio> LaunchAsync(
string application,
string executable,
Version version,
string hive,
CancellationToken cancel
) {
var installDir = GetInstallDir(application, version, hive);
if (!Directory.Exists(installDir)) {
throw new DirectoryNotFoundException("Cannot find Visual Studio install path");
}
var devenv = Path.Combine(installDir, executable);
if (!File.Exists(devenv)) {
throw new FileNotFoundException("Cannot find Visual Studio executable");
}
var psi = new ProcessStartInfo(devenv);
if (!string.IsNullOrEmpty(hive)) {
psi.Arguments = "/rootSuffix " + hive;
}
cancel.ThrowIfCancellationRequested();
var process = Process.Start(psi);
await Task.Run(() => process.WaitForInputIdle(), cancel);
var vs = new VisualStudio(process.Id, version, true);
try {
while (true) {
cancel.ThrowIfCancellationRequested();
if (vs.DTE != null) {
return Interlocked.Exchange(ref vs, null);
}
await Task.Delay(1000, cancel);
}
} finally {
if (vs != null) {
vs.Dispose();
}
}
}
private static string GetInstallDir(string application, Version version, string hive) {
using (var root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var key = root.OpenSubKey("Software\\Microsoft\\" + application)) {
if (key == null) {
return null;
}
string installDir;
using (var subkey = key.OpenSubKey(version.ToString())) {
if (subkey == null) {
return null;
}
installDir = subkey.GetValue("InstallDir") as string;
}
if (!Directory.Exists(installDir)) {
return null;
}
return installDir;
}
}
public DTE DTE {
get {
if (_dte == null) {
_dte = GetDTE(_processId, _version);
}
return _dte;
}
}
public T GetService<T>(Type type = null) where T : class {
var sp = ((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)DTE);
return new ServiceProvider(sp).GetService(type ?? typeof(T)) as T;
}
// Source from
// http://blogs.msdn.com/b/kirillosenkov/archive/2011/08/10/how-to-get-dte-from-visual-studio-process-id.aspx
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
private static DTE GetDTE(int processId, Version version) {
MessageFilter.Register();
var process = Process.GetProcessById(processId);
string progIdName = "VisualStudio";
switch (process.MainModule.ModuleName.ToLowerInvariant()) {
case "wdexpress.exe":
progIdName = "WDExpress";
break;
case "vwdexpress.exe":
progIdName = "VWDExpress";
break;
}
string progId = string.Format("!{0}.DTE.{1}:{2}", progIdName, version, processId);
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
uint numberFetched = 0;
while (enumMonikers.Next(1, moniker, out numberFetched) == 0) {
IMoniker runningObjectMoniker = moniker[0];
string name = null;
try {
if (runningObjectMoniker != null) {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
} catch (UnauthorizedAccessException) {
// Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal)) {
rot.GetObject(runningObjectMoniker, out runningObject);
break;
}
}
return (DTE)runningObject;
}
public class MessageFilter : IOleMessageFilter {
// Start the filter.
public static void Register() {
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke() {
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
const int SERVERCALL_ISHANDLED = 0;
const int SERVERCALL_RETRYLATER = 2;
const int PENDINGMSG_WAITDEFPROCESS = 2;
private MessageFilter() { }
// IOleMessageFilter functions.
// Handle incoming thread requests.
int IOleMessageFilter.HandleInComingCall(int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo) {
return SERVERCALL_ISHANDLED;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) {
if (dwRejectType == SERVERCALL_RETRYLATER && dwTickCount < 10000) {
// Retry the thread call after 250ms
return 250;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType) {
return PENDINGMSG_WAITDEFPROCESS;
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
[ComImport]
[Guid("00000016-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter {
[PreserveSig]
int HandleInComingCall(int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
}
}