remove baseapplicationstate and just merge it back in
This commit is contained in:
parent
56850639c5
commit
9b3b9e0109
|
@ -64,13 +64,81 @@ using namespace ::Microsoft::Terminal::Settings::Model;
|
||||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
{
|
{
|
||||||
ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
|
ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
|
||||||
BaseApplicationState{ path } {}
|
_path{ std::move(path) },
|
||||||
|
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
|
||||||
|
{
|
||||||
|
_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The destructor ensures that the last write is flushed to disk before returning.
|
||||||
|
ApplicationState::~ApplicationState()
|
||||||
|
{
|
||||||
|
// This will ensure that we not just cancel the last outstanding timer,
|
||||||
|
// but instead force it to run as soon as possible and wait for it to complete.
|
||||||
|
_throttler.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-read the state.json from disk.
|
||||||
|
void ApplicationState::Reload() const noexcept
|
||||||
|
{
|
||||||
|
_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the state.json path on the disk.
|
||||||
|
winrt::hstring ApplicationState::FilePath() const noexcept
|
||||||
|
{
|
||||||
|
return winrt::hstring{ _path.wstring() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserializes the state.json at _path into this ApplicationState.
|
||||||
|
// * ANY errors during app state will result in the creation of a new empty state.
|
||||||
|
// * ANY errors during runtime will result in changes being partially ignored.
|
||||||
|
void ApplicationState::_read() const noexcept
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use the derived class's implementation of _readFileContents to get the
|
||||||
|
// actual contents of the file.
|
||||||
|
const auto data = _readFileContents().value_or(std::string{});
|
||||||
|
if (data.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string errs;
|
||||||
|
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||||
|
|
||||||
|
Json::Value root;
|
||||||
|
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
||||||
|
{
|
||||||
|
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||||
|
}
|
||||||
|
|
||||||
|
FromJson(root);
|
||||||
|
}
|
||||||
|
CATCH_LOG()
|
||||||
|
|
||||||
|
// Serialized this ApplicationState (in `context`) into the state.json at _path.
|
||||||
|
// * Errors are only logged.
|
||||||
|
// * _state->_writeScheduled is set to false, signaling our
|
||||||
|
// setters that _synchronize() needs to be called again.
|
||||||
|
void ApplicationState::_write() const noexcept
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json::Value root{ this->ToJson() };
|
||||||
|
|
||||||
|
Json::StreamWriterBuilder wbuilder;
|
||||||
|
const auto content = Json::writeString(wbuilder, root);
|
||||||
|
|
||||||
|
// Use the derived class's implementation of _writeFileContents to write the
|
||||||
|
// file to disk.
|
||||||
|
_writeFileContents(content);
|
||||||
|
}
|
||||||
|
CATCH_LOG()
|
||||||
|
|
||||||
// Returns the application-global ApplicationState object.
|
// Returns the application-global ApplicationState object.
|
||||||
Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
|
Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
|
||||||
{
|
{
|
||||||
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / (::Microsoft::Console::Utils::IsElevated() ? elevatedStateFileName : stateFileName));
|
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / (::Microsoft::Console::Utils::IsElevated() ? elevatedStateFileName : stateFileName));
|
||||||
state->Reload();
|
|
||||||
return *state;
|
return *state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ Abstract:
|
||||||
--*/
|
--*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseApplicationState.h"
|
|
||||||
#include "ApplicationState.g.h"
|
#include "ApplicationState.g.h"
|
||||||
#include "WindowLayout.g.h"
|
#include "WindowLayout.g.h"
|
||||||
|
|
||||||
|
@ -40,14 +39,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
friend ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<Model::WindowLayout>;
|
friend ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<Model::WindowLayout>;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApplicationState : public BaseApplicationState, ApplicationStateT<ApplicationState>
|
struct ApplicationState : public ApplicationStateT<ApplicationState>
|
||||||
{
|
{
|
||||||
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
|
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
|
||||||
|
|
||||||
ApplicationState(std::filesystem::path path) noexcept;
|
ApplicationState(std::filesystem::path path) noexcept;
|
||||||
|
~ApplicationState();
|
||||||
|
|
||||||
virtual void FromJson(const Json::Value& root) const noexcept override;
|
// Methods
|
||||||
virtual Json::Value ToJson() const noexcept override;
|
void Reload() const noexcept;
|
||||||
|
void FromJson(const Json::Value& root) const noexcept;
|
||||||
|
Json::Value ToJson() const noexcept;
|
||||||
|
|
||||||
|
// General getters/setters
|
||||||
|
winrt::hstring FilePath() const noexcept;
|
||||||
|
|
||||||
// State getters/setters
|
// State getters/setters
|
||||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \
|
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \
|
||||||
|
@ -64,9 +69,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
#undef MTSM_APPLICATION_STATE_GEN
|
#undef MTSM_APPLICATION_STATE_GEN
|
||||||
};
|
};
|
||||||
til::shared_mutex<state_t> _state;
|
til::shared_mutex<state_t> _state;
|
||||||
|
std::filesystem::path _path;
|
||||||
|
til::throttled_func_trailing<> _throttler;
|
||||||
|
|
||||||
virtual std::optional<std::string> _readFileContents() const override;
|
void _write() const noexcept;
|
||||||
virtual void _writeFileContents(const std::string_view content) const override;
|
void _read() const noexcept;
|
||||||
|
|
||||||
|
std::optional<std::string> _readFileContents() const;
|
||||||
|
void _writeFileContents(const std::string_view content) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
#include "pch.h"
|
|
||||||
#include "BaseApplicationState.h"
|
|
||||||
#include "CascadiaSettings.h"
|
|
||||||
|
|
||||||
#include "JsonUtils.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
|
|
||||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
|
||||||
|
|
||||||
BaseApplicationState::BaseApplicationState(std::filesystem::path path) noexcept :
|
|
||||||
_path{ std::move(path) },
|
|
||||||
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
|
|
||||||
{
|
|
||||||
// DON'T _read() here! _read() will call FromJson, which is virtual, and
|
|
||||||
// needs to be implemented in a derived class. Classes that derive from
|
|
||||||
// BaseApplicationState should make sure to call Reload() after construction
|
|
||||||
// to ensure the data is loaded.
|
|
||||||
}
|
|
||||||
|
|
||||||
// The destructor ensures that the last write is flushed to disk before returning.
|
|
||||||
BaseApplicationState::~BaseApplicationState()
|
|
||||||
{
|
|
||||||
// This will ensure that we not just cancel the last outstanding timer,
|
|
||||||
// but instead force it to run as soon as possible and wait for it to complete.
|
|
||||||
_throttler.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-read the state.json from disk.
|
|
||||||
void BaseApplicationState::Reload() const noexcept
|
|
||||||
{
|
|
||||||
_read();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the state.json path on the disk.
|
|
||||||
winrt::hstring BaseApplicationState::FilePath() const noexcept
|
|
||||||
{
|
|
||||||
return winrt::hstring{ _path.wstring() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserializes the state.json at _path into this ApplicationState.
|
|
||||||
// * ANY errors during app state will result in the creation of a new empty state.
|
|
||||||
// * ANY errors during runtime will result in changes being partially ignored.
|
|
||||||
void BaseApplicationState::_read() const noexcept
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Use the derived class's implementation of _readFileContents to get the
|
|
||||||
// actual contents of the file.
|
|
||||||
const auto data = _readFileContents().value_or(std::string{});
|
|
||||||
if (data.empty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string errs;
|
|
||||||
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
|
||||||
|
|
||||||
Json::Value root;
|
|
||||||
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
|
||||||
{
|
|
||||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
|
||||||
}
|
|
||||||
|
|
||||||
FromJson(root);
|
|
||||||
}
|
|
||||||
CATCH_LOG()
|
|
||||||
|
|
||||||
// Serialized this ApplicationState (in `context`) into the state.json at _path.
|
|
||||||
// * Errors are only logged.
|
|
||||||
// * _state->_writeScheduled is set to false, signaling our
|
|
||||||
// setters that _synchronize() needs to be called again.
|
|
||||||
void BaseApplicationState::_write() const noexcept
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Json::Value root{ this->ToJson() };
|
|
||||||
|
|
||||||
Json::StreamWriterBuilder wbuilder;
|
|
||||||
const auto content = Json::writeString(wbuilder, root);
|
|
||||||
|
|
||||||
// Use the derived class's implementation of _writeFileContents to write the
|
|
||||||
// file to disk.
|
|
||||||
_writeFileContents(content);
|
|
||||||
}
|
|
||||||
CATCH_LOG()
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*++
|
|
||||||
Copyright (c) Microsoft Corporation
|
|
||||||
Licensed under the MIT license.
|
|
||||||
|
|
||||||
Module Name:
|
|
||||||
- BaseApplicationState.h
|
|
||||||
|
|
||||||
Abstract:
|
|
||||||
- This is the common core for both ApplicationState and ElevatedState. This
|
|
||||||
handles more of the mechanics of serializing these structures to/from json, as
|
|
||||||
well as the mechanics of loading the file.
|
|
||||||
--*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct BaseApplicationState
|
|
||||||
{
|
|
||||||
BaseApplicationState(std::filesystem::path path) noexcept;
|
|
||||||
~BaseApplicationState();
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
void Reload() const noexcept;
|
|
||||||
|
|
||||||
// General getters/setters
|
|
||||||
winrt::hstring FilePath() const noexcept;
|
|
||||||
|
|
||||||
virtual void FromJson(const Json::Value& root) const noexcept = 0;
|
|
||||||
virtual Json::Value ToJson() const noexcept = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual std::optional<std::string> _readFileContents() const = 0;
|
|
||||||
virtual void _writeFileContents(const std::string_view content) const = 0;
|
|
||||||
|
|
||||||
void _write() const noexcept;
|
|
||||||
void _read() const noexcept;
|
|
||||||
|
|
||||||
std::filesystem::path _path;
|
|
||||||
til::throttled_func_trailing<> _throttler;
|
|
||||||
};
|
|
|
@ -1,95 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
#include "pch.h"
|
|
||||||
#include "ElevatedState.h"
|
|
||||||
#include "CascadiaSettings.h"
|
|
||||||
#include "ElevatedState.g.cpp"
|
|
||||||
|
|
||||||
#include "JsonUtils.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
|
|
||||||
#include <aclapi.h>
|
|
||||||
|
|
||||||
constexpr std::wstring_view stateFileName{ L"elevated-state.json" };
|
|
||||||
|
|
||||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
|
||||||
|
|
||||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|
||||||
{
|
|
||||||
ElevatedState::ElevatedState(std::filesystem::path path) noexcept :
|
|
||||||
BaseApplicationState{ path } {}
|
|
||||||
|
|
||||||
// Returns the application-global ElevatedState object.
|
|
||||||
Microsoft::Terminal::Settings::Model::ElevatedState ElevatedState::SharedInstance()
|
|
||||||
{
|
|
||||||
static auto state = winrt::make_self<ElevatedState>(GetBaseSettingsPath() / stateFileName);
|
|
||||||
state->Reload();
|
|
||||||
return *state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElevatedState::FromJson(const Json::Value& root) const noexcept
|
|
||||||
{
|
|
||||||
auto state = _state.lock();
|
|
||||||
// GetValueForKey() comes in two variants:
|
|
||||||
// * take a std::optional<T> reference
|
|
||||||
// * return std::optional<T> by value
|
|
||||||
// At the time of writing the former version skips missing fields in the json,
|
|
||||||
// but we want to explicitly clear state fields that were removed from state.json.
|
|
||||||
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
|
|
||||||
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
|
|
||||||
#undef MTSM_ELEVATED_STATE_GEN
|
|
||||||
}
|
|
||||||
Json::Value ElevatedState::ToJson() const noexcept
|
|
||||||
{
|
|
||||||
Json::Value root{ Json::objectValue };
|
|
||||||
|
|
||||||
{
|
|
||||||
auto state = _state.lock_shared();
|
|
||||||
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name);
|
|
||||||
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
|
|
||||||
#undef MTSM_ELEVATED_STATE_GEN
|
|
||||||
}
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate all getter/setters
|
|
||||||
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) \
|
|
||||||
type ElevatedState::name() const noexcept \
|
|
||||||
{ \
|
|
||||||
const auto state = _state.lock_shared(); \
|
|
||||||
const auto& value = state->name; \
|
|
||||||
return value ? *value : type{ __VA_ARGS__ }; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
void ElevatedState::name(const type& value) noexcept \
|
|
||||||
{ \
|
|
||||||
{ \
|
|
||||||
auto state = _state.lock(); \
|
|
||||||
state->name.emplace(value); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
_throttler(); \
|
|
||||||
}
|
|
||||||
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
|
|
||||||
#undef MTSM_ELEVATED_STATE_GEN
|
|
||||||
|
|
||||||
void ElevatedState::_writeFileContents(const std::string_view content) const
|
|
||||||
{
|
|
||||||
// DON'T use WriteUTF8FileAtomic, which will write to a temporary file
|
|
||||||
// then rename that file to the final filename. That actually lets us
|
|
||||||
// overwrite the elevate file's contents even when unelevated, because
|
|
||||||
// we're effectively deleting the original file, then renaming a
|
|
||||||
// different file in it's place.
|
|
||||||
//
|
|
||||||
// We're not worried about someone else doing that though, if they do
|
|
||||||
// that with the wrong permissions, then we'll just ignore the file and
|
|
||||||
// start over.
|
|
||||||
WriteUTF8File(_path, content, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> ElevatedState::_readFileContents() const
|
|
||||||
{
|
|
||||||
return ReadUTF8FileIfExists(_path, true);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*++
|
|
||||||
Copyright (c) Microsoft Corporation
|
|
||||||
Licensed under the MIT license.
|
|
||||||
|
|
||||||
Module Name:
|
|
||||||
- ElevatedState.h
|
|
||||||
|
|
||||||
Abstract:
|
|
||||||
- If the CascadiaSettings class were AppData, then this class would be LocalAppData.
|
|
||||||
Put anything in here that you wouldn't want to be stored next to user-editable settings.
|
|
||||||
- Modify ElevatedState.idl and MTSM_ELEVATED_STATE_FIELDS to add new fields.
|
|
||||||
--*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseApplicationState.h"
|
|
||||||
#include "ElevatedState.g.h"
|
|
||||||
#include <inc/cppwinrt_utils.h>
|
|
||||||
|
|
||||||
// This macro generates all getters and setters for ElevatedState.
|
|
||||||
// It provides X with the following arguments:
|
|
||||||
// (type, function name, JSON key, ...variadic construction arguments)
|
|
||||||
#define MTSM_ELEVATED_STATE_FIELDS(X) \
|
|
||||||
X(Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
|
|
||||||
|
|
||||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|
||||||
{
|
|
||||||
struct ElevatedState : ElevatedStateT<ElevatedState>, public BaseApplicationState
|
|
||||||
{
|
|
||||||
static Microsoft::Terminal::Settings::Model::ElevatedState SharedInstance();
|
|
||||||
|
|
||||||
ElevatedState(std::filesystem::path path) noexcept;
|
|
||||||
|
|
||||||
void FromJson(const Json::Value& root) const noexcept override;
|
|
||||||
Json::Value ToJson() const noexcept override;
|
|
||||||
|
|
||||||
// State getters/setters
|
|
||||||
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) \
|
|
||||||
type name() const noexcept; \
|
|
||||||
void name(const type& value) noexcept;
|
|
||||||
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
|
|
||||||
#undef MTSM_ELEVATED_STATE_GEN
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct state_t
|
|
||||||
{
|
|
||||||
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
|
|
||||||
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
|
|
||||||
#undef MTSM_ELEVATED_STATE_GEN
|
|
||||||
};
|
|
||||||
til::shared_mutex<state_t> _state;
|
|
||||||
|
|
||||||
virtual std::optional<std::string> _readFileContents() const override;
|
|
||||||
virtual void _writeFileContents(const std::string_view content) const override;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
|
||||||
{
|
|
||||||
BASIC_FACTORY(ElevatedState);
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Microsoft.Terminal.Settings.Model
|
|
||||||
{
|
|
||||||
[default_interface] runtimeclass ElevatedState {
|
|
||||||
static ElevatedState SharedInstance();
|
|
||||||
|
|
||||||
void Reload();
|
|
||||||
|
|
||||||
String FilePath { get; };
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,7 +21,6 @@
|
||||||
<ClInclude Include="IconPathConverter.h">
|
<ClInclude Include="IconPathConverter.h">
|
||||||
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="BaseApplicationState.h" />
|
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
<ClInclude Include="ActionArgs.h">
|
<ClInclude Include="ActionArgs.h">
|
||||||
<DependentUpon>ActionArgs.idl</DependentUpon>
|
<DependentUpon>ActionArgs.idl</DependentUpon>
|
||||||
|
@ -93,7 +92,6 @@
|
||||||
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="init.cpp" />
|
<ClCompile Include="init.cpp" />
|
||||||
<ClCompile Include="BaseApplicationState.cpp" />
|
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -267,4 +265,4 @@
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||||
<Import Project="..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
|
<Import Project="..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in a new issue