Skip to content

Commit

Permalink
Support detection of legacy VS products
Browse files Browse the repository at this point in the history
Resolves issues #24
  • Loading branch information
heaths committed Mar 10, 2017
1 parent c2445c9 commit 665c795
Show file tree
Hide file tree
Showing 23 changed files with 787 additions and 4 deletions.
54 changes: 54 additions & 0 deletions docker/Tests/legacy.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (C) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE.txt in the project root for license information.

Describe 'vswhere -legacy' {
BeforeEach {
# Make sure localized values are returned consistently across machines.
$enu = [System.Globalization.CultureInfo]::GetCultureInfo('en-US')

[System.Globalization.CultureInfo]::CurrentCulture = $enu
[System.Globalization.CultureInfo]::CurrentUICulture = $enu
}

AfterEach {
# Make sure the registry is cleaned up.
Remove-Item HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Force -ErrorAction 'SilentlyContinue'
}

Context 'no legacy' {
It 'returns 2 instances' {
$instances = C:\bin\vswhere.exe -legacy -format json | ConvertFrom-Json
$instances.Count | Should Be 2
}
}

Context 'has legacy' {
BeforeEach {
New-Item HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Force | ForEach-Object {
foreach ($version in '10.0', '14.0') {
$_ | New-ItemProperty -Name $version -Value "C:\VisualStudio\$version" -Force
}
}
}

It 'returns 4 instances' {
$instances = C:\bin\vswhere.exe -legacy -format json | ConvertFrom-Json
$instances.Count | Should Be 4
}

It '-version "10.0" returns 4 instances' {
$instances = C:\bin\vswhere.exe -legacy -version '10.0' -format json | ConvertFrom-Json
$instances.Count | Should Be 4
}

It '-version "14.0" returns 3 instances' {
$instances = C:\bin\vswhere.exe -legacy -version '14.0' -format json | ConvertFrom-Json
$instances.Count | Should Be 3
}

It '-version "[10.0,15.0)" returns 2 instances' {
$instances = C:\bin\vswhere.exe -legacy -version '[10.0,15.0)' -format json | ConvertFrom-Json
$instances.Count | Should Be 2
}
}
}
13 changes: 13 additions & 0 deletions src/vswhere.lib/CommandArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ void CommandArgs::Parse(_In_ vector<CommandParser::Token> args)
{
m_latest = true;
}
else if (ArgumentEquals(arg.Value, L"legacy"))
{
m_legacy = true;
}
else if (ArgumentEquals(arg.Value, L"format"))
{
auto format = ParseArgument(it, args.end(), arg);
Expand Down Expand Up @@ -121,6 +125,15 @@ void CommandArgs::Parse(_In_ vector<CommandParser::Token> args)
m_products.clear();
}

if (m_legacy)
{
if (!m_products.empty() || !m_requires.empty())
{
auto message = ResourceManager::GetString(IDS_E_LEGACY);
throw win32_error(ERROR_INVALID_PARAMETER, message);
}
}

