A Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj => Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj +174 -0
@@ 0,0 1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{ca2873d8-bd0b-4583-818d-b94a3c2abba3}</ProjectGuid>
+ <RootNamespace>NosSmoothInject</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="nossmooth.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="coreclr_delegates.h" />
+ <ClInclude Include="nethost.h" />
+ <ClInclude Include="hostfxr.h" />
+ <ClInclude Include="nossmooth.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project><
\ No newline at end of file
A Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj.filters => Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj.filters +39 -0
@@ 0,0 1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="nossmooth.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="nossmooth.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hostfxr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nethost.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="coreclr_delegates.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project><
\ No newline at end of file
A Local/NosSmooth.Inject/coreclr_delegates.h => Local/NosSmooth.Inject/coreclr_delegates.h +47 -0
@@ 0,0 1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __CORECLR_DELEGATES_H__
+#define __CORECLR_DELEGATES_H__
+
+#include <stdint.h>
+
+#if defined(_WIN32)
+#define CORECLR_DELEGATE_CALLTYPE __stdcall
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#define CORECLR_DELEGATE_CALLTYPE
+typedef char char_t;
+#endif
+
+#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1)
+
+// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer
+typedef int (CORECLR_DELEGATE_CALLTYPE* load_assembly_and_get_function_pointer_fn)(
+ const char_t* assembly_path /* Fully qualified path to assembly */,
+ const char_t* type_name /* Assembly qualified type name */,
+ const char_t* method_name /* Public static method name compatible with delegateType */,
+ const char_t* delegate_type_name /* Assembly qualified delegate type name or null
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void* reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void** delegate /* Pointer where to store the function pointer result */);
+
+// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default)
+typedef int (CORECLR_DELEGATE_CALLTYPE* component_entry_point_fn)(void* arg, int32_t arg_size_in_bytes);
+
+typedef int (CORECLR_DELEGATE_CALLTYPE* get_function_pointer_fn)(
+ const char_t* type_name /* Assembly qualified type name */,
+ const char_t* method_name /* Public static method name compatible with delegateType */,
+ const char_t* delegate_type_name /* Assembly qualified delegate type name or null,
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void* load_context /* Extensibility parameter (currently unused and must be 0) */,
+ void* reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void** delegate /* Pointer where to store the function pointer result */);
+
+#endif // __CORECLR_DELEGATES_H__<
\ No newline at end of file
A Local/NosSmooth.Inject/dllmain.cpp => Local/NosSmooth.Inject/dllmain.cpp +19 -0
@@ 0,0 1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include <windows.h>
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
A Local/NosSmooth.Inject/framework.h => Local/NosSmooth.Inject/framework.h +5 -0
@@ 0,0 1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files
+#include <windows.h>
A Local/NosSmooth.Inject/hostfxr.h => Local/NosSmooth.Inject/hostfxr.h +323 -0
@@ 0,0 1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __HOSTFXR_H__
+#define __HOSTFXR_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+#define HOSTFXR_CALLTYPE __cdecl
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#define HOSTFXR_CALLTYPE
+typedef char char_t;
+#endif
+
+enum hostfxr_delegate_type
+{
+ hdt_com_activation,
+ hdt_load_in_memory_assembly,
+ hdt_winrt_activation,
+ hdt_com_register,
+ hdt_com_unregister,
+ hdt_load_assembly_and_get_function_pointer,
+ hdt_get_function_pointer,
+};
+
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_fn)(const int argc, const char_t** argv);
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_startupinfo_fn)(
+ const int argc,
+ const char_t** argv,
+ const char_t* host_path,
+ const char_t* dotnet_root,
+ const char_t* app_path);
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)(
+ const int argc,
+ const char_t** argv,
+ const char_t* host_path,
+ const char_t* dotnet_root,
+ const char_t* app_path,
+ int64_t bundle_header_offset);
+
+typedef void(HOSTFXR_CALLTYPE* hostfxr_error_writer_fn)(const char_t* message);
+
+//
+// Sets a callback which is to be used to write errors to.
+//
+// Parameters:
+// error_writer
+// A callback function which will be invoked every time an error is to be reported.
+// Or nullptr to unregister previously registered callback and return to the default behavior.
+// Return value:
+// The previously registered callback (which is now unregistered), or nullptr if no previous callback
+// was registered
+//
+// The error writer is registered per-thread, so the registration is thread-local. On each thread
+// only one callback can be registered. Subsequent registrations overwrite the previous ones.
+//
+// By default no callback is registered in which case the errors are written to stderr.
+//
+// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
+// Multiple calls to the error writer may occure for one failure.
+//
+// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
+// will be propagated to hostpolicy for the duration of the call. This means that errors from
+// both hostfxr and hostpolicy will be reporter through the same error writer.
+//
+typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE* hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer);
+
+typedef void* hostfxr_handle;
+struct hostfxr_initialize_parameters
+{
+ size_t size;
+ const char_t* host_path;
+ const char_t* dotnet_root;
+};
+
+//
+// Initializes the hosting components for a dotnet command line running an application
+//
+// Parameters:
+// argc
+// Number of argv arguments
+// argv
+// Command-line arguments for running an application (as if through the dotnet executable).
+// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported.
+// For example 'app.dll app_argument_1 app_argument_2`.
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// HostInvalidState - Hosting components are already initialized
+//
+// This function parses the specified command-line arguments to determine the application to run. It will
+// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and
+// dependencies and prepare everything needed to load the runtime.
+//
+// This function only supports arguments for running an application. It does not support SDK commands.
+//
+// This function does not load the runtime.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_initialize_for_dotnet_command_line_fn)(
+ int argc,
+ const char_t** argv,
+ const struct hostfxr_initialize_parameters* parameters,
+ /*out*/ hostfxr_handle* host_context_handle);
+
+//
+// Initializes the hosting components using a .runtimeconfig.json file
+//
+// Parameters:
+// runtime_config_path
+// Path to the .runtimeconfig.json file
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
+// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
+// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
+//
+// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
+// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
+// may be next to the .runtimeconfig.json).
+//
+// This function does not load the runtime.
+//
+// If called when the runtime has already been loaded, this function will check if the specified runtime
+// config is compatible with the existing runtime.
+//
+// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful
+// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that
+// the difference in properties is acceptable.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_initialize_for_runtime_config_fn)(
+ const char_t* runtime_config_path,
+ const struct hostfxr_initialize_parameters* parameters,
+ /*out*/ hostfxr_handle* host_context_handle);
+
+//
+// Gets the runtime property value for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Out parameter. Pointer to a buffer with the property value.
+//
+// Return value:
+// The error code result.
+//
+// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// property value for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t* name,
+ /*out*/ const char_t** value);
+
+//
+// Sets the value of a runtime property for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Value to set
+//
+// Return value:
+// The error code result.
+//
+// Setting properties is only supported for the first host context, before the runtime has been loaded.
+//
+// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
+// property will be removed.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_set_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t* name,
+ const char_t* value);
+
+//
+// Gets all the runtime properties for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// count
+// [in] Size of the keys and values buffers
+// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
+// small or keys/values is nullptr, this is populated with the number of available properties
+// keys
+// Array of pointers to buffers with runtime property keys
+// values
+// Array of pointers to buffers with runtime property values
+//
+// Return value:
+// The error code result.
+//
+// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// properties for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_properties_fn)(
+ const hostfxr_handle host_context_handle,
+ /*inout*/ size_t* count,
+ /*out*/ const char_t** keys,
+ /*out*/ const char_t** values);
+
+//
+// Load CoreCLR and run the application for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
+//
+// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line.
+//
+// This function will not return until the managed application exits.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_run_app_fn)(const hostfxr_handle host_context_handle);
+
+//
+// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// type
+// Type of runtime delegate requested
+// delegate
+// An out parameter that will be assigned the delegate.
+//
+// Return value:
+// The error code result.
+//
+// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config,
+// then all delegate types are supported.
+// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line,
+// then only the following delegate types are currently supported:
+// hdt_load_assembly_and_get_function_pointer
+// hdt_get_function_pointer
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_delegate_fn)(
+ const hostfxr_handle host_context_handle,
+ enum hostfxr_delegate_type type,
+ /*out*/ void** delegate);
+
+//
+// Closes an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// The error code result.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_close_fn)(const hostfxr_handle host_context_handle);
+
+struct hostfxr_dotnet_environment_sdk_info
+{
+ size_t size;
+ const char_t* version;
+ const char_t* path;
+};
+
+typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
+ const struct hostfxr_dotnet_environment_info* info,
+ void* result_context);
+
+struct hostfxr_dotnet_environment_framework_info
+{
+ size_t size;
+ const char_t* name;
+ const char_t* version;
+ const char_t* path;
+};
+
+struct hostfxr_dotnet_environment_info
+{
+ size_t size;
+
+ const char_t* hostfxr_version;
+ const char_t* hostfxr_commit_hash;
+
+ size_t sdk_count;
+ const hostfxr_dotnet_environment_sdk_info* sdks;
+
+ size_t framework_count;
+ const hostfxr_dotnet_environment_framework_info* frameworks;
+};
+
+#endif //__HOSTFXR_H__<
\ No newline at end of file
A Local/NosSmooth.Inject/nethost.h => Local/NosSmooth.Inject/nethost.h +94 -0
@@ 0,0 1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __NETHOST_H__
+#define __NETHOST_H__
+
+#include <stddef.h>
+
+#if defined(_WIN32)
+#ifdef NETHOST_EXPORT
+#define NETHOST_API __declspec(dllexport)
+#else
+#define NETHOST_API __declspec(dllimport)
+#endif
+
+#define NETHOST_CALLTYPE __stdcall
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#ifdef NETHOST_EXPORT
+#define NETHOST_API __attribute__((__visibility__("default")))
+#else
+#define NETHOST_API
+#endif
+
+#define NETHOST_CALLTYPE
+typedef char char_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // Parameters for get_hostfxr_path
+ //
+ // Fields:
+ // size
+ // Size of the struct. This is used for versioning.
+ //
+ // assembly_path
+ // Path to the compenent's assembly.
+ // If specified, hostfxr is located as if the assembly_path is the apphost
+ //
+ // dotnet_root
+ // Path to directory containing the dotnet executable.
+ // If specified, hostfxr is located as if an application is started using
+ // 'dotnet app.dll', which means it will be searched for under the dotnet_root
+ // path and the assembly_path is ignored.
+ //
+ struct get_hostfxr_parameters {
+ size_t size;
+ const char_t* assembly_path;
+ const char_t* dotnet_root;
+ };
+
+ //
+ // Get the path to the hostfxr library
+ //
+ // Parameters:
+ // buffer
+ // Buffer that will be populated with the hostfxr path, including a null terminator.
+ //
+ // buffer_size
+ // [in] Size of buffer in char_t units.
+ // [out] Size of buffer used in char_t units. If the input value is too small
+ // or buffer is nullptr, this is populated with the minimum required size
+ // in char_t units for a buffer to hold the hostfxr path
+ //
+ // get_hostfxr_parameters
+ // Optional. Parameters that modify the behaviour for locating the hostfxr library.
+ // If nullptr, hostfxr is located using the enviroment variable or global registration
+ //
+ // Return value:
+ // 0 on success, otherwise failure
+ // 0x80008098 - buffer is too small (HostApiBufferTooSmall)
+ //
+ // Remarks:
+ // The full search for the hostfxr library is done on every call. To minimize the need
+ // to call this function multiple times, pass a large buffer (e.g. PATH_MAX).
+ //
+ NETHOST_API int NETHOST_CALLTYPE get_hostfxr_path(
+ char_t* buffer,
+ size_t* buffer_size,
+ const struct get_hostfxr_parameters* parameters);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __NETHOST_H__
A Local/NosSmooth.Inject/nossmooth.cpp => Local/NosSmooth.Inject/nossmooth.cpp +125 -0
@@ 0,0 1,125 @@
+#include "nossmooth.h"
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Standard headers
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <iostream>
+#include "nethost.h"
+#include "coreclr_delegates.h"
+#include "hostfxr.h"
+
+#include <cassert>
+
+#define STR(s) L ## s
+#define CH(c) L ## c
+#define DIR_SEPARATOR L'\\'
+
+using string_t = std::basic_string<char_t>;
+
+// Globals to hold hostfxr exports
+hostfxr_initialize_for_runtime_config_fn init_fptr;
+hostfxr_get_runtime_delegate_fn get_delegate_fptr;
+hostfxr_close_fn close_fptr;
+
+// Forward declarations
+bool load_hostfxr();
+load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* assembly);
+
+/********************************************************************************************
+ * Function used to load and activate .NET Core
+ ********************************************************************************************/
+
+ // Forward declarations
+void* load_library(const char_t*);
+void* get_export(void*, const char*);
+
+void* load_library(const char_t* path)
+{
+ HMODULE h = ::LoadLibraryW(path);
+ assert(h != nullptr);
+ return (void*)h;
+}
+void* get_export(void* h, const char* name)
+{
+ void* f = ::GetProcAddress((HMODULE)h, name);
+ assert(f != nullptr);
+ return f;
+}
+
+// Using the nethost library, discover the location of hostfxr and get exports
+bool load_hostfxr()
+{
+ // Pre-allocate a large buffer for the path to hostfxr
+ char_t buffer[MAX_PATH];
+ size_t buffer_size = sizeof(buffer) / sizeof(char_t);
+ int rc = get_hostfxr_path(buffer, &buffer_size, nullptr);
+ if (rc != 0)
+ return false;
+
+ // Load hostfxr and get desired exports
+ void* lib = load_library(buffer);
+ init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config");
+ get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
+ close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
+
+ return (init_fptr && get_delegate_fptr && close_fptr);
+}
+
+// Load and initialize .NET Core and get desired function pointer for scenario
+load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* config_path)
+{
+ // Load .NET Core
+ void* load_assembly_and_get_function_pointer = nullptr;
+ hostfxr_handle cxt = nullptr;
+ int rc = init_fptr(config_path, nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr)
+ {
+ std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
+ close_fptr(cxt);
+ return nullptr;
+ }
+
+ // Get the load assembly function pointer
+ rc = get_delegate_fptr(
+ cxt,
+ hdt_load_assembly_and_get_function_pointer,
+ &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
+ std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
+
+ close_fptr(cxt);
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
+}
+
+bool LoadAndCallMethod(LoadParams* params)
+{
+ if (!load_hostfxr())
+ {
+ assert(false && "Failure: load_hostfxr()");
+ return false;
+ }
+
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
+ load_assembly_and_get_function_pointer = get_dotnet_load_assembly(params->runtimeConfigPath);
+ assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()");
+
+ typedef void (CORECLR_DELEGATE_CALLTYPE* main_entry_point_fn)();
+ main_entry_point_fn main = nullptr;
+ int rc = load_assembly_and_get_function_pointer(
+ params->libraryPath,
+ params->typePath,
+ params->methodName,
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void**)&main);
+ assert(rc == 0 && main != nullptr && "Failure: load_assembly_and_get_function_pointer()");
+ main();
+ return true;
+}<
\ No newline at end of file
A Local/NosSmooth.Inject/nossmooth.h => Local/NosSmooth.Inject/nossmooth.h +15 -0
@@ 0,0 1,15 @@
+#pragma once
+#include <Windows.h>
+
+#pragma pack(push, 1)
+struct LoadParams
+{
+ wchar_t *libraryPath;
+ wchar_t *runtimeConfigPath;
+ wchar_t *typePath;
+ wchar_t *methodName;
+};
+#pragma pack(pop)
+#define DllExport extern "C" __declspec( dllexport )
+
+DllExport bool LoadAndCallMethod(LoadParams* params);<
\ No newline at end of file
A Local/NosSmooth.Inject/pch.cpp => Local/NosSmooth.Inject/pch.cpp +5 -0
@@ 0,0 1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
A Local/NosSmooth.Inject/pch.h => Local/NosSmooth.Inject/pch.h +13 -0
@@ 0,0 1,13 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+// add headers that you want to pre-compile here
+#include "framework.h"
+
+#endif //PCH_H
A Local/NosSmooth.Injector.CLI/Commands/InjectCommand.cs => Local/NosSmooth.Injector.CLI/Commands/InjectCommand.cs +56 -0
@@ 0,0 1,56 @@
+//
+// InjectCommand.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.ComponentModel;
+using Remora.Commands.Attributes;
+using Remora.Commands.Groups;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI.Commands
+{
+ /// <summary>
+ /// Injection command for injecting .NET 5+ libraries with UnmanagedCallersOnly method.
+ /// </summary>
+ internal class InjectCommand : CommandGroup
+ {
+ private readonly NosInjector _injector;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InjectCommand"/> class.
+ /// </summary>
+ /// <param name="injector">The nos smooth injector.</param>
+ public InjectCommand(NosInjector injector)
+ {
+ _injector = injector;
+ }
+
+ /// <summary>
+ /// The command to inject.
+ /// </summary>
+ /// <param name="processId">The id of the process.</param>
+ /// <param name="dllPath">The path to the dll to inject.</param>
+ /// <param name="typeName">The full type specifier. Default is LibraryName.DllMain, LibraryName.</param>
+ /// <param name="methodName">The name of the UnmanagedCallersOnly method. Default is Main.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Command("inject")]
+ public Task<Result> Inject
+ (
+ [Description("The id of the process to inject into.")]
+ int processId,
+ [Description("The path to the dll to inject.")]
+ string dllPath,
+ [Option('t', "type"), Description("The full type specifier. Default is LibraryName.DllMain, LibraryName")]
+ string? typeName = null,
+ [Option('m', "method"), Description("The name of the UnmanagedCallersOnly method. Default is Main")]
+ string? methodName = null
+ )
+ {
+ var dllName = Path.GetFileNameWithoutExtension(dllPath);
+ return Task.FromResult
+ (_injector.Inject(processId, dllPath, $"{dllName}.DllMain, {dllName}", methodName ?? "Main"));
+ }
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector.CLI/Commands/ListProcessesCommand.cs => Local/NosSmooth.Injector.CLI/Commands/ListProcessesCommand.cs +41 -0
@@ 0,0 1,41 @@
+//
+// ListProcessesCommand.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using Remora.Commands.Attributes;
+using Remora.Commands.Groups;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI.Commands
+{
+ /// <summary>
+ /// Command for listing processes to find id of NosTale process.
+ /// </summary>
+ internal class ListProcessesCommand : CommandGroup
+ {
+ /// <summary>
+ /// Lists processes by the given criteria.
+ /// </summary>
+ /// <param name="nameContains">What should the name of the process contain.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Command("list")]
+ public Task<Result> List(string nameContains = "Nostale")
+ {
+ var processes = Process.GetProcesses();
+ foreach (var process in processes.Where(x => x.ProcessName.Contains(nameContains, StringComparison.OrdinalIgnoreCase)))
+ {
+ Console.WriteLine(ProcessToString(process));
+ }
+
+ return Task.FromResult(Result.FromSuccess());
+ }
+
+ private string ProcessToString(Process process)
+ {
+ return $"{process.ProcessName} - {process.Id}";
+ }
+ }
+}
A Local/NosSmooth.Injector.CLI/NosSmooth.Injector.CLI.csproj => Local/NosSmooth.Injector.CLI/NosSmooth.Injector.CLI.csproj +28 -0
@@ 0,0 1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <ApplicationManifest>app.manifest</ApplicationManifest>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+ <PackageReference Include="Remora.Commands" Version="8.0.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <NativeLibs Remove="Program.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <NativeLibs Remove="app.manifest" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NosSmooth.Injector\NosSmooth.Injector.csproj" />
+ </ItemGroup>
+
+</Project>
A Local/NosSmooth.Injector.CLI/Program.cs => Local/NosSmooth.Injector.CLI/Program.cs +75 -0
@@ 0,0 1,75 @@
+//
+// Program.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.Injector.CLI.Commands;
+using Remora.Commands.Extensions;
+using Remora.Commands.Services;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI
+{
+ /// <summary>
+ /// The entrypoint class.
+ /// </summary>
+ public static class Program
+ {
+ /// <summary>
+ /// The entrypoint method.
+ /// </summary>
+ /// <param name="argv">The command line arguments.</param>
+ /// <returns>A task that may or may not have succeeded.</returns>
+ public static async Task Main(string[] argv)
+ {
+ var services = CreateServices();
+ var commandService = services.GetRequiredService<CommandService>();
+ var preparedCommandResult = await commandService.TryPrepareCommandAsync(string.Join(' ', argv), services);
+ if (!preparedCommandResult.IsSuccess)
+ {
+ Console.Error.WriteLine($"There was an error, {preparedCommandResult.Error.Message}");
+ return;
+ }
+
+ if (preparedCommandResult.Entity is null)
+ {
+ Console.Error.WriteLine("You must enter a command such ast list or inject.");
+ return;
+ }
+
+ var executionResult = await commandService.TryExecuteAsync(preparedCommandResult.Entity, services);
+ if (!executionResult.Entity.IsSuccess)
+ {
+ switch (executionResult.Entity.Error)
+ {
+ case ExceptionError exc:
+ Console.Error.WriteLine($"There was an exception, {exc.Exception.Message}");
+ break;
+ default:
+ Console.Error.WriteLine($"There was an error, {executionResult.Entity.Error!.Message}");
+ break;
+ }
+
+ return;
+ }
+ }
+
+ private static IServiceProvider CreateServices()
+ {
+ var collection = new ServiceCollection();
+ collection
+ .AddSingleton<NosInjector>()
+ .AddOptions<NosInjectorOptions>();
+
+ collection
+ .AddCommands()
+ .AddCommandTree()
+ .WithCommandGroup<InjectCommand>()
+ .WithCommandGroup<ListProcessesCommand>();
+
+ return collection.BuildServiceProvider();
+ }
+ }
+}
A Local/NosSmooth.Injector.CLI/app.manifest => Local/NosSmooth.Injector.CLI/app.manifest +11 -0
@@ 0,0 1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+ <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly><
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/InjectionFailedError.cs => Local/NosSmooth.Injector/Errors/InjectionFailedError.cs +15 -0
@@ 0,0 1,15 @@
+//
+// InjectionFailedError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The injection could not be finished successfully.
+/// </summary>
+/// <param name="DllPath">The path to the dll.</param>
+public record InjectionFailedError(string DllPath) : ResultError($"Could not inject {DllPath} dll into the process.");<
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/InsufficientPermissionsError.cs => Local/NosSmooth.Injector/Errors/InsufficientPermissionsError.cs +20 -0
@@ 0,0 1,20 @@
+//
+// InsufficientPermissionsError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The current user has insufficient permissions to inject into the given process.
+/// </summary>
+/// <param name="ProcessId">The id of the process.</param>
+/// <param name="ProcessName">The name of the process.</param>
+public record InsufficientPermissionsError(long ProcessId, string ProcessName)
+ : ResultError
+ (
+ $"Insufficient permissions to open process {ProcessId} ({ProcessName}). Try running the injector as administrator."
+ );<
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/ProcessNotFoundError.cs => Local/NosSmooth.Injector/Errors/ProcessNotFoundError.cs +16 -0
@@ 0,0 1,16 @@
+//
+// ProcessNotFoundError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The given process was not found.
+/// </summary>
+/// <param name="ProcessId">The id of the process.</param>
+public record ProcessNotFoundError(string ProcessId)
+ : NotFoundError($"Could not find process with the given id {ProcessId}.");<
\ No newline at end of file
A Local/NosSmooth.Injector/LoadParams.cs => Local/NosSmooth.Injector/LoadParams.cs +44 -0
@@ 0,0 1,44 @@
+//
+// LoadParams.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NosSmooth.Injector
+{
+ /// <summary>
+ /// The parameters passed to the inject module.
+ /// </summary>
+ internal struct LoadParams
+ {
+ /// <summary>
+ /// The full path of the library.
+ /// </summary>
+ public int LibraryPath;
+
+ /// <summary>
+ /// The full path of the library.
+ /// </summary>
+ public int RuntimeConfigPath;
+
+ /// <summary>
+ /// The full path to the type with the method marked as UnsafeCallersOnly.
+ /// </summary>
+ /// <remarks>
+ /// Can be for example "LibraryNamespace.Type, LibraryNamespace".
+ /// </remarks>
+ public int TypePath;
+
+ /// <summary>
+ /// The name of the method to execute.
+ /// </summary>
+ public int MethodName;
+ }
+}
A Local/NosSmooth.Injector/ManagedMemoryAllocation.cs => Local/NosSmooth.Injector/ManagedMemoryAllocation.cs +47 -0
@@ 0,0 1,47 @@
+//
+// ManagedMemoryAllocation.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Net.NetworkInformation;
+using Reloaded.Memory.Sources;
+
+namespace NosSmooth.Injector;
+
+/// <summary>
+/// Represents freeable memory allocation.
+/// </summary>
+internal class ManagedMemoryAllocation : IDisposable
+{
+ private readonly IMemory _memory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedMemoryAllocation"/> class.
+ /// </summary>
+ /// <param name="memory">The memory with allocation.</param>
+ /// <param name="pointer">The pointer to allocated memory.</param>
+ public ManagedMemoryAllocation(IMemory memory, IntPtr pointer)
+ {
+ Pointer = pointer;
+ _memory = memory;
+
+ }
+
+ /// <summary>
+ /// The allocated pointer number.
+ /// </summary>
+ public IntPtr Pointer { get; private set; }
+
+ /// <summary>
+ /// Whether the memory is currently allocated.
+ /// </summary>
+ public bool Allocated => Pointer != IntPtr.Zero;
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ _memory.Free(Pointer);
+ Pointer = IntPtr.Zero;
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector/NosInjector.cs => Local/NosSmooth.Injector/NosInjector.cs +155 -0
@@ 0,0 1,155 @@
+//
+// NosInjector.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using System.Security;
+using System.Text;
+using Microsoft.Extensions.Options;
+using NosSmooth.Injector.Errors;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.Injector;
+
+/// <summary>
+/// Nos smooth injector for .NET 5+ projects.
+/// </summary>
+/// <remarks>
+/// If you want to inject your project into NosTale that
+/// uses NosSmooth.LocalClient, use this injector.
+/// You must expose static UnmanagedCallersOnly method.
+/// </remarks>
+public class NosInjector
+{
+ private readonly NosInjectorOptions _options;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NosInjector"/> class.
+ /// </summary>
+ /// <param name="options">The injector options.</param>
+ public NosInjector(IOptions<NosInjectorOptions> options)
+ {
+ _options = options.Value;
+ }
+
+ /// <summary>
+ /// Inject the given .NET 5+ dll into NosTale process.
+ /// </summary>
+ /// <remarks>
+ /// The dll must also have .runtimeconfig.json present next to the dll.
+ /// </remarks>
+ /// <param name="processId">The id of the process to inject to.</param>
+ /// <param name="dllPath">The absolute path to the dll to inject.</param>
+ /// <param name="classPath">The full path to the class. Such as "MyLibrary.DllMain, MyLibrary".</param>
+ /// <param name="methodName">The name of the method to execute. The method should return void and have no parameters.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Inject
+ (
+ int processId,
+ string dllPath,
+ string classPath,
+ string methodName = "Main"
+ )
+ {
+ using var process = Process.GetProcessById(processId);
+ return Inject(process, dllPath, classPath, methodName);
+ }
+
+ /// <summary>
+ /// Inject the given .NET 5+ dll into NosTale process.
+ /// </summary>
+ /// <remarks>
+ /// The dll must also have .runtimeconfig.json present next to the dll.
+ /// </remarks>
+ /// <param name="process">The process to inject to.</param>
+ /// <param name="dllPath">The absolute path to the dll to inject.</param>
+ /// <param name="classPath">The full path to the class. Such as "MyLibrary.DllMain, MyLibrary".</param>
+ /// <param name="methodName">The name of the method to execute. The method should return void and have no parameters.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Inject
+ (
+ Process process,
+ string dllPath,
+ string classPath,
+ string methodName = "Main"
+ )
+ {
+ try
+ {
+ using var injector = new Reloaded.Injector.Injector(process);
+ var memory = new ExternalMemory(process);
+
+ var directoryName = Path.GetDirectoryName(dllPath);
+ if (directoryName is null)
+ {
+ return new GenericError("There was an error obtaining directory name of the dll path.");
+ }
+
+ var runtimePath = Path.Combine
+ (directoryName, Path.GetFileNameWithoutExtension(dllPath)) + ".runtimeconfig.json";
+
+ using var dllPathMemory = AllocateString(memory, dllPath);
+ using var classPathMemory = AllocateString(memory, classPath);
+ using var methodNameMemory = AllocateString(memory, methodName);
+ using var runtimePathMemory = AllocateString(memory, runtimePath);
+
+ if (!dllPathMemory.Allocated || !classPathMemory.Allocated || !methodNameMemory.Allocated
+ || !runtimePathMemory.Allocated)
+ {
+ return new GenericError("Could not allocate memory in the external process.");
+ }
+
+ var loadParams = new LoadParams()
+ {
+ LibraryPath = (int)dllPathMemory.Pointer,
+ MethodName = (int)methodNameMemory.Pointer,
+ RuntimeConfigPath = (int)runtimePathMemory.Pointer,
+ TypePath = (int)classPathMemory.Pointer
+ };
+
+ var nosSmoothInjectPath = Path.GetFullPath(_options.NosSmoothInjectPath);
+ var injected = injector.Inject(nosSmoothInjectPath);
+ if (injected == 0)
+ {
+ return new InjectionFailedError(nosSmoothInjectPath);
+ }
+
+ var functionResult = injector.CallFunction(nosSmoothInjectPath, "LoadAndCallMethod", loadParams);
+ if (functionResult != 0)
+ {
+ return new InjectionFailedError(dllPath);
+ }
+
+ return Result.FromSuccess();
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return new InsufficientPermissionsError(process.Id, process.ProcessName);
+ }
+ catch (SecurityException)
+ {
+ return new InsufficientPermissionsError(process.Id, process.ProcessName);
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ private ManagedMemoryAllocation AllocateString(IMemory memory, string str)
+ {
+ var bytes = Encoding.Unicode.GetBytes(str);
+ var allocated = memory.Allocate(bytes.Length + 1);
+ if (allocated == IntPtr.Zero)
+ {
+ return new ManagedMemoryAllocation(memory, allocated);
+ }
+
+ memory.SafeWriteRaw(allocated + bytes.Length, new byte[] { 0 });
+ memory.SafeWriteRaw(allocated, bytes);
+ return new ManagedMemoryAllocation(memory, allocated);
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector/NosInjectorOptions.cs => Local/NosSmooth.Injector/NosInjectorOptions.cs +32 -0
@@ 0,0 1,32 @@
+//
+// NosInjectorOptions.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+
+namespace NosSmooth.Injector
+{
+ /// <summary>
+ /// Options for NosInjector.
+ /// </summary>
+ public class NosInjectorOptions : IOptions<NosInjectorOptions>
+ {
+ /// <summary>
+ /// Gets or sets the path to the nos smooth inject dll.
+ /// </summary>
+ /// <remarks>
+ /// If not absolute path, then relative path from the current executing process is assumed.
+ /// </remarks>
+ public string NosSmoothInjectPath { get; set; } = "NosSmooth.Inject.dll";
+
+ /// <inheritdoc/>
+ public NosInjectorOptions Value => this;
+ }
+}
A Local/NosSmooth.Injector/NosSmooth.Injector.csproj => Local/NosSmooth.Injector/NosSmooth.Injector.csproj +15 -0
@@ 0,0 1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
+ <PackageReference Include="Reloaded.Injector" Version="1.2.4" />
+ <PackageReference Include="Remora.Results" Version="7.1.0" />
+ </ItemGroup>
+
+</Project>
A Local/NosSmooth.LocalCore/NosSmooth.LocalCore.vcxproj => Local/NosSmooth.LocalCore/NosSmooth.LocalCore.vcxproj +0 -0
M NosSmooth.sln => NosSmooth.sln +44 -43
@@ 48,11 48,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.PacketSerializers
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.Packets.Tests", "Tests\NosSmooth.Packets.Tests\NosSmooth.Packets.Tests.csproj", "{9BC56B40-64E3-4A8F-AD49-C52857A35026}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeExecutor", "NativeExecutor\NativeExecutor.vcxproj", "{1BA17328-38FE-4830-9F26-F24585D4CAAB}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NosSmooth.Inject", "Local\NosSmooth.Inject\NosSmooth.Inject.vcxproj", "{CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1\ClassLibrary1.csproj", "{4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.Injector.CLI", "Local\NosSmooth.Injector.CLI\NosSmooth.Injector.CLI.csproj", "{5D351C91-C631-40F6-82D2-F8D68468B076}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.LocalBinding", "Local\NosSmooth.LocalBinding\NosSmooth.LocalBinding.csproj", "{AA21666C-C2EF-4899-B047-889657CEB2D9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Injector", "Local\NosSmooth.Injector\NosSmooth.Injector.csproj", "{7B68DE7D-159B-42A7-8CF8-69F156CD04DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ 232,42 232,42 @@ Global
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x64.Build.0 = Release|Any CPU
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x86.ActiveCfg = Release|Any CPU
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x86.Build.0 = Release|Any CPU
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|Any CPU.ActiveCfg = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|Any CPU.Build.0 = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x64.ActiveCfg = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x64.Build.0 = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x86.ActiveCfg = Debug|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x86.Build.0 = Debug|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|Any CPU.ActiveCfg = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|Any CPU.Build.0 = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x64.ActiveCfg = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x64.Build.0 = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x86.ActiveCfg = Release|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x86.Build.0 = Release|Win32
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x64.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x86.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|Any CPU.Build.0 = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x64.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x64.Build.0 = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x86.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x86.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x64.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x86.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|Any CPU.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x64.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x64.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x86.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x86.Build.0 = Release|Any CPU
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|Any CPU.Build.0 = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x64.ActiveCfg = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x64.Build.0 = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x86.ActiveCfg = Debug|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x86.Build.0 = Debug|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|Any CPU.ActiveCfg = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|Any CPU.Build.0 = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x64.ActiveCfg = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x64.Build.0 = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x86.ActiveCfg = Release|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x86.Build.0 = Release|Win32
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x64.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x86.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x64.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x64.Build.0 = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x86.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x86.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x64.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x86.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x64.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x64.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x86.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ 278,18 278,19 @@ Global
{46D0A205-CA30-41CC-9A80-10B543FBD344} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
{05E3039D-EDF6-4CDC-B062-CB67760ACB5F} = {F9EFA63C-0A88-45EB-B36F-C4A9D63BDFD1}
{06F03B1F-F68A-4C5B-B7FE-128A7CE1A1D7} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
+ {63E97FF3-7E40-44DE-9E91-F5DEE79AF95F} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
{4017A4F4-5E59-48AA-A7D0-A8518148933A} = {9025731C-084E-4E82-8CD4-0F52D3AA1F54}
{726188BA-F0EA-4ECA-ACF4-CCC066464FF0} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
{F96F3AA0-131E-4B6B-AB21-BBE2DEBCEF3A} = {9025731C-084E-4E82-8CD4-0F52D3AA1F54}
{9025731C-084E-4E82-8CD4-0F52D3AA1F54} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {18A62EF6-ADDA-4224-90AB-2D5DCFC95D3E} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
{1A10C624-48E5-425D-938E-31A4CC7AC687} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
{C4DAFD83-C6DC-4597-AA1F-BA2F3ABB612C} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
{C61EBDB6-053C-48C3-B896-58642639C93F} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
{9BC56B40-64E3-4A8F-AD49-C52857A35026} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
- {1BA17328-38FE-4830-9F26-F24585D4CAAB} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {AA21666C-C2EF-4899-B047-889657CEB2D9} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {5D351C91-C631-40F6-82D2-F8D68468B076} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {18A62EF6-ADDA-4224-90AB-2D5DCFC95D3E} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C5F46653-4DEC-429B-8580-4ED18ED9B4CA}