Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
81 changes: 47 additions & 34 deletions src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,24 @@
#include <unordered_map>
#include <codecvt>
#include <locale>
#include <string>

#include "catalog.h"

using namespace std;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;

// TODO: Components won't respect COM lifetime. workaround to get them in the COM list?

extern "C"
{
typedef HRESULT(__stdcall* activation_factory_type)(HSTRING, IActivationFactory**);
typedef HRESULT (__stdcall * activation_factory_type)(HSTRING, IActivationFactory**) ;
}

// Intentionally no class factory cache here. That would be excessive since
// other layers already cache.
struct component
{
wstring module_path;
wstring module_name;
wstring xmlns;
HMODULE handle = nullptr;
activation_factory_type get_activation_factory;
Expand All @@ -42,15 +41,15 @@ struct component
{
if (handle == nullptr)
{
handle = LoadLibraryW(module_path.c_str());
handle = LoadLibraryExW(module_name.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (handle == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());;
return HRESULT_FROM_WIN32(GetLastError());
}
this->get_activation_factory = (activation_factory_type)GetProcAddress(handle, "DllGetActivationFactory");
if (this->get_activation_factory == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());;
return HRESULT_FROM_WIN32(GetLastError());
}
}
return (handle != nullptr && this->get_activation_factory != nullptr) ? S_OK : E_FAIL;
Expand Down Expand Up @@ -83,7 +82,9 @@ HRESULT WinRTLoadComponent(PCWSTR manifest_path)
auto locale = _create_locale(LC_ALL, "C");
auto this_component = make_shared<component>();

RETURN_IF_FAILED(SHCreateStreamOnFileEx(manifest_path, STGM_READ, FILE_ATTRIBUTE_NORMAL, false, nullptr, &fileStream));
wstring fullPath = exeFilePath + L"\\" + manifest_path;

RETURN_IF_FAILED(SHCreateStreamOnFileEx(fullPath.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, false, nullptr, &fileStream));
RETURN_IF_FAILED(CreateXmlReader(__uuidof(IXmlReader), (void**)&xmlReader, nullptr));
RETURN_IF_FAILED(xmlReader->SetInput(fileStream.Get()));
const WCHAR* fileName;
Expand All @@ -96,7 +97,7 @@ HRESULT WinRTLoadComponent(PCWSTR manifest_path)
{
RETURN_IF_FAILED(xmlReader->MoveToAttributeByName(L"name", nullptr));
RETURN_IF_FAILED(xmlReader->GetValue(&fileName, nullptr));
this_component->module_path = fileName;
this_component->module_name = fileName;
}
else if (_wcsicmp_l(localName, L"activatableClass", locale) == 0)
{
Expand All @@ -112,7 +113,7 @@ HRESULT WinRTLoadComponent(PCWSTR manifest_path)
{
this_component->threading_model = ABI::Windows::Foundation::ThreadingType::ThreadingType_MTA;
}
else if (_wcsicmp_l(L"both", threadingModel, locale) == 0)
else if(_wcsicmp_l(L"both", threadingModel, locale) == 0)
{
this_component->threading_model = ABI::Windows::Foundation::ThreadingType::ThreadingType_BOTH;
}
Expand Down Expand Up @@ -161,7 +162,7 @@ HRESULT WinRTGetActivationFactory(
return component_iter->second->GetActivationFactory(activatableClassId, iid, factory);
}
return REGDB_E_CLASSNOTREG;

}

HRESULT WinRTGetMetadataFile(
Expand All @@ -172,32 +173,44 @@ HRESULT WinRTGetMetadataFile(
mdTypeDef* typeDefToken
)
{
// documentation:
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files
// https://docs.microsoft.com/en-us/windows/win32/api/rometadataresolution/nf-rometadataresolution-rogetmetadatafile
if (metaDataDispenser != nullptr ||
metaDataImport != nullptr ||
typeDefToken != nullptr)
{
return E_NOTIMPL;
}

// algorithm: search our metadata, find best match if any.
// search system metedata.
// Take longest string match excluding extension (which should be winmd, but not clear it's required to be).
HSTRING substring;
if (SUCCEEDED(WindowsSubstringWithSpecifiedLength(name, 0, 7, &substring)))
{
int result;
if (SUCCEEDED(WindowsCompareStringOrdinal(substring, HStringReference(L"Windows").Get(), &result)) && (result == 0))
{
// Let original RoGetMetadataFile handle Windows namespace
return REGDB_E_CLASSNOTREG;
}
}

// TODO: Once implented, change the detour hook. no need to call the system API twice.
DWORD metaDataFilePathsCount = 0;
HSTRING* metaDataFilePaths;
RETURN_IF_FAILED(RoResolveNamespace(name, HStringReference(exeFilePath.c_str()).Get(),
0, nullptr,
&metaDataFilePathsCount, &metaDataFilePaths,
0, nullptr));

return REGDB_E_CLASSNOTREG;
}

HRESULT WinRTResolveNamespaceDetour(
const HSTRING name,
const HSTRING windowsMetaDataDir,
const DWORD packageGraphDirsCount,
const HSTRING* packageGraphDirs,
DWORD* metaDataFilePathsCount,
HSTRING** metaDataFilePaths,
DWORD* subNamespacesCount,
HSTRING** subNamespaces)
DWORD bestMatch = 0;
int bestMatchLength = 0;
for (DWORD i = 0; i < metaDataFilePathsCount; i++)
{
int length = WindowsGetStringLen(metaDataFilePaths[i]);
if (length > bestMatchLength)
{
bestMatch = i;
bestMatchLength = length;
}
}

{
// documentation:
// https://docs.microsoft.com/en-us/windows/win32/api/rometadataresolution/nf-rometadataresolution-roresolvenamespace
*metaDataFilePath = metaDataFilePaths[bestMatch];

return REGDB_E_CLASSNOTREG;
return S_OK;
}
12 changes: 3 additions & 9 deletions src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <activationregistration.h>
#include <string>
#include <cor.h>

#define RETURN_IF_FAILED( exp ) { HRESULT _hr_ = (exp); if( FAILED( _hr_ ) ) return _hr_; }

Expand All @@ -20,12 +22,4 @@ HRESULT WinRTGetMetadataFile(
IMetaDataImport2** metaDataImport,
mdTypeDef* typeDefToken);

HRESULT WinRTResolveNamespaceDetour(
const HSTRING name,
const HSTRING windowsMetaDataDir,
const DWORD packageGraphDirsCount,
const HSTRING* packageGraphDirs,
DWORD* metaDataFilePathsCount,
HSTRING** metaDataFilePaths,
DWORD* subNamespacesCount,
HSTRING** subNamespaces);
extern std::wstring exeFilePath;
109 changes: 76 additions & 33 deletions src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/dllmain.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,62 @@
#include <Windows.h>
#include <synchapi.h>
#include <roapi.h>
#include <rometadataresolution.h>
#include <windows.foundation.h>
#include <activationregistration.h>
#include <combaseapi.h>
#include <wrl.h>
#include <ctxtcall.h>
#include <Processthreadsapi.h>

#include "../detours/detours.h"
#include "catalog.h"
#include "extwinrt.h"
#include <activation.h>
#include <hstring.h>

// Ensure that metadata resolution functions are imported so they can be detoured
extern "C"
{
__declspec(dllimport) HRESULT WINAPI RoGetMetaDataFile(
const HSTRING name,
IMetaDataDispenserEx* metaDataDispenser,
HSTRING* metaDataFilePath,
IMetaDataImport2** metaDataImport,
mdTypeDef* typeDefToken);

__declspec(dllimport) HRESULT WINAPI RoParseTypeName(
HSTRING typeName,
DWORD* partsCount,
HSTRING** typeNameParts);

__declspec(dllimport) HRESULT WINAPI RoResolveNamespace(
const HSTRING name,
const HSTRING windowsMetaDataDir,
const DWORD packageGraphDirsCount,
const HSTRING* packageGraphDirs,
DWORD* metaDataFilePathsCount,
HSTRING** metaDataFilePaths,
DWORD* subNamespacesCount,
HSTRING** subNamespaces);

__declspec(dllimport) HRESULT WINAPI RoIsApiContractPresent(
PCWSTR name,
UINT16 majorVersion,
UINT16 minorVersion,
BOOL* present);

__declspec(dllimport) HRESULT WINAPI RoIsApiContractMajorVersionPresent(
PCWSTR name,
UINT16 majorVersion,
BOOL* present);
}

static decltype(RoActivateInstance)* TrueRoActivateInstance = RoActivateInstance;
static decltype(RoGetActivationFactory)* TrueRoGetActivationFactory = RoGetActivationFactory;
static decltype(RoGetMetaDataFile)* TrueRoGetMetaDataFile = RoGetMetaDataFile;
static decltype(RoResolveNamespace)* TrueRoResolveNamespace = RoResolveNamespace;

std::wstring exeFilePath;

VOID CALLBACK EnsureMTAInitializedCallBack
(
PTP_CALLBACK_INSTANCE instance,
Expand All @@ -29,12 +68,12 @@ VOID CALLBACK EnsureMTAInitializedCallBack
CoGetObjectContext(IID_PPV_ARGS(&spThreadingInfo));
}

/*
In the context callback call to the MTA apartment, there is a bug that prevents COM
from automatically initializing MTA remoting. It only allows NTA to be intialized
outside of the NTA and blocks all others. The workaround for this is to spin up another
thread that is not been CoInitialize. COM treats this thread as a implicit MTA and
when we call CoGetObjectContext on it we implicitily initialized the MTA.
/*
In the context callback call to the MTA apartment, there is a bug that prevents COM
from automatically initializing MTA remoting. It only allows NTA to be intialized
outside of the NTA and blocks all others. The workaround for this is to spin up another
thread that is not been CoInitialize. COM treats this thread as a implicit MTA and
when we call CoGetObjectContext on it we implicitily initialized the MTA.
*/
HRESULT EnsureMTAInitialized()
{
Expand All @@ -46,7 +85,7 @@ HRESULT EnsureMTAInitialized()
return HRESULT_FROM_WIN32(GetLastError());
}
SetThreadpoolThreadMaximum(pool, 1);
if (!SetThreadpoolThreadMinimum(pool, 1))
if (!SetThreadpoolThreadMinimum(pool, 1))
{
return HRESULT_FROM_WIN32(GetLastError());
}
Expand Down Expand Up @@ -92,28 +131,28 @@ HRESULT WINAPI RoActivateInstanceDetour(HSTRING activatableClassId, IInspectable
}
switch (threading_model)
{
case ABI::Windows::Foundation::ThreadingType_BOTH:
activationLocation = CurrentApartment;
break;
case ABI::Windows::Foundation::ThreadingType_STA:
if (aptType == APTTYPE_MTA)
{
return RO_E_UNSUPPORTED_FROM_MTA;
}
else
{
case ABI::Windows::Foundation::ThreadingType_BOTH :
activationLocation = CurrentApartment;
}
break;
case ABI::Windows::Foundation::ThreadingType_STA :
if (aptType == APTTYPE_MTA)
{
return RO_E_UNSUPPORTED_FROM_MTA;
}
else
{
activationLocation = CurrentApartment;
}
break;
case ABI::Windows::Foundation::ThreadingType_MTA:
if (aptType == APTTYPE_MTA)
{
activationLocation = CurrentApartment;
}
else
{
activationLocation = CrossApartmentMTA;
}
case ABI::Windows::Foundation::ThreadingType_MTA :
if (aptType == APTTYPE_MTA)
{
activationLocation = CurrentApartment;
}
else
{
activationLocation = CrossApartmentMTA;
}
break;
}
// Activate in current apartment
Expand All @@ -126,7 +165,7 @@ HRESULT WINAPI RoActivateInstanceDetour(HSTRING activatableClassId, IInspectable
// Cross apartment MTA activation
struct CrossApartmentMTAActData {
HSTRING activatableClassId;
IStream* stream;
IStream *stream;
};
CrossApartmentMTAActData cbdata{ activatableClassId };
CO_MTA_USAGE_COOKIE mtaUsageCookie;
Expand All @@ -142,7 +181,6 @@ HRESULT WINAPI RoActivateInstanceDetour(HSTRING activatableClassId, IInspectable
{
CrossApartmentMTAActData* data = reinterpret_cast<CrossApartmentMTAActData*>(pComCallData->pUserDefined);
Microsoft::WRL::ComPtr<IInspectable> instance;
HRESULT hr;
IActivationFactory* pFactory;
RETURN_IF_FAILED(WinRTGetActivationFactory(data->activatableClassId, __uuidof(IActivationFactory), (void**)&pFactory));
RETURN_IF_FAILED(pFactory->ActivateInstance(&instance));
Expand Down Expand Up @@ -189,12 +227,12 @@ HRESULT WINAPI RoResolveNamespaceDetour(
DWORD* subNamespacesCount,
HSTRING** subNamespaces)
{
HRESULT hr = WinRTResolveNamespaceDetour(name, windowsMetaDataDir,
HRESULT hr = TrueRoResolveNamespace(name, Microsoft::WRL::Wrappers::HStringReference(exeFilePath.c_str()).Get(),
packageGraphDirsCount, packageGraphDirs,
metaDataFilePathsCount, metaDataFilePaths,
subNamespacesCount, subNamespaces);

if (hr == REGDB_E_CLASSNOTREG)
if (hr == RO_E_METADATA_NAME_NOT_FOUND)
{
hr = TrueRoResolveNamespace(name, windowsMetaDataDir,
packageGraphDirsCount, packageGraphDirs,
Expand All @@ -210,6 +248,11 @@ void InstallHooks()
return;
}

WCHAR filePath[MAX_PATH];
GetModuleFileNameW(nullptr, filePath, _countof(filePath));
std::wstring::size_type pos = std::wstring(filePath).find_last_of(L"\\/");
exeFilePath = std::wstring(filePath).substr(0, pos);

DetourRestoreAfterWith();

DetourTransactionBegin();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>manifestTestSTA.manifest</TargetPath>
</ContentWithTargetPath >
<ContentWithTargetPath Include="test_files\RegFreeWinRtTest.winmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>RegFreeWinRtTest.winmd</TargetPath>
</ContentWithTargetPath >
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="test_files\manifestTestSTA.manifest" />
<CopyFileToFolders Include="test_files\RegFreeWinRtTest.winmd" />
</ItemGroup>
</Project>
Loading