Skip to content

PublishSingleFile dll and resources.pri redirection support #4067

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

Merged
merged 4 commits into from
Jan 16, 2024
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
12 changes: 9 additions & 3 deletions dev/MRTCore/mrt/Core/src/MRM.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include <Windows.h>
Expand Down Expand Up @@ -989,6 +989,12 @@ STDAPI MrmGetFilePathFromName(_In_opt_ PCWSTR filename, _Outptr_ PWSTR* filePath
PCWSTR filenameToUse = filename;
if (filename == nullptr || *filename == L'\0')
{
wil::unique_cotaskmem_string baseDir;
if(SUCCEEDED(wil::TryGetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", baseDir)))
{
path.swap(baseDir);
RETURN_IF_FAILED(StringCchLengthW(path.get(), STRSAFE_MAX_CCH, &bufferCount));
}
filenameToUse = c_defaultPriFilename;
}

Expand All @@ -997,8 +1003,8 @@ STDAPI MrmGetFilePathFromName(_In_opt_ PCWSTR filename, _Outptr_ PWSTR* filePath
// - search under exe path
// - if not exist, search parent path
// If filename is not provided:
// - search under exe path with default name (resources.pri)
// - if not exist, search under same path with [modulename].pri
// - search under exe path (or baseDir) with default name (resources.pri)
// - if not exist, search under exe path (or baseDir) with [modulename].pri
for (int i = 0; i < 2; i++)
{
size_t lengthOfName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// DO NOT MODIFY. Changes to this file may cause incorrect behavior and will be lost on updates.
// </auto-generated>

using System;
using System.Reflection;
using System.Runtime.InteropServices;

Expand All @@ -22,6 +23,9 @@ class AutoInitialize
[global::System.Runtime.CompilerServices.ModuleInitializer]
internal static void AccessWindowsAppSDK()
{
// Set base directory env var for PublishSingleFile support (referenced by SxS redirection)
Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", AppContext.BaseDirectory);

// No error handling needed as the target function does nothing (just {return S_OK}).
// It's the act of calling the function causing the DllImport to load the DLL that
// matters. This provides the moral equivalent of a native DLL's Import Address
Expand Down
99 changes: 73 additions & 26 deletions dev/UndockedRegFreeWinRT/catalog.cpp
Copy link
Contributor

Choose a reason for hiding this comment

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

These changes to UndockedRegFreeWinRT -- do we know that the Windows implementation behaves the same way? (on all supported platforms)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I've tested with SystemRegFreeWinRT as well (disabling UndockedRegFreeWinRT)

Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include <pch.h>

#include "catalog.h"
#include "Microsoft.Utf8.h"
#include "TypeResolution.h"

#include <activation.h>
Expand Down Expand Up @@ -125,11 +126,26 @@ HRESULT LoadFromEmbeddedManifest(PCWSTR path)

HRESULT WinRTLoadComponentFromFilePath(PCWSTR manifestPath)
{
ComPtr<IStream> fileStream;
RETURN_IF_FAILED(SHCreateStreamOnFileEx(manifestPath, STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &fileStream));
try
{
return ParseRootManifestFromXmlReaderInput(fileStream.Get());
wil::unique_hfile file{ ::CreateFileW(manifestPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
RETURN_HR_IF(HRESULT_FROM_WIN32(GetLastError()), !file);

LARGE_INTEGER fileSize{};
RETURN_IF_WIN32_BOOL_FALSE(::GetFileSizeEx(file.get(), &fileSize));
const auto dataSize{ fileSize.QuadPart };
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), dataSize > INT32_MAX);
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_SXS_XML_E_UNEXPECTEDEOF), dataSize == 0);

const auto bufferSize{ static_cast<DWORD>(dataSize) + 1 };
std::unique_ptr<char[]> buffer{ std::make_unique<char[]>(bufferSize) };

DWORD bytesRead{};
RETURN_IF_WIN32_BOOL_FALSE(::ReadFile(file.get(), buffer.get(), bufferSize, &bytesRead, nullptr));
file.reset();
buffer[bytesRead] = '\0';

return WinRTLoadComponentFromString(std::string_view(buffer.get(), bytesRead));
}
catch(...)
{
Expand All @@ -140,16 +156,23 @@ HRESULT WinRTLoadComponentFromFilePath(PCWSTR manifestPath)

HRESULT WinRTLoadComponentFromString(std::string_view xmlStringValue)
{
ComPtr<IStream> xmlStream;
auto xmlStringValueData{ xmlStringValue.data() };
auto xmlStringValueDataLength{ strlen(xmlStringValueData) };
auto xmlStringValueDataSize{ xmlStringValueDataLength * sizeof(*xmlStringValueData) };
xmlStream.Attach(SHCreateMemStream(reinterpret_cast<const BYTE*>(xmlStringValue.data()), static_cast<UINT>(xmlStringValueDataSize)));
RETURN_HR_IF_NULL(E_OUTOFMEMORY, xmlStream);
ComPtr<IXmlReaderInput> xmlReaderInput;
RETURN_IF_FAILED(CreateXmlReaderInputWithEncodingName(xmlStream.Get(), nullptr, L"utf-8", FALSE, nullptr, &xmlReaderInput));
try
{
auto wideXmlString = ::Microsoft::Utf8::ToUtf16(xmlStringValue.data());

// Expand any env vars, such as %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% in asmv3:file.loadFrom
auto expandedSize = ExpandEnvironmentStringsW(wideXmlString.data(), nullptr, 0);
RETURN_HR_IF(HRESULT_FROM_WIN32(GetLastError()), expandedSize == 0);
std::unique_ptr<WCHAR[]> expanded{ std::make_unique<WCHAR[]>(expandedSize) };
expandedSize = ExpandEnvironmentStringsW(wideXmlString.data(), expanded.get(), expandedSize);
RETURN_HR_IF(HRESULT_FROM_WIN32(GetLastError()), expandedSize == 0);

ComPtr<IStream> xmlStream;
xmlStream.Attach(SHCreateMemStream(reinterpret_cast<const BYTE*>(expanded.get()), static_cast<UINT>(expandedSize * sizeof(WCHAR))));
RETURN_HR_IF_NULL(E_OUTOFMEMORY, xmlStream);
ComPtr<IXmlReaderInput> xmlReaderInput;
RETURN_IF_FAILED(CreateXmlReaderInputWithEncodingName(xmlStream.Get(), nullptr, L"UCS-2", FALSE, nullptr, &xmlReaderInput));

return ParseRootManifestFromXmlReaderInput(xmlReaderInput.Get());
}
catch (...)
Expand Down Expand Up @@ -185,16 +208,47 @@ HRESULT ParseRootManifestFromXmlReaderInput(IUnknown* input)

HRESULT ParseFileTag(IXmlReader* xmlReader)
{
HRESULT hr = S_OK;
XmlNodeType nodeType;
PCWSTR localName = nullptr;
PCWSTR value = nullptr;
hr = xmlReader->MoveToAttributeByName(L"name", nullptr);
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR), hr != S_OK);
RETURN_IF_FAILED(xmlReader->GetValue(&value, nullptr));
std::wstring fileName{ !value ? L"" : value };
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR), fileName.empty());
std::wstring fileName;
auto locale = _create_locale(LC_ALL, "C");
HRESULT hr = xmlReader->MoveToFirstAttribute();
Copy link
Contributor

Choose a reason for hiding this comment

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

Presumably, since we change the way we parse things here, we could possibly make a 1.4 app that was working fine fail at runtime when built with 1.5, correct? I'm not saying I see such a problem, I'm just trying to make sure I understand the stakes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, in theory

if (S_FALSE == hr)
{
return HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR);
}
else
{
while (TRUE)
{
if (FAILED_LOG(xmlReader->GetLocalName(&localName, NULL)))
{
return HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR);
}
if (FAILED_LOG(xmlReader->GetValue(&value, NULL)))
{
return HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR);
}
if (localName != nullptr)
{
if ((_wcsicmp_l(L"name", localName, locale) == 0) && fileName.empty())
{
fileName = value;
}
else if (_wcsicmp_l(L"loadFrom", localName, locale) == 0)
{
// override bare filename with fully qualified loadFrom path if supplied
fileName = value;
}
}
if (xmlReader->MoveToNextAttribute() != S_OK)
{
break;
}
}
}
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR), fileName.empty());
XmlNodeType nodeType;
while (S_OK == xmlReader->Read(&nodeType))
{
if (nodeType == XmlNodeType_Element)
Expand All @@ -220,10 +274,8 @@ HRESULT ParseActivatableClassTag(IXmlReader* xmlReader, PCWSTR fileName)
auto this_component = make_shared<component>();
this_component->module_name = fileName;
HRESULT hr = xmlReader->MoveToFirstAttribute();
// Using this pattern intead of calling multiple MoveToAttributeByName improves performance
const WCHAR* activatableClass = nullptr;
const WCHAR* threadingModel = nullptr;
const WCHAR* xmlns = nullptr;
if (S_FALSE == hr)
{
return HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR);
Expand Down Expand Up @@ -252,10 +304,6 @@ HRESULT ParseActivatableClassTag(IXmlReader* xmlReader, PCWSTR fileName)
{
activatableClass = pwszValue;
}
else if (_wcsicmp_l(L"xmlns", pwszLocalName, locale) == 0)
{
xmlns = pwszValue;
}
}
if (xmlReader->MoveToNextAttribute() != S_OK)
{
Expand Down Expand Up @@ -288,7 +336,6 @@ HRESULT ParseActivatableClassTag(IXmlReader* xmlReader, PCWSTR fileName)
{
return HRESULT_FROM_WIN32(ERROR_SXS_MANIFEST_PARSE_ERROR);
}
this_component->xmlns = xmlns; // Should we care if this value is blank or missing?
// Check for duplicate activatable classes
auto component_iter = g_types.find(activatableClass);
if (component_iter != g_types.end())
Expand Down