зеркало из https://github.com/microsoft/TeamMate.git
Seed the repro from https://garage-02.visualstudio.com/_git/TeamMate with last commit being d7884798a72f7772f3a09618ff5d118be3433610
This commit is contained in:
Родитель
8c53aa2beb
Коммит
759aa882ec
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
<config>
|
||||
<add key="repositoryPath" value="..\packages" />
|
||||
</config>
|
||||
</configuration>
|
|
@ -0,0 +1,4 @@
|
|||
@echo off
|
||||
setlocal
|
||||
|
||||
powershell.exe -command ". %~dp0\%~n0.ps1" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
|
@ -0,0 +1,237 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
A script for building and publishing TeamMate to Toolbox.
|
||||
|
||||
.DESCRIPTION
|
||||
This scripts takes care of versioning TeamMate, performing a clean build, and generating
|
||||
ClickOnce files. It can also publish the ClickOnce files to the Toolbox file share.
|
||||
|
||||
.PARAMETER debug
|
||||
Builds the Debug flavor as opposed to the (default) Relase flavor.
|
||||
|
||||
.PARAMETER updateVersion
|
||||
If true, generates a new version and updates source files with that value
|
||||
|
||||
.PARAMETER build
|
||||
If true, performs a build (default value is true unless upload is specified).
|
||||
|
||||
.PARAMETER upload
|
||||
If true, uploads the previous build to Toolbox. This must be invoked after having performed a build.
|
||||
|
||||
.PARAMETER noresign
|
||||
If specified, avoids re-signing the ClickOnce manifest with the custom patched manifest.
|
||||
|
||||
.PARAMETER local
|
||||
If specified, generates a published build targetted for local testing only, NOT FOR UPLOADING TO TOOLBOX.
|
||||
#>
|
||||
|
||||
param([switch]$debug, [switch] $updateVersion, [switch] $build, [switch] $upload, [switch]$noresign, [switch]$local)
|
||||
|
||||
###############################################################################
|
||||
# Helper Functions
|
||||
###############################################################################
|
||||
|
||||
function Get-ScriptDirectory()
|
||||
{
|
||||
$invocation = (Get-Variable MyInvocation -Scope 1).Value;
|
||||
return Split-Path $invocation.MyCommand.Path;
|
||||
}
|
||||
|
||||
function Write-Info($message)
|
||||
{
|
||||
Write-Host -ForegroundColor Cyan $message;
|
||||
}
|
||||
|
||||
function Write-Warning($message)
|
||||
{
|
||||
Write-Host -ForegroundColor Yellow $message;
|
||||
}
|
||||
|
||||
function Write-Error($message)
|
||||
{
|
||||
Write-Host -ForegroundColor Red $message;
|
||||
}
|
||||
|
||||
# Updates a file with a new content, only if the content has changed.
|
||||
# Also adds the file to a list of files to checkout if the content did change.
|
||||
|
||||
function UpdateFile($file, $content, [switch] $UTF8)
|
||||
{
|
||||
$oldContent = [System.IO.File]::ReadAllText($file);
|
||||
|
||||
if( $content -ne $oldContent )
|
||||
{
|
||||
# Ensure version file is writeable
|
||||
$fileInfo = New-Object System.IO.FileInfo $file
|
||||
$fileInfo.IsReadOnly = $false;
|
||||
if( $utf8 )
|
||||
{
|
||||
Set-Content -path $file -value $content -encoding UTF8;
|
||||
}
|
||||
else
|
||||
{
|
||||
Set-Content -path $file -value $content;
|
||||
}
|
||||
|
||||
Write-Host "Updated $file with new version info";
|
||||
|
||||
return $true;
|
||||
}
|
||||
|
||||
return $false;
|
||||
}
|
||||
|
||||
# Generates a new build version based on the current date
|
||||
|
||||
function GenerateBuildVersion()
|
||||
{
|
||||
# We will calculate the 3rd component (upgrade segment) of the version number
|
||||
# based on the date. The number has to be less than 65355, so the first digit
|
||||
# should be a number between 1-6 (if we use 0 we mess up stuff). The first number
|
||||
# will be given by the year mod 6 + 1, e.g. 2009 % 6 -> 5 + 1 -> 6
|
||||
|
||||
$date = Get-Date;
|
||||
$firstDigit = ($date.Year % 6) + 1;
|
||||
$buildVersion = "{0}{1:00}{2:00}" -f $firstDigit, $date.Month, $date.Day;
|
||||
return $buildVersion;
|
||||
}
|
||||
|
||||
# Parses a full version, and returns an object with properties for each version component
|
||||
|
||||
function ParseVersion($version)
|
||||
{
|
||||
$regex = [regex] "(\d+)\.(\d+)\.(\d+)\.(\d+)";
|
||||
$match = $regex.Match($version);
|
||||
|
||||
if(!$match.Success)
|
||||
{
|
||||
throw ("String does not match expected version format: {0}" -f $version);
|
||||
}
|
||||
|
||||
$versionObject = new-object psobject;
|
||||
$versionObject | add-member noteproperty "Major" ([int] $match.Groups[1].Value);
|
||||
$versionObject | add-member noteproperty "Minor" ([int] $match.Groups[2].Value);
|
||||
$versionObject | add-member noteproperty "Build" ([int] $match.Groups[3].Value);
|
||||
$versionObject | add-member noteproperty "Revision" ([int] $match.Groups[4].Value);
|
||||
return $versionObject;
|
||||
}
|
||||
|
||||
# Updates the build version (3rd component) of a given full version
|
||||
|
||||
function UpdateBuildVersion($currentVersion, $buildVersion)
|
||||
{
|
||||
$currentBuildVersion = $currentVersion.Build;
|
||||
|
||||
if($buildVersion -ne $currentBuildVersion)
|
||||
{
|
||||
$currentVersion.Build = $buildVersion;
|
||||
$currentVersion.Revision = 0;
|
||||
|
||||
if($buildVersion -lt $currentBuildVersion)
|
||||
{
|
||||
$currentVersion.Minor += 1;
|
||||
Write-Warning ("WARNING: IMPORTANT!!!! Build version rolled over from {0} to {1} so incrementing minor version to {2}" -f $currentBuildVersion,$buildVersion,$currentVersion.Minor);
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentVersion.Build = $buildVersion;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentVersion.Revision = $version.Revision + 1;
|
||||
}
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Script
|
||||
###############################################################################
|
||||
|
||||
$msbuild = "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe"
|
||||
$scriptFolder = Get-ScriptDirectory;
|
||||
$versionFile = "$scriptFolder\version.txt";
|
||||
$buildInfoFile = "$scriptFolder\BuildInfo.cs";
|
||||
$versionTargetsFile = "$scriptFolder\Microsoft.Internal.Tools.TeamMate.Version.targets";
|
||||
$project = "$scriptFolder\..\TeamMate\TeamMate.csproj";
|
||||
$resign = (-not $noresign);
|
||||
|
||||
if(-not ($updateVersion -or $build -or $upload))
|
||||
{
|
||||
Write-Error "Please specify one or more of: -updateVersion, -build, -upload";
|
||||
exit 1
|
||||
}
|
||||
|
||||
if($debug)
|
||||
{
|
||||
$configs = ( "Debug" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$configs = ( "Release" );
|
||||
}
|
||||
|
||||
if($updateVersion)
|
||||
{
|
||||
$buildVersion = GenerateBuildVersion
|
||||
$version = ParseVersion (gc $versionFile);
|
||||
UpdateBuildVersion $version $buildVersion;
|
||||
$newVersion = "{0}.{1}.{2}.{3}" -f $version.Major,$version.Minor,$version.Build,$version.Revision;
|
||||
|
||||
Write-Info "Updating Build Version to $newVersion..."
|
||||
|
||||
$ignore = UpdateFile $versionFile $newVersion;
|
||||
|
||||
$buildInfo = Get-Content $buildInfoFile;
|
||||
$buildInfo = $buildInfo -replace "AssemblyVersion\(.*", "AssemblyVersion(""$newVersion"")]";
|
||||
$buildInfo = $buildInfo -replace "AssemblyFileVersion\(.*", "AssemblyFileVersion(""$newVersion"")]";
|
||||
$ignore = UpdateFile $buildInfoFile $buildInfo;
|
||||
|
||||
$versionTargets = Get-Content $versionTargetsFile;
|
||||
$versionTargets = $versionTargets -replace "<ApplicationVersion>.*</ApplicationVersion>", "<ApplicationVersion>$newVersion</ApplicationVersion>";
|
||||
$ignore = UpdateFile $versionTargetsFile $versionTargets -UTF8;
|
||||
}
|
||||
|
||||
if($build)
|
||||
{
|
||||
foreach($config in $configs)
|
||||
{
|
||||
Write-Info "Building $config...";
|
||||
. "$msbuild" "$project" "/p:Configuration=$config" "/p:Publish=true" "/p:Local=$local" /nologo /v:m /t:rebuild
|
||||
|
||||
if($LastExitCode -ne 0 )
|
||||
{
|
||||
Write-Error "MSBuild failed. Exiting early!";
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
foreach($config in $configs)
|
||||
{
|
||||
Write-Info "Publishing ClickOnce files for $config...";
|
||||
. "$msbuild" "$project" "/p:Configuration=$config" "/p:Publish=true" "/p:Resign=$resign" "/p:Local=$local" /nologo /v:m /t:publish
|
||||
|
||||
if($LastExitCode -ne 0 )
|
||||
{
|
||||
Write-Error "Publishing failed!";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($upload)
|
||||
{
|
||||
foreach($config in $configs)
|
||||
{
|
||||
Write-Info "Uploading ClickOnce files for $config...";
|
||||
. "$msbuild" "$project" "/p:Configuration=$config" "/p:Publish=true" /nologo /v:m /t:upload
|
||||
|
||||
if($LastExitCode -ne 0 )
|
||||
{
|
||||
Write-Error "Uploading failed!";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host -ForegroundColor Green "Done...";
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("TeamMate")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("2.1.21106.0")]
|
||||
[assembly: AssemblyFileVersion("2.1.21106.0")]
|
|
@ -0,0 +1,9 @@
|
|||
@echo off
|
||||
setlocal
|
||||
REM http://msdn.microsoft.com/en-us/library/ff699202.aspx
|
||||
|
||||
set CWD=%~dp0
|
||||
SET CWD=%CWD:~0,-1%
|
||||
|
||||
makecert -sv "%CWD%\TeamMate.pvk" -r "%CWD%\TeamMate.cer" -n "CN=Ben Amodio"
|
||||
pvk2pfx -pvk "%CWD%\TeamMate.pvk" -spc "%CWD%\TeamMate.cer" -pfx "%CWD%\TeamMate.pfx"
|
|
@ -0,0 +1 @@
|
|||
sn -d TEAMMATE
|
|
@ -0,0 +1,7 @@
|
|||
@echo off
|
||||
setlocal
|
||||
set SN="%~dp0Tools\sn.exe"
|
||||
set PFXFILE="%~dp0TeamMate.pfx"
|
||||
|
||||
%SN% -i %PFXFILE% TEAMMATE
|
||||
%PFXFILE%
|
Двоичный файл не отображается.
|
@ -0,0 +1,58 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.Internal.Tools.TeamMate.targets
|
||||
|
||||
This file defines the custom variables and steps in the standard build process for TeamMate
|
||||
C# .NET projects.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<NoWarn>467;618</NoWarn>
|
||||
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Publish)' == 'true' ">
|
||||
<!-- Only sign assemblies if publishing to Toolbox -->
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\TeamMate.pfx</AssemblyOriginatorKeyFile>
|
||||
|
||||
<!-- For safety purposes, never assign the InstallUrl unless Publish was explicitly set -->
|
||||
<InstallUrl Condition=" $(Local) != 'true' ">\\tkfiltoolbox\tools\teammate\releases\production\</InstallUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<BuildScripts>$(MSBuildThisFileDirectory)</BuildScripts>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ExternalPath>$(MSBuildThisFileDirectory)\..\External</ExternalPath>
|
||||
<OfficeExternalPath>$(ExternalPath)\Office\14.0</OfficeExternalPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask TaskName="MSBuildTasks.KillTask" AssemblyFile="$(BuildScripts)\Libraries\MSBuildTasks.dll" />
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,24 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.Internal.Tools.TeamMate.targets
|
||||
|
||||
This file defines the custom variables and steps in the standard build process for TeamMate
|
||||
C# .NET projects.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup>
|
||||
<ApplicationVersion>2.1.21106.0</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)\BuildInfo.cs">
|
||||
<InProject>False</InProject>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.Internal.Tools.TeamMate.targets
|
||||
|
||||
This file defines the custom variables and steps in the standard build process for TeamMate
|
||||
C# .NET projects.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup>
|
||||
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
|
||||
<BaseBinOutputPath>bin\</BaseBinOutputPath>
|
||||
<BasePublishOutputPath>publish\</BasePublishOutputPath>
|
||||
|
||||
<OutputPath>$(BaseBinOutputPath)$(Configuration)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(ClickOncePublish)' == 'True'">
|
||||
<PublishUrl>$(BasePublishOutputPath)$(Configuration)\</PublishUrl>
|
||||
|
||||
<!-- KLUDGE: Required when doing msbuild /t:publish on the command-line -->
|
||||
<PublishDir Condition="'$(BuildingInsideVisualStudio)' != 'true'">$(PublishUrl)</PublishDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="Microsoft.Internal.Tools.TeamMate.Version.targets" />
|
||||
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
</Project>
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,9 @@
|
|||
<?xml version ="1.0"?>
|
||||
<!-- This allows mt.exe to run on machines with the CLR v4 installed but not 1.1 or 2.0 -->
|
||||
<configuration>
|
||||
<startup useLegacyV2RuntimeActivationPolicy="true">
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
<supportedRuntime version="v1.1.4322"/>
|
||||
</startup>
|
||||
</configuration>
|
Двоичный файл не отображается.
|
@ -0,0 +1,6 @@
|
|||
<?xml version ="1.0"?>
|
||||
<configuration>
|
||||
<startup useLegacyV2RuntimeActivationPolicy="true">
|
||||
<requiredRuntime safemode="true" imageVersion="v4.0.30319" version="v4.0.30319"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,120 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Updates TeamMate's published ClickOnce application to enable Visual Styles on the WPF app.
|
||||
|
||||
.DESCRIPTION
|
||||
This kludgy workaround is an automation of the steps described in
|
||||
http://msdn.microsoft.com/en-us/library/hh323463.aspx
|
||||
|
||||
The ONLY reason why we need to introduce Microsoft.Windows.Common-Controls
|
||||
in our custom manifest is to support previewing Outlook message files.
|
||||
|
||||
.PARAMETER publishDir
|
||||
The location of the ClickOnce published files built by MSBuild.
|
||||
|
||||
.PARAMETER manifestFile
|
||||
A custom .manifest file that will be embedded in the resulting TeamMate.exe.deploy file
|
||||
|
||||
.PARAMETER certFile
|
||||
Specifies the name of an X509 certificate file with which to sign a manifest or license file.
|
||||
#>
|
||||
|
||||
param($publishDir, $manifestFile, $certFile);
|
||||
|
||||
# Constants used below
|
||||
$keyContainer = "TEAMMATE";
|
||||
$password = "TeamMate";
|
||||
|
||||
function Get-ScriptDirectory()
|
||||
{
|
||||
$invocation = (Get-Variable MyInvocation -Scope 1).Value;
|
||||
return Split-Path $invocation.MyCommand.Path;
|
||||
}
|
||||
|
||||
function Join-Path-And-Test($p1, $p2)
|
||||
{
|
||||
$result = Join-Path $p1 $p2;
|
||||
if(-not (Test-Path $result))
|
||||
{
|
||||
$message = "Expected file {0} was not found" -f $result;
|
||||
Write-Host -ForegroundColor Red $message;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$buildFolder = Get-ScriptDirectory;
|
||||
$mageExe = Join-Path-And-Test $buildFolder "Tools\mage.exe";
|
||||
$mtExe = Join-Path-And-Test $buildFolder "Tools\mt.exe";
|
||||
$snExe = Join-Path-And-Test $buildFolder "Tools\sn.exe";
|
||||
|
||||
if(-not (Test-Path -PathType Container $publishDir))
|
||||
{
|
||||
throw ("Publish directory {0} was not found." -f $publishDir);
|
||||
}
|
||||
|
||||
if(-not (Test-Path -PathType Leaf $manifestFile))
|
||||
{
|
||||
throw ("Manifest file {0} was not found." -f $manifestFile);
|
||||
}
|
||||
|
||||
if(-not (Test-Path -PathType Leaf $certFile))
|
||||
{
|
||||
throw ("Certificate file {0} was not found." -f $certFile);
|
||||
}
|
||||
|
||||
$applicationFile = (dir $publishDir -filter *.application)[0].FullName;
|
||||
$appFiles = Join-Path-And-Test $publishDir "Application Files";
|
||||
|
||||
$latestPublishPath = (dir $appFiles| sort -Descending LastWriteTime )[0].FullName;
|
||||
$exeDeployFile = (dir $latestPublishPath -filter *.exe.deploy)[0].FullName;
|
||||
$publishedManifestFile = (dir $latestPublishPath -filter *.manifest)[0].FullName;
|
||||
|
||||
Write-Host "Updating published ClickOnce application at $latestPublishPath";
|
||||
Write-Host "";
|
||||
|
||||
Write-Host "Embedding custom manifest file for published TeamMate.exe.deploy...";
|
||||
& $mtExe -nologo -manifest "$manifestFile" "-outputresource:$exeDeployFile";
|
||||
|
||||
if($LastExitCode -ne 0)
|
||||
{
|
||||
Write-Error "$mtExe -nologo -manifest `"$manifestFile`" `"-outputresource:$exeDeployFile`"";
|
||||
Write-Error "Failed with exit code $LastExitCode";
|
||||
exit $LastExitCode;
|
||||
}
|
||||
|
||||
Write-Host "Re-signing TeamMate.exe.deploy...";
|
||||
& $snExe -q -Rca $exeDeployFile $keyContainer;
|
||||
|
||||
if($LastExitCode -ne 0)
|
||||
{
|
||||
Write-Error "$snExe -q -Rca `"$exeDeployFile`" $keyContainer";
|
||||
Write-Error "Failed with exit code $LastExitCode";
|
||||
exit $LastExitCode;
|
||||
}
|
||||
|
||||
Write-Host "Updating published manifest TeamMate.exe.manifest...";
|
||||
$deployFiles = dir "$latestPublishPath\*.deploy";
|
||||
$deployFiles | %{ $newName = $_.FullName.Substring(0, $_.FullName.Length - 7); ren $_.FullName $newName };
|
||||
& $mageExe -u "$publishedManifestFile" -cf "$certFile" -password "$password";
|
||||
$deployFiles | %{ $oldName = $_.FullName.Substring(0, $_.FullName.Length - 7); ren $oldName $_.FullName };
|
||||
|
||||
if($LastExitCode -ne 0)
|
||||
{
|
||||
Write-Error "$mageExe -u `"$publishedManifestFile`" -cf `"$certFile`" -password pwd";
|
||||
Write-Error "Failed with exit code $LastExitCode";
|
||||
exit $LastExitCode;
|
||||
}
|
||||
|
||||
Write-Host "Updating published TeamMate.application...";
|
||||
& $mageExe -u "$applicationFile" -appm "$publishedManifestFile" -cf "$certFile" -password "$password";
|
||||
|
||||
if($LastExitCode -ne 0)
|
||||
{
|
||||
Write-Error "$mageExe -u `"$applicationFile`" -appm `"$publishedManifestFile`" -cf `"$certFile`" -password pwd";
|
||||
Write-Error "Failed with exit code $LastExitCode";
|
||||
exit $LastExitCode;
|
||||
}
|
||||
|
||||
Write-Host "Done";
|
|
@ -0,0 +1 @@
|
|||
2.1.21106.0
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), Build\Microsoft.Internal.Tools.TeamMate.Settings.targets))\Build\Microsoft.Internal.Tools.TeamMate.Settings.targets" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{9AC62340-334D-489B-8C36-5EC5F4C56C8E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.Internal.Tools.TeamMate.Client</RootNamespace>
|
||||
<AssemblyName>Microsoft.Internal.Tools.TeamMate.Client</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="WorkItemCreationInfo.cs" />
|
||||
<Compile Include="PathUtilities.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TeamMateClient.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(BuildScripts)\Microsoft.Internal.Tools.TeamMate.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for manipulating file system paths.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a trimmed local copy of Foundation.IO.PathUtilities, as we do not want dependencies
|
||||
/// to another assembly in this client one.
|
||||
/// </remarks>
|
||||
internal static class PathUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a unique filename that doesn't exist in the given path,
|
||||
/// by appending an incremented number to the filename (without the
|
||||
/// extension).
|
||||
/// </summary>
|
||||
/// <param name="path">An initial path.</param>
|
||||
/// <returns>A unique filename path derived from the input.</returns>
|
||||
public static string EnsureFilenameIsUnique(string path)
|
||||
{
|
||||
if (!Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
string name = Path.GetFileNameWithoutExtension(path);
|
||||
string extension = Path.GetExtension(path);
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
int increment = 1;
|
||||
|
||||
do
|
||||
{
|
||||
increment++;
|
||||
string newName = String.Format("{0} ({1}){2}", name, increment, extension);
|
||||
|
||||
// TODO: Need to make sure path did not exceed max path length.
|
||||
path = Path.Combine(dir, newName);
|
||||
}
|
||||
while (Exists(path));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified path exists (as a file or directory).
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if the path exists; <c>false</c> if otherwise.</returns>
|
||||
private static bool Exists(string path)
|
||||
{
|
||||
return File.Exists(path) || Directory.Exists(path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Client")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9ac62340-334d-489b-8c36-5ec5f4c56c8e")]
|
|
@ -0,0 +1,96 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// A client class to launch and interact with an installed TeamMateClient instace.
|
||||
/// </summary>
|
||||
public class TeamMateClient
|
||||
{
|
||||
private const string TeamMateDownloadUrl = "http://toolbox/teammate";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether TeamMate is installed for the current user.
|
||||
/// </summary>
|
||||
public static bool IsInstalled()
|
||||
{
|
||||
bool isInstalled = Registry.GetValue(@"HKEY_CLASSES_ROOT\TeamMate.tmx", null, null) != null;
|
||||
return isInstalled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the TeamMate download page in an external web browser.
|
||||
/// </summary>
|
||||
public static void LaunchDownloadPage()
|
||||
{
|
||||
Process.Start(TeamMateDownloadUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launches TeamMate, creating a work item with the given creation information.
|
||||
/// </summary>
|
||||
/// <param name="createInfo">The create information.</param>
|
||||
public void CreateWorkItem(WorkItemCreationInfo createInfo)
|
||||
{
|
||||
if (createInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("createInfo");
|
||||
}
|
||||
|
||||
EnsureIsInstalled();
|
||||
|
||||
string tmxFile = Path.Combine(Path.GetTempPath(), "Outlook.tmx");
|
||||
tmxFile = PathUtilities.EnsureFilenameIsUnique(tmxFile);
|
||||
|
||||
XDocument doc = CreateActionXml(createInfo);
|
||||
doc.Save(tmxFile);
|
||||
Process.Start(tmxFile);
|
||||
}
|
||||
|
||||
private static XDocument CreateActionXml(WorkItemCreationInfo createInfo)
|
||||
{
|
||||
XElement fieldsElement = new XElement("Fields");
|
||||
foreach (var entry in createInfo.Fields)
|
||||
{
|
||||
fieldsElement.Add(new XElement("Field", new XAttribute("Name", entry.Key), entry.Value));
|
||||
}
|
||||
|
||||
XElement attachmentsElement = new XElement("Attachments");
|
||||
foreach (var attachment in createInfo.Attachments)
|
||||
{
|
||||
attachmentsElement.Add(new XElement("Attachment",
|
||||
new XAttribute("Path", attachment.Path),
|
||||
new XAttribute("DeleteOnSave", attachment.DeleteOnSave)
|
||||
));
|
||||
}
|
||||
|
||||
XDocument doc = new XDocument(
|
||||
new XElement("TeamMate",
|
||||
new XAttribute("Version", "1.0"),
|
||||
new XAttribute("DeleteOnLoad", "true"),
|
||||
new XElement("Action",
|
||||
new XAttribute("Type", "CreateWorkItem"),
|
||||
new XElement("WorkItem",
|
||||
fieldsElement,
|
||||
attachmentsElement
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void EnsureIsInstalled()
|
||||
{
|
||||
if (!IsInstalled())
|
||||
{
|
||||
throw new InvalidOperationException("TeamMate cannot be launched. Please install TeamMate from " + TeamMateDownloadUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Client
|
||||
{
|
||||
public class WorkItemCreationInfo
|
||||
{
|
||||
private IDictionary<string, object> fields = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public WorkItemCreationInfo()
|
||||
{
|
||||
this.Attachments = new List<AttachmentInfo>();
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return this.GetField("System.Title") as string; }
|
||||
set { this.SetField("System.Title", value); }
|
||||
}
|
||||
|
||||
public string History
|
||||
{
|
||||
get { return this.GetField("System.History") as string; }
|
||||
set { this.SetField("System.History", value); }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get { return this.GetField("System.Description") as string; }
|
||||
set { this.SetField("System.Description", value); }
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Fields
|
||||
{
|
||||
get { return this.fields; }
|
||||
}
|
||||
|
||||
public void SetField(string name, object value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
this.fields[name] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fields.Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public object GetField(string name)
|
||||
{
|
||||
object result;
|
||||
this.fields.TryGetValue(name, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public ICollection<AttachmentInfo> Attachments { get; private set; }
|
||||
}
|
||||
|
||||
public class AttachmentInfo
|
||||
{
|
||||
public AttachmentInfo(string path, bool deleteOnSave = false)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
this.Path = path;
|
||||
this.DeleteOnSave = deleteOnSave;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public bool DeleteOnSave { get; set; }
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Excel.dll
поставляемый
Normal file
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Excel.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.OneNote.dll
поставляемый
Normal file
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.OneNote.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Outlook.dll
поставляемый
Normal file
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Outlook.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.PowerPoint.dll
поставляемый
Normal file
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.PowerPoint.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Word.dll
поставляемый
Normal file
Двоичные данные
Source/External/Office/14.0/Microsoft.Office.Interop.Word.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
Source/External/Unofficial.HockeySDK.WPF/Microsoft.HockeyApp.Core45.dll
поставляемый
Normal file
Двоичные данные
Source/External/Unofficial.HockeySDK.WPF/Microsoft.HockeyApp.Core45.dll
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
Source/External/Unofficial.HockeySDK.WPF/Microsoft.HockeyApp.Kit.dll
поставляемый
Normal file
Двоичные данные
Source/External/Unofficial.HockeySDK.WPF/Microsoft.HockeyApp.Kit.dll
поставляемый
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,5 @@
|
|||
Signed versions of the Unofficial HockeyApp SDK for WPF applications, by MACK Mathieu.
|
||||
|
||||
See https://github.com/mathieumack/HockeySDK-Windows for more info.
|
||||
|
||||
Signing was required to consume in a full trust application in ClickOnce.
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// A comparer that compares absolute URIs.
|
||||
/// </summary>
|
||||
public class AbsoluteUriComparer : IEqualityComparer<Uri>
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified objects are equal.
|
||||
/// </summary>
|
||||
/// <param name="x">The first object of type <paramref name="T" /> to compare.</param>
|
||||
/// <param name="y">The second object of type <paramref name="T" /> to compare.</param>
|
||||
/// <returns>
|
||||
/// true if the specified objects are equal; otherwise, false.
|
||||
/// </returns>
|
||||
public bool Equals(Uri x, Uri y)
|
||||
{
|
||||
return String.Equals(Normalize(x), Normalize(y), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public int GetHashCode(Uri obj)
|
||||
{
|
||||
return Normalize(obj).GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the specified URI to a comparable value.
|
||||
/// </summary>
|
||||
/// <param name="uri">The URI.</param>
|
||||
/// <returns>The normalized URI string.</returns>
|
||||
private string Normalize(Uri uri)
|
||||
{
|
||||
// TODO: Match this more closely to TFS AbsoluteURIComparer. E.g. do we need to trim slashes et al?
|
||||
return uri.AbsoluteUri.ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Chaos
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception thrown by the chaos monkey whenever it introduces chaos into our product.
|
||||
/// </summary>
|
||||
public class ChaosException : Exception
|
||||
{
|
||||
public ChaosException()
|
||||
: base("The chaos monkey was naughty and injected this exception!")
|
||||
{
|
||||
}
|
||||
|
||||
public ChaosException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ChaosException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Chaos
|
||||
{
|
||||
public static class ChaosMonkey
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static Random random;
|
||||
private static readonly Random GlobalRandom = new Random();
|
||||
|
||||
private static readonly Task NullTask = Task.FromResult<object>(null);
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables chaos in the system.
|
||||
/// </summary>
|
||||
public static bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Potentially introduce chaos in the system, for a given chaos scenario.
|
||||
/// </summary>
|
||||
/// <param name="scenario">The scenario.</param>
|
||||
public static Task ChaosAsync(ChaosScenario scenario)
|
||||
{
|
||||
Assert.ParamIsNotNull(scenario, nameof(scenario));
|
||||
|
||||
if (IsEnabled)
|
||||
{
|
||||
return DoChaosAsync(scenario);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NullTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Potentially introduce chaos in the system, for a given chaos scenario.
|
||||
/// </summary>
|
||||
/// <param name="scenario">The scenario.</param>
|
||||
public static void Chaos(ChaosScenario scenario)
|
||||
{
|
||||
Assert.ParamIsNotNull(scenario, nameof(scenario));
|
||||
|
||||
if (IsEnabled)
|
||||
{
|
||||
if (scenario.Delay > 0)
|
||||
{
|
||||
DelayAsync(scenario).Wait();
|
||||
}
|
||||
|
||||
Fail(scenario);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task DoChaosAsync(ChaosScenario scenario)
|
||||
{
|
||||
if (IsEnabled)
|
||||
{
|
||||
if (scenario.Delay > 0)
|
||||
{
|
||||
await DelayAsync(scenario);
|
||||
}
|
||||
|
||||
Fail(scenario);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Fail(ChaosScenario scenario)
|
||||
{
|
||||
if (ShouldFail(scenario.FailureRate))
|
||||
{
|
||||
throw new ChaosException($"The chaos monkey was naughty and injected this exception! ({scenario.Name})");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldFail(double failureRate)
|
||||
{
|
||||
return failureRate > 0 && GetThreadSafeRandom().NextDouble() <= failureRate;
|
||||
}
|
||||
|
||||
private static Task DelayAsync(ChaosScenario scenario)
|
||||
{
|
||||
Log.Info($"Delaying {scenario.Delay}ms for chaos scenario '{scenario.Name}'");
|
||||
return Task.Delay(scenario.Delay);
|
||||
}
|
||||
|
||||
public static Task ChaosAsync(object chooseProject)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// Random is not thread-safe so use an instance per thread
|
||||
// See https://blogs.msdn.microsoft.com/pfxteam/2009/02/19/getting-random-numbers-in-a-thread-safe-way/
|
||||
private static Random GetThreadSafeRandom()
|
||||
{
|
||||
if (random == null)
|
||||
{
|
||||
int seed;
|
||||
lock (GlobalRandom)
|
||||
{
|
||||
seed = GlobalRandom.Next();
|
||||
random = new Random(seed);
|
||||
}
|
||||
}
|
||||
|
||||
return random;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Chaos
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a chaos scenario and its metadata for use by the Chaos moneky.
|
||||
/// </summary>
|
||||
public class ChaosScenario : INotifyPropertyChanged
|
||||
{
|
||||
public const double NeverFail = 0.0;
|
||||
public const double FailSeldomly = 0.25;
|
||||
public const double FailSometimes = 0.5;
|
||||
public const double FailFrequently = 0.75;
|
||||
public const double FailAlways = 1.0;
|
||||
|
||||
public const int Slow = 1000;
|
||||
public const int VerySlow = 3000;
|
||||
public const int SuperSlow = 10000;
|
||||
|
||||
private double failureRate;
|
||||
private int delay;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChaosScenario"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The scenario name.</param>
|
||||
/// <param name="failureRate">The failure rate (from 0.0 to 1.0).</param>
|
||||
/// <param name="delay">The delay (in milliseconds).</param>
|
||||
public ChaosScenario(string name, double failureRate = 0, int delay = 0)
|
||||
{
|
||||
this.Name = name;
|
||||
this.FailureRate = failureRate;
|
||||
this.Delay = delay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scenario name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current failure rate for this scenario (from 0.0 to 1.0, 1.0 being 100% failure).
|
||||
/// </summary>
|
||||
public double FailureRate
|
||||
{
|
||||
get { return this.failureRate; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value != this.failureRate)
|
||||
{
|
||||
if (value < 0 || value > 1.0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("FailureRate", "Failure rate must be between 0.0 and 1.0");
|
||||
}
|
||||
|
||||
this.failureRate = value;
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FailureRate)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current time delay, in milliseconds, for this scenario.
|
||||
/// </summary>
|
||||
public int Delay
|
||||
{
|
||||
get { return this.delay; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value != this.delay)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay", "Delay must be greater or equal to 0.");
|
||||
}
|
||||
|
||||
this.delay = value;
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Delay)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the failure rate and delay for this scenario back to 0.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
this.FailureRate = 0;
|
||||
this.Delay = 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
|
||||
public static ICollection<ChaosScenario> LoadScenariosFromStaticFields(Type type)
|
||||
{
|
||||
var allFields = type.GetTypeInfo().DeclaredFields;
|
||||
var scenarioFields = allFields.Where(f => f.IsPublic && f.IsStatic && f.FieldType == typeof(ChaosScenario));
|
||||
var scenarios = scenarioFields.Select(f => f.GetValue(null)).OfType<ChaosScenario>().OrderBy(s => s.Name).ToList();
|
||||
return scenarios;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines extension methods for collection types.
|
||||
/// </summary>
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the elements in another collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="items">The items to add.</param>
|
||||
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
|
||||
{
|
||||
Assert.ParamIsNotNull(collection, "collection");
|
||||
Assert.ParamIsNotNull(items, "items");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
collection.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the elements in another collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="items">The items to add.</param>
|
||||
public static void AddRange(this System.Collections.IList collection, System.Collections.IEnumerable items)
|
||||
{
|
||||
Assert.ParamIsNotNull(collection, "collection");
|
||||
Assert.ParamIsNotNull(items, "items");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
collection.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the elements in another collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="items">The items to remove.</param>
|
||||
public static void RemoveRange<T>(this ICollection<T> collection, IEnumerable<T> items)
|
||||
{
|
||||
Assert.ParamIsNotNull(collection, "collection");
|
||||
Assert.ParamIsNotNull(items, "items");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
collection.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the elements in another collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="items">The items to remove.</param>
|
||||
public static void RemoveRange(this System.Collections.IList collection, System.Collections.IEnumerable items)
|
||||
{
|
||||
Assert.ParamIsNotNull(collection, "collection");
|
||||
Assert.ParamIsNotNull(items, "items");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
collection.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get a value for a given key. If not present, returns the default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dict">The dictionary.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>The looked up value, or if not found, the default value for the corresponding type.</returns>
|
||||
public static TValue TryGetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
|
||||
{
|
||||
TValue value;
|
||||
if (!dict.TryGetValue(key, out value))
|
||||
{
|
||||
value = default(TValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for collections.
|
||||
/// </summary>
|
||||
public static class CollectionUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an item to a list in MRU fashion, keeping the number of items in the list under
|
||||
/// a maximum limit.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <param name="maxItems">The maximum items to keep in the list.</param>
|
||||
/// <returns><c>true</c> if changes to the list were made, otherwise <c>false</c>.</returns>
|
||||
public static bool AddToMruList<T>(this IList<T> list, T item, int maxItems)
|
||||
{
|
||||
Assert.ParamIsNotNull(list, "list");
|
||||
Assert.ParamIsNotNull(item, "item");
|
||||
Assert.ParamIsNotNegative(maxItems, "maxItems");
|
||||
|
||||
int originalIndex = list.IndexOf(item);
|
||||
if (originalIndex == 0)
|
||||
{
|
||||
// Item is already at the front, no changes required
|
||||
}
|
||||
else if (originalIndex == -1)
|
||||
{
|
||||
// Item is not in the list, add it to the front and trim
|
||||
list.Insert(0, item);
|
||||
while (list.Count > maxItems)
|
||||
{
|
||||
list.RemoveAt(list.Count - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item is in the list, it just needs to be moved to the front
|
||||
ObservableCollection<T> observable = list as ObservableCollection<T>;
|
||||
if (observable != null)
|
||||
{
|
||||
// Optimization for observable collection, to avoid firing multiple events.
|
||||
observable.Move(originalIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.RemoveAt(originalIndex);
|
||||
list.Insert(0, item);
|
||||
}
|
||||
}
|
||||
|
||||
bool neededModifications = (originalIndex != 0);
|
||||
return neededModifications;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.CommandLine
|
||||
{
|
||||
public abstract class CommandBase : ICommand
|
||||
{
|
||||
protected CommandBase()
|
||||
{
|
||||
}
|
||||
|
||||
protected void Initialize(string name, string description, string usage)
|
||||
{
|
||||
this.CommandName = name;
|
||||
this.CommandDescription = description;
|
||||
this.CommandUsage = usage;
|
||||
}
|
||||
|
||||
public string CommandName { get; private set; }
|
||||
|
||||
public string CommandDescription { get; private set; }
|
||||
|
||||
public string CommandUsage { get; private set; }
|
||||
|
||||
public abstract void Run(string[] args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.CommandLine
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception class that indicates an error condition when parsing command line arguments.
|
||||
/// </summary>
|
||||
public class CommandLineArgumentException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of this exception.
|
||||
/// </summary>
|
||||
public CommandLineArgumentException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the this exception class with a specified
|
||||
/// error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public CommandLineArgumentException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the System.Exception class with a specified
|
||||
/// error message and a reference to the inner exception that is the cause of
|
||||
/// this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception,
|
||||
/// or a <c>null</c> reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public CommandLineArgumentException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.CommandLine
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to parse command-lines.
|
||||
/// </summary>
|
||||
public class CommandLineArgumentParser
|
||||
{
|
||||
private static readonly ICollection<string> EmptyArray = new string[0];
|
||||
|
||||
private ICollection<string> validOptions = EmptyArray;
|
||||
private ICollection<string> validMultiValueOptions = EmptyArray;
|
||||
private ICollection<string> validSwitches = EmptyArray;
|
||||
private IDictionary<string, IList<string>> arguments = new Dictionary<string, IList<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CommandLineArgumentParser"/> class.
|
||||
/// </summary>
|
||||
public CommandLineArgumentParser()
|
||||
{
|
||||
}
|
||||
|
||||
public ICollection<string> ValidOptions
|
||||
{
|
||||
get { return this.validOptions; }
|
||||
set { this.validOptions = value ?? EmptyArray; }
|
||||
}
|
||||
|
||||
public ICollection<string> ValidSwitches
|
||||
{
|
||||
get { return this.validSwitches; }
|
||||
set { this.validSwitches = value ?? EmptyArray; }
|
||||
}
|
||||
|
||||
public ICollection<string> ValidMultiValueOptions
|
||||
{
|
||||
get { return this.validMultiValueOptions; }
|
||||
set { this.validMultiValueOptions = value ?? EmptyArray; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has any arguments.
|
||||
/// </summary>
|
||||
public bool HasArguments
|
||||
{
|
||||
get { return arguments.Any(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a given switch was specified.
|
||||
/// </summary>
|
||||
public bool HasSwitch(string name)
|
||||
{
|
||||
return arguments.Keys.Contains(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for a given argument. Returns <c>null</c> if the argument was not defined.
|
||||
/// </summary>
|
||||
public string GetValue(string name)
|
||||
{
|
||||
IList<string> values = GetValues(name);
|
||||
return values.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the multiple available values for a given argument.
|
||||
/// </summary>
|
||||
public IList<string> GetValues(string name)
|
||||
{
|
||||
IList<string> values;
|
||||
arguments.TryGetValue(name, out values);
|
||||
return (values != null) ? values : new string[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for a given argument. Throws an exception if the argument was not defined.
|
||||
/// </summary>
|
||||
public string GetRequiredValue(string name)
|
||||
{
|
||||
string value = GetValue(name);
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new CommandLineArgumentException(String.Format("Please specify a value for required option '{0}'.", name));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an input argument as a required value that needs to be an existing file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>The validated input file.</returns>
|
||||
public string GetRequiredFile(string name)
|
||||
{
|
||||
string value = GetRequiredValue(name);
|
||||
|
||||
if (!File.Exists(value))
|
||||
{
|
||||
throw new CommandLineArgumentException(String.Format("File {0} does not exist", value));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URI for a given argument. Returns <c>null</c> if the argument was not defined.
|
||||
/// </summary>
|
||||
public Uri GetUri(string name)
|
||||
{
|
||||
Uri result = null;
|
||||
string value = GetValue(name);
|
||||
if (value != null)
|
||||
{
|
||||
if (!Uri.TryCreate(value, UriKind.Absolute, out result))
|
||||
{
|
||||
throw new CommandLineArgumentException(value + " is not a valid absolute URL");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filename for a given argument. Returns <c>null</c> if the argument was not defined.
|
||||
/// </summary>
|
||||
public string GetExistingFile(string name)
|
||||
{
|
||||
string filename = GetValue(name);
|
||||
if (filename != null)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
throw new CommandLineArgumentException("Could not find file " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the command-line arguments into a dictionary.
|
||||
/// </summary>
|
||||
/// <param name="args">The command-line arguments.</param>
|
||||
public void Parse(IEnumerable<string> args)
|
||||
{
|
||||
arguments.Clear();
|
||||
|
||||
Regex regex = new Regex("/([^:]+)(?::(.*))?");
|
||||
foreach (var arg in args)
|
||||
{
|
||||
Match match = regex.Match(arg);
|
||||
if (match.Success)
|
||||
{
|
||||
string argName = match.Groups[1].Value;
|
||||
string value = (match.Groups.Count >= 2) ? match.Groups[2].Value : String.Empty;
|
||||
|
||||
bool isSwitch = validSwitches != null && validSwitches.Contains(argName);
|
||||
bool isOption = validOptions != null && validOptions.Contains(argName);
|
||||
bool isMultiValueOpton = validMultiValueOptions != null && validMultiValueOptions.Contains(argName);
|
||||
|
||||
if (!isSwitch && !isOption && !isMultiValueOpton)
|
||||
{
|
||||
throw new CommandLineArgumentException("Unrecognized option " + arg);
|
||||
}
|
||||
|
||||
if ((isOption || isMultiValueOpton) && String.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new CommandLineArgumentException("Please specify a value for the " + argName + " option");
|
||||
}
|
||||
|
||||
if ((isSwitch || isOption) && arguments.Keys.Contains(argName))
|
||||
{
|
||||
throw new CommandLineArgumentException("Argument " + argName + " was specified more than once");
|
||||
}
|
||||
|
||||
IList<string> values;
|
||||
if (!arguments.TryGetValue(argName, out values))
|
||||
{
|
||||
values = new List<string>();
|
||||
arguments[argName] = values;
|
||||
}
|
||||
|
||||
values.Add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CommandLineArgumentException("Unexpected argument " + arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.CommandLine
|
||||
{
|
||||
public class CommandLineTool
|
||||
{
|
||||
private IDictionary<string, ICommand> commands = new Dictionary<string, ICommand>();
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public void RegisterCommandsInNamespace(Type typeInNamespace)
|
||||
{
|
||||
string nsName = typeInNamespace.Namespace;
|
||||
var typesInNamespace = typeInNamespace.Assembly.GetTypes().Where(t => t.Namespace == nsName).ToArray();
|
||||
var commandTypes = typesInNamespace.Where(t => typeof(ICommand).IsAssignableFrom(t)).ToArray();
|
||||
|
||||
foreach (var commandType in commandTypes)
|
||||
{
|
||||
var command = (ICommand)Activator.CreateInstance(commandType);
|
||||
RegisterCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterCommand(ICommand command)
|
||||
{
|
||||
if (command.CommandName == null)
|
||||
{
|
||||
throw new ArgumentException(String.Format("Cannot register a command with a null name. Command type was {0}", command.GetType().FullName), "command");
|
||||
}
|
||||
|
||||
ICommand existingCommand;
|
||||
if (this.commands.TryGetValue(command.CommandName, out existingCommand))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("A command with the name {0} already exists. Command type is {1}",
|
||||
command.CommandName, existingCommand.GetType().Name));
|
||||
}
|
||||
|
||||
this.commands[command.CommandName] = command;
|
||||
}
|
||||
|
||||
public void Run(string[] args)
|
||||
{
|
||||
string commandName = args.FirstOrDefault();
|
||||
string[] commandArgs = args.Skip(1).ToArray();
|
||||
|
||||
if (commandName == null
|
||||
|| commandName == "/?"
|
||||
|| commandName == "-?"
|
||||
|| (commandName == "help" && commandArgs.Length == 0))
|
||||
{
|
||||
Console.Write(GetHelp());
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandName == "help")
|
||||
{
|
||||
string helpCommandName = commandArgs.First();
|
||||
ICommand helpCommand = GetCommand(helpCommandName);
|
||||
Console.WriteLine(GetCommandHelp(helpCommand));
|
||||
return;
|
||||
}
|
||||
|
||||
ICommand command = GetCommand(commandName);
|
||||
command.Run(commandArgs);
|
||||
}
|
||||
|
||||
private ICommand GetCommand(string commandName)
|
||||
{
|
||||
ICommand command;
|
||||
if (!commands.TryGetValue(commandName, out command))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Unrecognized command: {0}.", commandName));
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
private string GetHelp()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
AppendDescription(sb);
|
||||
|
||||
string processName = GetProcessName();
|
||||
int maxCommandNameLength = commands.Values.Select(c => c.CommandName.Length).Max();
|
||||
int helpLinePaddingLength = processName.Length + 1 + maxCommandNameLength + 2;
|
||||
string helpLinePadding = GetPadding(helpLinePaddingLength);
|
||||
|
||||
foreach (var command in commands.Values.OrderBy(c => c.CommandName))
|
||||
{
|
||||
string padding = GetPadding(maxCommandNameLength - command.CommandName.Length);
|
||||
var description = String.Join('\n' + helpLinePadding, command.CommandDescription.Trim().Split('\n'));
|
||||
|
||||
sb.AppendFormat("{0} {1}{2} {3}", processName, command.CommandName, padding, description);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetProcessName()
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.ModuleName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current program version.
|
||||
/// </summary>
|
||||
public static string GetProcessVersion()
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
AssemblyFileVersionAttribute fileVersion = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>();
|
||||
|
||||
if (fileVersion != null)
|
||||
{
|
||||
return fileVersion.Version;
|
||||
}
|
||||
|
||||
// Fall back to assembly version if a file version is not available...
|
||||
return new AssemblyName(assembly.FullName).Version.ToString();
|
||||
}
|
||||
|
||||
private void AppendDescription(StringBuilder sb)
|
||||
{
|
||||
if (this.Description != null)
|
||||
{
|
||||
var description = this.Description.Trim();
|
||||
if (!String.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
sb.AppendLine(description);
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCommandHelp(ICommand helpCommand)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
AppendDescription(sb);
|
||||
sb.AppendLine(helpCommand.CommandDescription.Trim());
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(helpCommand.CommandUsage.Trim());
|
||||
sb.AppendLine();
|
||||
|
||||
string commandHelp = sb.ToString();
|
||||
return commandHelp;
|
||||
}
|
||||
|
||||
private static string GetPadding(int paddingLength)
|
||||
{
|
||||
return new StringBuilder(paddingLength).Append(' ', paddingLength).ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.CommandLine
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
string CommandName { get; }
|
||||
|
||||
string CommandDescription { get; }
|
||||
|
||||
string CommandUsage { get; }
|
||||
|
||||
void Run(string[] args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.ComponentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for types in the ComponentModel namespace.
|
||||
/// </summary>
|
||||
public static class ComponentModelExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the count of items in a collection view.
|
||||
/// </summary>
|
||||
/// <param name="collectionView">The collection view.</param>
|
||||
/// <returns>The number of items in the collection view.</returns>
|
||||
public static int GetCount(this ICollectionView collectionView)
|
||||
{
|
||||
Assert.ParamIsNotNull(collectionView, "collectionView");
|
||||
|
||||
var collection = collectionView.SourceCollection as System.Collections.ICollection;
|
||||
if (collection != null)
|
||||
{
|
||||
// If the source collection is a collection (most likely the case), optimize to get the count directly
|
||||
return collection.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
var enumerable = collectionView.SourceCollection;
|
||||
if (enumerable != null)
|
||||
{
|
||||
return enumerable.OfType<object>().Count();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current item is in the first position of the collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionView">The collection view.</param>
|
||||
/// <returns><c>true</c> if the current item is in the first position, otherwise <c>false</c>.</returns>
|
||||
public static bool IsCurrentFirst(this ICollectionView collectionView)
|
||||
{
|
||||
Assert.ParamIsNotNull(collectionView, "collectionView");
|
||||
|
||||
return collectionView.CurrentPosition == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current item is in the last position of the collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionView">The collection view.</param>
|
||||
/// <returns><c>true</c> if the current item is in the last position, otherwise <c>false</c>.</returns>
|
||||
public static bool IsCurrentLast(this ICollectionView collectionView)
|
||||
{
|
||||
Assert.ParamIsNotNull(collectionView, "collectionView");
|
||||
|
||||
int count = collectionView.GetCount();
|
||||
return count > 0 && collectionView.CurrentPosition == count - 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.ComponentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for observable objects that fire property notification changes.
|
||||
/// </summary>
|
||||
public abstract class ObservableObjectBase : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when a property value changes.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raises PropertyChanged event. This method can only be called on the thread associated with this object's dispatcher.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property that changed.</param>
|
||||
/// <exception cref="System.InvalidOperationException">The calling thread does not have access to this object.</exception>
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
Assert.ParamIsNotNull(propertyName, "propertyName");
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper method that sets property value and raises PropertyChanged event if the value has changed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the property being set.</typeparam>
|
||||
/// <param name="propertyDataField">A reference to the data member which is used to store property value.</param>
|
||||
/// <param name="value">New property value.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <returns>
|
||||
/// True if the property value has changed, false otherwise.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
protected bool SetProperty<T>(ref T propertyDataField, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(propertyName, "propertyName");
|
||||
|
||||
if (!Object.Equals(propertyDataField, value))
|
||||
{
|
||||
propertyDataField = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper method that sets property value and raises PropertyChanged event if the value has changed.
|
||||
/// Optimized implementation for string type.
|
||||
/// </summary>
|
||||
/// <param name="field">A reference to the data member which is used to store property value.</param>
|
||||
/// <param name="value">New property value.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <returns>True if the property value has changed, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
protected bool SetProperty(ref string field, string value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(propertyName, "propertyName");
|
||||
|
||||
if (!string.Equals(field, value, StringComparison.Ordinal))
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper method that sets property value and raises PropertyChanged event if the value has changed.
|
||||
/// Optimized implementation for System.Int32 type.
|
||||
/// </summary>
|
||||
/// <param name="field">A reference to the data member which is used to store property value.</param>
|
||||
/// <param name="value">New property value.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <returns>True if the property value has changed, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
protected bool SetProperty(ref int field, int value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(propertyName, "propertyName");
|
||||
|
||||
if (field != value)
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper method that sets property value and raises PropertyChanged event if the value has changed.
|
||||
/// Optimized implementation for System.Boolean type.
|
||||
/// </summary>
|
||||
/// <param name="field">A reference to the data member which is used to store property value.</param>
|
||||
/// <param name="value">New property value.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <returns>True if the property value has changed, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
protected bool SetProperty(ref bool field, bool value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(propertyName, "propertyName");
|
||||
|
||||
if (field != value)
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for outputting to the console.
|
||||
/// </summary>
|
||||
public static class ConsoleUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a line to the console using the given foreground color.
|
||||
/// </summary>
|
||||
/// <param name="foregroundColor">Color of the foreground.</param>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void WriteLine(ConsoleColor foregroundColor, string format, params object[] args)
|
||||
{
|
||||
ConsoleColor originalColor = Console.ForegroundColor;
|
||||
|
||||
try
|
||||
{
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.WriteLine(format, args);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ForegroundColor = originalColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line to the console using the default color for warnings (yellow).
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void WriteWarning(string format, params object[] args)
|
||||
{
|
||||
WriteLine(ConsoleColor.Yellow, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a line to the console using the default color for errors (red).
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void WriteError(string format, params object[] args)
|
||||
{
|
||||
WriteLine(ConsoleColor.Red, format, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for converting types.
|
||||
/// </summary>
|
||||
public static class ConvertUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes an input value to the target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The target type.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The converted type.</returns>
|
||||
public static T ChangeType<T>(object value)
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
object result = null;
|
||||
|
||||
// In the future, add more types here (e.g. Version, TimeSpan, DateTime, etc...)
|
||||
if (targetType == typeof(Uri) && value is string)
|
||||
{
|
||||
result = new Uri((string)value);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = Convert.ChangeType(value, targetType);
|
||||
}
|
||||
|
||||
return (T)result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Resources;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for working with date times.
|
||||
/// </summary>
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
private static string[] Numbers = {
|
||||
"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified date is after another date.
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <param name="other">The other.</param>
|
||||
/// <returns><c>true</c> if a date was after the other date.</returns>
|
||||
public static bool IsAfter(this DateTime date, DateTime? other)
|
||||
{
|
||||
// Very important to make sure both dates are in the same space, otherwise the comparison gives you bogus values
|
||||
return other == null || other.Value.ToUniversalTime() < date.ToUniversalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified date is between (inclusive) two dates.
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <param name="start">The start date.</param>
|
||||
/// <param name="end">The end date.</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsBetween(this DateTime date, DateTime start, DateTime end)
|
||||
{
|
||||
return start <= date && date <= end;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string to describe the time interval between this date and the current time.
|
||||
/// E.g. "3 minutes", or "11 seconds".
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
public static string ToFriendlyElapsedTimeString(this DateTime date)
|
||||
{
|
||||
TimeSpan difference = (DateTime.UtcNow - date.ToUniversalTime());
|
||||
|
||||
// TODO: Assert is positive difference or handle future date?
|
||||
return difference.ToFriendlyString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string to describe the time interval between this date and the current time, with the "ago" word.
|
||||
/// E.g. "3 minutes ago", or "11 seconds ago".
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
public static string ToFriendlyElapsedTimeStringWithAgo(this DateTime date)
|
||||
{
|
||||
string result = date.ToFriendlyElapsedTimeString();
|
||||
return String.Format(ResourceStrings.Ago, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a short date string that represents this date. The string format depends on the
|
||||
/// the time difference with now (e.g. "3 minutes ago", "Today at 3:00PM", "Yesterday at 11:00AM",
|
||||
/// or "on Monday 10/16/2014");
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
public static string ToFriendlyShortDateString(this DateTime date)
|
||||
{
|
||||
return date.ToFriendlyDateString(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a date string that represents this date. The string format depends on the
|
||||
/// the time difference with now (e.g. "3 minutes ago", "Today at 3:00PM", "Yesterday at 11:00AM",
|
||||
/// or "on Monday 10/16/2014");
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
public static string ToFriendlyLongDateString(this DateTime date)
|
||||
{
|
||||
return date.ToFriendlyDateString(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the date in cormparison to the current time, only taking into
|
||||
/// account the date component (eg. "Today", "Yesterday", "Monday", "Last Week", "3 Weeks Ago", "Last Month",
|
||||
/// "Older", "Newer").
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <remarks>
|
||||
/// Based on Outlook's date grouping for conversations.
|
||||
/// </remarks>
|
||||
public static string ToFriendlyDatePeriod(this DateTime date)
|
||||
{
|
||||
DateTime today = DateTime.Today;
|
||||
|
||||
TimeSpan difference = today - date.ToLocalTime().Date;
|
||||
|
||||
if (difference >= TimeSpan.Zero)
|
||||
{
|
||||
if (difference.TotalDays < 1)
|
||||
{
|
||||
return "Today";
|
||||
}
|
||||
|
||||
if (difference.TotalDays < 2)
|
||||
{
|
||||
return "Yesterday";
|
||||
}
|
||||
|
||||
int weeksDifference = today.WeeksDifference(date);
|
||||
if (weeksDifference == 0)
|
||||
{
|
||||
// A day in this week
|
||||
return date.ToString("dddd");
|
||||
}
|
||||
|
||||
if (weeksDifference > 0 && weeksDifference <= 3 && weeksDifference < Numbers.Length)
|
||||
{
|
||||
// A previous week in this month
|
||||
return (weeksDifference == 1) ? "Last Week" : String.Format("{0} Weeks Ago", Numbers[weeksDifference]);
|
||||
}
|
||||
|
||||
int monthsDifference = today.MonthsDifference(date);
|
||||
if (monthsDifference == 1)
|
||||
{
|
||||
return "Last Month";
|
||||
}
|
||||
|
||||
return "Older";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Newer";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a short date string that represents this date. The string format depends on the
|
||||
/// the time difference with now (e.g. "3 minutes ago", "Today at 3:00PM", "Yesterday at 11:00AM",
|
||||
/// or "on Monday 10/16/2014");
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
private static string ToFriendlyDateString(this DateTime date, bool longDate)
|
||||
{
|
||||
// Make sure the date is in local time
|
||||
date = date.ToLocalTime();
|
||||
DateTime now = DateTime.Now;
|
||||
|
||||
TimeSpan timeDifference = (now - date);
|
||||
|
||||
// TODO: Assert is positive difference or handle future date?
|
||||
|
||||
if (timeDifference.TotalHours < 7)
|
||||
{
|
||||
string result = timeDifference.ToFriendlyString();
|
||||
return String.Format(ResourceStrings.Ago, result);
|
||||
}
|
||||
else if (date.Date == now.Date)
|
||||
{
|
||||
string time = date.ToShortTimeString();
|
||||
return String.Format(ResourceStrings.TimeStampTodayAt, time);
|
||||
}
|
||||
else if ((date + TimeSpan.FromDays(1)).Date == now.Date)
|
||||
{
|
||||
string time = date.ToShortTimeString();
|
||||
return String.Format(ResourceStrings.TimeStampYesterdayAt, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (longDate)
|
||||
{
|
||||
if (timeDifference.TotalDays <= 5)
|
||||
{
|
||||
string day = date.ToString("dddd", CultureInfo.CurrentCulture);
|
||||
string time = date.ToShortTimeString();
|
||||
return String.Format(ResourceStrings.TimeStampDayAt, day, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
string day = date.ToString("ddd", CultureInfo.CurrentCulture);
|
||||
string time = date.ToString("g", CultureInfo.CurrentCulture);
|
||||
return String.Format(ResourceStrings.TimeStampFullDate, day, time);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string day = date.ToString("ddd", CultureInfo.CurrentCulture);
|
||||
string fullDate = String.Format(ResourceStrings.DayAndDate, day, date.ToShortDateString());
|
||||
return String.Format(ResourceStrings.OnDate, fullDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of months between two dates.
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <param name="otherDate">The other date.</param>
|
||||
/// <returns>The number of months between the two dates (negative if otherDate is later than this date).</returns>
|
||||
private static int MonthsDifference(this DateTime date, DateTime otherDate)
|
||||
{
|
||||
var diff = (date.Year - otherDate.Year) * 12 + (date.Month - otherDate.Month);
|
||||
return diff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of weeks between two dates.
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <param name="otherDate">The other date.</param>
|
||||
/// <returns>The number of weeks between the two dates (negative if otherDate is later than this date).</returns>
|
||||
private static int WeeksDifference(this DateTime date, DateTime otherDate)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
var calendarWeekRule = CalendarWeekRule.FirstDay;
|
||||
var firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;
|
||||
int week1 = culture.Calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek);
|
||||
int week2 = culture.Calendar.GetWeekOfYear(otherDate, calendarWeekRule, firstDayOfWeek);
|
||||
|
||||
var diff = (date.Year - otherDate.Year) * 52 + (week1 - week2);
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
public class DeferredAction : IDisposable
|
||||
{
|
||||
private object sempahoreCountLock = new object();
|
||||
private int semaphoreCount;
|
||||
|
||||
public DeferredAction(Action action)
|
||||
{
|
||||
Assert.ParamIsNotNull(action, "action");
|
||||
|
||||
this.Action = action;
|
||||
}
|
||||
|
||||
public bool IsDeferring
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.semaphoreCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Action Action { get; private set; }
|
||||
|
||||
public void InvokeIfNotDeferred()
|
||||
{
|
||||
if (this.ShouldInvoke())
|
||||
{
|
||||
this.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Acquire()
|
||||
{
|
||||
lock (this.sempahoreCountLock)
|
||||
{
|
||||
this.semaphoreCount++;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void Release()
|
||||
{
|
||||
bool shouldInvoke = false;
|
||||
|
||||
lock (this.sempahoreCountLock)
|
||||
{
|
||||
if (this.semaphoreCount > 0)
|
||||
{
|
||||
this.semaphoreCount--;
|
||||
shouldInvoke = this.ShouldInvoke();
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldInvoke)
|
||||
{
|
||||
Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void Invoke()
|
||||
{
|
||||
this.Action.Invoke();
|
||||
}
|
||||
|
||||
private bool ShouldInvoke()
|
||||
{
|
||||
lock (sempahoreCountLock)
|
||||
{
|
||||
return (this.semaphoreCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
this.Release();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to implement disposable patterns for API usability to execute
|
||||
/// an action when Dispose() is called.
|
||||
/// </summary>
|
||||
public class DelegateDisposable : IDisposable
|
||||
{
|
||||
private Action disposeAction;
|
||||
private bool isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DelegateDisposable"/> class.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to be invoked on dispose.</param>
|
||||
public DelegateDisposable(Action action)
|
||||
{
|
||||
Assert.ParamIsNotNull(action, "disposeAction");
|
||||
|
||||
this.disposeAction = action;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
disposeAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper static class used to assert common conditions.
|
||||
/// </summary>
|
||||
public static class Assert
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter value satisfies an arbitrary assertion.
|
||||
/// </summary>
|
||||
/// <param name="assertion">If false, then an argument exception will be thrown.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
/// <param name="message">The message to throw as part of the assertion.</param>
|
||||
public static void ParamIs(bool assertion, string paramName, string message)
|
||||
{
|
||||
if (!assertion)
|
||||
{
|
||||
throw new ArgumentException(message, paramName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter value is not <c>null</c>.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsNotNull(object value, string paramName)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given string parameter value is not <c>null</c> or empty.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsNotNullOrEmpty(string value, string paramName)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new ArgumentException("String parameter cannot be empty", paramName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is equal to or greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsNotNegative(int value, string paramName)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Parameter must be equal to or greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is equal to or greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsNotNegative(long value, string paramName)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Parameter must be equal to or greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is equal to or greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsNotNegative(TimeSpan value, string paramName)
|
||||
{
|
||||
if (value < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "TimeSpan must be equal to or greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsGreaterThanZero(int value, string paramName)
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Parameter must greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsGreaterThanZero(TimeSpan value, string paramName)
|
||||
{
|
||||
if (value <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "TimeSpan must greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is greater than 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsGreaterThanZero(double value, string paramName)
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Value must greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is within a range (inclusive of the low and high bounds).
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="lowBound">The low bound.</param>
|
||||
/// <param name="highBound">The high bound.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsWithinRange(double value, double lowBound, double highBound, string paramName)
|
||||
{
|
||||
if (value < lowBound || value > highBound)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Parameter must be a value between " + lowBound + " and " + highBound);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a given parameter is a valid index for a given count or length of collection.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value.</param>
|
||||
/// <param name="count">The maximum count.</param>
|
||||
/// <param name="paramName">The parameter name.</param>
|
||||
public static void ParamIsValidIndex(int value, int count, string paramName)
|
||||
{
|
||||
if (value < 0 || value >= count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(paramName, value, "Index was out of range. Must be non-negative and less than the size of the collection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to build command line arguments for starting processes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class takes care of escaping arguments as needed for argument values that
|
||||
/// require escaping.
|
||||
/// </remarks>
|
||||
public class CommandLineBuilder
|
||||
{
|
||||
private static readonly char[] EscapeChars = new char[] { ' ', '\t', '\"' };
|
||||
|
||||
private List<string> args = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines if an argument value requires escaping.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns><c>true</c> if it requires esaping, <c>false</c> if it can be used as is.</returns>
|
||||
public static bool NeedsEscaping(string value)
|
||||
{
|
||||
return value.IndexOfAny(EscapeChars) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an argument.
|
||||
/// </summary>
|
||||
/// <param name="arg">The argument.</param>
|
||||
public void Add(string arg)
|
||||
{
|
||||
this.args.Add(arg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one or more arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public void Add(params string[] args)
|
||||
{
|
||||
this.args.AddRange(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one or more arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public void Add(IEnumerable<string> args)
|
||||
{
|
||||
this.args.AddRange(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two arguments, a switch name and a switch value (e.g. "/priority" "1"), if and only
|
||||
/// if, the value is not empty.
|
||||
/// </summary>
|
||||
/// <param name="switchName">The switch name.</param>
|
||||
/// <param name="switchValue">The switch value (possibly empty).</param>
|
||||
public void AddSwitchIfNotNull(string switchName, string switchValue)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(switchValue))
|
||||
{
|
||||
Add(switchName, switchValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the escaped command line arguments as a single string.
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Join(" ", args.Select(arg => EscapeArgument(arg)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes an argument if required.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The escaped value (or the original one if no escaping was required).</returns>
|
||||
private static string EscapeArgument(string value)
|
||||
{
|
||||
bool needsEscaping = NeedsEscaping(value);
|
||||
if (needsEscaping)
|
||||
{
|
||||
char escapeChar = (value.Contains('"')) ? '\'' : '\"';
|
||||
return escapeChar + value + escapeChar;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Xml;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes & deserializes elements in the Diagnostics namespace to/from XML.
|
||||
/// </summary>
|
||||
public class DiagnosticsSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A special marker to indicate that an unknwon XML element was deserialized from a telemetry stream.
|
||||
/// </summary>
|
||||
public static readonly object UnknownElement = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified system information to a stream.
|
||||
/// </summary>
|
||||
/// <param name="info">The information.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
public void Serialize(SystemInfo info, Stream stream)
|
||||
{
|
||||
new XDocument(Serialize(info)).Save(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified exception information to a stream.
|
||||
/// </summary>
|
||||
/// <param name="info">The information.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
public void Serialize(ExceptionInfo info, Stream stream)
|
||||
{
|
||||
new XDocument(Serialize(info)).Save(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the event information.
|
||||
/// </summary>
|
||||
/// <param name="element">The XML element.</param>
|
||||
/// <returns>The event information.</returns>
|
||||
private EventInfo DeserializeEventInfo(XElement element)
|
||||
{
|
||||
string name = element.GetAttribute<string>(EventInfoSchema.Name);
|
||||
DateTime time = element.GetAttribute<DateTime>(EventInfoSchema.Time);
|
||||
TelemetryEventProperties eventProperties = null;
|
||||
|
||||
var properties = element.Element(EventInfoSchema.Properties);
|
||||
if (properties != null)
|
||||
{
|
||||
eventProperties = new TelemetryEventProperties();
|
||||
|
||||
foreach (var property in properties.Elements(EventInfoSchema.Property))
|
||||
{
|
||||
string propertyName = property.GetAttribute<string>(EventInfoSchema.Name);
|
||||
string propertyValue = property.GetAttribute<string>(EventInfoSchema.Value);
|
||||
|
||||
eventProperties[propertyName] = propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
return new EventInfo(time, name, eventProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified event information.
|
||||
/// </summary>
|
||||
/// <param name="info">The information.</param>
|
||||
/// <returns>The XML element.</returns>
|
||||
public XElement Serialize(EventInfo info)
|
||||
{
|
||||
XElement e = new XElement(EventInfoSchema.Event,
|
||||
new XAttribute(EventInfoSchema.Name, info.Name),
|
||||
new XAttribute(EventInfoSchema.Time, XmlExtensions.ToXmlString(info.Time))
|
||||
);
|
||||
|
||||
var properties = info.Properties;
|
||||
if (properties != null && properties.Any())
|
||||
{
|
||||
XElement propertiesElement = new XElement(EventInfoSchema.Properties);
|
||||
foreach (var item in properties)
|
||||
{
|
||||
if (item.Value != null)
|
||||
{
|
||||
propertiesElement.Add(new XElement(EventInfoSchema.Property,
|
||||
new XAttribute(EventInfoSchema.Name, item.Key),
|
||||
new XAttribute(EventInfoSchema.Value, XmlExtensions.ToXmlString(item.Value))
|
||||
));
|
||||
}
|
||||
}
|
||||
e.Add(propertiesElement);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified exception exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception information.</param>
|
||||
/// <returns>The XML element.</returns>
|
||||
public XElement Serialize(ExceptionInfo exception)
|
||||
{
|
||||
return Serialize(exception, ExceptionInfoSchema.Exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified exception information to an element with a given element name.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <param name="name">The output element name.</param>
|
||||
/// <returns>The XML element.</returns>
|
||||
private XElement Serialize(ExceptionInfo exception, XName name)
|
||||
{
|
||||
XElement e = new XElement(name);
|
||||
|
||||
e.SetElementValue<string>(ExceptionInfoSchema.Source, exception.Source);
|
||||
e.SetElementValue<string>(ExceptionInfoSchema.Type, exception.Type);
|
||||
e.SetElementValue<string>(ExceptionInfoSchema.Message, exception.Message);
|
||||
e.SetCDataElementValue(ExceptionInfoSchema.StackTrace, exception.StackTrace);
|
||||
e.SetElementValue<int>(ExceptionInfoSchema.HResult, exception.HResult);
|
||||
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
e.Add(Serialize(exception.InnerException, ExceptionInfoSchema.InnerException));
|
||||
}
|
||||
|
||||
e.SetCDataElementValue(ExceptionInfoSchema.FullText, exception.FullText);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified system information.
|
||||
/// </summary>
|
||||
/// <param name="info">The information.</param>
|
||||
/// <returns>The XML element.</returns>
|
||||
public XElement Serialize(SystemInfo info)
|
||||
{
|
||||
var memory = info.Memory;
|
||||
|
||||
return new XElement(SystemInfoSchema.SystemInfo, new XAttribute(SystemInfoSchema.SchemaVersion, SystemInfoSchema.CurrentVersion),
|
||||
new XElement(SystemInfoSchema.OSName, info.OSName),
|
||||
new XElement(SystemInfoSchema.OSVersion, info.OSVersion),
|
||||
new XElement(SystemInfoSchema.Is64BitOperatingSystem, info.Is64BitOperatingSystem),
|
||||
new XElement(SystemInfoSchema.ClrVersion, info.ClrVersion),
|
||||
new XElement(SystemInfoSchema.SystemCultureName, info.SystemCultureName),
|
||||
new XElement(SystemInfoSchema.UserCultureName, info.UserCultureName),
|
||||
new XElement(SystemInfoSchema.TimeZoneName, info.TimeZoneName),
|
||||
new XElement(SystemInfoSchema.TimeZoneUtcOffset, XmlExtensions.ToXmlString(info.TimeZoneUtcOffset)),
|
||||
new XElement(SystemInfoSchema.IsRemoteDesktopSession, info.IsRemoteDesktopSession),
|
||||
new XElement(SystemInfoSchema.IsMultiTouchEnabled, info.IsMultiTouchEnabled),
|
||||
|
||||
new XElement(SystemInfoSchema.Memory,
|
||||
new XElement(SystemInfoSchema.TotalPhysicalMemory, memory.TotalPhysicalMemory),
|
||||
new XElement(SystemInfoSchema.AvailablePhysicalMemory, memory.AvailablePhysicalMemory),
|
||||
new XElement(SystemInfoSchema.TotalVirtualMemory, memory.TotalVirtualMemory),
|
||||
new XElement(SystemInfoSchema.AvailableVirtualMemory, memory.AvailableVirtualMemory),
|
||||
new XElement(SystemInfoSchema.MemoryLoad, memory.MemoryLoad)
|
||||
),
|
||||
|
||||
new XElement(SystemInfoSchema.Processors,
|
||||
info.Processors.Select(processor => new XElement(SystemInfoSchema.Processor,
|
||||
new XElement(SystemInfoSchema.Name, processor.Name),
|
||||
new XElement(SystemInfoSchema.Family, processor.Family),
|
||||
new XElement(SystemInfoSchema.NumberOfCores, processor.NumberOfCores),
|
||||
new XElement(SystemInfoSchema.NumberOfLogicalProcessors, processor.NumberOfLogicalProcessors),
|
||||
new XElement(SystemInfoSchema.MaxClockSpeed, processor.MaxClockSpeed)
|
||||
))
|
||||
),
|
||||
|
||||
new XElement(SystemInfoSchema.VirtualScreen,
|
||||
new XElement(SystemInfoSchema.Width, info.VirtualScreen.Width),
|
||||
new XElement(SystemInfoSchema.Height, info.VirtualScreen.Height)
|
||||
),
|
||||
|
||||
new XElement(SystemInfoSchema.Screens,
|
||||
info.Screens.Select(screen => new XElement(SystemInfoSchema.Screen,
|
||||
new XElement(SystemInfoSchema.Width, screen.Width),
|
||||
new XElement(SystemInfoSchema.Height, screen.Height),
|
||||
new XElement(SystemInfoSchema.BitsPerPixel, screen.BitsPerPixel),
|
||||
new XElement(SystemInfoSchema.IsPrimary, screen.IsPrimary)
|
||||
))
|
||||
),
|
||||
|
||||
new XElement(SystemInfoSchema.IEVersion, info.IEVersion),
|
||||
|
||||
new XElement(SystemInfoSchema.VisualStudioVersions,
|
||||
info.VisualStudioVersions.Select(version => new XElement(SystemInfoSchema.VisualStudioVersion, version))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the exception information.
|
||||
/// </summary>
|
||||
/// <param name="element">The XML element.</param>
|
||||
/// <returns>The exception information.</returns>
|
||||
public ExceptionInfo DeserializeExceptionInfo(XElement element)
|
||||
{
|
||||
ExceptionInfo exception = new ExceptionInfo();
|
||||
exception.Source = element.GetElementValue<string>(ExceptionInfoSchema.Source);
|
||||
exception.Type = element.GetElementValue<string>(ExceptionInfoSchema.Type);
|
||||
exception.Message = element.GetElementValue<string>(ExceptionInfoSchema.Message);
|
||||
exception.StackTrace = element.GetElementValue<string>(ExceptionInfoSchema.StackTrace);
|
||||
exception.HResult = element.GetElementValue<int>(ExceptionInfoSchema.HResult);
|
||||
|
||||
var innerExceptionElement = element.Element(ExceptionInfoSchema.InnerException);
|
||||
if (innerExceptionElement != null)
|
||||
{
|
||||
exception.InnerException = DeserializeExceptionInfo(innerExceptionElement);
|
||||
}
|
||||
|
||||
exception.FullText = element.GetElementValue<string>(ExceptionInfoSchema.FullText);
|
||||
|
||||
return exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an arbitrary telemetry object from an XML element. The result could be an event,
|
||||
/// exception, application information, session information, or system information, or the unknown element.
|
||||
/// </summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>A deserialized telemetry object, or the "Unknown" element if unrecognized.</returns>
|
||||
public object DeserializeElement(XElement element)
|
||||
{
|
||||
// Ordered in descending order of expected frequency
|
||||
if (element.Name == EventInfoSchema.Event)
|
||||
{
|
||||
return DeserializeEventInfo(element);
|
||||
}
|
||||
else if (element.Name == ExceptionInfoSchema.Exception)
|
||||
{
|
||||
return DeserializeExceptionInfo(element);
|
||||
}
|
||||
else if (element.Name == SystemInfoSchema.SystemInfo)
|
||||
{
|
||||
return DeserializeSystemInfo(element);
|
||||
}
|
||||
|
||||
return UnknownElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the exception information.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <returns>The exception information.</returns>
|
||||
public ExceptionInfo DeserializeExceptionInfo(Stream stream)
|
||||
{
|
||||
var doc = XDocument.Load(stream);
|
||||
var root = doc.GetExpectedRoot(ExceptionInfoSchema.Exception);
|
||||
return DeserializeExceptionInfo(root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the system information.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <returns>The system information.</returns>
|
||||
public SystemInfo DeserializeSystemInfo(Stream stream)
|
||||
{
|
||||
var doc = XDocument.Load(stream);
|
||||
var root = doc.GetExpectedRoot(SystemInfoSchema.SystemInfo);
|
||||
return DeserializeSystemInfo(root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the system information.
|
||||
/// </summary>
|
||||
/// <param name="e">The XML element.</param>
|
||||
/// <returns>The system information.</returns>
|
||||
public SystemInfo DeserializeSystemInfo(XElement e)
|
||||
{
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
systemInfo.OSName = e.GetElementValue<string>(SystemInfoSchema.OSName);
|
||||
systemInfo.OSVersion = e.GetElementValue<string>(SystemInfoSchema.OSName);
|
||||
systemInfo.Is64BitOperatingSystem = e.GetElementValue<bool>(SystemInfoSchema.Is64BitOperatingSystem);
|
||||
systemInfo.ClrVersion = e.GetElementValue<string>(SystemInfoSchema.ClrVersion);
|
||||
systemInfo.SystemCultureName = e.GetElementValue<string>(SystemInfoSchema.SystemCultureName);
|
||||
systemInfo.UserCultureName = e.GetElementValue<string>(SystemInfoSchema.UserCultureName);
|
||||
systemInfo.TimeZoneName = e.GetElementValue<string>(SystemInfoSchema.TimeZoneName);
|
||||
systemInfo.TimeZoneUtcOffset = e.GetElementValue<TimeSpan>(SystemInfoSchema.TimeZoneUtcOffset);
|
||||
systemInfo.IsRemoteDesktopSession = e.GetElementValue<bool>(SystemInfoSchema.IsRemoteDesktopSession);
|
||||
systemInfo.IsMultiTouchEnabled = e.GetElementValue<bool>(SystemInfoSchema.IsMultiTouchEnabled);
|
||||
|
||||
var memoryElement = e.Element(SystemInfoSchema.Memory);
|
||||
MemoryInfo memory = new MemoryInfo();
|
||||
memory.TotalPhysicalMemory = memoryElement.GetElementValue<long>(SystemInfoSchema.TotalPhysicalMemory);
|
||||
memory.AvailablePhysicalMemory = memoryElement.GetElementValue<long>(SystemInfoSchema.AvailablePhysicalMemory);
|
||||
memory.TotalVirtualMemory = memoryElement.GetElementValue<long>(SystemInfoSchema.TotalVirtualMemory);
|
||||
memory.AvailableVirtualMemory = memoryElement.GetElementValue<long>(SystemInfoSchema.AvailableVirtualMemory);
|
||||
memory.MemoryLoad = memoryElement.GetElementValue<int>(SystemInfoSchema.MemoryLoad);
|
||||
systemInfo.Memory = memory;
|
||||
|
||||
var processors = e.Elements(SystemInfoSchema.Processors, SystemInfoSchema.Processor);
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
ProcessorInfo processorInfo = new ProcessorInfo();
|
||||
processorInfo.Name = processor.GetElementValue<string>(SystemInfoSchema.Name);
|
||||
processorInfo.Family = processor.GetElementValue<string>(SystemInfoSchema.Family);
|
||||
processorInfo.NumberOfCores = processor.GetElementValue<int>(SystemInfoSchema.NumberOfCores);
|
||||
processorInfo.NumberOfLogicalProcessors = processor.GetElementValue<int>(SystemInfoSchema.NumberOfLogicalProcessors);
|
||||
processorInfo.MaxClockSpeed = processor.GetElementValue<int>(SystemInfoSchema.MaxClockSpeed);
|
||||
|
||||
systemInfo.Processors.Add(processorInfo);
|
||||
}
|
||||
|
||||
var virtualScreenElement = e.Element(SystemInfoSchema.VirtualScreen);
|
||||
VirtualScreenInfo virtualScreen = new VirtualScreenInfo()
|
||||
{
|
||||
Width = virtualScreenElement.GetElementValue<int>(SystemInfoSchema.Width),
|
||||
Height = virtualScreenElement.GetElementValue<int>(SystemInfoSchema.Height)
|
||||
};
|
||||
systemInfo.VirtualScreen = virtualScreen;
|
||||
|
||||
var screens = e.Elements(SystemInfoSchema.Screens, SystemInfoSchema.Screen);
|
||||
foreach (var screen in screens)
|
||||
{
|
||||
ScreenInfo screenInfo = new ScreenInfo();
|
||||
screenInfo.Width = screen.GetElementValue<int>(SystemInfoSchema.Width);
|
||||
screenInfo.Height = screen.GetElementValue<int>(SystemInfoSchema.Height);
|
||||
screenInfo.BitsPerPixel = screen.GetElementValue<int>(SystemInfoSchema.BitsPerPixel);
|
||||
screenInfo.IsPrimary = screen.GetElementValue<bool>(SystemInfoSchema.IsPrimary);
|
||||
systemInfo.Screens.Add(screenInfo);
|
||||
}
|
||||
|
||||
systemInfo.IEVersion = e.GetElementValue<string>(SystemInfoSchema.IEVersion);
|
||||
|
||||
var visualStudioVersions = e.Elements(SystemInfoSchema.VisualStudioVersions, SystemInfoSchema.VisualStudioVersion);
|
||||
foreach (var version in visualStudioVersions)
|
||||
{
|
||||
systemInfo.VisualStudioVersions.Add(version.Value);
|
||||
}
|
||||
|
||||
return systemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the XML schema for an EventInfo type.
|
||||
/// </summary>
|
||||
private static class EventInfoSchema
|
||||
{
|
||||
public static XName Event = "Event";
|
||||
public static string Name = "Name";
|
||||
public static string Time = "Time";
|
||||
public static XName Properties = "Properties";
|
||||
public static XName Property = "Property";
|
||||
public static string Value = "Value";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the XML schema for an ExceptionInfo type.
|
||||
/// </summary>
|
||||
public static class ExceptionInfoSchema
|
||||
{
|
||||
public static readonly XName Exception = "Exception";
|
||||
public static readonly XName Type = "Type";
|
||||
public static readonly XName StackTrace = "StackTrace";
|
||||
public static readonly XName Message = "Message";
|
||||
public static readonly XName HResult = "HResult";
|
||||
public static readonly XName Source = "Source";
|
||||
public static readonly XName InnerException = "InnerException";
|
||||
public static readonly XName FullText = "FullText";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the XML schema for a SystemInfo type.
|
||||
/// </summary>
|
||||
private static class SystemInfoSchema
|
||||
{
|
||||
public static readonly Version CurrentVersion = new Version("1.0");
|
||||
public static readonly string SchemaVersion = "Version";
|
||||
|
||||
public static readonly XName SystemInfo = "SystemInfo";
|
||||
public static readonly XName OSName = "OSName";
|
||||
public static readonly XName OSVersion = "OSVersion";
|
||||
public static readonly XName Is64BitOperatingSystem = "Is64BitOperatingSystem";
|
||||
public static readonly XName ClrVersion = "ClrVersion";
|
||||
public static readonly XName SystemCultureName = "SystemCultureName";
|
||||
public static readonly XName UserCultureName = "UserCultureName";
|
||||
public static readonly XName TimeZoneName = "TimeZoneName";
|
||||
public static readonly XName TimeZoneUtcOffset = "TimeZoneUtcOffset";
|
||||
public static readonly XName IsRemoteDesktopSession = "IsRemoteDesktopSession";
|
||||
public static readonly XName IsMultiTouchEnabled = "IsMultiTouchEnabled";
|
||||
public static readonly XName Memory = "Memory";
|
||||
public static readonly XName TotalPhysicalMemory = "TotalPhysicalMemory";
|
||||
public static readonly XName AvailablePhysicalMemory = "AvailablePhysicalMemory";
|
||||
public static readonly XName TotalVirtualMemory = "TotalVirtualMemory";
|
||||
public static readonly XName AvailableVirtualMemory = "AvailableVirtualMemory";
|
||||
public static readonly XName MemoryLoad = "MemoryLoad";
|
||||
public static readonly XName Processors = "Processors";
|
||||
public static readonly XName Processor = "Processor";
|
||||
public static readonly XName Name = "Name";
|
||||
public static readonly XName Family = "Family";
|
||||
public static readonly XName NumberOfCores = "NumberOfCores";
|
||||
public static readonly XName NumberOfLogicalProcessors = "NumberOfLogicalProcessors";
|
||||
public static readonly XName MaxClockSpeed = "MaxClockSpeed";
|
||||
public static readonly XName VirtualScreen = "VirtualScreen";
|
||||
public static readonly XName Width = "Width";
|
||||
public static readonly XName Height = "Height";
|
||||
public static readonly XName Screens = "Screens";
|
||||
public static readonly XName Screen = "Screen";
|
||||
public static readonly XName BitsPerPixel = "BitsPerPixel";
|
||||
public static readonly XName IsPrimary = "IsPrimary";
|
||||
public static readonly XName IEVersion = "IEVersion";
|
||||
public static readonly XName VisualStudioVersions = "VisualStudioVersions";
|
||||
public static readonly XName VisualStudioVersion = "VisualStudioVersion";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Representts telemetry information for a given "event" in an application. An event has a name,
|
||||
/// an occurrence timestamp, and an optional set of properties.
|
||||
/// </summary>
|
||||
public class EventInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EventInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="time">The time.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="properties">The (optional) event properties.</param>
|
||||
public EventInfo(DateTime time, string name, TelemetryEventProperties properties)
|
||||
{
|
||||
Assert.ParamIsNotNull(name, "name");
|
||||
|
||||
this.Time = time;
|
||||
this.Name = name;
|
||||
this.Properties = properties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time.
|
||||
/// </summary>
|
||||
public DateTime Time { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties (could be <c>null</c>).
|
||||
/// </summary>
|
||||
public TelemetryEventProperties Properties { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates the captured information for a given exception.
|
||||
/// </summary>
|
||||
public class ExceptionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stack trace.
|
||||
/// </summary>
|
||||
public string StackTrace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source.
|
||||
/// </summary>
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the h result.
|
||||
/// </summary>
|
||||
public int HResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full text of the exception (message, stack trace, plus inner exceptions).
|
||||
/// </summary>
|
||||
public string FullText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the inner exception.
|
||||
/// </summary>
|
||||
public ExceptionInfo InnerException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates exception information from a given exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <returns>The exception information.</returns>
|
||||
public static ExceptionInfo Create(Exception exception)
|
||||
{
|
||||
Assert.ParamIsNotNull(exception, "exception");
|
||||
|
||||
return Create(exception, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates exception information from a given exception.
|
||||
/// </summary>
|
||||
/// <param name="error">The error.</param>
|
||||
/// <param name="captureFullText">if set to <c>true</c> captures the full text for the exception (used only for the root exception,
|
||||
/// but not for inner exceptions).</param>
|
||||
/// <returns>
|
||||
/// The exception information.
|
||||
/// </returns>
|
||||
private static ExceptionInfo Create(Exception error, bool captureFullText)
|
||||
{
|
||||
ExceptionInfo info = new ExceptionInfo();
|
||||
info.Type = error.GetType().FullName;
|
||||
info.Message = error.Message;
|
||||
info.StackTrace = error.StackTrace;
|
||||
info.Source = error.Source;
|
||||
info.HResult = error.HResult;
|
||||
|
||||
if (captureFullText)
|
||||
{
|
||||
info.FullText = error.ToString();
|
||||
}
|
||||
|
||||
if (error.InnerException != null)
|
||||
{
|
||||
info.InnerException = Create(error.InnerException, false);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.FullText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for tracing information.
|
||||
/// </summary>
|
||||
public static class Log
|
||||
{
|
||||
static Log()
|
||||
{
|
||||
Level = LogLevel.Performance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the diagnosis level. Only items at a level matching or above the current
|
||||
/// level will be emitted.
|
||||
/// </summary>
|
||||
public static LogLevel Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Traces the performance of the block enclosed in the using statement.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>A disposable item that can be used in a using() statement to trace its performance.</returns>
|
||||
public static IDisposable PerformanceBlock(string message, params object[] args)
|
||||
{
|
||||
Assert.ParamIsNotNull(message, "message");
|
||||
|
||||
return (Log.AcceptsLevel(LogLevel.Performance)) ? new PerformanceScopeTracer(message, args) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Performance.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Performance(string message, params object[] args)
|
||||
{
|
||||
Trace(LogLevel.Performance, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Error.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Error(string message, params object[] args)
|
||||
{
|
||||
Trace(LogLevel.Error, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Information.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Info(string message, params object[] args)
|
||||
{
|
||||
Trace(LogLevel.Info, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Warn(string message, params object[] args)
|
||||
{
|
||||
Trace(LogLevel.Warn, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of error.
|
||||
/// </summary>
|
||||
/// <param name="exception">An exception.</param>
|
||||
public static void Error(Exception exception)
|
||||
{
|
||||
Trace(LogLevel.Error, exception.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Error.
|
||||
/// </summary>
|
||||
/// <param name="exception">An exception.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Error(Exception exception, string message, params object[] args)
|
||||
{
|
||||
string formattedMessage = FormatMessage(message, args, exception);
|
||||
Trace(LogLevel.Error, formattedMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning.
|
||||
/// </summary>
|
||||
/// <param name="exception">An exception.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void Warn(Exception exception, string message, params object[] args)
|
||||
{
|
||||
string formattedMessage = FormatMessage(message, args, exception);
|
||||
Trace(LogLevel.Warn, formattedMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning.
|
||||
/// </summary>
|
||||
/// <param name="exception">An exception.</param>
|
||||
public static void Warn(Exception exception)
|
||||
{
|
||||
Trace(LogLevel.Warn, exception.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Error, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void ErrorAndBreak(string message, params object[] args)
|
||||
{
|
||||
ErrorAndBreak(null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Error, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
public static void ErrorAndBreak(Exception exception)
|
||||
{
|
||||
ErrorAndBreak(exception, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Error, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void ErrorAndBreak(Exception exception, string message, params object[] args)
|
||||
{
|
||||
TraceAndBreak(LogLevel.Error, exception, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void WarnAndBreak(string message, params object[] args)
|
||||
{
|
||||
WarnAndBreak(null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
public static void WarnAndBreak(Exception exception)
|
||||
{
|
||||
WarnAndBreak(exception, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a diagnose level of Warning, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public static void WarnAndBreak(Exception exception, string message, params object[] args)
|
||||
{
|
||||
TraceAndBreak(LogLevel.Warn, exception, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with the specified diagnose level.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
private static void Trace(LogLevel level, string message, object[] args = null)
|
||||
{
|
||||
if (AcceptsLevel(level))
|
||||
{
|
||||
string formattedMessage = FormatMessage(message, args);
|
||||
string fullMessage = String.Format("{0}: {1}", GetHeader(level), formattedMessage);
|
||||
System.Diagnostics.Trace.WriteLine(fullMessage);
|
||||
|
||||
/*
|
||||
switch (level)
|
||||
{
|
||||
case DiagnoseLevel.Warning:
|
||||
System.Diagnostics.Trace.TraceWarning(fullMessage);
|
||||
break;
|
||||
|
||||
case DiagnoseLevel.Error:
|
||||
System.Diagnostics.Trace.TraceError(fullMessage);
|
||||
break;
|
||||
|
||||
case DiagnoseLevel.Information:
|
||||
default:
|
||||
System.Diagnostics.Trace.TraceInformation(fullMessage);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a message with a given level should be outputted.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <returns><c>true</c> if it should be outputted, otherwise <c>false</c></returns>
|
||||
private static bool AcceptsLevel(LogLevel level)
|
||||
{
|
||||
return level <= Log.Level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a message, optionally appending an exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <returns>The formatted message.</returns>
|
||||
private static string FormatMessage(string message, object[] args, Exception exception = null)
|
||||
{
|
||||
string formattedMessage = (args != null && args.Length > 0) ? String.Format(message, args) : message;
|
||||
if (exception != null)
|
||||
{
|
||||
formattedMessage += Environment.NewLine + exception.ToString();
|
||||
}
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces a message with a given diagnose level, and raises an assertion (when in debug mode) to
|
||||
/// potentially start the debugger.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
private static void TraceAndBreak(LogLevel level, Exception exception, string message, object[] args = null)
|
||||
{
|
||||
if (message == null && exception != null)
|
||||
{
|
||||
message = "Unexpected exception thrown";
|
||||
}
|
||||
|
||||
string formattedMessage = FormatMessage(message, args, exception);
|
||||
Trace(level, formattedMessage);
|
||||
Debug.Fail(formattedMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message header for a given diagnose level.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <returns>The header text.</returns>
|
||||
private static string GetHeader(LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel.Info: return "INFORMATION";
|
||||
case LogLevel.Warn: return "WARNING";
|
||||
case LogLevel.Error: return "ERROR";
|
||||
case LogLevel.Performance: return "PERFORMANCE";
|
||||
case LogLevel.Debug : return "DEBUG";
|
||||
|
||||
default:
|
||||
Debug.Fail("Need to update GetHeader() to handle new DiagnoseLevel: " + level);
|
||||
return level.ToString().ToUpper();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper disposable class to start measure the performance in a using statement, and trace
|
||||
/// its result when the item is disposed.
|
||||
/// </summary>
|
||||
private class PerformanceScopeTracer : IDisposable
|
||||
{
|
||||
private DateTime startTime;
|
||||
private string formattedMessage;
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PerformanceScopeTracer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public PerformanceScopeTracer(string message, object[] args)
|
||||
{
|
||||
this.formattedMessage = Log.FormatMessage(message, args);
|
||||
this.startTime = DateTime.Now;
|
||||
|
||||
if (Log.AcceptsLevel(LogLevel.Debug))
|
||||
{
|
||||
Log.Performance(formattedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traces the elapsed time of the block.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if(!disposed)
|
||||
{
|
||||
disposed = true;
|
||||
TimeSpan elapsed = DateTime.Now - startTime;
|
||||
string message = String.Format("END {0}. ELAPSED TIME: {1}", this.formattedMessage, elapsed);
|
||||
Log.Performance(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An output level for diagnosis messages.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
// Update GetHeader() whenever you add more levels here...
|
||||
None,
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Performance,
|
||||
Debug,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Management;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the Process class.
|
||||
/// </summary>
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Kills the process tree.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
public static void KillTree(this Process process)
|
||||
{
|
||||
if (!process.HasExited)
|
||||
{
|
||||
ICollection<Process> childProcesses = GetChildren(process);
|
||||
foreach (var child in childProcesses)
|
||||
{
|
||||
KillTree(child);
|
||||
}
|
||||
|
||||
if (!process.HasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
// Ignore, process might have exited
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child processes.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <returns>A collection of child processes.</returns>
|
||||
public static ICollection<Process> GetChildren(this Process process)
|
||||
{
|
||||
ICollection<Process> childProcesses = new List<Process>();
|
||||
|
||||
try
|
||||
{
|
||||
if (!process.HasExited)
|
||||
{
|
||||
ManagementObjectSearcher searcher = new ManagementObjectSearcher(
|
||||
"Select * From Win32_Process Where ParentProcessID = " + process.Id.ToString(CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
foreach (ManagementObject item in searcher.Get())
|
||||
{
|
||||
try
|
||||
{
|
||||
var childPid = Convert.ToInt32(item["ProcessID"]);
|
||||
childProcesses.Add(Process.GetProcessById(childPid));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
return childProcesses;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics.Reports
|
||||
{
|
||||
/// <summary>
|
||||
/// An item attached to a diagnostics report.
|
||||
/// </summary>
|
||||
public class Attachment
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Attachment"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The attachment name.</param>
|
||||
/// <param name="content">The attachment content.</param>
|
||||
public Attachment(string name, byte[] content)
|
||||
{
|
||||
Assert.ParamIsNotNull(name, "name");
|
||||
Assert.ParamIsNotNull(content, "content");
|
||||
|
||||
this.Name = name;
|
||||
this.Content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content.
|
||||
/// </summary>
|
||||
public byte[] Content { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics.Reports
|
||||
{
|
||||
/// <summary>
|
||||
/// An error report that contains exception information.
|
||||
/// </summary>
|
||||
public class ErrorReport : UserReportBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The default file extension for error report files.
|
||||
/// </summary>
|
||||
public const string FileExtension = ".errx";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception in this report.
|
||||
/// </summary>
|
||||
public ExceptionInfo Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new error report, and initializes it with a unique id and current timestamp.
|
||||
/// </summary>
|
||||
/// <param name="error">The exception.</param>
|
||||
/// <returns>The created error report.</returns>
|
||||
public static ErrorReport Create(Exception error)
|
||||
{
|
||||
Assert.ParamIsNotNull(error, "error");
|
||||
|
||||
ErrorReport report = new ErrorReport();
|
||||
report.Initialize();
|
||||
report.Exception = ExceptionInfo.Create(error);
|
||||
return report;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an error report from an error report file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The parsed error report.</returns>
|
||||
public static ErrorReport FromFile(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
ReportSerializer serializer = new ReportSerializer();
|
||||
return serializer.ReadErrorReport(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the error report to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public void Save(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
ReportSerializer serializer = new ReportSerializer();
|
||||
serializer.Write(this, filename);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(String.Format("Date: {0}", Date));
|
||||
|
||||
if (!String.IsNullOrEmpty(EmailAddress))
|
||||
{
|
||||
sb.AppendLine(String.Format("From: {0}", EmailAddress));
|
||||
}
|
||||
|
||||
sb.AppendLine("Exception:");
|
||||
sb.AppendLine(Exception.ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics.Reports
|
||||
{
|
||||
/// <summary>
|
||||
/// A "Send a Smile" feedback report from a user containing the type (smile or frown) and
|
||||
/// optional user text.
|
||||
/// </summary>
|
||||
public class FeedbackReport : UserReportBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The default file extension for feedback report files.
|
||||
/// </summary>
|
||||
public const string FileExtension = ".fdbx";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the feedback type.
|
||||
/// </summary>
|
||||
public FeedbackType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the feedback report text.
|
||||
/// </summary>
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance, and initializes it with a unique id and current timestamp.
|
||||
/// </summary>
|
||||
/// <returns>The created report.</returns>
|
||||
public static FeedbackReport Create()
|
||||
{
|
||||
FeedbackReport item = new FeedbackReport();
|
||||
item.Initialize();
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a feedback report from a given file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The feedback report.</returns>
|
||||
public static FeedbackReport FromFile(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
ReportSerializer serializer = new ReportSerializer();
|
||||
return serializer.ReadFeedbackReport(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the feedback report to the specified filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public void Save(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
ReportSerializer serializer = new ReportSerializer();
|
||||
serializer.WriteFeedbackReport(this, filename);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of feedback report.
|
||||
/// </summary>
|
||||
public enum FeedbackType
|
||||
{
|
||||
/// <summary>
|
||||
/// A positive report (send a smile).
|
||||
/// </summary>
|
||||
Smile,
|
||||
|
||||
/// <summary>
|
||||
/// A negative report (send a frown).
|
||||
/// </summary>
|
||||
Frown
|
||||
}
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.IO.Packaging;
|
||||
using Microsoft.Internal.Tools.TeamMate.Foundation.Net.Mime;
|
||||
using Microsoft.Internal.Tools.TeamMate.Foundation.Xml;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Packaging;
|
||||
using System.Net.Mime;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics.Reports
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes and deserializes user reports from files.
|
||||
/// </summary>
|
||||
internal class ReportSerializer
|
||||
{
|
||||
private DiagnosticsSerializer diagnosticsSerializer = new DiagnosticsSerializer();
|
||||
|
||||
/// <summary>
|
||||
/// Reads a feedback report from a file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The read report.</returns>
|
||||
public FeedbackReport ReadFeedbackReport(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
FeedbackReport report = new FeedbackReport();
|
||||
|
||||
using (Package package = Package.Open(filename, FileMode.Open))
|
||||
{
|
||||
var feedbackPart = package.GetSingleRelatedPart(Schema.Relationships.Feedback);
|
||||
if (feedbackPart == null)
|
||||
{
|
||||
throw new Exception("Invalid package");
|
||||
}
|
||||
|
||||
ReadUserReport(report, package);
|
||||
ReadFeedbackReport(report, feedbackPart);
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the base information from a user report.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package containing the information.</param>
|
||||
private void ReadUserReport(UserReportBase report, Package package)
|
||||
{
|
||||
ReadReportInfo(report, package);
|
||||
report.SystemInfo = ReadSystemInfo(package);
|
||||
ReadAttachments(report, package);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the system information from a package.
|
||||
/// </summary>
|
||||
/// <param name="package">The package.</param>
|
||||
/// <returns>The system information.</returns>
|
||||
private SystemInfo ReadSystemInfo(Package package)
|
||||
{
|
||||
SystemInfo result = null;
|
||||
|
||||
var systemInfoPart = package.GetSingleRelatedPart(Schema.Relationships.SystemInfo);
|
||||
if (systemInfoPart != null)
|
||||
{
|
||||
using (Stream stream = systemInfoPart.OpenRead())
|
||||
{
|
||||
result = diagnosticsSerializer.DeserializeSystemInfo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the attachments contained in a package into a user report.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void ReadAttachments(UserReportBase report, Package package)
|
||||
{
|
||||
foreach (var relationship in package.GetRelationshipsByType(Schema.Relationships.Attachment))
|
||||
{
|
||||
PackagePart part = relationship.TryGetInternalPart();
|
||||
if (part != null)
|
||||
{
|
||||
string name = Path.GetFileName(part.Uri.OriginalString);
|
||||
byte[] content;
|
||||
|
||||
using (var inputStream = part.OpenRead())
|
||||
using (var outputStream = new MemoryStream())
|
||||
{
|
||||
inputStream.CopyTo(outputStream);
|
||||
content = outputStream.GetBuffer();
|
||||
}
|
||||
|
||||
Attachment attachment = new Attachment(name, content);
|
||||
report.Attachments.Add(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the feedback report information from a package part.
|
||||
/// </summary>
|
||||
/// <param name="feedback">The feedback.</param>
|
||||
/// <param name="part">The part.</param>
|
||||
private void ReadFeedbackReport(FeedbackReport feedback, PackagePart part)
|
||||
{
|
||||
XDocument doc;
|
||||
using (var stream = part.GetStream())
|
||||
{
|
||||
doc = XDocument.Load(stream);
|
||||
}
|
||||
|
||||
var feedbackElement = doc.Element(Schema.FeedbackXml.Feedback);
|
||||
feedback.Type = feedbackElement.GetElementValue<FeedbackType>(Schema.FeedbackXml.Type);
|
||||
feedback.Text = feedbackElement.GetElementValue<string>(Schema.FeedbackXml.Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the core report information from a package.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void ReadReportInfo(UserReportBase report, Package package)
|
||||
{
|
||||
var part = package.GetSingleRelatedPart(Schema.Relationships.ReportInfo);
|
||||
if (part != null)
|
||||
{
|
||||
XDocument doc;
|
||||
using (var stream = part.GetStream())
|
||||
{
|
||||
doc = XDocument.Load(stream);
|
||||
}
|
||||
|
||||
var feedbackElement = doc.Element(Schema.ReportInfoXml.ReportInfo);
|
||||
report.Id = feedbackElement.GetElementValue<Guid>(Schema.ReportInfoXml.Id);
|
||||
report.Date = feedbackElement.GetElementValue<DateTime>(Schema.ReportInfoXml.Date);
|
||||
report.EmailAddress = feedbackElement.GetElementValue<string>(Schema.ReportInfoXml.EmailAddress);
|
||||
report.UserDisplayName = feedbackElement.GetElementValue<string>(Schema.ReportInfoXml.UserDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a feedback report to a file.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public void WriteFeedbackReport(FeedbackReport report, string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(report, "report");
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
using (Package package = Package.Open(filename, FileMode.Create))
|
||||
{
|
||||
var feedbackPart = package.CreateRelatedPart(Schema.Parts.Feedback, MediaTypeNames.Text.Xml, Schema.Relationships.Feedback);
|
||||
|
||||
WriteUserReport(report, package);
|
||||
WriteFeedback(report, feedbackPart);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the system information to a package.
|
||||
/// </summary>
|
||||
/// <param name="systemInfo">The system information.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void WriteSystemInfo(SystemInfo systemInfo, Package package)
|
||||
{
|
||||
// For API usability, allowing parameter to be null
|
||||
if (systemInfo != null)
|
||||
{
|
||||
var systemInfoPart = package.CreateRelatedPart(Schema.Parts.SystemInfo, MediaTypeNames.Text.Xml, Schema.Relationships.SystemInfo);
|
||||
using (Stream stream = systemInfoPart.OpenWrite())
|
||||
{
|
||||
diagnosticsSerializer.Serialize(systemInfo, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the attachments to a package.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void WriteAttachments(UserReportBase report, Package package)
|
||||
{
|
||||
foreach (var attachment in report.Attachments)
|
||||
{
|
||||
Uri partUri = new Uri(Schema.Parts.AttachmentsBaseUri + attachment.Name, UriKind.Relative);
|
||||
string mimeType = MimeTypes.GetMimeType(Path.GetExtension(attachment.Name));
|
||||
|
||||
var attachmentPart = package.CreateRelatedPart(partUri, mimeType, Schema.Relationships.Attachment);
|
||||
using (var stream = attachmentPart.OpenWrite())
|
||||
{
|
||||
stream.Write(attachment.Content, 0, attachment.Content.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the feedback report to an output package part.
|
||||
/// </summary>
|
||||
/// <param name="feedback">The feedback.</param>
|
||||
/// <param name="part">The part.</param>
|
||||
private void WriteFeedback(FeedbackReport feedback, PackagePart part)
|
||||
{
|
||||
var element = new XElement(Schema.FeedbackXml.Feedback);
|
||||
element.SetElementValue<FeedbackType>(Schema.FeedbackXml.Type, feedback.Type);
|
||||
element.SetElementValue<string>(Schema.FeedbackXml.Text, feedback.Text);
|
||||
|
||||
XDocument doc = new XDocument(element);
|
||||
using (var stream = part.OpenWrite())
|
||||
{
|
||||
doc.Save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the report information to a package.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void WriteReportInfo(UserReportBase report, Package package)
|
||||
{
|
||||
var element = new XElement(Schema.ReportInfoXml.ReportInfo);
|
||||
element.SetElementValue<Guid>(Schema.ReportInfoXml.Id, report.Id);
|
||||
element.SetElementValue<DateTime>(Schema.ReportInfoXml.Date, report.Date);
|
||||
element.SetElementValue<string>(Schema.ReportInfoXml.EmailAddress, report.EmailAddress);
|
||||
element.SetElementValue<string>(Schema.ReportInfoXml.UserDisplayName, report.UserDisplayName);
|
||||
|
||||
XDocument doc = new XDocument(element);
|
||||
|
||||
var part = package.CreateRelatedPart(Schema.Parts.ReportInfo, MediaTypeNames.Text.Xml, Schema.Relationships.ReportInfo);
|
||||
using (Stream stream = part.OpenWrite())
|
||||
{
|
||||
doc.Save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an error report from a file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The read error report.</returns>
|
||||
public ErrorReport ReadErrorReport(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
ErrorReport errorReport = new ErrorReport();
|
||||
|
||||
using (Package package = Package.Open(filename, FileMode.Open))
|
||||
{
|
||||
ReadUserReport(errorReport, package);
|
||||
errorReport.Exception = ReadExceptionInfo(package);
|
||||
}
|
||||
|
||||
return errorReport;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the exception information from a package..
|
||||
/// </summary>
|
||||
/// <param name="package">The package.</param>
|
||||
/// <returns>The exception information or <c>null if none was contained</c>.</returns>
|
||||
private ExceptionInfo ReadExceptionInfo(Package package)
|
||||
{
|
||||
ExceptionInfo exceptionInfo = null;
|
||||
|
||||
var part = package.GetSingleRelatedPart(Schema.Relationships.Exception);
|
||||
if (part != null)
|
||||
{
|
||||
using (Stream stream = part.OpenRead())
|
||||
{
|
||||
exceptionInfo = diagnosticsSerializer.DeserializeExceptionInfo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return exceptionInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an error report to a file.
|
||||
/// </summary>
|
||||
/// <param name="errorReport">The error report.</param>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public void Write(ErrorReport errorReport, string filename)
|
||||
{
|
||||
Assert.ParamIsNotNull(errorReport, "errorReport");
|
||||
Assert.ParamIsNotNull(filename, "filename");
|
||||
|
||||
using (Package package = Package.Open(filename, FileMode.Create))
|
||||
{
|
||||
WriteUserReport(errorReport, package);
|
||||
WriteExceptionInfo(errorReport.Exception, package);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the common user report information to a package.
|
||||
/// </summary>
|
||||
/// <param name="report">The report.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void WriteUserReport(UserReportBase report, Package package)
|
||||
{
|
||||
WriteReportInfo(report, package);
|
||||
WriteSystemInfo(report.SystemInfo, package);
|
||||
WriteAttachments(report, package);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the exception information to a package.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <param name="package">The package.</param>
|
||||
private void WriteExceptionInfo(ExceptionInfo exception, Package package)
|
||||
{
|
||||
// For API usability, allowing parameter to be null
|
||||
if (exception != null)
|
||||
{
|
||||
var part = package.CreateRelatedPart(Schema.Parts.Exception, MediaTypeNames.Text.Xml, Schema.Relationships.Exception);
|
||||
using (Stream stream = part.OpenWrite())
|
||||
{
|
||||
diagnosticsSerializer.Serialize(exception, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The XML schema for user reports.
|
||||
/// </summary>
|
||||
internal static class Schema
|
||||
{
|
||||
public const string Id = "http://schemas.microsoft.com/TeamMate/UserReports/2014";
|
||||
|
||||
public static class Parts
|
||||
{
|
||||
public static readonly Uri Feedback = new Uri("feedback.xml", UriKind.Relative);
|
||||
public static readonly Uri Exception = new Uri("exception.xml", UriKind.Relative);
|
||||
public static readonly Uri SystemInfo = new Uri("systemInfo.xml", UriKind.Relative);
|
||||
public static readonly Uri ApplicationInfo = new Uri("applicationInfo.xml", UriKind.Relative);
|
||||
public static readonly Uri ReportInfo = new Uri("reportInfo.xml", UriKind.Relative);
|
||||
|
||||
public const string AttachmentsBaseUri = "attachments/";
|
||||
}
|
||||
|
||||
public static class Relationships
|
||||
{
|
||||
// Follow a convention similar to the OpenXML format, e.g. URL: http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate
|
||||
private const string RelationshipBaseUri = Schema.Id + "/relationships/";
|
||||
|
||||
public const string Feedback = RelationshipBaseUri + "feedback";
|
||||
public const string Exception = RelationshipBaseUri + "exception";
|
||||
public const string SystemInfo = RelationshipBaseUri + "systemInfo";
|
||||
public const string ApplicationInfo = RelationshipBaseUri + "applicationInfo";
|
||||
public const string ReportInfo = RelationshipBaseUri + "reportInfo";
|
||||
public const string Attachment = RelationshipBaseUri + "attachment";
|
||||
}
|
||||
|
||||
public static class ReportInfoXml
|
||||
{
|
||||
public static readonly XNamespace Namespace = Schema.Id + "/report";
|
||||
|
||||
public static readonly XName ReportInfo = Namespace + "ReportInfo";
|
||||
public static readonly XName Id = Namespace + "Id";
|
||||
public static readonly XName Date = Namespace + "Date";
|
||||
public static readonly XName EmailAddress = Namespace + "EmailAddress";
|
||||
public static readonly XName UserDisplayName = Namespace + "UserDisplayName";
|
||||
}
|
||||
|
||||
public static class FeedbackXml
|
||||
{
|
||||
public static readonly XNamespace Namespace = Schema.Id + "/feedback";
|
||||
|
||||
public static readonly XName Feedback = Namespace + "Feedback";
|
||||
public static readonly XName Type = Namespace + "Type";
|
||||
public static readonly XName Text = Namespace + "Text";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics.Reports
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for diagnostic reports from end users.
|
||||
/// </summary>
|
||||
public abstract class UserReportBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique id for this report.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date of the report.
|
||||
/// </summary>
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the system information.
|
||||
/// </summary>
|
||||
public SystemInfo SystemInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email address of the person reporting the issue.
|
||||
/// </summary>
|
||||
public string EmailAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the User display name.
|
||||
/// </summary>
|
||||
public string UserDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of attachments associated with the report.
|
||||
/// </summary>
|
||||
public ICollection<Attachment> Attachments { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserReportBase"/> class.
|
||||
/// </summary>
|
||||
protected UserReportBase()
|
||||
{
|
||||
this.Attachments = new List<Attachment>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this instance with a unique id and timestamp.
|
||||
/// </summary>
|
||||
protected void Initialize()
|
||||
{
|
||||
this.Id = Guid.NewGuid();
|
||||
this.Date = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Collections;
|
||||
using Microsoft.Internal.Tools.TeamMate.Foundation.Native;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents system information of where an application is running.
|
||||
/// </summary>
|
||||
public class SystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemInfo"/> class.
|
||||
/// </summary>
|
||||
public SystemInfo()
|
||||
{
|
||||
this.Processors = new List<ProcessorInfo>();
|
||||
this.Screens = new List<ScreenInfo>();
|
||||
this.VisualStudioVersions = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [is64 bit operating system].
|
||||
/// </summary>
|
||||
public bool Is64BitOperatingSystem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the OS version.
|
||||
/// </summary>
|
||||
public string OSVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the OS.
|
||||
/// </summary>
|
||||
public string OSName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CLR version.
|
||||
/// </summary>
|
||||
public string ClrVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the system culture.
|
||||
/// </summary>
|
||||
/// </value>
|
||||
public string SystemCultureName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the user culture.
|
||||
/// </summary>
|
||||
public string UserCultureName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the time zone.
|
||||
/// </summary>
|
||||
public string TimeZoneName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the offset of the user's time zone from UTC.
|
||||
/// </summary>
|
||||
public TimeSpan TimeZoneUtcOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the current session is running in remote desktop.
|
||||
/// </summary>
|
||||
public bool IsRemoteDesktopSession { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the memory information.
|
||||
/// </summary>
|
||||
public MemoryInfo Memory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the processor information.
|
||||
/// </summary>
|
||||
public ICollection<ProcessorInfo> Processors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the virtual screen information.
|
||||
/// </summary>
|
||||
public VirtualScreenInfo VirtualScreen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screens in the system.
|
||||
/// </summary>
|
||||
public ICollection<ScreenInfo> Screens { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether multi touch is enabled.
|
||||
/// </summary>
|
||||
public bool IsMultiTouchEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Internet Explorer version.
|
||||
/// </summary>
|
||||
public string IEVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of Visual Studio versions.
|
||||
/// </summary>
|
||||
public ICollection<string> VisualStudioVersions { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Captures a snapshot of the current system information.
|
||||
/// </summary>
|
||||
/// <returns>The captured system information.</returns>
|
||||
/// <remarks>
|
||||
/// This method can take 1+ second, so you might want to call it in an asynchronous fashion.
|
||||
/// </remarks>
|
||||
public static SystemInfo Capture()
|
||||
{
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
|
||||
// OS
|
||||
systemInfo.Is64BitOperatingSystem = Environment.Is64BitOperatingSystem;
|
||||
systemInfo.OSVersion = Environment.OSVersion.Version.ToString();
|
||||
var operatingSystem = GetSingleManagementObject("Win32_OperatingSystem");
|
||||
if (operatingSystem != null)
|
||||
{
|
||||
systemInfo.OSName = GetPropertyValue<string>(operatingSystem, "Caption");
|
||||
}
|
||||
|
||||
// CLR
|
||||
systemInfo.ClrVersion = Environment.Version.ToString();
|
||||
|
||||
// Cultures
|
||||
systemInfo.SystemCultureName = CultureInfo.InstalledUICulture.Name;
|
||||
systemInfo.UserCultureName = CultureInfo.CurrentCulture.Name;
|
||||
|
||||
// TimeZone
|
||||
systemInfo.TimeZoneName = TimeZone.CurrentTimeZone.StandardName;
|
||||
systemInfo.TimeZoneUtcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(new DateTime());
|
||||
|
||||
// Memory, Processor, Screens
|
||||
systemInfo.Memory = GetMemoryInfo();
|
||||
|
||||
systemInfo.Processors.AddRange(GetProcessorInfos());
|
||||
systemInfo.Screens.AddRange(GetScreensInfo());
|
||||
|
||||
systemInfo.IEVersion = GetIEVersion();
|
||||
systemInfo.VisualStudioVersions.AddRange(GetVisualStudioVersions());
|
||||
|
||||
systemInfo.IsMultiTouchEnabled = GetMultiTouchEnabled();
|
||||
|
||||
systemInfo.IsRemoteDesktopSession = System.Windows.Forms.SystemInformation.TerminalServerSession;
|
||||
|
||||
var virtualScreen = System.Windows.Forms.SystemInformation.VirtualScreen;
|
||||
systemInfo.VirtualScreen = new VirtualScreenInfo()
|
||||
{
|
||||
Width = virtualScreen.Width,
|
||||
Height = virtualScreen.Height
|
||||
};
|
||||
|
||||
return systemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate whether multi touch is enabled or not.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if multi touch is enabled.</returns>
|
||||
private static bool GetMultiTouchEnabled()
|
||||
{
|
||||
NID digitizer = (NID) NativeMethods.GetSystemMetrics((int)SystemMetric.SM_DIGITIZER);
|
||||
|
||||
bool isMultiTouchEnabled = digitizer.HasFlag(NID.NID_READY)
|
||||
&& digitizer.HasFlag(NID.NID_MULTI_INPUT)
|
||||
&& digitizer.HasFlag(NID.NID_INTEGRATED_TOUCH); // Trying to use integrated touch to differentiate from USB pens and digitizers
|
||||
|
||||
return isMultiTouchEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Internet Explorer version.
|
||||
/// </summary>
|
||||
private static string GetIEVersion()
|
||||
{
|
||||
string result = null;
|
||||
|
||||
using (RegistryKey internetExplorerKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\MICROSOFT\Internet Explorer"))
|
||||
{
|
||||
if (internetExplorerKey != null)
|
||||
{
|
||||
// For IE10 and above, we need to rely on svcVersion key instead of Version key
|
||||
// to get the version info for internet explorer. So here we first try to get the
|
||||
// version from svcVersion key and if the key is not present then we fall back to
|
||||
// the Version key.
|
||||
object internetExplorerVersionKeyValue = internetExplorerKey.GetValue("svcVersion");
|
||||
if (internetExplorerVersionKeyValue == null)
|
||||
{
|
||||
// svcVersion key does not exist. Get the value from 'Version' key
|
||||
internetExplorerVersionKeyValue = internetExplorerKey.GetValue("Version");
|
||||
}
|
||||
|
||||
if (internetExplorerVersionKeyValue != null)
|
||||
{
|
||||
result = internetExplorerVersionKeyValue.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Visual Studio versions.
|
||||
/// </summary>
|
||||
private static ICollection<string> GetVisualStudioVersions()
|
||||
{
|
||||
List<string> results = new List<string>();
|
||||
|
||||
// KLUDGE: Update this with new versions that we want to recognize on demand
|
||||
string[] probingVersions = new string[] { "9.0", "10.0", "11.0", "12.0", "13.0", "14.0", "15.0" };
|
||||
|
||||
foreach (string probingVersion in probingVersions)
|
||||
{
|
||||
var key = Registry.ClassesRoot.OpenSubKey("VisualStudio.DTE." + probingVersion);
|
||||
if (key != null)
|
||||
{
|
||||
results.Add(probingVersion);
|
||||
key.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screens information.
|
||||
/// </summary>
|
||||
private static IEnumerable<ScreenInfo> GetScreensInfo()
|
||||
{
|
||||
var allScreens = System.Windows.Forms.Screen.AllScreens;
|
||||
foreach (var screen in allScreens)
|
||||
{
|
||||
ScreenInfo screenInfo = new ScreenInfo();
|
||||
screenInfo.Width = screen.Bounds.Width;
|
||||
screenInfo.Height = screen.Bounds.Height;
|
||||
screenInfo.BitsPerPixel = screen.BitsPerPixel;
|
||||
screenInfo.IsPrimary = screen.Primary;
|
||||
|
||||
yield return screenInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the processor information.
|
||||
/// </summary>
|
||||
private static IEnumerable<ProcessorInfo> GetProcessorInfos()
|
||||
{
|
||||
foreach (var processor in GetManagementObjects("Win32_processor"))
|
||||
{
|
||||
ProcessorInfo processorInfo = new ProcessorInfo();
|
||||
processorInfo.Name = GetPropertyValue<string>(processor, "Name");
|
||||
processorInfo.Family = GetPropertyValue<string>(processor, "Caption");
|
||||
processorInfo.NumberOfCores = (int)GetPropertyValue<uint>(processor, "NumberOfCores");
|
||||
processorInfo.NumberOfLogicalProcessors = (int)GetPropertyValue<uint>(processor, "NumberOfLogicalProcessors");
|
||||
processorInfo.MaxClockSpeed = (int)GetPropertyValue<uint>(processor, "MaxClockSpeed");
|
||||
yield return processorInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the memory information.
|
||||
/// </summary>
|
||||
private static MemoryInfo GetMemoryInfo()
|
||||
{
|
||||
MEMORYSTATUSEX memoryStatus = new MEMORYSTATUSEX();
|
||||
NativeMethods.GlobalMemoryStatusEx(memoryStatus);
|
||||
|
||||
MemoryInfo memoryInfo = new MemoryInfo();
|
||||
memoryInfo.AvailablePhysicalMemory = (long)memoryStatus.ullAvailPhys;
|
||||
memoryInfo.AvailableVirtualMemory = (long)memoryStatus.ullAvailVirtual;
|
||||
memoryInfo.MemoryLoad = (int)memoryStatus.dwMemoryLoad;
|
||||
memoryInfo.TotalPhysicalMemory = (long)memoryStatus.ullTotalPhys;
|
||||
memoryInfo.TotalVirtualMemory = (long)memoryStatus.ullTotalVirtual;
|
||||
|
||||
return memoryInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the a set of management objects for a given class name.
|
||||
/// </summary>
|
||||
/// <param name="className">Name of the class.</param>
|
||||
/// <returns>The resulting management object collection.</returns>
|
||||
private static ManagementObjectCollection GetManagementObjects(string className)
|
||||
{
|
||||
SelectQuery query = new SelectQuery(className);
|
||||
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
|
||||
return searcher.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single management object for a given class name.
|
||||
/// </summary>
|
||||
/// <param name="className">Name of the class.</param>
|
||||
/// <returns>The single management object, or <c>null</c> if none found.</returns>
|
||||
private static ManagementBaseObject GetSingleManagementObject(string className)
|
||||
{
|
||||
return GetManagementObjects(className).OfType<ManagementBaseObject>().FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value for a given management object property.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the proeprty value.</typeparam>
|
||||
/// <param name="item">The management object.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns>The property value, or the default type value if not found or compatible.</returns>
|
||||
private static T GetPropertyValue<T>(ManagementBaseObject item, string propertyName)
|
||||
{
|
||||
var property = item.Properties[propertyName];
|
||||
return (property != null && property.Value is T) ? (T)property.Value : default(T);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents processor (CPU) information.
|
||||
/// </summary>
|
||||
public class ProcessorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the family.
|
||||
/// </summary>
|
||||
public string Family { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of cores.
|
||||
/// </summary>
|
||||
public int NumberOfCores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of logical processors.
|
||||
/// </summary>
|
||||
public int NumberOfLogicalProcessors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum clock speed.
|
||||
/// </summary>
|
||||
public int MaxClockSpeed { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the system memory information.
|
||||
/// </summary>
|
||||
public class MemoryInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the total physical memory.
|
||||
/// </summary>
|
||||
public long TotalPhysicalMemory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available physical memory.
|
||||
/// </summary>
|
||||
public long AvailablePhysicalMemory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the memory load.
|
||||
/// </summary>
|
||||
public int MemoryLoad { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total virtual memory.
|
||||
/// </summary>
|
||||
public long TotalVirtualMemory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available virtual memory.
|
||||
/// </summary>
|
||||
public long AvailableVirtualMemory { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents screen information.
|
||||
/// </summary>
|
||||
public class ScreenInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the height in pixels.
|
||||
/// </summary>
|
||||
public int Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width in pixels.
|
||||
/// </summary>
|
||||
public int Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bits per pixel.
|
||||
/// </summary>
|
||||
public int BitsPerPixel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the screen is the primary screen or not.
|
||||
/// </summary>
|
||||
public bool IsPrimary { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the virtual screen information (for multiple screens).
|
||||
/// </summary>
|
||||
public class VirtualScreenInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the height in pixels.
|
||||
/// </summary>
|
||||
public int Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width in pixels.
|
||||
/// </summary>
|
||||
public int Width { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility mehtods for logging telemetry events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class follows a similar pattern to the Trace class, and notifications to TraceListeners.
|
||||
/// </remarks>
|
||||
public static class Telemetry
|
||||
{
|
||||
private static bool isEnabled;
|
||||
private static bool hasListeners;
|
||||
private static ICollection<TelemetryListener> listeners = new TelemetryListener[0];
|
||||
|
||||
private static Thread workerThread;
|
||||
|
||||
private static ConcurrentQueue<NotifyDelegate> workQueue = new ConcurrentQueue<NotifyDelegate>();
|
||||
private static AutoResetEvent workQueuedEvent = new AutoResetEvent(false);
|
||||
private static ManualResetEvent workQueuedEmptiedEvent = new ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether telemetry logging is enabled.
|
||||
/// </summary>
|
||||
public static bool IsEnabled
|
||||
{
|
||||
get { return isEnabled; }
|
||||
set
|
||||
{
|
||||
if (isEnabled != value)
|
||||
{
|
||||
isEnabled = value;
|
||||
InvalidateHasListeners();
|
||||
|
||||
if (IsEnabled)
|
||||
{
|
||||
workerThread = new Thread(DoWork);
|
||||
workerThread.Name = "Telemetry Worker";
|
||||
workerThread.Priority = ThreadPriority.Lowest;
|
||||
workerThread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A worker loop that will dequeue telemetry events and notify telemetry listeners.
|
||||
/// </summary>
|
||||
private static void DoWork()
|
||||
{
|
||||
while (workQueuedEvent.WaitOne())
|
||||
{
|
||||
workQueuedEmptiedEvent.Reset();
|
||||
|
||||
NotifyDelegate notifyDelegate;
|
||||
while (workQueue.TryDequeue(out notifyDelegate))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (TelemetryListener listener in listeners)
|
||||
{
|
||||
notifyDelegate(listener);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.ErrorAndBreak(e, "Unexpected error in Telemetry background work");
|
||||
}
|
||||
}
|
||||
|
||||
workQueuedEmptiedEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes any pending telemetry events in this class to telemetry listeners.
|
||||
/// </summary>
|
||||
public static void Flush()
|
||||
{
|
||||
workQueuedEmptiedEvent.WaitOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">The listener.</param>
|
||||
public static void AddListener(TelemetryListener listener)
|
||||
{
|
||||
Assert.ParamIsNotNull(listener, "listener");
|
||||
|
||||
var newListeners = listeners.ToList();
|
||||
newListeners.Add(listener);
|
||||
listeners = newListeners.ToArray();
|
||||
InvalidateHasListeners();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">The listener.</param>
|
||||
public static void RemoveListener(TelemetryListener listener)
|
||||
{
|
||||
Assert.ParamIsNotNull(listener, "listener");
|
||||
|
||||
var newListeners = listeners.ToList();
|
||||
newListeners.Remove(listener);
|
||||
listeners = newListeners.ToArray();
|
||||
InvalidateHasListeners();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given telemetry event.
|
||||
/// </summary>
|
||||
/// <param name="name">The event name.</param>
|
||||
/// <param name="properties">The (optional) event properties.</param>
|
||||
public static void Event(string name, TelemetryEventProperties properties = null)
|
||||
{
|
||||
Assert.ParamIsNotNull(name, "name");
|
||||
|
||||
if (!hasListeners)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Event(new EventInfo(DateTime.Now, name, properties));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the given event information.
|
||||
/// </summary>
|
||||
/// <param name="info">The event information.</param>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)] // Marked as non-browsable as we want developers to use the more usable overload above
|
||||
public static void Event(EventInfo info)
|
||||
{
|
||||
QueueNotification(l => l.Event(info));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the given exception information.
|
||||
/// </summary>
|
||||
/// <param name="info">The exception information.</param>
|
||||
public static void Exception(Exception info)
|
||||
{
|
||||
QueueNotification(l => l.Exception(info));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the has listeners boolean flag based on the current set of listeners.
|
||||
/// </summary>
|
||||
private static void InvalidateHasListeners()
|
||||
{
|
||||
hasListeners = (isEnabled && listeners.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the notification of the given delegate for later.
|
||||
/// </summary>
|
||||
/// <param name="notifyDelegate">The notify delegate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void QueueNotification(NotifyDelegate notifyDelegate)
|
||||
{
|
||||
if (!hasListeners)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
workQueue.Enqueue(notifyDelegate);
|
||||
workQueuedEvent.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that actions (notifies) a given listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">The listener.</param>
|
||||
private delegate void NotifyDelegate(TelemetryListener listener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a bag of properties for a telemetry event.
|
||||
/// </summary>
|
||||
public class TelemetryEventProperties : Dictionary<string,object>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a basic telemetry listener.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Follows the same pattern as the base TraceListener.
|
||||
/// </remarks>
|
||||
public abstract class TelemetryListener
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked to log event information.
|
||||
/// </summary>
|
||||
/// <param name="info">The event information.</param>
|
||||
public virtual void Event(EventInfo info)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to log exception information.
|
||||
/// </summary>
|
||||
/// <param name="info">The exception information.</param>
|
||||
public virtual void Exception(Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to register and unregister the logging of Trace information to a log file.
|
||||
/// </summary>
|
||||
public class TraceLogFile
|
||||
{
|
||||
private bool registered;
|
||||
private TextWriterTraceListener traceListener;
|
||||
private bool originalAutoFlush;
|
||||
private string traceFile;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TraceLogFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The output file path.</param>
|
||||
public TraceLogFile(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
this.traceFile = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the trace log file as a global trace listener. Traced information
|
||||
/// will start to be output to the output trace file.
|
||||
/// </summary>
|
||||
public void Register()
|
||||
{
|
||||
if (!registered)
|
||||
{
|
||||
this.traceListener = new TextWriterTraceListener(traceFile);
|
||||
this.originalAutoFlush = Trace.AutoFlush;
|
||||
Trace.Listeners.Add(this.traceListener);
|
||||
Trace.AutoFlush = true;
|
||||
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters this instance as a global trace listener. Traced information will no longer
|
||||
/// be output to the output trace file.
|
||||
/// </summary>
|
||||
public void Unregister()
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
Trace.Listeners.Remove(this.traceListener);
|
||||
Trace.AutoFlush = originalAutoFlush;
|
||||
this.traceListener.Close();
|
||||
this.traceListener = null;
|
||||
this.originalAutoFlush = false;
|
||||
|
||||
registered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.DirectoryServices;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.DirectoryServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for reading user information from Active Directory.
|
||||
/// </summary>
|
||||
public class DirectoryBrowser
|
||||
{
|
||||
private bool defaultSearchRootResolved;
|
||||
private DirectoryEntry defaultSearchRoot;
|
||||
private DirectoryBrowserMode mode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DirectoryBrowser"/> class.
|
||||
/// </summary>
|
||||
public DirectoryBrowser()
|
||||
: this(DirectoryBrowserMode.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DirectoryBrowser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mode">The brower mode to use.</param>
|
||||
public DirectoryBrowser(DirectoryBrowserMode mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the entry for the active user.
|
||||
/// </summary>
|
||||
/// <returns>The active user entry, or <c>null</c> if not found.</returns>
|
||||
public UserEntry FindMe()
|
||||
{
|
||||
var sid = WindowsIdentity.GetCurrent().User.Value;
|
||||
var result = FindUsersBySids(new string[] { sid }).FirstOrDefault();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the entry for a given user by account name.
|
||||
/// </summary>
|
||||
/// <param name="accountName">Name of the account.</param>
|
||||
/// <returns>The user entry, or <c>null</c> if not found.</returns>
|
||||
public UserEntry FindUserByAccountName(string accountName)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(accountName, "accountName");
|
||||
|
||||
return FindUsersByAccountName(new string[] { accountName }).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for one or more account names.
|
||||
/// </summary>
|
||||
/// <param name="mailAddresses">The account names.</param>
|
||||
/// <returns>The found user entries. Entries might be missing for non matching user names
|
||||
/// (so the result array does not necessarily match the input collection count)</returns>
|
||||
public UserEntry[] FindUsersByAccountName(IEnumerable<string> accountNames)
|
||||
{
|
||||
Assert.ParamIsNotNull(accountNames, "accountNames");
|
||||
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.SAMAccountName, accountNames));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for one or more account names.
|
||||
/// </summary>
|
||||
/// <param name="mailAddresses">The account names.</param>
|
||||
/// <returns>The found user entries. Entries might be missing for non matching user names
|
||||
/// (so the result array does not necessarily match the input collection count)</returns>
|
||||
public UserEntry[] FindUsersByManager(string managerDistinguishedName)
|
||||
{
|
||||
Assert.ParamIsNotNull(managerDistinguishedName, "managerDistinguishedName");
|
||||
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.Manager, new string[] { managerDistinguishedName }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the entry for a given user by mail address.
|
||||
/// </summary>
|
||||
/// <param name="mailAddress">Mail address.</param>
|
||||
/// <returns>The user entry, or <c>null</c> if not found.</returns>
|
||||
public UserEntry FindUserByMailAddress(string mailAddress)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(mailAddress, "mailAddress");
|
||||
|
||||
return FindUsersByMailAddress(new string[] { mailAddress }).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for one or more mail adderss.
|
||||
/// </summary>
|
||||
/// <param name="accountNames">The account names.</param>
|
||||
/// <returns>The found user entries. Entries might be missing for non matching user names
|
||||
/// (so the result array does not necessarily match the input collection count)</returns>
|
||||
public UserEntry[] FindUsersByMailAddress(IEnumerable<string> mailAddresses)
|
||||
{
|
||||
Assert.ParamIsNotNull(mailAddresses, "mailAddresses");
|
||||
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.Mail, mailAddresses));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for a given display name (multiple users might have the same display name).
|
||||
/// </summary>
|
||||
/// <param name="displayName">The display name.</param>
|
||||
/// <returns>The matching user entries.</returns>
|
||||
public UserEntry[] FindUsersByDisplayName(string displayName)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(displayName, "displayName");
|
||||
|
||||
return FindUsersByDisplayName(new string[] { displayName });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for one or more display names.
|
||||
/// </summary>
|
||||
/// <param name="displayNames">The display names.</param>
|
||||
/// <returns>The matching user entries.</returns>
|
||||
public UserEntry[] FindUsersByDisplayName(IEnumerable<string> displayNames)
|
||||
{
|
||||
Assert.ParamIsNotNull(displayNames, "displayNames");
|
||||
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.DisplayName, displayNames));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries for one or more SIDs (security identifiers).
|
||||
/// </summary>
|
||||
/// <param name="sids">The SIDs.</param>
|
||||
/// <returns>The matching user entries.</returns>
|
||||
public UserEntry[] FindUsersBySids(IEnumerable<string> sids)
|
||||
{
|
||||
Assert.ParamIsNotNull(sids, "sids");
|
||||
|
||||
// See http://us.generation-nt.com/answer/how-get-correct-sid-format-so-i-can-search-help-37063152.html
|
||||
// We need to convert the string sids into a more readable hex format
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.ObjectSid, sids.Select(s => SidToHex(s))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the user entries by distinguished names.
|
||||
/// </summary>
|
||||
/// <param name="distinguishedNames">The distinguished names.</param>
|
||||
/// <returns>The matching user entries.</returns>
|
||||
public UserEntry[] FindUsersByDistinguishedNames(IEnumerable<string> distinguishedNames)
|
||||
{
|
||||
Assert.ParamIsNotNull(distinguishedNames, "distinguishedNames");
|
||||
|
||||
return UserEntry.FromResults(SearchUsersByProperty(DirectoryProperties.DistinguishedName, distinguishedNames));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory searcher and initializes it with the appropriate search root and properties to load.
|
||||
/// </summary>
|
||||
private DirectorySearcher CreateDirectorySearcher()
|
||||
{
|
||||
DirectorySearcher searcher = new DirectorySearcher(DefaultSearchRoot);
|
||||
|
||||
foreach (string property in UserEntry.RequiredPropertyNames)
|
||||
{
|
||||
searcher.PropertiesToLoad.Add(property);
|
||||
}
|
||||
|
||||
if ((mode & DirectoryBrowserMode.Light) != DirectoryBrowserMode.Light)
|
||||
{
|
||||
foreach (string property in UserEntry.ExtraPropertyNames)
|
||||
{
|
||||
searcher.PropertiesToLoad.Add(property);
|
||||
}
|
||||
}
|
||||
|
||||
return searcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a SID (security identifier) to hexadecimal format used for searching. This is the format in which
|
||||
/// SIDs are stored and searched for in Active Directory.
|
||||
/// </summary>
|
||||
/// <param name="sid">The sid.</param>
|
||||
/// <returns>The converted sid string.</returns>
|
||||
private static string SidToHex(string sid)
|
||||
{
|
||||
SecurityIdentifier s = new SecurityIdentifier(sid);
|
||||
byte[] sidBytes = new byte[s.BinaryLength];
|
||||
s.GetBinaryForm(sidBytes, 0);
|
||||
string hexSid = '\\' + BitConverter.ToString(sidBytes).ToLower().Replace('-', '\\');
|
||||
return hexSid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches Active Directory for users by a given property name and one or more matching values.
|
||||
/// </summary>
|
||||
/// <param name="property">The property.</param>
|
||||
/// <param name="values">The matching values.</param>
|
||||
/// <returns>The search result collection.</returns>
|
||||
private SearchResultCollection SearchUsersByProperty(string property, IEnumerable<string> values)
|
||||
{
|
||||
DirectorySearcher searcher = CreateDirectorySearcher();
|
||||
searcher.Filter = "(&(objectClass=user)" + OrFilter(property, values) + ")";
|
||||
return searcher.FindAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an Active Directory search expression filter to match the given property name and one or more values.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>The search expression filter.</returns>
|
||||
private static string OrFilter(string propertyName, IEnumerable<string> values)
|
||||
{
|
||||
StringBuilder filter = new StringBuilder();
|
||||
foreach (object value in values)
|
||||
{
|
||||
filter.AppendFormat("({0}={1})", propertyName, value);
|
||||
}
|
||||
|
||||
return (filter.Length > 0) ? "(|" + filter + ")" : String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the default search root for the current domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default search root.
|
||||
/// </value>
|
||||
public DirectoryEntry DefaultSearchRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
// See http://www.infini-tec.de/post/2004/12/30/Find-an-user-in-a-multi-domain-Active-Directory-enironment-programmatically.aspx
|
||||
if (!defaultSearchRootResolved)
|
||||
{
|
||||
// Get the directory entry for the global catalog root
|
||||
DirectoryEntry gc = new DirectoryEntry("GC:");
|
||||
|
||||
// The catalog root has a single child which is the root node for searching
|
||||
defaultSearchRoot = gc.Children.OfType<DirectoryEntry>().FirstOrDefault();
|
||||
defaultSearchRootResolved = true;
|
||||
}
|
||||
|
||||
return defaultSearchRoot;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we are currently in the context of an Active Directory domain.
|
||||
/// </summary>
|
||||
public bool IsInDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
// Seems to work, alternatively look into something like Domain.GetComputerDomain() maybe...
|
||||
return (DefaultSearchRoot != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A set of flags that indicate what values a directory browser should populate in returned
|
||||
/// user entries.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DirectoryBrowserMode
|
||||
{
|
||||
Default,
|
||||
|
||||
/// <summary>
|
||||
/// Populate lightweight user values only (e.g. no user thumbnail)
|
||||
/// </summary>
|
||||
Light = 0x01
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the well known names of certain user Active Directory properties.
|
||||
/// </summary>
|
||||
internal static class DirectoryProperties
|
||||
{
|
||||
public const string DisplayName = "displayname"; // Joe Stevens
|
||||
public const string CommonName = "cn"; // Joseph Stevens
|
||||
public const string UserPrincipalName = "userprincipalname"; // jstevens@microsoft.com
|
||||
public const string ThumbnailPhoto = "thumbnailphoto"; // bytes[]
|
||||
public const string MailNickname = "mailnickname"; // jstevens
|
||||
public const string Title = "title"; // SENIOR SDE
|
||||
public const string Mail = "mail"; // jstevens@microsoft.com
|
||||
public const string DistinguishedName = "distinguishedname"; // CN=Joe Stevens,OU=UserAccounts,DC=redmond,DC=corp,DC=microsoft,DC=com
|
||||
public const string Manager = "manager"; // CN=Peter Parker,OU=UserAccounts,DC=europe,DC=corp,DC=microsoft,DC=com
|
||||
public const string SpokenName = "msexchumspokenname"; // bytes[]
|
||||
public const string TelephoneNumber = "telephonenumber"; //
|
||||
public const string HomePage = "wwwhomepage"; //
|
||||
public const string StreetAddress = "streetaddress"; //
|
||||
public const string State = "st"; //
|
||||
public const string GivenName = "givenname"; //
|
||||
public const string Surname = "sn"; //
|
||||
public const string PostalCode = "postalcode"; //
|
||||
public const string SAMAccountName = "sAMAccountName"; // jstevens
|
||||
public const string ObjectSid = "objectsid"; //
|
||||
public const string Sip = "msRTCSIP-PrimaryUserAddress"; // sip:jstevens@microsoft.com (used for OCS)
|
||||
public const string Department = "department"; // Visual Studio
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.DirectoryServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for Active Directory.
|
||||
/// </summary>
|
||||
public static class DirectoryUtilities
|
||||
{
|
||||
private static Lazy<bool> isComputerInDomain = new Lazy<bool>(() => EvaluateIsComputerInDomain());
|
||||
private static bool? isCurrentUserInDomain;
|
||||
private static string lastUserSid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current computer is in a domain.
|
||||
/// </summary>
|
||||
public static bool IsComputerInDomain
|
||||
{
|
||||
get { return isComputerInDomain.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the curren tuser is in a domain.
|
||||
/// </summary>
|
||||
public static bool IsCurrentUserInDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
string currentSid = WindowsIdentity.GetCurrent().User.Value;
|
||||
if (!String.Equals(lastUserSid, currentSid))
|
||||
{
|
||||
// A simple check for impersonation, clear caches when the user changes...
|
||||
isCurrentUserInDomain = null;
|
||||
}
|
||||
|
||||
if (isCurrentUserInDomain == null)
|
||||
{
|
||||
lastUserSid = currentSid;
|
||||
isCurrentUserInDomain = EvaluateIsCurrentUserInDomain();
|
||||
}
|
||||
|
||||
return isCurrentUserInDomain.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current computer is in a domain.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the current computer is in a domain.</returns>
|
||||
[DebuggerStepThrough]
|
||||
private static bool EvaluateIsComputerInDomain()
|
||||
{
|
||||
try
|
||||
{
|
||||
var ignore = Domain.GetComputerDomain();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current user is in a domain.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the current user is in a domain.</returns>
|
||||
[DebuggerStepThrough]
|
||||
private static bool EvaluateIsCurrentUserInDomain()
|
||||
{
|
||||
try
|
||||
{
|
||||
var ignore = Domain.GetCurrentDomain();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.DirectoryServices;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.DirectoryServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes an entry for a user in Active Directory.
|
||||
/// </summary>
|
||||
public class UserEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The names of the required properties to load from Active Directory to populate a user.
|
||||
/// </summary>
|
||||
public static readonly ICollection<string> RequiredPropertyNames = new ReadOnlyCollection<string>(new string[] {
|
||||
DirectoryProperties.SAMAccountName, DirectoryProperties.DisplayName, DirectoryProperties.GivenName,
|
||||
DirectoryProperties.Surname, DirectoryProperties.DistinguishedName, DirectoryProperties.UserPrincipalName,
|
||||
DirectoryProperties.MailNickname, DirectoryProperties.Mail, DirectoryProperties.CommonName, DirectoryProperties.Sip,
|
||||
DirectoryProperties.ObjectSid, DirectoryProperties.Title, DirectoryProperties.Department
|
||||
});
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The advanced additional property names to load from Active Directory for a user (depending on the mode).
|
||||
/// </summary>
|
||||
public static readonly ICollection<string> ExtraPropertyNames = new ReadOnlyCollection<string>(new string[] {
|
||||
DirectoryProperties.ThumbnailPhoto
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user entry from a directory entry.
|
||||
/// </summary>
|
||||
/// <param name="entry">The directory entry.</param>
|
||||
/// <returns>The user entry.</returns>
|
||||
public static UserEntry FromEntry(DirectoryEntry entry)
|
||||
{
|
||||
Assert.ParamIsNotNull(entry, "entry");
|
||||
|
||||
return FromEntryOrResult(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user entry from a search result.
|
||||
/// </summary>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <returns>A user entry.</returns>
|
||||
public static UserEntry FromResult(SearchResult result)
|
||||
{
|
||||
Assert.ParamIsNotNull(result, "result");
|
||||
|
||||
return FromEntryOrResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates one or more user entries from a search result collection.
|
||||
/// </summary>
|
||||
/// <param name="results">The search results.</param>
|
||||
/// <returns>The user entries.</returns>
|
||||
public static UserEntry[] FromResults(SearchResultCollection results)
|
||||
{
|
||||
Assert.ParamIsNotNull(results, "results");
|
||||
|
||||
UserEntry[] userEntries = new UserEntry[results.Count];
|
||||
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
userEntries[i] = UserEntry.FromResult(results[i]);
|
||||
}
|
||||
|
||||
return userEntries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user entry from a directory entry or search result.
|
||||
/// </summary>
|
||||
/// <param name="entryOrResult">The directory entry or search result.</param>
|
||||
/// <returns>The user entry.</returns>
|
||||
public static UserEntry FromEntryOrResult(object entryOrResult)
|
||||
{
|
||||
Assert.ParamIsNotNull(entryOrResult, "entryOrResult");
|
||||
|
||||
UserEntry item = new UserEntry();
|
||||
item.AccountName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.SAMAccountName);
|
||||
item.CommonName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.CommonName);
|
||||
item.DisplayName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.DisplayName);
|
||||
item.GivenName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.GivenName);
|
||||
item.Surname = GetPropertyValue<string>(entryOrResult, DirectoryProperties.Surname);
|
||||
item.DistinguishedName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.DistinguishedName);
|
||||
item.UserPrincipalName = GetPropertyValue<string>(entryOrResult, DirectoryProperties.UserPrincipalName);
|
||||
item.MailNickname = GetPropertyValue<string>(entryOrResult, DirectoryProperties.MailNickname);
|
||||
item.Mail = GetPropertyValue<string>(entryOrResult, DirectoryProperties.Mail);
|
||||
item.Sip = GetPropertyValue<string>(entryOrResult, DirectoryProperties.Sip);
|
||||
item.Title = GetPropertyValue<string>(entryOrResult, DirectoryProperties.Title);
|
||||
item.Department = GetPropertyValue<string>(entryOrResult, DirectoryProperties.Department);
|
||||
|
||||
byte[] sidBytes = GetPropertyValue<byte[]>(entryOrResult, DirectoryProperties.ObjectSid);
|
||||
if (sidBytes != null)
|
||||
{
|
||||
item.Sid = new SecurityIdentifier(sidBytes, 0).Value;
|
||||
}
|
||||
|
||||
byte[] photoBytes = GetPropertyValue<byte[]>(entryOrResult, DirectoryProperties.ThumbnailPhoto);
|
||||
item.ThumbnailPhotoBytes = photoBytes;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property value by name from a directory entry or search result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="entryOrResult">The directory entry or search result.</param>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns>The property value, or default value type if the property was not available.</returns>
|
||||
private static T GetPropertyValue<T>(object entryOrResult, string propertyName)
|
||||
{
|
||||
object value = null;
|
||||
|
||||
if (entryOrResult is DirectoryEntry)
|
||||
{
|
||||
PropertyValueCollection values = ((DirectoryEntry)entryOrResult).Properties[propertyName];
|
||||
value = (values != null && values.Count > 0) ? values[0] : null;
|
||||
}
|
||||
else if (entryOrResult is SearchResult)
|
||||
{
|
||||
ResultPropertyValueCollection values = ((SearchResult)entryOrResult).Properties[propertyName];
|
||||
value = (values != null && values.Count > 0) ? values[0] : null;
|
||||
}
|
||||
|
||||
return (value is T) ? (T)value : default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the account.
|
||||
/// </summary>
|
||||
public string AccountName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name.
|
||||
/// </summary>
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the given.
|
||||
/// </summary>
|
||||
public string GivenName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the surname.
|
||||
/// </summary>
|
||||
public string Surname { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distinguished name.
|
||||
/// </summary>
|
||||
public string DistinguishedName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user principal name.
|
||||
/// </summary>
|
||||
public string UserPrincipalName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mail nickname.
|
||||
/// </summary>
|
||||
public string MailNickname { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mail address.
|
||||
/// </summary>
|
||||
public string Mail { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the thumbnail photo bytes.
|
||||
/// </summary>
|
||||
public byte[] ThumbnailPhotoBytes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the the common name.
|
||||
/// </summary>
|
||||
public string CommonName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the users SIP address (e.g. for Lync). Might or might not match the users email address.
|
||||
/// </summary>
|
||||
public string Sip { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sid.
|
||||
/// </summary>
|
||||
public string Sid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the department
|
||||
/// </summary>
|
||||
public string Department { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods to format data.
|
||||
/// </summary>
|
||||
public static class FormatUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats bytes into a friendly string (e.g. KB, MB, GB, etc.)
|
||||
/// </summary>
|
||||
/// <param name="bytes">The bytes.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string FormatBytes(long bytes)
|
||||
{
|
||||
string[] units = { "bytes", "KB", "MB", "GB", "TB", "PB" };
|
||||
|
||||
int unitIndex = 0;
|
||||
|
||||
// TODO: We are losing some rounding here, e.g. 3.72 MB gets rounded to 3MB
|
||||
while (bytes >= 1024 && unitIndex < units.Length)
|
||||
{
|
||||
bytes /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return String.Format("{0} {1}", bytes, units[unitIndex]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the IO package.
|
||||
/// </summary>
|
||||
public static class IOExtensions
|
||||
{
|
||||
// Copied from .NET. 80K.
|
||||
private const int DefaultCopyBufferSize = 0x14000;
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream asynchronously, optionally reporting percentage progress based on the stream bytes.
|
||||
/// </summary>
|
||||
/// <param name="source">The source stream.</param>
|
||||
/// <param name="destination">The destination stream.</param>
|
||||
/// <param name="progress">The progress reporter.</param>
|
||||
/// <param name="length">The optional well known stream length.</param>
|
||||
public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<double> progress, long? length)
|
||||
{
|
||||
Assert.ParamIsNotNull(source, "from");
|
||||
Assert.ParamIsNotNull(destination, "destination");
|
||||
|
||||
if (length != null && length.Value <= 0)
|
||||
{
|
||||
throw new ArgumentException("Length must be greater than 0");
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(0);
|
||||
}
|
||||
|
||||
long totalBytesCopied = 0;
|
||||
int byteCount;
|
||||
byte[] buffer = new byte[DefaultCopyBufferSize];
|
||||
while ((byteCount = await source.ReadAsync(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
await destination.WriteAsync(buffer, 0, byteCount);
|
||||
totalBytesCopied += byteCount;
|
||||
if (length != null && progress != null)
|
||||
{
|
||||
double calculatedProgress = (double)totalBytesCopied / (double)length.Value;
|
||||
|
||||
// In case length was not a trustable number, never make progress greater than 100%
|
||||
calculatedProgress = Math.Min(calculatedProgress, 1.0);
|
||||
|
||||
progress.Report(calculatedProgress);
|
||||
}
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,408 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Packaging;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.IO.Packaging
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods to the System.IO.Packaing.Package type
|
||||
/// designed to make writing both files and steams into packages.
|
||||
/// </summary>
|
||||
public static class PackageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a package part in the package.
|
||||
/// </summary>
|
||||
/// <param name="package">The package.</param>
|
||||
/// <param name="partUri">Destination Uri of the file in the package.</param>
|
||||
/// <param name="contentType">Media type of the file.</param>
|
||||
/// <param name="relationshipType">The relationship of the package part to its parent.</param>
|
||||
/// <returns>A package part.</returns>
|
||||
public static PackagePart CreateRelatedPart(this Package package, Uri partUri, string contentType, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(partUri, "partUri");
|
||||
Assert.ParamIsNotNull(contentType, "contentType");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
partUri = PackUriHelper.CreatePartUri(partUri);
|
||||
PackagePart part = package.CreatePart(partUri, contentType);
|
||||
package.CreateRelationship(part.Uri, TargetMode.Internal, relationshipType);
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a related part.
|
||||
/// </summary>
|
||||
/// <param name="relatedPart">The related part.</param>
|
||||
/// <param name="partUri">The part URI.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="relationshipType">Type of the relationship.</param>
|
||||
/// <returns>A package part.</returns>
|
||||
public static PackagePart CreateRelatedPart(this PackagePart relatedPart, Uri partUri, string contentType, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(relatedPart, "relatedPart");
|
||||
Assert.ParamIsNotNull(partUri, "partUri");
|
||||
Assert.ParamIsNotNull(contentType, "contentType");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
partUri = PackUriHelper.CreatePartUri(partUri);
|
||||
PackagePart part = relatedPart.Package.CreatePart(partUri, contentType);
|
||||
relatedPart.CreateRelationship(part.Uri, TargetMode.Internal, relationshipType);
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing package part (based on the part uri), or creates it if it doesn't already exist, including a relationship for the part.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="partUri">The target part URI.</param>
|
||||
/// <param name="contentType">The media type describing the part (used for creation).</param>
|
||||
/// <param name="relationshipType">The relationship type (used for creating the relationship).</param>
|
||||
/// <returns>The looked up or created part.</returns>
|
||||
public static PackagePart GetOrCreateRelatedPart(this Package package, Uri partUri, string contentType, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
|
||||
return DoGetOrCreatePart(package, partUri, contentType, relationshipType, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing package part (based on the part uri), or creates it if it doesn't already exist, including a relationship for the part.
|
||||
/// </summary>
|
||||
/// <param name="parentPart">A parent part (for creating the relationship).</param>
|
||||
/// <param name="partUri">The target part URI.</param>
|
||||
/// <param name="contentType">The media type describing the part (used for creation).</param>
|
||||
/// <param name="relationshipType">The relationship type (used for creating the relationship).</param>
|
||||
/// <returns>The looked up or created part.</returns>
|
||||
public static PackagePart GetOrCreateRelatedPart(this PackagePart parentPart, Uri partUri, string contentType, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(parentPart, "parentPart");
|
||||
|
||||
return DoGetOrCreatePart(parentPart.Package, partUri, contentType, relationshipType, parentPart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing package part (based on the part uri), or creates it if it doesn't already exist, including a relationship for the part.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="partUri">The target part URI.</param>
|
||||
/// <param name="contentType">The media type describing the part (used for creation).</param>
|
||||
/// <param name="relationshipType">The relationship type (used for creating the relationship).</param>
|
||||
/// <param name="relatedPart">An optional parent part (for creating the relationship), or <c>null</c> to add a global relationship with the package.</param>
|
||||
/// <returns>The looked up or created part.</returns>
|
||||
private static PackagePart DoGetOrCreatePart(Package package, Uri partUri, string contentType, string relationshipType, PackagePart relatedPart)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(partUri, "partUri");
|
||||
Assert.ParamIsNotNull(contentType, "contentType");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
PackagePart result = package.TryGetPart(partUri);
|
||||
if (result != null)
|
||||
{
|
||||
// Found an existing part, at least assert that the content-type is what we expected
|
||||
Debug.Assert(String.Equals(contentType, result.ContentType), String.Format(CultureInfo.CurrentCulture,
|
||||
"Unexpected content type for part {0}. Expected {1}, actual was {2}", partUri, contentType, result.ContentType));
|
||||
}
|
||||
else if(relatedPart != null)
|
||||
{
|
||||
result = relatedPart.CreateRelatedPart(partUri, contentType, relationshipType);
|
||||
}
|
||||
else if (relatedPart != null)
|
||||
{
|
||||
result = package.CreateRelatedPart(partUri, contentType, relationshipType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a writeable stream for a package part (overwriting the part contents if it already exists).
|
||||
/// </summary>
|
||||
/// <param name="part">A part.</param>
|
||||
/// <returns>A writeable stream for the part.</returns>
|
||||
public static Stream OpenWrite(this PackagePart part)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
|
||||
return part.GetStream(FileMode.Create, FileAccess.Write);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a read-only stream for a package part..
|
||||
/// </summary>
|
||||
/// <param name="part">The part.</param>
|
||||
/// <returns>The opened stream for reading.</returns>
|
||||
public static Stream OpenRead(this PackagePart part)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
|
||||
return part.GetStream(FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the contents of a package part to an output file.
|
||||
/// </summary>
|
||||
/// <param name="part">A part.</param>
|
||||
/// <param name="outputFile">The output file.</param>
|
||||
public static void ExtractTo(this PackagePart part, string outputFile)
|
||||
{
|
||||
Assert.ParamIsNotNull(outputFile, "outputFile");
|
||||
|
||||
using (Stream sourceStream = part.GetStream())
|
||||
using (Stream outputStream = File.Create(outputFile))
|
||||
{
|
||||
sourceStream.CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a package part and all of its relationships and owned parts in a cascading
|
||||
/// (recursive) fashion.
|
||||
/// </summary>
|
||||
/// <param name="rootPart">The root package part to remove.</param>
|
||||
public static void CascadeDelete(this PackagePart rootPart)
|
||||
{
|
||||
Assert.ParamIsNotNull(rootPart, "rootPart");
|
||||
|
||||
// Do a breadth first traversal of relationships, avoiding circular loops
|
||||
Package package = rootPart.Package;
|
||||
ICollection<PackagePart> partsToRemove = new HashSet<PackagePart>();
|
||||
Queue<PackagePart> partsToProcess = new Queue<PackagePart>();
|
||||
partsToProcess.Enqueue(rootPart);
|
||||
|
||||
while (partsToProcess.Any())
|
||||
{
|
||||
PackagePart part = partsToProcess.Dequeue();
|
||||
partsToRemove.Add(part);
|
||||
|
||||
foreach (PackageRelationship relationship in part.GetRelationships())
|
||||
{
|
||||
PackagePart targetPart = relationship.TryGetInternalPart();
|
||||
if (targetPart != null && !partsToRemove.Contains(targetPart))
|
||||
{
|
||||
partsToProcess.Enqueue(targetPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PackagePart part in partsToRemove)
|
||||
{
|
||||
package.DeletePart(part.Uri);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a relationship and all of its target parts (and relationships) in a cascading
|
||||
/// (recursive) fashion.
|
||||
/// </summary>
|
||||
/// <param name="relationship">A package relationship.</param>
|
||||
public static void CascadeDeleteRelationship(this Package package, PackageRelationship relationship)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(relationship, "relationship");
|
||||
|
||||
CascadeDelete(relationship);
|
||||
package.DeleteRelationship(relationship.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a relationship and all of its target parts (and relationships) in a cascading
|
||||
/// (recursive) fashion.
|
||||
/// </summary>
|
||||
/// <param name="relationship">A package relationship.</param>
|
||||
public static void CascadeDeleteRelationship(this PackagePart part, PackageRelationship relationship)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
Assert.ParamIsNotNull(relationship, "relationship");
|
||||
|
||||
CascadeDelete(relationship);
|
||||
part.DeleteRelationship(relationship.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all of the related parts of a relationship in a cascading (recursive) fashion.
|
||||
/// </summary>
|
||||
/// <param name="relationship"></param>
|
||||
private static void CascadeDelete(PackageRelationship relationship)
|
||||
{
|
||||
PackagePart part = relationship.TryGetInternalPart();
|
||||
if (part != null)
|
||||
{
|
||||
part.CascadeDelete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single relationship by type.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="relationshipType">The relationship type.</param>
|
||||
/// <returns>The single relationship of that type that was found, or <c>null</c> if none were found.</returns>
|
||||
public static PackageRelationship GetSingleRelationshipByType(this Package package, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
return GetFirstRelationship(package.GetRelationshipsByType(relationshipType), relationshipType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single relationship by type.
|
||||
/// </summary>
|
||||
/// <param name="part">A package part.</param>
|
||||
/// <param name="relationshipType">The relationship type.</param>
|
||||
/// <returns>The single relationship of that type that was found, or <c>null</c> if none were found.</returns>
|
||||
public static PackageRelationship GetSingleRelationshipByType(this PackagePart part, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
return GetFirstRelationship(part.GetRelationshipsByType(relationshipType), relationshipType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single relationship by matching the target URI.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="targetUri">The target URI.</param>
|
||||
/// <returns>The matching relationship, or <c>null</c> if none found.</returns>
|
||||
public static PackageRelationship GetSingleRelationshipByTargetUri(this Package package, Uri targetUri)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(targetUri, "targetUri");
|
||||
|
||||
return package.GetRelationships().FirstOrDefault(rel => rel.TargetUri.Equals(targetUri));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single relationship by matching the target URI.
|
||||
/// </summary>
|
||||
/// <param name="part">A package part.</param>
|
||||
/// <param name="targetUri">The target URI.</param>
|
||||
/// <returns>The matching relationship, or <c>null</c> if none found.</returns>
|
||||
public static PackageRelationship GetSingleRelationshipByTargetUri(this PackagePart part, Uri targetUri)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
Assert.ParamIsNotNull(targetUri, "targetUri");
|
||||
|
||||
return part.GetRelationships().FirstOrDefault(rel => rel.TargetUri.Equals(targetUri));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single part associated through a given relationship type.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="relationshipType">The relationship type.</param>
|
||||
/// <returns>The single related part, or <c>null</c> if not found.</returns>
|
||||
public static PackagePart GetSingleRelatedPart(this Package package, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
PackageRelationship relationship = package.GetSingleRelationshipByType(relationshipType);
|
||||
return (relationship != null) ? relationship.TryGetInternalPart() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single part associated through a given relationship type.
|
||||
/// </summary>
|
||||
/// <param name="part">A package part.</param>
|
||||
/// <param name="relationshipType">The relationship type.</param>
|
||||
/// <returns>The single related part, or <c>null</c> if not found.</returns>
|
||||
public static PackagePart GetSingleRelatedPart(this PackagePart part, string relationshipType)
|
||||
{
|
||||
Assert.ParamIsNotNull(part, "part");
|
||||
Assert.ParamIsNotNull(relationshipType, "relationshipType");
|
||||
|
||||
PackageRelationship relationship = part.GetSingleRelationshipByType(relationshipType);
|
||||
return (relationship != null) ? relationship.TryGetInternalPart() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package part of a relationship, if it is internal and it exists.
|
||||
/// </summary>
|
||||
/// <param name="relationship">A relationship.</param>
|
||||
/// <returns>The related part, or <c>null</c> if not internal or doesn't exist.</returns>
|
||||
public static PackagePart TryGetInternalPart(this PackageRelationship relationship)
|
||||
{
|
||||
Assert.ParamIsNotNull(relationship, "relationship");
|
||||
|
||||
PackagePart part = null;
|
||||
|
||||
if (relationship.TargetMode == TargetMode.Internal)
|
||||
{
|
||||
var partUri = PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri);
|
||||
part = relationship.Package.TryGetPart(partUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
string message = "Requested part that is not internal for relationship: " + relationship;
|
||||
Debug.Fail(message);
|
||||
}
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a package part, if the uri exists. This is different from GetPart() in that that will
|
||||
/// throw if the URI doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="package">A package.</param>
|
||||
/// <param name="partUri">A part uri.</param>
|
||||
/// <returns>The package part, or <c>null</c> if the URI doesn't exist.</returns>
|
||||
public static PackagePart TryGetPart(this Package package, Uri partUri)
|
||||
{
|
||||
Assert.ParamIsNotNull(package, "package");
|
||||
Assert.ParamIsNotNull(partUri, "partUri");
|
||||
|
||||
PackagePart part = null;
|
||||
|
||||
if (package.PartExists(partUri))
|
||||
{
|
||||
part = package.GetPart(partUri);
|
||||
}
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a package part from a given package.
|
||||
/// </summary>
|
||||
/// <param name="part">The part.</param>
|
||||
public static void Delete(this PackagePart part)
|
||||
{
|
||||
part.Package.DeletePart(part.Uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first relationship in a collection of relationships.
|
||||
/// </summary>
|
||||
/// <param name="relationships">A collection of relationships.</param>
|
||||
/// <param name="relationshipType">The relationship type that was used to query these relationships.</param>
|
||||
/// <returns>The first relationship (if available) or <c>null</c> if none are available.</returns>
|
||||
private static PackageRelationship GetFirstRelationship(PackageRelationshipCollection relationships, string relationshipType)
|
||||
{
|
||||
int relationShipCount = relationships.Count();
|
||||
if (relationShipCount > 0)
|
||||
{
|
||||
if (relationShipCount != 1)
|
||||
{
|
||||
string message = String.Format(CultureInfo.CurrentCulture, "Expected single relationship of type {0}. Instead, we found {1}", relationshipType, relationShipCount);
|
||||
Debug.Fail(message);
|
||||
}
|
||||
|
||||
return relationships.First();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,818 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for manipulating file system paths.
|
||||
/// </summary>
|
||||
public static class PathUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// A fully qualified file name length must be less or equal to this value.
|
||||
/// </summary>
|
||||
public const int MaxPathLength = 260 - 1; // Remove one character for NULL, this is a legacy string constant from C
|
||||
|
||||
/// <summary>
|
||||
/// A directory name must be less than or equal to this value.
|
||||
/// </summary>
|
||||
public const int MaxDirectoryLength = 248 - 1; // Remove one character for NULL, this is a legacy string constant from C
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two filenames are equal.
|
||||
/// </summary>
|
||||
/// <param name="filename1">A filename.</param>
|
||||
/// <param name="filename2">Another filename.</param>
|
||||
/// <returns><c>true</c> if the filenames are equal.</returns>
|
||||
public static bool FileNamesAreEqual(string filename1, string filename2)
|
||||
{
|
||||
return String.Equals(filename1, filename2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two paths are equal.
|
||||
/// </summary>
|
||||
/// <param name="path1">A path.</param>
|
||||
/// <param name="path2">Another path.</param>
|
||||
/// <returns><c>true</c> if the paths are equal.</returns>
|
||||
public static bool PathsAreEqual(string path1, string path2)
|
||||
{
|
||||
return String.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two file extensions are equal.
|
||||
/// </summary>
|
||||
/// <param name="extension1">A file extension.</param>
|
||||
/// <param name="extension2">Another file extension.</param>
|
||||
/// <returns><c>true</c> if the extensions are equal.</returns>
|
||||
public static bool ExtensionsAreEqual(string extension1, string extension2)
|
||||
{
|
||||
return String.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a file in the PATH environment variable.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The full path to the found file, or <c>null</c> if it was not found in any of the folders
|
||||
/// in the PATH environment variable.</returns>
|
||||
public static string FindInPath(string filename)
|
||||
{
|
||||
string path = Environment.GetEnvironmentVariable("PATH");
|
||||
if (path != null)
|
||||
{
|
||||
var pathTokens = path.Split(Path.PathSeparator).Select(p => p.Trim()).Where(p => !String.IsNullOrEmpty(p));
|
||||
foreach (string pathEntry in pathTokens)
|
||||
{
|
||||
string fullPath = Path.Combine(pathEntry, filename);
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts an arbitrary string name to a valid filename by trimming or replacing invalid characters.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="replacement">An optional replacement character (none by default, invalid characters will be removed).</param>
|
||||
/// <returns>A valid filename to use for saving to a file.</returns>
|
||||
public static string ToValidFileName(string name, char replacement = (char) 0)
|
||||
{
|
||||
Assert.ParamIsNotNull(name, "name");
|
||||
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
StringBuilder sb = new StringBuilder(name);
|
||||
for (int i = 0; i < sb.Length; i++)
|
||||
{
|
||||
if (invalidChars.Contains(sb[i]))
|
||||
{
|
||||
sb.Remove(i, 1);
|
||||
|
||||
if (replacement > 0)
|
||||
{
|
||||
sb.Insert(i, replacement);
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name.Length > 0 && sb.Length == 0)
|
||||
{
|
||||
// TODO: All characters were trimmed, how do we make this a valid filename? Arbitrarily assign some value?
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique temporary filename in the current user's temporary folder.
|
||||
/// </summary>
|
||||
/// <param name="preferredName">An otional preferred name for the file (<c>null</c> by default, which generates a random name).</param>
|
||||
/// <returns>The unique or random filename under the current user's temporary folder.</returns>
|
||||
public static string GetTempFilename(string preferredName = null)
|
||||
{
|
||||
return GetUniqueOrRandomFilename(Path.GetTempPath(), preferredName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique or random filename under a given parent path.
|
||||
/// </summary>
|
||||
/// <param name="parentPath">The parent path.</param>
|
||||
/// <param name="preferredName">An otional preferred name for the file (<c>null</c> by default, which generates a random name).</param>
|
||||
/// <returns>The unique or random filename under a given directory.</returns>
|
||||
public static string GetUniqueOrRandomFilename(string parentPath, string preferredName = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(parentPath, "parentPath");
|
||||
|
||||
if (preferredName == null)
|
||||
{
|
||||
preferredName = Path.GetRandomFileName();
|
||||
}
|
||||
|
||||
string path = Path.Combine(parentPath, preferredName);
|
||||
|
||||
// Use a padding in case the name is made unique further down, where e.g. " (17)" is appended (giving ourselves a 5 char buffer)
|
||||
int additionalPadding = 5;
|
||||
|
||||
int excessLength = (path.Length + additionalPadding) - MaxPathLength;
|
||||
if (excessLength > 0)
|
||||
{
|
||||
bool trimSucceeded = false;
|
||||
string baseName = Path.GetFileNameWithoutExtension(preferredName);
|
||||
if (baseName.Length > excessLength)
|
||||
{
|
||||
// We can trim the base filename (keeping the extension as is) to a safer full path length
|
||||
baseName = baseName.Substring(0, baseName.Length - excessLength).TrimEnd();
|
||||
if (baseName.Length > 0)
|
||||
{
|
||||
string extension = Path.GetExtension(preferredName);
|
||||
path = Path.Combine(parentPath, baseName + extension);
|
||||
trimSucceeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trimSucceeded)
|
||||
{
|
||||
Log.WarnAndBreak("Could not trim path length to fit within valid length, directory name was too long: {0}", path);
|
||||
}
|
||||
}
|
||||
|
||||
path = EnsureFilenameIsUnique(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unique filename that doesn't exist in the given path,
|
||||
/// by appending an incremented number to the filename (without the
|
||||
/// extension).
|
||||
/// </summary>
|
||||
/// <param name="path">An initial path.</param>
|
||||
/// <returns>A unique filename path derived from the input.</returns>
|
||||
public static string EnsureFilenameIsUnique(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
if (!Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
string name = Path.GetFileNameWithoutExtension(path);
|
||||
string extension = Path.GetExtension(path);
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
int increment = 1;
|
||||
|
||||
do
|
||||
{
|
||||
increment++;
|
||||
string newName = String.Format("{0} ({1}){2}", name, increment, extension);
|
||||
|
||||
// TODO: Need to make sure path did not exceed max path length.
|
||||
path = Path.Combine(dir, newName);
|
||||
}
|
||||
while (Exists(path));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified path exists (as a file or directory).
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if the path exists; <c>false</c> if otherwise.</returns>
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
return File.Exists(path) || Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to delete a file or folder.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The deletion mode.</param>
|
||||
/// <returns><c>true</c> if th edeletion was successfull or there was nothign to delete (the file didn't exist). <c>false</c> if the deletion failed.</returns>
|
||||
public static bool TryDelete(string path, DeleteMode mode = DeleteMode.None)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
DeleteRecursively(path, mode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.ErrorAndBreak(e, "Error attempting to delete file or folder with path: " + path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory recursively.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The deletion mode.</param>
|
||||
public static void DeleteRecursively(string path, DeleteMode mode = DeleteMode.None)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
DeleteRecursively(new DirectoryInfo(path), mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The base directory.</param>
|
||||
public static void DeleteRecursively(DirectoryInfo directory, DeleteMode mode = DeleteMode.None)
|
||||
{
|
||||
Assert.ParamIsNotNull(directory, "directory");
|
||||
|
||||
DeleteContents(directory, mode);
|
||||
|
||||
bool force = (mode & DeleteMode.Force) == DeleteMode.Force;
|
||||
|
||||
try
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
directory.Attributes &= ~FileAttributes.ReadOnly;
|
||||
}
|
||||
|
||||
directory.Delete();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new IOException("Couldn't delete " + directory.FullName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the contents of a given directory (but not the directory itself).
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The deletion mode.</param>
|
||||
public static void DeleteContents(string path, DeleteMode mode = DeleteMode.None)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
DeleteContents(new DirectoryInfo(path), mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the contents of a given directory (but not the directory itself).
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The deletion mode.</param>
|
||||
public static void DeleteContents(DirectoryInfo directory, DeleteMode mode = DeleteMode.None)
|
||||
{
|
||||
Assert.ParamIsNotNull(directory, "directory");
|
||||
|
||||
|
||||
foreach (DirectoryInfo subDirectory in directory.GetDirectories())
|
||||
DeleteRecursively(subDirectory, mode);
|
||||
|
||||
bool force = (mode & DeleteMode.Force) == DeleteMode.Force;
|
||||
|
||||
foreach (FileInfo file in directory.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
file.Attributes &= ~FileAttributes.ReadOnly;
|
||||
}
|
||||
|
||||
file.Delete();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new IOException("Couldn't delete " + file.FullName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a directory recursively.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">The source directory.</param>
|
||||
/// <param name="targetDirectory">The target directory.</param>
|
||||
public static void CopyRecursively(string sourceDirectory, string targetDirectory)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(sourceDirectory, "sourceDirectory");
|
||||
Assert.ParamIsNotNullOrEmpty(targetDirectory, "targetDirectory");
|
||||
|
||||
if (!Directory.Exists(sourceDirectory))
|
||||
{
|
||||
throw new DirectoryNotFoundException("Invalid source directory " + sourceDirectory);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(targetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
}
|
||||
|
||||
string[] sources = Directory.GetFileSystemEntries(sourceDirectory);
|
||||
foreach (string source in sources)
|
||||
{
|
||||
string target = Path.Combine(targetDirectory, Path.GetFileName(source));
|
||||
if (File.Exists(source))
|
||||
{
|
||||
File.Copy(source, target, true);
|
||||
}
|
||||
else if (Directory.Exists(source))
|
||||
{
|
||||
CopyRecursively(source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the given directory exists, and creates it if it doesn't.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void EnsureDirectoryExists(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the parent directory of the given path exists, and creates it if it doesn't.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void EnsureParentDirectoryExists(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
EnsureDirectoryExists(Path.GetDirectoryName(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a file exists, or creates a 0-byte file if it doesn't.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void EnsureFileExists(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
EnsureParentDirectoryExists(path);
|
||||
File.WriteAllBytes(path, new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a file path is writeable. If the file exists and is read-only,
|
||||
/// it will be made writeable. Otherwise, we ensure that its parent directory
|
||||
/// exists.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename that will be written to.</param>
|
||||
public static void EnsureFileIsWriteable(string filename)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(filename, "filename");
|
||||
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
// Ensure the file is not read-only
|
||||
FileAttributes attributes = File.GetAttributes(filename);
|
||||
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||
{
|
||||
attributes &= ~FileAttributes.ReadOnly;
|
||||
File.SetAttributes(filename, attributes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureParentDirectoryExists(filename);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path of the given full path relative to a base path.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The base path.</param>
|
||||
/// <param name="fullPath">The full path.</param>
|
||||
/// <returns>The path of the given full path relative to a base path.</returns>
|
||||
public static string GetRelativePath(string basePath, string fullPath)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(basePath, "basePath");
|
||||
Assert.ParamIsNotNullOrEmpty(fullPath, "fullPath");
|
||||
|
||||
if (!Path.IsPathRooted(basePath))
|
||||
{
|
||||
throw new ArgumentException("Base path " + basePath + " is not rooted");
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(fullPath))
|
||||
{
|
||||
throw new ArgumentException("Full path " + fullPath + " is not rooted");
|
||||
}
|
||||
|
||||
if (!fullPath.StartsWith(basePath, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
throw new ArgumentException("Full path " + fullPath + " is not a descendant of " + basePath);
|
||||
}
|
||||
|
||||
return fullPath.Substring(basePath.Length).TrimStart(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a set of files and directories matching the specified search pattern.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string to match against the names of files.
|
||||
/// Can include the following wildcards: ..., *, ?.</param>
|
||||
/// <param name="baseDirectory">The base directory to start the search from (Optional). If
|
||||
/// the search pattern defines a root path, this parameter is ignored. If <c>null</c>,
|
||||
/// then the current directory will be used.</param>
|
||||
/// <returns>
|
||||
/// A collection of the files and directories matching the search pattern.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>The following wildcards are permitted in the search pattern:</para>
|
||||
/// <list>
|
||||
/// <item>*: Matches zero or more characters</item>
|
||||
/// <item>?: Exactly one character</item>
|
||||
/// <item>...: Search in all subdirectories</item>
|
||||
/// </list>
|
||||
/// <para>If the search pattern does not define a root path, then the current directory
|
||||
/// will be the starting point of the search.</para>
|
||||
/// </remarks>
|
||||
public static ICollection<string> FindFileSystemEntries(string searchPattern, string baseDirectory = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(searchPattern, "searchPattern");
|
||||
|
||||
return Find(searchPattern, SearchType.FilesAndDirectories, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a set of directories matching the specified search pattern.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string to match against the names of directories.
|
||||
/// Can include the following wildcards: ..., *, ?.</param>
|
||||
/// <param name="baseDirectory">The base directory to start the search from (Optional). If
|
||||
/// the search pattern defines a root path, this parameter is ignored. If <c>null</c>,
|
||||
/// then the current directory will be used.</param>
|
||||
/// <returns>A collection of the directories matching the search pattern.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The following wildcards are permitted in the search pattern:</para>
|
||||
/// <list>
|
||||
/// <item>*: Matches zero or more characters</item>
|
||||
/// <item>?: Exactly one character</item>
|
||||
/// <item>...: Search in all subdirectories</item>
|
||||
/// </list>
|
||||
/// <para>If the search pattern does not define a root path, then the current directory
|
||||
/// will be the starting point of the search.</para>
|
||||
/// </remarks>
|
||||
public static ICollection<string> FindDirectories(string searchPattern, string baseDirectory = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(searchPattern, "searchPattern");
|
||||
|
||||
return Find(searchPattern, SearchType.Directories, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a set of files matching the specified search pattern.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string to match against the names of files.
|
||||
/// Can include the following wildcards: ..., *, ?.</param>
|
||||
/// <param name="baseDirectory">The base directory to start the search from (Optional). If
|
||||
/// the search pattern defines a root path, this parameter is ignored. If <c>null</c>,
|
||||
/// then the current directory will be used.</param>
|
||||
/// <returns>A collection of the files matching the search pattern.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The following wildcards are permitted in the search pattern:</para>
|
||||
/// <list>
|
||||
/// <item>*: Matches zero or more characters</item>
|
||||
/// <item>?: Exactly one character</item>
|
||||
/// <item>...: Search in all subdirectories</item>
|
||||
/// </list>
|
||||
/// <para>If the search pattern does not define a root path, then the current directory
|
||||
/// will be the starting point of the search.</para>
|
||||
/// </remarks>
|
||||
public static ICollection<string> FindFiles(string searchPattern, string baseDirectory = null)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(searchPattern, "searchPattern");
|
||||
|
||||
return Find(searchPattern, SearchType.Files, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a set of files and/or directories matching the specified search pattern.
|
||||
/// </summary>
|
||||
/// <param name="searchPattern">The search string to match against the names of files.
|
||||
/// Can include the following wildcards: ..., *, ?.</param>
|
||||
/// <param name="searchType">Defines the type of file system entry being searched for.</param>
|
||||
/// <param name="baseDirectory">The base directory to start the search from (Optional). If
|
||||
/// the search pattern defines a root path, this parameter is ignored. If <c>null</c>,
|
||||
/// then the current directory will be used.</param>
|
||||
/// <returns>
|
||||
/// A collection of the files and directories matching the search pattern.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>The following wildcards are permitted in the search pattern:</para>
|
||||
/// <list>
|
||||
/// <item>*: Matches zero or more characters</item>
|
||||
/// <item>?: Exactly one character</item>
|
||||
/// <item>...: Search in all subdirectories</item>
|
||||
/// </list>
|
||||
/// <para>If the search pattern does not define a root path, then the current directory
|
||||
/// will be the starting point of the search.</para>
|
||||
/// </remarks>
|
||||
private static ICollection<string> Find(string searchPattern, SearchType searchType, string baseDirectory)
|
||||
{
|
||||
// First expand any environment variable definition in the search pattern.
|
||||
searchPattern = Environment.ExpandEnvironmentVariables(searchPattern);
|
||||
|
||||
if (baseDirectory == null)
|
||||
{
|
||||
baseDirectory = Environment.CurrentDirectory;
|
||||
}
|
||||
|
||||
if (!ContainsWildcards(searchPattern))
|
||||
{
|
||||
IList<string> result = new List<string>();
|
||||
|
||||
// No wildcards, optimized to search for an absolut or relative path
|
||||
string fullPath = Path.Combine(baseDirectory, searchPattern);
|
||||
if (PathMatches(fullPath, searchType))
|
||||
{
|
||||
result.Add(Path.GetFullPath(fullPath));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The pattern contains wildcards, tokenize and resolve each level...
|
||||
ICollection<string> currentLevel = new List<string>();
|
||||
ICollection<string> nextLevel = new List<string>();
|
||||
|
||||
// Figure out if the search pattern defines a root, otherwise we need to use the
|
||||
// base directory as the starting point
|
||||
string root = Path.GetPathRoot(searchPattern);
|
||||
if (!String.IsNullOrEmpty(root))
|
||||
{
|
||||
searchPattern = searchPattern.Substring(root.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
root = baseDirectory;
|
||||
}
|
||||
|
||||
// Push the root as the current level of the search
|
||||
currentLevel.Add(root);
|
||||
|
||||
// Split the pattern into each directory level (RemoveEmptyEntries also cleans up
|
||||
// two or more contiguos directory separators, e.g. c:\foo\\bar, as well as any
|
||||
// starting or ending separator).
|
||||
string[] tokens = searchPattern.Split(new char[] { Path.DirectorySeparatorChar },
|
||||
StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// For each token we have, will we have base paths to search from
|
||||
for (int i = 0; i < tokens.Length && currentLevel.Count > 0; i++)
|
||||
{
|
||||
string token = tokens[i];
|
||||
bool isLastToken = (i == tokens.Length - 1);
|
||||
|
||||
// Search for directories with every token, except for the last. At that point
|
||||
// we need to return the search type passed as an argument to the method
|
||||
SearchType currentSearchType = (isLastToken) ? searchType : SearchType.Directories;
|
||||
currentLevel = GetDescendants(currentLevel, token, currentSearchType);
|
||||
}
|
||||
|
||||
// At the end of the loops, the current level will contain the result
|
||||
return currentLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified value contains wildcards.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified value contains wildcards; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
private static bool ContainsWildcards(string value)
|
||||
{
|
||||
return value.Contains("?") || value.Contains("*") || value.Contains("...");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direct descendants of a given set of base paths that match a given search pattern.
|
||||
/// If the recursive wildcard (...) is used, then all descendants are searched for, not only the
|
||||
/// direct ones.
|
||||
/// </summary>
|
||||
/// <param name="basePaths">The base paths that will be searched.</param>
|
||||
/// <param name="searchPattern">The search pattern.</param>
|
||||
/// <param name="searchType">Defines the type of file system entry being searched for.</param>
|
||||
/// <returns>A collection of matching files/directories for all the input base paths.</returns>
|
||||
private static ICollection<string> GetDescendants(ICollection<string> basePaths, string searchPattern, SearchType searchType)
|
||||
{
|
||||
List<string> results = new List<string>();
|
||||
bool patternHasWildcards = ContainsWildcards(searchPattern);
|
||||
|
||||
foreach (string basePath in basePaths)
|
||||
{
|
||||
if (!Directory.Exists(basePath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patternHasWildcards)
|
||||
{
|
||||
results.AddRange(GetDescendants(basePath, searchPattern, searchType));
|
||||
}
|
||||
else
|
||||
{
|
||||
string next = Path.Combine(basePath, searchPattern);
|
||||
if (PathMatches(next, searchType))
|
||||
{
|
||||
results.Add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direct descendants of a given set base path that match a given search pattern.
|
||||
/// If the recursive wildcard (...) is used, then all descendants are searched for, not only the
|
||||
/// direct ones.
|
||||
/// </summary>
|
||||
/// <param name="path">The starting base path.</param>
|
||||
/// <param name="searchPattern">The search pattern.</param>
|
||||
/// <param name="searchType">Defines the type of file system entry being searched for.</param>
|
||||
/// <returns>A collection of matching files/directories for the input base path.</returns>
|
||||
private static string[] GetDescendants(string path, string searchPattern, SearchType searchType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Recursive wildcard
|
||||
if (searchPattern.Equals("..."))
|
||||
{
|
||||
if (searchType == SearchType.Directories)
|
||||
{
|
||||
return Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
|
||||
}
|
||||
else if (searchType == SearchType.Files)
|
||||
{
|
||||
return Directory.GetFiles(path, "*", SearchOption.AllDirectories);
|
||||
}
|
||||
|
||||
// KLUDGE: Directory doesn't provide a Directory.GetFileSystemEntries() that
|
||||
// has a 3rd parameter. Emulate it on our own...
|
||||
string[] dirs = Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
|
||||
string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
|
||||
return LightMerge(dirs, files);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (searchType == SearchType.Directories)
|
||||
{
|
||||
return Directory.GetDirectories(path, searchPattern);
|
||||
}
|
||||
else if (searchType == SearchType.Files)
|
||||
{
|
||||
return Directory.GetFiles(path, searchPattern);
|
||||
}
|
||||
|
||||
return Directory.GetFileSystemEntries(path, searchPattern);
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
catch (UnauthorizedAccessException) { }
|
||||
|
||||
// If we got here, there was an IO error and an access control error,
|
||||
// return 0 results...
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges two arrays in an optimized way when one of the arrays is of zero length.
|
||||
/// If arrays need to be merged, then the result will also be sorted.
|
||||
/// </summary>
|
||||
/// <param name="array1">One array.</param>
|
||||
/// <param name="array2">Another array.</param>
|
||||
/// <returns>The resulting array (will be one of the input arrays if the other
|
||||
/// was empty).</returns>
|
||||
private static string[] LightMerge(string[] array1, string[] array2)
|
||||
{
|
||||
if (array1.Length == 0)
|
||||
{
|
||||
return array2;
|
||||
}
|
||||
|
||||
if (array2.Length == 0)
|
||||
{
|
||||
return array1;
|
||||
}
|
||||
|
||||
string[] result = new string[array1.Length + array2.Length];
|
||||
array1.CopyTo(result, 0);
|
||||
array2.CopyTo(result, array1.Length);
|
||||
Array.Sort<string>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a given path matches a given search type (if it is an existing file
|
||||
/// or directory).
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="searchType">Defines the type of file system entry being searched for.</param>
|
||||
/// <returns><c>true</c> if the path matches the search type; otherwise <c>false</c>.</returns>
|
||||
private static bool PathMatches(string path, SearchType searchType)
|
||||
{
|
||||
switch (searchType)
|
||||
{
|
||||
case SearchType.Directories:
|
||||
return Directory.Exists(path);
|
||||
|
||||
case SearchType.Files:
|
||||
return File.Exists(path);
|
||||
|
||||
case SearchType.FilesAndDirectories:
|
||||
return Directory.Exists(path) || File.Exists(path);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unrecognized search type: " + searchType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines constants for scoping a search for files only, directories only,
|
||||
/// or both.
|
||||
/// </summary>
|
||||
private enum SearchType
|
||||
{
|
||||
Files, Directories, FilesAndDirectories
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affects how file and directory deletions occur.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DeleteMode
|
||||
{
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Force deletes read-only files by changing their bit before deleting.
|
||||
/// </summary>
|
||||
Force = 0x01
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to manage the creation and disposal of temporary directories.
|
||||
/// </summary>
|
||||
public class TempDirectory : IDisposable
|
||||
{
|
||||
private string path;
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary directory for the current process (based on the process name).
|
||||
/// </summary>
|
||||
/// <returns>The temporary directory.</returns>
|
||||
public static TempDirectory CreateForProcess()
|
||||
{
|
||||
Process process = Process.GetCurrentProcess();
|
||||
string folderName = String.Format("{0}.{1}", System.IO.Path.GetFileNameWithoutExtension(process.ProcessName), process.Id);
|
||||
folderName = PathUtilities.ToValidFileName(folderName);
|
||||
string path = PathUtilities.GetUniqueOrRandomFilename(System.IO.Path.GetTempPath(), folderName);
|
||||
return new TempDirectory(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary directory in the TEMP directory, with an optional preferred name.
|
||||
/// </summary>
|
||||
/// <param name="preferredName">An optional preferred name.</param>
|
||||
/// <returns>The temporary directory.</returns>
|
||||
public static TempDirectory CreateInTempPath(string preferredName = null)
|
||||
{
|
||||
string path = PathUtilities.GetTempFilename(preferredName);
|
||||
return new TempDirectory(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TempDirectory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the temporary directory.</param>
|
||||
/// <param name="createImmediately">if set to <c>true</c>, creates the directory immediately. Otherwise, the directory is created later on demand.</param>
|
||||
public TempDirectory(string path, bool createImmediately = true)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
this.path = path;
|
||||
if (createImmediately)
|
||||
{
|
||||
EnsureIsCreated();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary sub directory.
|
||||
/// </summary>
|
||||
/// <param name="preferredName">An optional preferred name..</param>
|
||||
/// <param name="createImmediately">if set to <c>true</c>, creates the directory immediately. Otherwise, the directory is created later on demand.</param>
|
||||
/// <returns></returns>
|
||||
public TempDirectory CreateTempSubDirectory(string preferredName = null, bool createImmediately = true)
|
||||
{
|
||||
return new TempDirectory(GetUniqueOrRandomFilename(preferredName), createImmediately);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the temporary directory has been created (used when the instance was created with createImmediately = false).
|
||||
/// </summary>
|
||||
public void EnsureIsCreated()
|
||||
{
|
||||
PathUtilities.EnsureDirectoryExists(this.path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the temp directory.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get { return this.path; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique or random filename in the temp directory.
|
||||
/// </summary>
|
||||
/// <param name="preferredName">An optional preferred name.</param>
|
||||
/// <returns>The file path.</returns>
|
||||
public string GetUniqueOrRandomFilename(string preferredName = null)
|
||||
{
|
||||
return PathUtilities.GetUniqueOrRandomFilename(this.path, preferredName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the directory and all of its contents.
|
||||
/// </summary>
|
||||
public void Delete()
|
||||
{
|
||||
if (Directory.Exists(this.path))
|
||||
{
|
||||
PathUtilities.DeleteRecursively(this.path, DeleteMode.Force);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the directory contents..
|
||||
/// </summary>
|
||||
public void DeleteContents()
|
||||
{
|
||||
PathUtilities.DeleteContents(this.path, DeleteMode.Force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to delete the directory and all of its contents.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the directory was succesfully deleted.</returns>
|
||||
public bool TryDelete()
|
||||
{
|
||||
return PathUtilities.TryDelete(this.path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.path;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
Delete();
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using Microsoft.Internal.Tools.TeamMate.Foundation.Diagnostics;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the creation and deletion of temporary files.
|
||||
/// </summary>
|
||||
public class TempFile : IDisposable
|
||||
{
|
||||
private string path;
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary file in the TEMP folder, with an optional preferred name.
|
||||
/// </summary>
|
||||
/// <param name="preferredName">An optional preferred name.</param>
|
||||
/// <returns>The temporary file.</returns>
|
||||
public static TempFile Create(string preferredName = null)
|
||||
{
|
||||
string path = PathUtilities.GetTempFilename(preferredName);
|
||||
return new TempFile(path);
|
||||
}
|
||||
|
||||
internal TempFile(string path)
|
||||
{
|
||||
Assert.ParamIsNotNullOrEmpty(path, "path");
|
||||
|
||||
// Make sure file exists
|
||||
PathUtilities.EnsureFileExists(path);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get { return this.path; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes temporary file if it exists.
|
||||
/// </summary>
|
||||
public void Delete()
|
||||
{
|
||||
if (File.Exists(this.path))
|
||||
{
|
||||
File.Delete(this.path);
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
Delete();
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation
|
||||
{
|
||||
public class LazyWeakReference<T> where T : class
|
||||
{
|
||||
private Func<T> valueFactory;
|
||||
private WeakReference weakRefence = new WeakReference(null);
|
||||
|
||||
public LazyWeakReference(Func<T> valueFactory)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
T result = (T)weakRefence.Target;
|
||||
if (result == null)
|
||||
{
|
||||
result = valueFactory();
|
||||
weakRefence.Target = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValueCreated
|
||||
{
|
||||
get
|
||||
{
|
||||
return weakRefence != null && weakRefence.IsAlive;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), Build\Microsoft.Internal.Tools.TeamMate.Settings.targets))\Build\Microsoft.Internal.Tools.TeamMate.Settings.targets" />
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{1967369E-0368-4888-B743-B16ABAE28B1F}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.Internal.Tools.TeamMate.Foundation</RootNamespace>
|
||||
<AssemblyName>Microsoft.Internal.Tools.TeamMate.Foundation</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.DirectoryServices" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UIAutomationProvider" />
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="WindowsFormsIntegration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AbsoluteUriComparer.cs" />
|
||||
<Compile Include="Chaos\ChaosException.cs" />
|
||||
<Compile Include="Chaos\ChaosMonkey.cs" />
|
||||
<Compile Include="Chaos\ChaosScenario.cs" />
|
||||
<Compile Include="Collections\CollectionExtensions.cs" />
|
||||
<Compile Include="Collections\CollectionUtilities.cs" />
|
||||
<Compile Include="CommandLine\CommandBase.cs" />
|
||||
<Compile Include="CommandLine\CommandLineArgumentException.cs" />
|
||||
<Compile Include="CommandLine\CommandLineArgumentParser.cs" />
|
||||
<Compile Include="CommandLine\CommandLineTool.cs" />
|
||||
<Compile Include="CommandLine\ICommand.cs" />
|
||||
<Compile Include="ComponentModel\ComponentModelExtensions.cs" />
|
||||
<Compile Include="ComponentModel\ObservableObjectBase.cs" />
|
||||
<Compile Include="ConsoleUtilities.cs" />
|
||||
<Compile Include="DateTimeExtensions.cs" />
|
||||
<Compile Include="DeferredAction.cs" />
|
||||
<Compile Include="DelegateDisposable.cs" />
|
||||
<Compile Include="Diagnostics\Assert.cs" />
|
||||
<Compile Include="Diagnostics\CommandLineBuilder.cs" />
|
||||
<Compile Include="Diagnostics\Log.cs" />
|
||||
<Compile Include="Diagnostics\EventInfo.cs" />
|
||||
<Compile Include="Diagnostics\ProcessExtensions.cs" />
|
||||
<Compile Include="Diagnostics\SystemInfo.cs" />
|
||||
<Compile Include="Diagnostics\DiagnosticsSerializer.cs" />
|
||||
<Compile Include="Diagnostics\Telemetry.cs" />
|
||||
<Compile Include="Diagnostics\TelemetryEventProperties.cs" />
|
||||
<Compile Include="Diagnostics\TelemetryListener.cs" />
|
||||
<Compile Include="Diagnostics\TraceLogFile.cs" />
|
||||
<Compile Include="DirectoryServices\DirectoryBrowser.cs" />
|
||||
<Compile Include="DirectoryServices\DirectoryUtilities.cs" />
|
||||
<Compile Include="DirectoryServices\UserEntry.cs" />
|
||||
<Compile Include="ConvertUtilities.cs" />
|
||||
<Compile Include="Diagnostics\Reports\Attachment.cs" />
|
||||
<Compile Include="Diagnostics\Reports\ErrorReport.cs" />
|
||||
<Compile Include="Diagnostics\ExceptionInfo.cs" />
|
||||
<Compile Include="Diagnostics\Reports\FeedbackReport.cs" />
|
||||
<Compile Include="Diagnostics\Reports\ReportSerializer.cs" />
|
||||
<Compile Include="Diagnostics\Reports\UserReportBase.cs" />
|
||||
<Compile Include="FormatUtilities.cs" />
|
||||
<Compile Include="IO\IOExtensions.cs" />
|
||||
<Compile Include="IO\Packaging\PackageExtensions.cs" />
|
||||
<Compile Include="IO\PathUtilities.cs" />
|
||||
<Compile Include="IO\TempDirectory.cs" />
|
||||
<Compile Include="IO\TempFile.cs" />
|
||||
<Compile Include="LazyWeakReference.cs" />
|
||||
<Compile Include="Native\ComCtl32.cs" />
|
||||
<Compile Include="Native\ComImports.cs" />
|
||||
<Compile Include="Native\Clr.cs" />
|
||||
<Compile Include="Native\DwmApi.cs" />
|
||||
<Compile Include="Native\Enums.cs" />
|
||||
<Compile Include="Native\Gdi32.cs" />
|
||||
<Compile Include="Native\Kernel32.cs" />
|
||||
<Compile Include="Native\Ole32.cs" />
|
||||
<Compile Include="Native\OleAut32.cs" />
|
||||
<Compile Include="Native\PropertyKey.cs" />
|
||||
<Compile Include="Native\Propsys.cs" />
|
||||
<Compile Include="Native\PropVariant.cs" />
|
||||
<Compile Include="Native\Shell32.cs" />
|
||||
<Compile Include="Native\ComStreamAdapter.cs" />
|
||||
<Compile Include="Native\StreamAdapter.cs" />
|
||||
<Compile Include="Native\Structs.cs" />
|
||||
<Compile Include="Native\User32.cs" />
|
||||
<Compile Include="Native\WtsApi32.cs" />
|
||||
<Compile Include="Net\Mime\MimeTypes.cs" />
|
||||
<Compile Include="ObjectUtilities.cs" />
|
||||
<Compile Include="PredicateUtilities.cs" />
|
||||
<Compile Include="Reflection\ReflectionExtensions.cs" />
|
||||
<Compile Include="Reflection\ReflectionUtilities.cs" />
|
||||
<Compile Include="Resources\ResourceStrings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>ResourceStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Resources\FoundationResources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>FoundationResources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Runtime\InteropServices\InteropUtilities.cs" />
|
||||
<Compile Include="Runtime\InteropServices\RetryMessageFilterScope.cs" />
|
||||
<Compile Include="Runtime\InteropServices\RetryMesssageFillter.cs" />
|
||||
<Compile Include="Runtime\Serialization\SerializationUtilities.cs" />
|
||||
<Compile Include="ServiceModel\WcfClientProxy.cs" />
|
||||
<Compile Include="Shell\ApplicationRegistrationServices.cs" />
|
||||
<Compile Include="Shell\ExternalWebBrowser.cs" />
|
||||
<Compile Include="Shell\SessionNotificationHelper.cs" />
|
||||
<Compile Include="Shell\ShortcutUtilities.cs" />
|
||||
<Compile Include="Shell\WndProcHelper.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="StringUtilities.cs" />
|
||||
<Compile Include="SystemExtensions.cs" />
|
||||
<Compile Include="Text\CsvReader.cs" />
|
||||
<Compile Include="Text\CsvWriter.cs" />
|
||||
<Compile Include="Text\RtfUtilities.cs" />
|
||||
<Compile Include="Threading\ITaskContext.cs" />
|
||||
<Compile Include="Threading\LinearRegression.cs" />
|
||||
<Compile Include="Threading\SingleTaskRunner.cs" />
|
||||
<Compile Include="Threading\TaskContext.cs" />
|
||||
<Compile Include="Threading\TaskUtilities.cs" />
|
||||
<Compile Include="TimeSpanExtensions.cs" />
|
||||
<Compile Include="UriUtilities.cs" />
|
||||
<Compile Include="Validation\DefaultValidations.cs" />
|
||||
<Compile Include="Validation\FluentUtilities.cs" />
|
||||
<Compile Include="Validation\IValidationRule.cs" />
|
||||
<Compile Include="Validation\PropertyValidator.cs" />
|
||||
<Compile Include="Validation\PropertyValidatorContext.cs" />
|
||||
<Compile Include="Validation\ValidationRule.cs" />
|
||||
<Compile Include="Windows\ClipboardUtilities.cs" />
|
||||
<Compile Include="Windows\Controls\Data\CompoundFilter.cs" />
|
||||
<Compile Include="Windows\Controls\Data\GroupNameConverter.cs" />
|
||||
<Compile Include="Windows\Controls\Data\ISelectableItem.cs" />
|
||||
<Compile Include="Windows\Controls\Data\ListFieldInfo.cs" />
|
||||
<Compile Include="Windows\Controls\Data\ListViewFilter.cs" />
|
||||
<Compile Include="Windows\Controls\FontIcon.cs" />
|
||||
<Compile Include="Windows\Controls\ImageViewer.xaml.cs">
|
||||
<DependentUpon>ImageViewer.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Preview\IFilePreviewPlugin.cs" />
|
||||
<Compile Include="Windows\Controls\SplitViewButton.cs" />
|
||||
<Compile Include="Windows\Controls\Symbol.cs" />
|
||||
<Compile Include="Windows\Controls\SymbolIcon.cs" />
|
||||
<Compile Include="Windows\Controls\TreeItemViewModelBase.cs" />
|
||||
<Compile Include="Windows\Converters\BrushLuminosityConverter.cs" />
|
||||
<Compile Include="Windows\Converters\EnumDisplayStringConverter.cs" />
|
||||
<Compile Include="Windows\Converters\RemainingTimeConverter.cs" />
|
||||
<Compile Include="Windows\Converters\StaticMapConverter.cs" />
|
||||
<Compile Include="Windows\DelegateFactory.cs" />
|
||||
<Compile Include="Windows\HslColor.cs" />
|
||||
<Compile Include="Windows\Input\CommandDictionary.cs" />
|
||||
<Compile Include="Windows\Media\Effects\GrayscaleEffect.cs" />
|
||||
<Compile Include="Windows\MVVM\IGlobalCommandProvider.cs" />
|
||||
<Compile Include="Windows\MVVM\ValidatableViewModelBase.cs" />
|
||||
<Compile Include="Validation\ValidationContext.cs" />
|
||||
<Compile Include="Validation\ValidationFailure.cs" />
|
||||
<Compile Include="Validation\ValidationResult.cs" />
|
||||
<Compile Include="Validation\PropertyValidationRule.cs" />
|
||||
<Compile Include="Validation\PropertyValidationRuleBuilder.cs" />
|
||||
<Compile Include="Validation\Validator.cs" />
|
||||
<Compile Include="Web\HttpUtility.cs" />
|
||||
<Compile Include="Win32\FileTypeInfo.cs" />
|
||||
<Compile Include="Win32\FileTypeRegistry.cs" />
|
||||
<Compile Include="Win32\ProtocolUtilities.cs" />
|
||||
<Compile Include="Win32\RegistryViewUtilities.cs" />
|
||||
<Compile Include="Win32\UnsafeFileExtensions.cs" />
|
||||
<Compile Include="Windows\Controls\BusySpinner.xaml.cs">
|
||||
<DependentUpon>BusySpinner.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\ButtonPanel.cs" />
|
||||
<Compile Include="Windows\Controls\Callout.cs" />
|
||||
<Compile Include="Windows\Controls\ControlResources.cs" />
|
||||
<Compile Include="Windows\Controls\Data\GroupingViewUtilities.cs" />
|
||||
<Compile Include="Windows\Controls\Data\ListView.cs" />
|
||||
<Compile Include="Windows\Controls\Data\ListViewModel.cs" />
|
||||
<Compile Include="Windows\Controls\DialogPanel.cs" />
|
||||
<Compile Include="Windows\Controls\ExceptionDialog.xaml.cs">
|
||||
<DependentUpon>ExceptionDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\FileBrowser.xaml.cs">
|
||||
<DependentUpon>FileBrowser.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\HintTextAdorner.cs" />
|
||||
<Compile Include="Windows\Controls\MetroAnimations.cs" />
|
||||
<Compile Include="Windows\Controls\RibbonWindow.cs" />
|
||||
<Compile Include="Windows\Controls\Preview\FilePreviewControl.xaml.cs">
|
||||
<DependentUpon>FilePreviewControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Preview\IFilePreviewControl.cs" />
|
||||
<Compile Include="Windows\Controls\Preview\ImagePreviewControl.xaml.cs">
|
||||
<DependentUpon>ImagePreviewControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Preview\LoadEventArgs.cs" />
|
||||
<Compile Include="Windows\Controls\Preview\NativePreviewControl.xaml.cs">
|
||||
<DependentUpon>NativePreviewControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Preview\VideoPreviewControl.xaml.cs">
|
||||
<DependentUpon>VideoPreviewControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Preview\WebPreviewControl.xaml.cs">
|
||||
<DependentUpon>WebPreviewControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\ProgressDialog.xaml.cs">
|
||||
<DependentUpon>ProgressDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\ProgressIndicator.xaml.cs">
|
||||
<DependentUpon>ProgressIndicator.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\ProgressRing.xaml.cs">
|
||||
<DependentUpon>ProgressRing.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\Transition.cs" />
|
||||
<Compile Include="Windows\Controls\TransitionControl.xaml.cs">
|
||||
<DependentUpon>TransitionControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Controls\VideoPlayer.xaml.cs">
|
||||
<DependentUpon>VideoPlayer.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Converters\BooleanConverter.cs" />
|
||||
<Compile Include="Windows\Converters\Converters.cs" />
|
||||
<Compile Include="Windows\Converters\DateGroupingConverter.cs" />
|
||||
<Compile Include="Windows\Converters\PercentageConverter.cs" />
|
||||
<Compile Include="Windows\Converters\OneWayConverterBase.cs" />
|
||||
<Compile Include="Windows\Converters\StringConverter.cs" />
|
||||
<Compile Include="Windows\Converters\ThicknessValueConverter.cs" />
|
||||
<Compile Include="Windows\Converters\VisibilityConverter.cs" />
|
||||
<Compile Include="Windows\Documents\Highlighter.cs" />
|
||||
<Compile Include="Windows\Documents\TextFragment.cs" />
|
||||
<Compile Include="Windows\Documents\TextUtilities.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\ItemsControlDragDropService.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\DragDropHelper.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\DraggedAdorner.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\DragMoveWithinBoundsHelper.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\InsertionAdorner.cs" />
|
||||
<Compile Include="Windows\DragAndDrop\DragDropUtilities.cs" />
|
||||
<Compile Include="Windows\ExtendedSystemParameters.cs" />
|
||||
<Compile Include="Windows\Input\KeyGestureUtilities.cs" />
|
||||
<Compile Include="Windows\Media\Animation\AnimationHelper.cs" />
|
||||
<Compile Include="Windows\MVVM\ICommandProvider.cs" />
|
||||
<Compile Include="Windows\MVVM\ValidationUtilities.cs" />
|
||||
<Compile Include="Windows\MVVM\ViewAttribute.cs" />
|
||||
<Compile Include="Windows\MVVM\ViewCatalog.cs" />
|
||||
<Compile Include="Windows\Transfer\FileGroupDataObject.cs" />
|
||||
<Compile Include="Windows\Forms\NativePreviewControl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Forms\NativePreviewControl.designer.cs">
|
||||
<DependentUpon>NativePreviewControl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\Forms\TemporaryGlobalCursor.cs" />
|
||||
<Compile Include="Windows\Input\RelayCommand.cs" />
|
||||
<Compile Include="Windows\Input\RoutedCommandBase.cs" />
|
||||
<Compile Include="Windows\Input\UICommand.cs" />
|
||||
<Compile Include="Windows\InteropExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Windows\Interop\ApplicationHotKeys.cs" />
|
||||
<Compile Include="Windows\Interop\InteropUtilities.cs" />
|
||||
<Compile Include="Windows\LogicalTreeUtilities.cs" />
|
||||
<Compile Include="Windows\Media\Capture\BoundsSelectionWindow.cs" />
|
||||
<Compile Include="Windows\Media\Capture\ScreenCapture.cs" />
|
||||
<Compile Include="Windows\Media\Imaging\BitmapUtilities.cs" />
|
||||
<Compile Include="Windows\Media\VisualTreeUtilities.cs" />
|
||||
<Compile Include="Windows\MVVM\View.cs" />
|
||||
<Compile Include="Windows\MVVM\ViewModelBase.cs" />
|
||||
<Compile Include="Windows\Shell\ApplicationInstance.cs" />
|
||||
<Compile Include="Windows\Shell\ShellFileInfo.cs" />
|
||||
<Compile Include="Windows\Shell\ShellFileInfoCache.cs" />
|
||||
<Compile Include="Windows\Shell\ShellUtilities.cs" />
|
||||
<Compile Include="Windows\Shell\SystemImageCache.cs" />
|
||||
<Compile Include="Windows\Shell\SystemImageInfo.cs" />
|
||||
<Compile Include="Windows\Shell\SystemImageList.cs" />
|
||||
<Compile Include="Windows\Shell\WindowInfo.cs" />
|
||||
<Compile Include="Windows\SystemIcons.cs" />
|
||||
<Compile Include="Windows\TemporaryCursorManager.cs" />
|
||||
<Compile Include="Windows\Transfer\DataObjectExtensions.cs" />
|
||||
<Compile Include="Windows\Transfer\FileGroup.cs" />
|
||||
<Compile Include="Windows\Transfer\CustomDataFormats.cs" />
|
||||
<Compile Include="Windows\Transfer\HtmlDataFactory.cs" />
|
||||
<Compile Include="Windows\UI.cs" />
|
||||
<Compile Include="Windows\UserFeedback.cs" />
|
||||
<Compile Include="Windows\WindowStateInfo.cs" />
|
||||
<Compile Include="Windows\WindowUtilities.cs" />
|
||||
<Compile Include="Windows\WpfExtensions.cs" />
|
||||
<Compile Include="Xml\XmlExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\FoundationResources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>FoundationResources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Windows\Forms\NativePreviewControl.resx">
|
||||
<DependentUpon>NativePreviewControl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Resources\ResourceStrings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>ResourceStrings.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Resource Include="Windows\Media\Effects\GrayscaleEffect.ps" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Windows\Controls\Resources\Controls.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Office.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\BusySpinner.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\ExceptionDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\FileBrowser.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\ImageViewer.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\Preview\FilePreviewControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\Preview\ImagePreviewControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\Preview\NativePreviewControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\Preview\VideoPreviewControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\Preview\WebPreviewControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\ProgressDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\ProgressIndicator.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Windows\Controls\ProgressRing.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Resource Include="Windows\Controls\Resources\Animations.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Resource>
|
||||
<Page Include="Windows\Controls\TransitionControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Resource Include="Windows\Controls\Resources\Transitions.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Resource>
|
||||
<Page Include="Windows\Controls\VideoPlayer.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Cursors\ClosedHand.cur" />
|
||||
<Content Include="Resources\Cursors\OpenHand.cur" />
|
||||
<Resource Include="Resources\Icons\Empty.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="Shell32">
|
||||
<Guid>{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Fonts\segmdl2.ttf">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(BuildScripts)\Microsoft.Internal.Tools.TeamMate.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,14 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in clr.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("clr.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
|
||||
public static extern void CorLaunchApplication(uint hostType, string applicationFullName, int manifestPathsCount,
|
||||
string[] manifestPaths, int activationDataCount, string[] activationData, PROCESS_INFORMATION processInformation);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in comctl32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("comctl32.dll")]
|
||||
public extern static int ImageList_Draw(IntPtr hIml, int i, IntPtr hdcDst, int x, int y, int fStyle);
|
||||
|
||||
[DllImport("comctl32.dll")]
|
||||
public extern static int ImageList_DrawIndirect(ref IMAGELISTDRAWPARAMS pimldp);
|
||||
|
||||
[DllImport("comctl32.dll")]
|
||||
public extern static int ImageList_GetIconSize(IntPtr himl, ref int cx, ref int cy);
|
||||
|
||||
[DllImport("comctl32.dll")]
|
||||
public extern static IntPtr ImageList_GetIcon(IntPtr himl, int i, int flags);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,411 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
// Defines COM imports to expose well-known COM objects as C# classes
|
||||
|
||||
[ComImport]
|
||||
[Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IImageList
|
||||
{
|
||||
[PreserveSig]
|
||||
int Add(IntPtr hbmImage, IntPtr hbmMask, ref int pi);
|
||||
|
||||
[PreserveSig]
|
||||
int ReplaceIcon(int i, IntPtr hicon, ref int pi);
|
||||
|
||||
[PreserveSig]
|
||||
int SetOverlayImage(int iImage, int iOverlay);
|
||||
|
||||
[PreserveSig]
|
||||
int Replace(int i, IntPtr hbmImage, IntPtr hbmMask);
|
||||
|
||||
[PreserveSig]
|
||||
int AddMasked(IntPtr hbmImage, int crMask, ref int pi);
|
||||
|
||||
[PreserveSig]
|
||||
int Draw(ref IMAGELISTDRAWPARAMS pimldp);
|
||||
|
||||
[PreserveSig]
|
||||
int Remove(int i);
|
||||
|
||||
[PreserveSig]
|
||||
int GetIcon(int i, int flags, ref IntPtr picon);
|
||||
|
||||
[PreserveSig]
|
||||
int GetImageInfo(int i, ref IMAGEINFO pImageInfo);
|
||||
|
||||
[PreserveSig]
|
||||
int Copy(int iDst, IImageList punkSrc, int iSrc, int uFlags);
|
||||
|
||||
[PreserveSig]
|
||||
int Merge(int i1, IImageList punk2, int i2, int dx, int dy, ref Guid riid, ref IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int Clone(ref Guid riid, ref IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetImageRect(int i, ref RECT prc);
|
||||
|
||||
[PreserveSig]
|
||||
int GetIconSize(ref int cx, ref int cy);
|
||||
|
||||
[PreserveSig]
|
||||
int SetIconSize(int cx, int cy);
|
||||
|
||||
[PreserveSig]
|
||||
int GetImageCount(ref int pi);
|
||||
|
||||
[PreserveSig]
|
||||
int SetImageCount(int uNewCount);
|
||||
|
||||
[PreserveSig]
|
||||
int SetBkColor(int clrBk, ref int pclr);
|
||||
|
||||
[PreserveSig]
|
||||
int GetBkColor(ref int pclr);
|
||||
|
||||
[PreserveSig]
|
||||
int BeginDrag(int iTrack, int dxHotspot, int dyHotspot);
|
||||
|
||||
[PreserveSig]
|
||||
int EndDrag();
|
||||
|
||||
[PreserveSig]
|
||||
int DragEnter(IntPtr hwndLock, int x, int y);
|
||||
|
||||
[PreserveSig]
|
||||
int DragLeave(IntPtr hwndLock);
|
||||
|
||||
[PreserveSig]
|
||||
int DragMove(int x, int y);
|
||||
|
||||
[PreserveSig]
|
||||
int SetDragCursorImage(ref IImageList punk, int iDrag, int dxHotspot, int dyHotspot);
|
||||
|
||||
[PreserveSig]
|
||||
int DragShowNolock(int fShow);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDragImage(ref POINT ppt, ref POINT pptHotspot, ref Guid riid, ref IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetItemFlags(int i, ref int dwFlags);
|
||||
|
||||
[PreserveSig]
|
||||
int GetOverlayImage(int iOverlay, ref int piIndex);
|
||||
};
|
||||
|
||||
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000B-0000-0000-C000-000000000046")]
|
||||
public interface IStorage
|
||||
{
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
IStream CreateStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
IStorage CreateStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
|
||||
|
||||
void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] pIIDExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest);
|
||||
|
||||
void MoveElementTo([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName, [In, MarshalAs(UnmanagedType.U4)] int grfFlags);
|
||||
|
||||
void Commit(int grfCommitFlags);
|
||||
|
||||
void Revert();
|
||||
|
||||
void EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out object ppVal);
|
||||
|
||||
void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
|
||||
|
||||
void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
|
||||
|
||||
void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] System.Runtime.InteropServices.ComTypes.FILETIME pctime, [In] System.Runtime.InteropServices.ComTypes.FILETIME patime, [In] System.Runtime.InteropServices.ComTypes.FILETIME pmtime);
|
||||
|
||||
void SetClass([In] ref Guid clsid);
|
||||
|
||||
void SetStateBits(int grfStateBits, int grfMask);
|
||||
|
||||
void Stat([Out]out System.Runtime.InteropServices.ComTypes.STATSTG pStatStg, int grfStatFlag);
|
||||
}
|
||||
|
||||
[ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]
|
||||
public class ApplicationRegistrationClass
|
||||
{
|
||||
}
|
||||
|
||||
[CoClass(typeof(ApplicationRegistrationClass))]
|
||||
[ComImport, Guid("4e530b0a-e611-4c77-a3ac-9031d022281b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IApplicationRegistration
|
||||
{
|
||||
[return: MarshalAs(UnmanagedType.LPWStr)]
|
||||
string QueryCurrentDefault(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string query,
|
||||
AssociationType queryType,
|
||||
AssociationLevel queryLevel);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
bool QueryAppIsDefault(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string query,
|
||||
AssociationType queryType,
|
||||
AssociationLevel queryLevel,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appRegistryName);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
bool QueryAppIsDefaultAll(
|
||||
AssociationLevel queryLevel,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appRegistryName);
|
||||
|
||||
void SetAppAsDefault(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appRegistryName,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string set,
|
||||
AssociationType setType);
|
||||
|
||||
void SetAppAsDefaultAll(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appRegistryName);
|
||||
|
||||
void ClearUserAssociations();
|
||||
}
|
||||
|
||||
public enum AssociationType
|
||||
{
|
||||
FileExtension,
|
||||
UrlProtocol,
|
||||
StartMenuClient,
|
||||
MimeType
|
||||
}
|
||||
|
||||
public enum AssociationLevel
|
||||
{
|
||||
Machine,
|
||||
Effective,
|
||||
User
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("8895b1c6-b41f-4c1c-a562-0d564250836f")]
|
||||
public interface IPreviewHandler
|
||||
{
|
||||
void SetWindow(IntPtr hwnd, ref RECT rect);
|
||||
void SetRect(ref RECT rect);
|
||||
void DoPreview();
|
||||
void Unload();
|
||||
void SetFocus();
|
||||
void QueryFocus(out IntPtr phwnd);
|
||||
[PreserveSig]
|
||||
uint TranslateAccelerator(ref MSG pmsg);
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")]
|
||||
public interface IInitializeWithStream
|
||||
{
|
||||
void Initialize(IStream pstream, uint grfMode);
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("b7d14566-0509-4cce-a71f-0a554233bd9b")]
|
||||
public interface IInitializeWithFile
|
||||
{
|
||||
void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode);
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("7F73BE3F-FB79-493C-A6C7-7EE14E245841")]
|
||||
public interface IInitializeWithItem
|
||||
{
|
||||
void Initialize(IShellItem psi, uint grfMode);
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
|
||||
public interface IShellItem
|
||||
{
|
||||
void BindToHandler(IntPtr pbc,
|
||||
[MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
|
||||
[MarshalAs(UnmanagedType.LPStruct)]Guid riid,
|
||||
out IntPtr ppv);
|
||||
|
||||
void GetParent(out IShellItem ppsi);
|
||||
|
||||
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
|
||||
|
||||
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
|
||||
|
||||
void Compare(IShellItem psi, uint hint, out int piOrder);
|
||||
};
|
||||
|
||||
[ComImportAttribute()]
|
||||
[GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
|
||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IShellItemImageFactory
|
||||
{
|
||||
void GetImage(
|
||||
[In, MarshalAs(UnmanagedType.Struct)] SIZE size,
|
||||
[In] SIIGBF flags,
|
||||
[Out] out IntPtr phbm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The IOleMessageFilter interface provides COM servers and applications with the ability to selectively
|
||||
/// handle incoming and outgoing COM messages while waiting for responses from synchronous calls.
|
||||
/// Filtering messages helps to ensure that calls are handled in a manner that improves
|
||||
/// performance and avoids deadlocks. COM messages can be synchronous, asynchronous, or input-synchronized;
|
||||
/// the majority of interface calls are synchronous.
|
||||
/// </summary>
|
||||
[ComImport, Guid("00000016-0000-0000-C000-000000000046"),
|
||||
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IOleMessageFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// This method is an object-based method that provides the ability to filter or reject incoming calls
|
||||
/// (or call backs) to an object or a process. This method is called prior to each method
|
||||
/// invocation originating outside the current process.
|
||||
/// </summary>
|
||||
/// <param name="dwCallType">Kind of incoming call that has been received.</param>
|
||||
/// <param name="hTaskCaller">Handle of the task calling this task.</param>
|
||||
/// <param name="dwTickCount">Elapsed tick count since the outgoing call was made if dwCallType is not CALLTYPE_TOPLEVEL.</param>
|
||||
/// <param name="lpInterfaceInfo">Pointer to an INTERFACEINFO structure, which identifies the object, the interface, and the method making the call.</param>
|
||||
/// <returns>
|
||||
/// <para>SERVERCALL_ISHANDLED: The application might be able to process the call.</para>
|
||||
/// <para>SERVERCALL_REJECTED: The application cannot handle the call due to an unforeseen problem,
|
||||
/// such as network unavailability, or if it is in the process of terminating.</para>
|
||||
/// <para>SERVERCALL_RETRYLATER: The application cannot handle the call at this time.</para>
|
||||
/// </returns>
|
||||
[PreserveSig]
|
||||
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
|
||||
|
||||
/// <summary>
|
||||
/// This client-based method gives the application an opportunity to display a dialog box so
|
||||
/// the user can retry or cancel the call, or switch to the task identified by hTaskCallee.
|
||||
/// </summary>
|
||||
/// <param name="hTaskCallee">Handle of the server task that rejected the call.</param>
|
||||
/// <param name="dwTickCount">Number of elapsed ticks since the call was made.</param>
|
||||
/// <param name="dwRejectType">Specifies either SERVERCALL_REJECTED or SERVERCALL_RETRYLATER, as returned by the object application.</param>
|
||||
/// <returns>
|
||||
/// <para>-1: The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the original method call.</para>
|
||||
/// <para>Value >= 0 and <100: The call is to be retried immediately.</para>
|
||||
/// <para>Value >= 100: COM will wait for this many milliseconds and then retry the call.</para>
|
||||
/// </returns>
|
||||
[PreserveSig]
|
||||
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
|
||||
|
||||
/// <summary>
|
||||
/// This client-based method is called by COM when a Windows message appears in a COM application's
|
||||
/// message queue while the application is waiting for a reply to a remote call.
|
||||
/// </summary>
|
||||
/// <param name="hTaskCallee">Task handle of the called application that has not yet responded.</param>
|
||||
/// <param name="dwTickCount">Number of ticks since the call was made.</param>
|
||||
/// <param name="dwPendingType">Type of call made during which a message or event was received.</param>
|
||||
[PreserveSig]
|
||||
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
|
||||
}
|
||||
|
||||
|
||||
[ComImport, Guid("7E5FE3D9-985F-4908-91F9-EE19F9FD1514")]
|
||||
public class AppVisibilityClass
|
||||
{
|
||||
}
|
||||
|
||||
[CoClass(typeof(AppVisibilityClass))]
|
||||
[ComImport, Guid("2246EA2D-CAEA-4444-A3C4-6DE827E44313"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppVisibility
|
||||
{
|
||||
MONITOR_APP_VISIBILITY GetAppVisibilityOnMonitor(IntPtr hMonitor);
|
||||
|
||||
bool IsLauncherVisible();
|
||||
|
||||
int Advise(IAppVisibilityEvents pCallback);
|
||||
|
||||
void Unadvise([In] int dwCookie);
|
||||
}
|
||||
|
||||
public enum MONITOR_APP_VISIBILITY
|
||||
{
|
||||
MAV_UNKNOWN = 0,
|
||||
MAV_NO_APP_VISIBLE = 1,
|
||||
MAV_APP_VISIBLE = 2
|
||||
}
|
||||
|
||||
[ComImport, Guid("6584CE6B-7D82-49C2-89C9-C6BC02BA8C38"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppVisibilityEvents
|
||||
{
|
||||
void AppVisibilityOnMonitorChanged(IntPtr hMonitor, MONITOR_APP_VISIBILITY previousMode, MONITOR_APP_VISIBILITY currentMode);
|
||||
|
||||
void LauncherVisibilityChange(bool currentVisibleState);
|
||||
}
|
||||
|
||||
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
||||
[CoClass(typeof(CShellLink))]
|
||||
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
|
||||
interface IShellLinkW
|
||||
{
|
||||
/// <summary>Retrieves the path and file name of a Shell link object</summary>
|
||||
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, IntPtr pfd, uint fFlags);
|
||||
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
|
||||
void GetIDList(out IntPtr ppidl);
|
||||
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
|
||||
void SetIDList(IntPtr pidl);
|
||||
/// <summary>Retrieves the description string for a Shell link object</summary>
|
||||
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
|
||||
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
|
||||
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
||||
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
|
||||
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
|
||||
/// <summary>Sets the name of the working directory for a Shell link object</summary>
|
||||
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
||||
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
|
||||
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
|
||||
/// <summary>Sets the command-line arguments for a Shell link object</summary>
|
||||
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
|
||||
/// <summary>Retrieves the hot key for a Shell link object</summary>
|
||||
void GetHotkey(out short pwHotkey);
|
||||
/// <summary>Sets a hot key for a Shell link object</summary>
|
||||
void SetHotkey(short wHotkey);
|
||||
/// <summary>Retrieves the show command for a Shell link object</summary>
|
||||
void GetShowCmd(out int piShowCmd);
|
||||
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
|
||||
void SetShowCmd(int iShowCmd);
|
||||
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
|
||||
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
|
||||
int cchIconPath, out int piIcon);
|
||||
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
|
||||
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
|
||||
/// <summary>Sets the relative path to the Shell link object</summary>
|
||||
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
|
||||
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
|
||||
void Resolve(IntPtr hwnd, uint fFlags);
|
||||
/// <summary>Sets the path and file name of a Shell link object</summary>
|
||||
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
|
||||
}
|
||||
|
||||
[ComImport, Guid("00021401-0000-0000-C000-000000000046"), ClassInterface(ClassInterfaceType.None)]
|
||||
public class CShellLink
|
||||
{
|
||||
}
|
||||
|
||||
[ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IPropertyStore
|
||||
{
|
||||
UInt32 GetCount([Out] out uint propertyCount);
|
||||
UInt32 GetAt([In] uint propertyIndex, out PropertyKey key);
|
||||
UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
|
||||
UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv);
|
||||
UInt32 Commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic implementation of the IStream interface (wraps a .NET Stream).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some of the methods are not supported supported, see implementation for more details.
|
||||
/// </remarks>
|
||||
internal class ComStreamAdapter : IStream, IDisposable
|
||||
{
|
||||
private const long POSITION_NOT_SET = -1;
|
||||
private const int STG_E_INVALIDFUNCTION = 32774;
|
||||
private const int CHUNK = 4096;
|
||||
|
||||
private const string METHOD_NOT_SUPPORTED = "Method not supported.";
|
||||
private const string UNKNOWN_ERROR = "Unknown error.";
|
||||
|
||||
private long indexPosition = POSITION_NOT_SET;
|
||||
private Stream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the StreamWrapper with the specified input stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream being wrapped.</param>
|
||||
internal ComStreamAdapter(Stream stream)
|
||||
{
|
||||
this.indexPosition = POSITION_NOT_SET;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
#region IStream Members
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new stream object with its own seek pointer that references the same bytes as the original stream.
|
||||
/// </summary>
|
||||
/// <param name="ppstm">When this method returns, contains the new stream object. This parameter is passed uninitialized.</param>
|
||||
public void Clone(out IStream ppstm)
|
||||
{
|
||||
ppstm = null;
|
||||
ThrowNotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that any changes made to a stream object that is open in transacted mode are reflected in the parent storage.
|
||||
/// </summary>
|
||||
/// <param name="grfCommitFlags">A value that controls how the changes for the stream object are committed.</param>
|
||||
public void Commit(int grfCommitFlags)
|
||||
{
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream.
|
||||
/// </summary>
|
||||
/// <param name="pstm">A reference to the destination stream.</param>
|
||||
/// <param name="cb">The number of bytes to copy from the source stream.</param>
|
||||
/// <param name="pcbRead">On successful return, contains the actual number of bytes read from the source.</param>
|
||||
/// <param name="pcbWritten">On successful return, contains the actual number of bytes written to the destination.</param>
|
||||
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
|
||||
{
|
||||
byte[] buffer = new byte[CHUNK];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
|
||||
if (cb != 0)
|
||||
{
|
||||
SetSizeToPosition();
|
||||
|
||||
do
|
||||
{
|
||||
int count = CHUNK;
|
||||
if (written + CHUNK > cb)
|
||||
{
|
||||
count = (int)(cb - written);
|
||||
}
|
||||
|
||||
if ((read = stream.Read(buffer, 0, count)) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pstm.Write(buffer, read, IntPtr.Zero);
|
||||
written += read;
|
||||
|
||||
}
|
||||
while (written < cb);
|
||||
}
|
||||
|
||||
if (pcbRead != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt64(pcbRead, written);
|
||||
}
|
||||
|
||||
if (pcbWritten != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt64(pcbWritten, written);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.
|
||||
/// </summary>
|
||||
/// <param name="pv">When this method returns, contains the data read from the stream. This parameter is passed uninitialized.</param>
|
||||
/// <param name="cb">The number of bytes to read from the stream object.</param>
|
||||
/// <param name="pcbRead">A pointer to a ULONG variable that receives the actual number of bytes read from the stream object.</param>
|
||||
public void Read(byte[] pv, int cb, IntPtr pcbRead)
|
||||
{
|
||||
int read = 0;
|
||||
|
||||
if (cb != 0)
|
||||
{
|
||||
SetSizeToPosition();
|
||||
read = stream.Read(pv, 0, cb);
|
||||
}
|
||||
|
||||
if (pcbRead != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt32(pcbRead, read);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the seek pointer to a new location relative to the beginning of the stream, to the end of the stream, or to the current seek pointer.
|
||||
/// </summary>
|
||||
/// <param name="dlibMove">The displacement to add to <paramref name="dwOrigin" />.</param>
|
||||
/// <param name="dwOrigin">The origin of the seek. The origin can be the beginning of the file, the current seek pointer, or the end of the file.</param>
|
||||
/// <param name="plibNewPosition">On successful return, contains the offset of the seek pointer from the beginning of the stream.</param>
|
||||
/// <exception cref="System.Runtime.InteropServices.ExternalException">
|
||||
/// </exception>
|
||||
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
|
||||
{
|
||||
long newPosition = 0;
|
||||
SeekOrigin seekOrigin = SeekOrigin.Begin;
|
||||
|
||||
try
|
||||
{
|
||||
seekOrigin = (SeekOrigin)dwOrigin;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new ExternalException(UNKNOWN_ERROR, STG_E_INVALIDFUNCTION);
|
||||
}
|
||||
|
||||
if (stream.CanWrite)
|
||||
{
|
||||
switch (seekOrigin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
newPosition = dlibMove;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
newPosition = indexPosition;
|
||||
if (newPosition == POSITION_NOT_SET)
|
||||
{
|
||||
newPosition = stream.Position;
|
||||
}
|
||||
|
||||
newPosition += dlibMove;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
newPosition = stream.Length + dlibMove;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen
|
||||
throw new ExternalException(UNKNOWN_ERROR, STG_E_INVALIDFUNCTION);
|
||||
}
|
||||
|
||||
if (newPosition > stream.Length)
|
||||
{
|
||||
indexPosition = newPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Position = newPosition;
|
||||
indexPosition = POSITION_NOT_SET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
newPosition = stream.Seek(dlibMove, seekOrigin);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new ExternalException(UNKNOWN_ERROR, STG_E_INVALIDFUNCTION);
|
||||
}
|
||||
|
||||
indexPosition = POSITION_NOT_SET;
|
||||
}
|
||||
|
||||
if (plibNewPosition != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt64(plibNewPosition, newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the size of the stream object.
|
||||
/// </summary>
|
||||
/// <param name="libNewSize">The new size of the stream as a number of bytes.</param>
|
||||
public void SetSize(long libNewSize)
|
||||
{
|
||||
stream.SetLength(libNewSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="T:System.Runtime.InteropServices.STATSTG" /> structure for this stream.
|
||||
/// </summary>
|
||||
/// <param name="pstatstg">When this method returns, contains a STATSTG structure that describes this stream object. This parameter is passed uninitialized.</param>
|
||||
/// <param name="grfStatFlag">Members in the STATSTG structure that this method does not return, thus saving some memory allocation operations.</param>
|
||||
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
|
||||
{
|
||||
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
|
||||
pstatstg.cbSize = stream.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a specified number of bytes into the stream object starting at the current seek pointer.
|
||||
/// </summary>
|
||||
/// <param name="pv">The buffer to write this stream to.</param>
|
||||
/// <param name="cb">The number of bytes to write to the stream.</param>
|
||||
/// <param name="pcbWritten">On successful return, contains the actual number of bytes written to the stream object. If the caller sets this pointer to <see cref="F:System.IntPtr.Zero" />, this method does not provide the actual number of bytes written.</param>
|
||||
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
|
||||
{
|
||||
if (cb != 0)
|
||||
{
|
||||
SetSizeToPosition();
|
||||
stream.Write(pv, 0, cb);
|
||||
}
|
||||
|
||||
if (pcbWritten != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt32(pcbWritten, cb);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discards all changes that have been made to a transacted stream since the last <see cref="M:System.Runtime.InteropServices.ComTypes.IStream.Commit(System.Int32)" /> call.
|
||||
/// </summary>
|
||||
/// <exception cref="System.Runtime.InteropServices.ExternalException"></exception>
|
||||
public void Revert()
|
||||
{
|
||||
throw new ExternalException(METHOD_NOT_SUPPORTED, STG_E_INVALIDFUNCTION);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restricts access to a specified range of bytes in the stream.
|
||||
/// </summary>
|
||||
/// <param name="libOffset">The byte offset for the beginning of the range.</param>
|
||||
/// <param name="cb">The length of the range, in bytes, to restrict.</param>
|
||||
/// <param name="dwLockType">The requested restrictions on accessing the range.</param>
|
||||
public void LockRegion(long libOffset, long cb, int dwLockType)
|
||||
{
|
||||
ThrowNotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the access restriction on a range of bytes previously restricted with the <see cref="M:System.Runtime.InteropServices.ComTypes.IStream.LockRegion(System.Int64,System.Int64,System.Int32)" /> method.
|
||||
/// </summary>
|
||||
/// <param name="libOffset">The byte offset for the beginning of the range.</param>
|
||||
/// <param name="cb">The length, in bytes, of the range to restrict.</param>
|
||||
/// <param name="dwLockType">The access restrictions previously placed on the range.</param>
|
||||
public void UnlockRegion(long libOffset, long cb, int dwLockType)
|
||||
{
|
||||
ThrowNotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Throws a not supported exception.
|
||||
/// </summary>
|
||||
private void ThrowNotSupportedException()
|
||||
{
|
||||
throw new ExternalException(METHOD_NOT_SUPPORTED, STG_E_INVALIDFUNCTION);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the stream length to the current index position.
|
||||
/// </summary>
|
||||
private void SetSizeToPosition()
|
||||
{
|
||||
if (indexPosition != POSITION_NOT_SET)
|
||||
{
|
||||
// position requested greater than current length?
|
||||
if (indexPosition > stream.Length)
|
||||
{
|
||||
// expand stream
|
||||
stream.SetLength(indexPosition);
|
||||
}
|
||||
|
||||
// set new position
|
||||
stream.Position = indexPosition;
|
||||
indexPosition = POSITION_NOT_SET;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this instance.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in dwmapi.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern void DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern void DwmUnregisterThumbnail(IntPtr HThumbnail);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern void DwmUpdateThumbnailProperties(IntPtr hThumbnail, ref DWM_THUMBNAIL_PROPERTIES props);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern void DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out SIZE size);
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
public static extern Int32 DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = true)]
|
||||
public static extern Int32 DwmSetWindowAttribute(IntPtr hwnd, Int32 attr, ref Int32 attrValue, Int32 attrSize);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DWM_THUMBNAIL_PROPERTIES
|
||||
{
|
||||
public ThumbnailFlags dwFlags;
|
||||
public RECT rcDestination;
|
||||
public RECT rcSource;
|
||||
public byte opacity;
|
||||
public bool fVisible;
|
||||
public bool fSourceClientAreaOnly;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ThumbnailFlags
|
||||
{
|
||||
DWM_TNP_RECTDESTINATION = 0x00000001,
|
||||
DWM_TNP_RECTSOURCE = 0x00000002,
|
||||
DWM_TNP_OPACITY = 0x00000004,
|
||||
DWM_TNP_VISIBLE = 0x00000008,
|
||||
DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in gdi32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
|
||||
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern bool DeleteDC(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern bool DeleteObject(IntPtr hObject);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, RasterOperations dwRop);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest,
|
||||
int nWidthDest, int nHeightDest,
|
||||
IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
|
||||
RasterOperations dwRop);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern bool SetWorldTransform(IntPtr hdc, [In] ref XFORM lpXform);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern int SetGraphicsMode(IntPtr hdc, GM iMode);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern int SetStretchBltMode(IntPtr hdc, STRETCH iStretchMode);
|
||||
|
||||
/// <summary>
|
||||
/// The GetDeviceCaps function retrieves device-specific information for the specified device.
|
||||
/// </summary>
|
||||
/// <param name="hdc">A handle to the DC.</param>
|
||||
/// <param name="nIndex">The item to be returned.</param>
|
||||
/// <returns>The return value specifies the value of the desired item.</returns>
|
||||
[DllImport("gdi32.dll")]
|
||||
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current system DPI setting.
|
||||
/// </summary>
|
||||
/// <returns>The system DPI setting.</returns>
|
||||
public static void GetSystemDpi(out int dpiX, out int dpiY)
|
||||
{
|
||||
IntPtr hDC = GetDC(IntPtr.Zero);
|
||||
|
||||
try
|
||||
{
|
||||
dpiX = GetDeviceCaps(hDC, (int) SystemMetric.SM_LOGPIXELSX);
|
||||
dpiY = GetDeviceCaps(hDC, (int) SystemMetric.SM_LOGPIXELSY);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseDC(IntPtr.Zero, hDC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct XFORM
|
||||
{
|
||||
public float eM11;
|
||||
public float eM12;
|
||||
public float eM21;
|
||||
public float eM22;
|
||||
public float eDx;
|
||||
public float eDy;
|
||||
}
|
||||
|
||||
public enum GM : uint
|
||||
{
|
||||
GM_COMPATIBLE = 1,
|
||||
GM_ADVANCED = 2,
|
||||
}
|
||||
|
||||
public enum STRETCH
|
||||
{
|
||||
STRETCH_ANDSCANS = 1,
|
||||
STRETCH_ORSCANS = 2,
|
||||
STRETCH_DELETESCANS = 3,
|
||||
STRETCH_HALFTONE = 4,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in kernel32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
|
||||
public static extern bool CloseHandle(HandleRef handle);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr LoadLibrary(string lpFileName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
public static extern SafeFileHandle CreateFile(
|
||||
string lpFileName,
|
||||
uint dwDesiredAccess,
|
||||
uint dwShareMode,
|
||||
uint SecurityAttributes,
|
||||
uint dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
int hTemplateFile
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
public static extern SafeFileHandle CreateMailslot(string lpName, uint nMaxMessageSize,
|
||||
int lReadTimeout, SECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
public static extern bool GetMailslotInfo(SafeFileHandle hMailslot,
|
||||
out int lpMaxMessageSize,
|
||||
out int lpNextSize,
|
||||
out int lpMessageCount,
|
||||
out int lpReadTimeout);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||
public static extern IntPtr GlobalAlloc(int uFlags, int dwBytes);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||
public static extern IntPtr GlobalFree(HandleRef handle);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in ole32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("ole32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
|
||||
public static extern int StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)]string pwcsName, uint grfMode, uint reserved, out IStorage ppstgOpen);
|
||||
|
||||
/// <summary>
|
||||
/// This function registers with OLE the instance of an EXE application's IOleMessageFilter interface,
|
||||
/// which is to be used for handling concurrency issues. DLL object applications cannot register
|
||||
/// a message filter.
|
||||
/// </summary>
|
||||
/// <param name="newFilter">IOleMessageFilter interface on the message filter supplied by the application.
|
||||
/// Can be <c>null</c>, indicating that the current IOleMessageFilter registration should be revoked.</param>
|
||||
/// <param name="oldFilter">Variable that receives the previously registered message filter.</param>
|
||||
[DllImport("ole32.dll")]
|
||||
public static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
|
||||
|
||||
[DllImport("Ole32.dll", PreserveSig = false)] // returns hresult
|
||||
public extern static void PropVariantClear([In, Out] PropVariant pvar);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in OleAut32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("OleAut32.dll", PreserveSig = true)] // psa is actually returned, not hresult
|
||||
public extern static IntPtr SafeArrayCreateVector(ushort vt, int lowerBound, uint cElems);
|
||||
|
||||
[DllImport("OleAut32.dll", PreserveSig = false)] // returns hresult
|
||||
public extern static IntPtr SafeArrayAccessData(IntPtr psa);
|
||||
|
||||
[DllImport("OleAut32.dll", PreserveSig = false)] // returns hresult
|
||||
public extern static void SafeArrayUnaccessData(IntPtr psa);
|
||||
|
||||
[DllImport("OleAut32.dll", PreserveSig = true)] // retuns uint32
|
||||
public extern static uint SafeArrayGetDim(IntPtr psa);
|
||||
|
||||
[DllImport("OleAut32.dll", PreserveSig = false)] // returns hresult
|
||||
public extern static int SafeArrayGetLBound(IntPtr psa, uint nDim);
|
||||
|
||||
[DllImport("OleAut32.dll", PreserveSig = false)] // returns hresult
|
||||
public extern static int SafeArrayGetUBound(IntPtr psa, uint nDim);
|
||||
|
||||
// This decl for SafeArrayGetElement is only valid for cDims==1!
|
||||
[DllImport("OleAut32.dll", PreserveSig = false)] // returns hresult
|
||||
[return: MarshalAs(UnmanagedType.IUnknown)]
|
||||
public extern static object SafeArrayGetElement(IntPtr psa, ref int rgIndices);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,805 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the OLE struct PROPVARIANT.
|
||||
/// This class is intended for internal use only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Originally sourced from http://blogs.msdn.com/adamroot/pages/interop-with-propvariants-in-net.aspx
|
||||
/// and modified to support additional types including vectors and ability to set values
|
||||
/// </remarks>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", MessageId = "_ptr2")]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public sealed class PropVariant : IDisposable
|
||||
{
|
||||
#region Vector Action Cache
|
||||
|
||||
// A static dictionary of delegates to get data from array's contained within PropVariants
|
||||
private static Dictionary<Type, Action<PropVariant, Array, uint>> _vectorActions = null;
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
||||
private static Dictionary<Type, Action<PropVariant, Array, uint>> GenerateVectorActions()
|
||||
{
|
||||
Dictionary<Type, Action<PropVariant, Array, uint>> cache = new Dictionary<Type, Action<PropVariant, Array, uint>>();
|
||||
|
||||
cache.Add(typeof(Int16), (pv, array, i) =>
|
||||
{
|
||||
short val;
|
||||
NativeMethods.PropVariantGetInt16Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(UInt16), (pv, array, i) =>
|
||||
{
|
||||
ushort val;
|
||||
NativeMethods.PropVariantGetUInt16Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Int32), (pv, array, i) =>
|
||||
{
|
||||
int val;
|
||||
NativeMethods.PropVariantGetInt32Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(UInt32), (pv, array, i) =>
|
||||
{
|
||||
uint val;
|
||||
NativeMethods.PropVariantGetUInt32Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Int64), (pv, array, i) =>
|
||||
{
|
||||
long val;
|
||||
NativeMethods.PropVariantGetInt64Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(UInt64), (pv, array, i) =>
|
||||
{
|
||||
ulong val;
|
||||
NativeMethods.PropVariantGetUInt64Elem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(DateTime), (pv, array, i) =>
|
||||
{
|
||||
System.Runtime.InteropServices.ComTypes.FILETIME val;
|
||||
NativeMethods.PropVariantGetFileTimeElem(pv, i, out val);
|
||||
|
||||
long fileTime = GetFileTimeAsLong(ref val);
|
||||
|
||||
array.SetValue(DateTime.FromFileTime(fileTime), i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Boolean), (pv, array, i) =>
|
||||
{
|
||||
bool val;
|
||||
NativeMethods.PropVariantGetBooleanElem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Double), (pv, array, i) =>
|
||||
{
|
||||
double val;
|
||||
NativeMethods.PropVariantGetDoubleElem(pv, i, out val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Single), (pv, array, i) => // float
|
||||
{
|
||||
float[] val = new float[1];
|
||||
Marshal.Copy(pv._ptr2, val, (int)i, 1);
|
||||
array.SetValue(val[0], (int)i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(Decimal), (pv, array, i) =>
|
||||
{
|
||||
int[] val = new int[4];
|
||||
for (int a = 0; a < val.Length; a++)
|
||||
{
|
||||
val[a] = Marshal.ReadInt32(pv._ptr2,
|
||||
(int)i * sizeof(decimal) + a * sizeof(int)); //index * size + offset quarter
|
||||
}
|
||||
array.SetValue(new decimal(val), i);
|
||||
});
|
||||
|
||||
cache.Add(typeof(String), (pv, array, i) =>
|
||||
{
|
||||
string val = string.Empty;
|
||||
NativeMethods.PropVariantGetStringElem(pv, i, ref val);
|
||||
array.SetValue(val, i);
|
||||
});
|
||||
|
||||
return cache;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Dynamic Construction / Factory (Expressions)
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create a PropVariant by finding an appropriate constructor.
|
||||
/// </summary>
|
||||
/// <param name="value">Object from which PropVariant should be created.</param>
|
||||
public static PropVariant FromObject(object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return new PropVariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
var func = GetDynamicConstructor(value.GetType());
|
||||
return func(value);
|
||||
}
|
||||
}
|
||||
|
||||
// A dictionary and lock to contain compiled expression trees for constructors
|
||||
private static Dictionary<Type, Func<object, PropVariant>> _cache = new Dictionary<Type, Func<object, PropVariant>>();
|
||||
private static object _padlock = new object();
|
||||
|
||||
// Retrieves a cached constructor expression.
|
||||
// If no constructor has been cached, it attempts to find/add it. If it cannot be found
|
||||
// an exception is thrown.
|
||||
// This method looks for a public constructor with the same parameter type as the object.
|
||||
private static Func<object, PropVariant> GetDynamicConstructor(Type type)
|
||||
{
|
||||
lock (_padlock)
|
||||
{
|
||||
// initial check, if action is found, return it
|
||||
Func<object, PropVariant> action;
|
||||
if (!_cache.TryGetValue(type, out action))
|
||||
{
|
||||
// iterates through all constructors
|
||||
ConstructorInfo constructor = typeof(PropVariant)
|
||||
.GetConstructor(new Type[] { type });
|
||||
|
||||
if (constructor == null)
|
||||
{ // if the method was not found, throw.
|
||||
throw new ArgumentException("This Value type is not supported.");
|
||||
}
|
||||
else // if the method was found, create an expression to call it.
|
||||
{
|
||||
// create parameters to action
|
||||
var arg = Expression.Parameter(typeof(object), "arg");
|
||||
|
||||
// create an expression to invoke the constructor with an argument cast to the correct type
|
||||
var create = Expression.New(constructor, Expression.Convert(arg, type));
|
||||
|
||||
// compiles expression into an action delegate
|
||||
action = Expression.Lambda<Func<object, PropVariant>>(create, arg).Compile();
|
||||
_cache.Add(type, action);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
[FieldOffset(0)]
|
||||
decimal _decimal;
|
||||
|
||||
// This is actually a VarEnum value, but the VarEnum type
|
||||
// requires 4 bytes instead of the expected 2.
|
||||
[FieldOffset(0)]
|
||||
ushort _valueType;
|
||||
|
||||
// Reserved Fields
|
||||
//[FieldOffset(2)]
|
||||
//ushort _wReserved1;
|
||||
//[FieldOffset(4)]
|
||||
//ushort _wReserved2;
|
||||
//[FieldOffset(6)]
|
||||
//ushort _wReserved3;
|
||||
|
||||
// In order to allow x64 compat, we need to allow for
|
||||
// expansion of the IntPtr. However, the BLOB struct
|
||||
// uses a 4-byte int, followed by an IntPtr, so
|
||||
// although the valueData field catches most pointer values,
|
||||
// we need an additional 4-bytes to get the BLOB
|
||||
// pointer. The valueDataExt field provides this, as well as
|
||||
// the last 4-bytes of an 8-byte value on 32-bit
|
||||
// architectures.
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
|
||||
[FieldOffset(12)]
|
||||
IntPtr _ptr2;
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
|
||||
[FieldOffset(8)]
|
||||
IntPtr _ptr;
|
||||
[FieldOffset(8)]
|
||||
Int32 _int32;
|
||||
[FieldOffset(8)]
|
||||
UInt32 _uint32;
|
||||
[FieldOffset(8)]
|
||||
byte _byte;
|
||||
[FieldOffset(8)]
|
||||
sbyte _sbyte;
|
||||
[FieldOffset(8)]
|
||||
short _short;
|
||||
[FieldOffset(8)]
|
||||
ushort _ushort;
|
||||
[FieldOffset(8)]
|
||||
long _long;
|
||||
[FieldOffset(8)]
|
||||
ulong _ulong;
|
||||
[FieldOffset(8)]
|
||||
double _double;
|
||||
[FieldOffset(8)]
|
||||
float _float;
|
||||
|
||||
#endregion // struct fields
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Default constrcutor
|
||||
/// </summary>
|
||||
public PropVariant()
|
||||
{
|
||||
// left empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a string value
|
||||
/// </summary>
|
||||
public PropVariant(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentException("String argument cannot be null or empty.", "value");
|
||||
}
|
||||
|
||||
_valueType = (ushort)VarEnum.VT_LPWSTR;
|
||||
_ptr = Marshal.StringToCoTaskMemUni(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a string vector
|
||||
/// </summary>
|
||||
public PropVariant(string[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromStringVector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a bool vector
|
||||
/// </summary>
|
||||
public PropVariant(bool[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromBooleanVector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a short vector
|
||||
/// </summary>
|
||||
public PropVariant(short[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromInt16Vector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a short vector
|
||||
/// </summary>
|
||||
public PropVariant(ushort[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromUInt16Vector(value, (uint)value.Length, this);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an int vector
|
||||
/// </summary>
|
||||
public PropVariant(int[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromInt32Vector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an uint vector
|
||||
/// </summary>
|
||||
public PropVariant(uint[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromUInt32Vector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a long vector
|
||||
/// </summary>
|
||||
public PropVariant(long[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromInt64Vector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a ulong vector
|
||||
/// </summary>
|
||||
public PropVariant(ulong[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromUInt64Vector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>>
|
||||
/// Set a double vector
|
||||
/// </summary>
|
||||
public PropVariant(double[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
NativeMethods.InitPropVariantFromDoubleVector(value, (uint)value.Length, this);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set a DateTime vector
|
||||
/// </summary>
|
||||
public PropVariant(DateTime[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
System.Runtime.InteropServices.ComTypes.FILETIME[] fileTimeArr =
|
||||
new System.Runtime.InteropServices.ComTypes.FILETIME[value.Length];
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
fileTimeArr[i] = DateTimeToFileTime(value[i]);
|
||||
}
|
||||
|
||||
NativeMethods.InitPropVariantFromFileTimeVector(fileTimeArr, (uint)fileTimeArr.Length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a bool value
|
||||
/// </summary>
|
||||
public PropVariant(bool value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_BOOL;
|
||||
_int32 = (value == true) ? -1 : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a DateTime value
|
||||
/// </summary>
|
||||
public PropVariant(DateTime value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_FILETIME;
|
||||
|
||||
System.Runtime.InteropServices.ComTypes.FILETIME ft = DateTimeToFileTime(value);
|
||||
NativeMethods.InitPropVariantFromFileTime(ref ft, this);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set a byte value
|
||||
/// </summary>
|
||||
public PropVariant(byte value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_UI1;
|
||||
_byte = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a sbyte value
|
||||
/// </summary>
|
||||
public PropVariant(sbyte value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_I1;
|
||||
_sbyte = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a short value
|
||||
/// </summary>
|
||||
public PropVariant(short value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_I2;
|
||||
_short = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an unsigned short value
|
||||
/// </summary>
|
||||
public PropVariant(ushort value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_UI2;
|
||||
_ushort = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an int value
|
||||
/// </summary>
|
||||
public PropVariant(int value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_I4;
|
||||
_int32 = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an unsigned int value
|
||||
/// </summary>
|
||||
public PropVariant(uint value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_UI4;
|
||||
_uint32 = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a decimal value
|
||||
/// </summary>
|
||||
public PropVariant(decimal value)
|
||||
{
|
||||
_decimal = value;
|
||||
|
||||
// It is critical that the value type be set after the decimal value, because they overlap.
|
||||
// If valuetype is written first, its value will be lost when _decimal is written.
|
||||
_valueType = (ushort)VarEnum.VT_DECIMAL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PropVariant with a contained decimal array.
|
||||
/// </summary>
|
||||
/// <param name="value">Decimal array to wrap.</param>
|
||||
public PropVariant(decimal[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
_valueType = (ushort)(VarEnum.VT_DECIMAL | VarEnum.VT_VECTOR);
|
||||
_int32 = value.Length;
|
||||
|
||||
// allocate required memory for array with 128bit elements
|
||||
_ptr2 = Marshal.AllocCoTaskMem(value.Length * sizeof(decimal));
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value[i]);
|
||||
Marshal.Copy(bits, 0, _ptr2, bits.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PropVariant containing a float type.
|
||||
/// </summary>
|
||||
public PropVariant(float value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_R4;
|
||||
|
||||
_float = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PropVariant containing a float[] array.
|
||||
/// </summary>
|
||||
public PropVariant(float[] value)
|
||||
{
|
||||
if (value == null) { throw new ArgumentNullException("value"); }
|
||||
|
||||
_valueType = (ushort)(VarEnum.VT_R4 | VarEnum.VT_VECTOR);
|
||||
_int32 = value.Length;
|
||||
|
||||
_ptr2 = Marshal.AllocCoTaskMem(value.Length * sizeof(float));
|
||||
|
||||
Marshal.Copy(value, 0, _ptr2, value.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a long
|
||||
/// </summary>
|
||||
public PropVariant(long value)
|
||||
{
|
||||
_long = value;
|
||||
_valueType = (ushort)VarEnum.VT_I8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a ulong
|
||||
/// </summary>
|
||||
public PropVariant(ulong value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_UI8;
|
||||
_ulong = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a double
|
||||
/// </summary>
|
||||
public PropVariant(double value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_R8;
|
||||
_double = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Uncalled methods - These are currently not called, but I think may be valid in the future.
|
||||
|
||||
/// <summary>
|
||||
/// Set an IUnknown value
|
||||
/// </summary>
|
||||
/// <param name="value">The new value to set.</param>
|
||||
internal void SetIUnknown(object value)
|
||||
{
|
||||
_valueType = (ushort)VarEnum.VT_UNKNOWN;
|
||||
_ptr = Marshal.GetIUnknownForObject(value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set a safe array value
|
||||
/// </summary>
|
||||
/// <param name="array">The new value to set.</param>
|
||||
internal void SetSafeArray(Array array)
|
||||
{
|
||||
if (array == null) { throw new ArgumentNullException("array"); }
|
||||
const ushort vtUnknown = 13;
|
||||
IntPtr psa = NativeMethods.SafeArrayCreateVector(vtUnknown, 0, (uint)array.Length);
|
||||
|
||||
IntPtr pvData = NativeMethods.SafeArrayAccessData(psa);
|
||||
try // to remember to release lock on data
|
||||
{
|
||||
for (int i = 0; i < array.Length; ++i)
|
||||
{
|
||||
object obj = array.GetValue(i);
|
||||
IntPtr punk = (obj != null) ? Marshal.GetIUnknownForObject(obj) : IntPtr.Zero;
|
||||
Marshal.WriteIntPtr(pvData, i * IntPtr.Size, punk);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.SafeArrayUnaccessData(psa);
|
||||
}
|
||||
|
||||
_valueType = (ushort)VarEnum.VT_ARRAY | (ushort)VarEnum.VT_UNKNOWN;
|
||||
_ptr = psa;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the variant type.
|
||||
/// </summary>
|
||||
public VarEnum VarType
|
||||
{
|
||||
get { return (VarEnum)_valueType; }
|
||||
set { _valueType = (ushort)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this has an empty or null value
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsNullOrEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_valueType == (ushort)VarEnum.VT_EMPTY || _valueType == (ushort)VarEnum.VT_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the variant value.
|
||||
/// </summary>
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
switch ((VarEnum)_valueType)
|
||||
{
|
||||
case VarEnum.VT_I1:
|
||||
return _sbyte;
|
||||
case VarEnum.VT_UI1:
|
||||
return _byte;
|
||||
case VarEnum.VT_I2:
|
||||
return _short;
|
||||
case VarEnum.VT_UI2:
|
||||
return _ushort;
|
||||
case VarEnum.VT_I4:
|
||||
case VarEnum.VT_INT:
|
||||
return _int32;
|
||||
case VarEnum.VT_UI4:
|
||||
case VarEnum.VT_UINT:
|
||||
return _uint32;
|
||||
case VarEnum.VT_I8:
|
||||
return _long;
|
||||
case VarEnum.VT_UI8:
|
||||
return _ulong;
|
||||
case VarEnum.VT_R4:
|
||||
return _float;
|
||||
case VarEnum.VT_R8:
|
||||
return _double;
|
||||
case VarEnum.VT_BOOL:
|
||||
return _int32 == -1;
|
||||
case VarEnum.VT_ERROR:
|
||||
return _long;
|
||||
case VarEnum.VT_CY:
|
||||
return _decimal;
|
||||
case VarEnum.VT_DATE:
|
||||
return DateTime.FromOADate(_double);
|
||||
case VarEnum.VT_FILETIME:
|
||||
return DateTime.FromFileTime(_long);
|
||||
case VarEnum.VT_BSTR:
|
||||
return Marshal.PtrToStringBSTR(_ptr);
|
||||
case VarEnum.VT_BLOB:
|
||||
return GetBlobData();
|
||||
case VarEnum.VT_LPSTR:
|
||||
return Marshal.PtrToStringAnsi(_ptr);
|
||||
case VarEnum.VT_LPWSTR:
|
||||
return Marshal.PtrToStringUni(_ptr);
|
||||
case VarEnum.VT_UNKNOWN:
|
||||
return Marshal.GetObjectForIUnknown(_ptr);
|
||||
case VarEnum.VT_DISPATCH:
|
||||
return Marshal.GetObjectForIUnknown(_ptr);
|
||||
case VarEnum.VT_DECIMAL:
|
||||
return _decimal;
|
||||
case VarEnum.VT_ARRAY | VarEnum.VT_UNKNOWN:
|
||||
return CrackSingleDimSafeArray(_ptr);
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_LPWSTR):
|
||||
return GetVector<string>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_I2):
|
||||
return GetVector<Int16>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_UI2):
|
||||
return GetVector<UInt16>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_I4):
|
||||
return GetVector<Int32>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_UI4):
|
||||
return GetVector<UInt32>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_I8):
|
||||
return GetVector<Int64>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_UI8):
|
||||
return GetVector<UInt64>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_R4):
|
||||
return GetVector<float>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_R8):
|
||||
return GetVector<Double>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_BOOL):
|
||||
return GetVector<Boolean>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_FILETIME):
|
||||
return GetVector<DateTime>();
|
||||
case (VarEnum.VT_VECTOR | VarEnum.VT_DECIMAL):
|
||||
return GetVector<Decimal>();
|
||||
default:
|
||||
// if the value cannot be marshaled
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static long GetFileTimeAsLong(ref System.Runtime.InteropServices.ComTypes.FILETIME val)
|
||||
{
|
||||
return (((long)val.dwHighDateTime) << 32) + val.dwLowDateTime;
|
||||
}
|
||||
|
||||
private static System.Runtime.InteropServices.ComTypes.FILETIME DateTimeToFileTime(DateTime value)
|
||||
{
|
||||
long hFT = value.ToFileTime();
|
||||
System.Runtime.InteropServices.ComTypes.FILETIME ft =
|
||||
new System.Runtime.InteropServices.ComTypes.FILETIME();
|
||||
ft.dwLowDateTime = (int)(hFT & 0xFFFFFFFF);
|
||||
ft.dwHighDateTime = (int)(hFT >> 32);
|
||||
return ft;
|
||||
}
|
||||
|
||||
private object GetBlobData()
|
||||
{
|
||||
byte[] blobData = new byte[_int32];
|
||||
|
||||
IntPtr pBlobData = _ptr2;
|
||||
Marshal.Copy(pBlobData, blobData, 0, _int32);
|
||||
|
||||
return blobData;
|
||||
}
|
||||
|
||||
private Array GetVector<T>()
|
||||
{
|
||||
int count = NativeMethods.PropVariantGetElementCount(this);
|
||||
if (count <= 0) { return null; }
|
||||
|
||||
lock (_padlock)
|
||||
{
|
||||
if (_vectorActions == null)
|
||||
{
|
||||
_vectorActions = GenerateVectorActions();
|
||||
}
|
||||
}
|
||||
|
||||
Action<PropVariant, Array, uint> action;
|
||||
if (!_vectorActions.TryGetValue(typeof(T), out action))
|
||||
{
|
||||
throw new InvalidCastException("Cannot be cast to unsupported type.");
|
||||
}
|
||||
|
||||
Array array = new T[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
action(this, array, i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static Array CrackSingleDimSafeArray(IntPtr psa)
|
||||
{
|
||||
uint cDims = NativeMethods.SafeArrayGetDim(psa);
|
||||
if (cDims != 1)
|
||||
throw new ArgumentException("Multi-dimensional SafeArrays not supported.", "psa");
|
||||
|
||||
int lBound = NativeMethods.SafeArrayGetLBound(psa, 1U);
|
||||
int uBound = NativeMethods.SafeArrayGetUBound(psa, 1U);
|
||||
|
||||
int n = uBound - lBound + 1; // uBound is inclusive
|
||||
|
||||
object[] array = new object[n];
|
||||
for (int i = lBound; i <= uBound; ++i)
|
||||
{
|
||||
array[i] = NativeMethods.SafeArrayGetElement(psa, ref i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object, calls the clear function.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
NativeMethods.PropVariantClear(this);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer
|
||||
/// </summary>
|
||||
~PropVariant()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Provides an simple string representation of the contained data and type.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"{0}: {1}", Value, VarType.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a unique key for a Shell Property
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct PropertyKey : IEquatable<PropertyKey>
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private Guid formatId;
|
||||
private Int32 propertyId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// A unique GUID for the property
|
||||
/// </summary>
|
||||
public Guid FormatId
|
||||
{
|
||||
get
|
||||
{
|
||||
return formatId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property identifier (PID)
|
||||
/// </summary>
|
||||
public Int32 PropertyId
|
||||
{
|
||||
get
|
||||
{
|
||||
return propertyId;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Construction
|
||||
|
||||
/// <summary>
|
||||
/// PropertyKey Constructor
|
||||
/// </summary>
|
||||
/// <param name="formatId">A unique GUID for the property</param>
|
||||
/// <param name="propertyId">Property identifier (PID)</param>
|
||||
public PropertyKey(Guid formatId, Int32 propertyId)
|
||||
{
|
||||
this.formatId = formatId;
|
||||
this.propertyId = propertyId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PropertyKey Constructor
|
||||
/// </summary>
|
||||
/// <param name="formatId">A string represenstion of a GUID for the property</param>
|
||||
/// <param name="propertyId">Property identifier (PID)</param>
|
||||
public PropertyKey(string formatId, Int32 propertyId)
|
||||
{
|
||||
this.formatId = new Guid(formatId);
|
||||
this.propertyId = propertyId;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<PropertyKey> Members
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this object is equal to another. This is vital for performance of value types.
|
||||
/// </summary>
|
||||
/// <param name="other">The object to compare against.</param>
|
||||
/// <returns>Equality result.</returns>
|
||||
public bool Equals(PropertyKey other)
|
||||
{
|
||||
return other.Equals((object)this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region equality and hashing
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code of the object. This is vital for performance of value types.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return formatId.GetHashCode() ^ propertyId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this object is equal to another. This is vital for performance of value types.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare against.</param>
|
||||
/// <returns>Equality result.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
if (!(obj is PropertyKey))
|
||||
return false;
|
||||
|
||||
PropertyKey other = (PropertyKey)obj;
|
||||
return other.formatId.Equals(formatId) && (other.propertyId == propertyId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the == (equality) operator.
|
||||
/// </summary>
|
||||
/// <param name="propKey1">First property key to compare.</param>
|
||||
/// <param name="propKey2">Second property key to compare.</param>
|
||||
/// <returns>true if object a equals object b. false otherwise.</returns>
|
||||
public static bool operator ==(PropertyKey propKey1, PropertyKey propKey2)
|
||||
{
|
||||
return propKey1.Equals(propKey2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the != (inequality) operator.
|
||||
/// </summary>
|
||||
/// <param name="propKey1">First property key to compare</param>
|
||||
/// <param name="propKey2">Second property key to compare.</param>
|
||||
/// <returns>true if object a does not equal object b. false otherwise.</returns>
|
||||
public static bool operator !=(PropertyKey propKey1, PropertyKey propKey2)
|
||||
{
|
||||
return !propKey1.Equals(propKey2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override ToString() to provide a user friendly string representation
|
||||
/// </summary>
|
||||
/// <returns>String representing the property key</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"{0}, {1}",
|
||||
formatId.ToString("B"), propertyId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in propsys.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromPropVariantVectorElem([In] PropVariant propvarIn, uint iElem, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromFileTime([In] ref System.Runtime.InteropServices.ComTypes.FILETIME pftIn, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.I4)]
|
||||
public static extern int PropVariantGetElementCount([In] PropVariant propVar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetBooleanElem([In] PropVariant propVar, [In]uint iElem, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetInt16Elem([In] PropVariant propVar, [In] uint iElem, [Out] out short pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetUInt16Elem([In] PropVariant propVar, [In] uint iElem, [Out] out ushort pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetInt32Elem([In] PropVariant propVar, [In] uint iElem, [Out] out int pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetUInt32Elem([In] PropVariant propVar, [In] uint iElem, [Out] out uint pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetInt64Elem([In] PropVariant propVar, [In] uint iElem, [Out] out Int64 pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetUInt64Elem([In] PropVariant propVar, [In] uint iElem, [Out] out UInt64 pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetDoubleElem([In] PropVariant propVar, [In] uint iElem, [Out] out double pnVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetFileTimeElem([In] PropVariant propVar, [In] uint iElem, [Out, MarshalAs(UnmanagedType.Struct)] out System.Runtime.InteropServices.ComTypes.FILETIME pftVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void PropVariantGetStringElem([In] PropVariant propVar, [In] uint iElem, [MarshalAs(UnmanagedType.LPWStr)] ref string ppszVal);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromBooleanVector([In, MarshalAs(UnmanagedType.LPArray)] bool[] prgf, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromInt16Vector([In, Out] Int16[] prgn, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromUInt16Vector([In, Out] UInt16[] prgn, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromInt32Vector([In, Out] Int32[] prgn, uint cElems, [Out] PropVariant propVar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromUInt32Vector([In, Out] UInt32[] prgn, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromInt64Vector([In, Out] Int64[] prgn, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromUInt64Vector([In, Out] UInt64[] prgn, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromDoubleVector([In, Out] double[] prgn, uint cElems, [Out] PropVariant propvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromFileTimeVector([In, Out] System.Runtime.InteropServices.ComTypes.FILETIME[] prgft, uint cElems, [Out] PropVariant ppropvar);
|
||||
|
||||
[DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
public static extern void InitPropVariantFromStringVector([In, Out] string[] prgsz, uint cElems, [Out] PropVariant ppropvar);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Internal.Tools.TeamMate.Foundation.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes PInvoke method wrappers for functions in shell32.dll.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("shell32.dll", EntryPoint = "ExtractIconA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
public static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern uint ExtractIconEx(string szFileName, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, uint nIcons);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
|
||||
|
||||
/// <summary>
|
||||
/// SHGetImageList is not exported correctly in XP. See KB316931
|
||||
/// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q316931
|
||||
/// Apparently (and hopefully) ordinal 727 isn't going to change.
|
||||
/// </summary>
|
||||
[DllImport("shell32.dll", EntryPoint = "#727")]
|
||||
public extern static int SHGetImageList(int iImageList, ref Guid riid, ref IImageList ppv);
|
||||
|
||||
[DllImport("shell32.dll", EntryPoint = "#727")]
|
||||
public extern static int SHGetImageListHandle(int iImageList, ref Guid riid, ref IntPtr handle);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
|
||||
public static extern void SHCreateItemFromParsingName(
|
||||
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
|
||||
[In] IntPtr pbc,
|
||||
[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче