Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support detection of legacy VS products #36

Merged
merged 1 commit into from
Mar 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name this NotFound? My initial reaction when reading this was that it would return E_NOTIMPL

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my initial thought as well, but the method is to indicate its not really implemented while the convention is to return E_NOTFOUND if the property wasn't defined (at least for the original interfaces).

{
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