if (!m_property.empty() && m_format.empty())
{
m_format = L"value";
Expand Down
8 changes: 8 additions & 0 deletions src/vswhere.lib/CommandArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class CommandArgs
m_all(false),
m_productsAll(false),
m_latest(false),
m_legacy(false),
m_nologo(false),
m_help(false)
{
Expand All @@ -27,6 +28,7 @@ class CommandArgs
m_requires(obj.m_requires),
m_version(obj.m_version),
m_latest(obj.m_latest),
m_legacy(obj.m_legacy),
m_format(obj.m_format),
m_property(obj.m_property),
m_nologo(obj.m_nologo),
Expand Down Expand Up @@ -69,6 +71,11 @@ class CommandArgs
return m_latest;
}

const bool get_Legacy() const noexcept
{
return m_legacy;
}

const std::wstring& get_Format() const noexcept
{
if (m_format.empty())
Expand Down Expand Up @@ -112,6 +119,7 @@ class CommandArgs
std::vector<std::wstring> m_requires;
std::wstring m_version;
bool m_latest;
bool m_legacy;
std::wstring m_format;
std::wstring m_property;
bool m_nologo;
Expand Down
13 changes: 13 additions & 0 deletions src/vswhere.lib/ILegacyProvider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// <copyright file="ILegacyProvider.h" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>

#pragma once

class ILegacyProvider
{
public:
virtual bool HasLegacyInstances() const = 0;
virtual bool TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const = 0;
};
25 changes: 24 additions & 1 deletion src/vswhere.lib/InstanceSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
using namespace std;
using std::placeholders::_1;

InstanceSelector::InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper) :
InstanceSelector::InstanceSelector(_In_ const CommandArgs& args, _In_ ILegacyProvider& provider, _In_opt_ ISetupHelper* pHelper) :
m_args(args),
m_provider(provider),
m_ullMinimumVersion(0),
m_ullMaximumVersion(0)
{
Expand Down Expand Up @@ -114,6 +115,28 @@ vector<ISetupInstancePtr> InstanceSelector::Select(_In_opt_ IEnumSetupInstances*
} while (SUCCEEDED(hr) && celtFetched);
}

if (m_args.get_Legacy() && m_provider.HasLegacyInstances())
{
vector<LPCWSTR> versions =
{
L"14.0",
L"12.0",
L"11.0",
L"10.0",
};

for (auto version : versions)
{
ISetupInstancePtr instance;

// Currently only support version checks.
if (m_provider.TryGetLegacyInstance(version, &instance) && IsVersionMatch(instance))
{
instances.push_back(instance);
}
}
}

if (m_args.get_Latest() && 1 < instances.size())
{
sort(instances.begin(), instances.end(), [&](const ISetupInstancePtr& a, const ISetupInstancePtr& b) -> bool
Expand Down
9 changes: 8 additions & 1 deletion src/vswhere.lib/InstanceSelector.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
class InstanceSelector
{
public:
InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper = NULL);
InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper = NULL) :
InstanceSelector(args, LegacyProvider::Instance, pHelper)
{
}

InstanceSelector(_In_ const CommandArgs& args, _In_ ILegacyProvider& provider, _In_opt_ ISetupHelper* pHelper = NULL);
InstanceSelector(_In_ const InstanceSelector& obj) :
m_args(obj.m_args),
m_provider(obj.m_provider),
m_helper(obj.m_helper),
m_ullMinimumVersion(obj.m_ullMinimumVersion),
m_ullMaximumVersion(obj.m_ullMaximumVersion)
Expand All @@ -32,6 +38,7 @@ class InstanceSelector
}

const CommandArgs& m_args;
const ILegacyProvider& m_provider;
ULONGLONG m_ullMinimumVersion;
ULONGLONG m_ullMaximumVersion;
ISetupHelperPtr m_helper;
Expand Down
130 changes: 130 additions & 0 deletions src/vswhere.lib/LegacyInstance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// <copyright file="LegacyInstance.cpp" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>

#include "stdafx.h"

using namespace std;

template <class T>
inline HRESULT NotImplemented(_In_ T* p)
{
if (!p)
{
return E_POINTER;
}

*p = NULL;
return E_NOTFOUND;
}

template<>
inline HRESULT NotImplemented(_In_ LPFILETIME pft)
{
if (!pft)
{
return E_POINTER;
}

::SecureZeroMemory(pft, sizeof(FILETIME));
return E_NOTFOUND;
}

STDMETHODIMP LegacyInstance::GetInstanceId(
_Out_ BSTR* pbstrInstanceId
)
{
if (!pbstrInstanceId)
{
return E_POINTER;
}

*pbstrInstanceId = NULL;

// Let exceptions throw since we're not a "true" COM object.
wstring instanceId(L"VisualStudio.");
instanceId += m_version;

*pbstrInstanceId = ::SysAllocString(instanceId.c_str());
if (!pbstrInstanceId)
{
return E_OUTOFMEMORY;
}

return S_OK;
}

STDMETHODIMP LegacyInstance::GetInstallDate(
_Out_ LPFILETIME pInstallDate
)
{
return NotImplemented(pInstallDate);
}

STDMETHODIMP LegacyInstance::GetInstallationName(
_Out_ BSTR* pbstrInstallationName
)
{
return NotImplemented(pbstrInstallationName);
}

STDMETHODIMP LegacyInstance::GetInstallationPath(
_Out_ BSTR* pbstrInstallationPath
)
{
if (!pbstrInstallationPath)
{
return E_POINTER;
}

*pbstrInstallationPath = ::SysAllocString(m_path.c_str());
if (!pbstrInstallationPath)
{
return E_OUTOFMEMORY;
}

return S_OK;
}

STDMETHODIMP LegacyInstance::GetInstallationVersion(
_Out_ BSTR* pbstrInstallationVersion
)
{
if (!pbstrInstallationVersion)
{
return E_POINTER;
}

*pbstrInstallationVersion = ::SysAllocString(m_version.c_str());
if (!pbstrInstallationVersion)
{
return E_OUTOFMEMORY;
}

return S_OK;
}

STDMETHODIMP LegacyInstance::GetDisplayName(
_In_ LCID lcid,
_Out_ BSTR* pbstrDisplayName
)
{
return NotImplemented(pbstrDisplayName);
}

STDMETHODIMP LegacyInstance::GetDescription(
_In_ LCID lcid,
_Out_ BSTR* pbstrDescription
)
{
return NotImplemented(pbstrDescription);
}

STDMETHODIMP LegacyInstance::ResolvePath(
_In_opt_z_ LPCOLESTR pwszRelativePath,
_Out_ BSTR* pbstrAbsolutePath
)
{
return NotImplemented(pbstrAbsolutePath);
}
Loading

0 comments on commit 665c795

Please sign in to comment.