├── .gitignore ├── CMakeLists.txt ├── README.md ├── bindings ├── Bindings.cs └── Bindings.csproj ├── cmake ├── yarc.cmake └── yarc.ps1 ├── include ├── coreclr_delegates.h ├── coreclrhost.h ├── hostfxr.h └── nethost.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | build/ 4 | Makefile 5 | CMakeCache.txt 6 | CMakeFiles/ 7 | cmake_install.cmake 8 | 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(native-host C CXX) 4 | 5 | set(CMAKE_C_STANDARD 99) 6 | set(CMAKE_CXX_STANDARD 11) 7 | 8 | include(cmake/yarc.cmake) 9 | 10 | find_program(DOTNET_EXECUTABLE dotnet REQUIRED) 11 | find_program(PWSH_EXECUTABLE pwsh REQUIRED) 12 | 13 | set(THREADS_PREFER_PTHREAD_FLAG ON) 14 | find_package(Threads REQUIRED) 15 | 16 | if(WIN32) 17 | set(C_FLAGS "") 18 | set(C_FLAGS "${C_FLAGS} -D_UNICODE") 19 | set(C_FLAGS "${C_FLAGS} -D_CRT_SECURE_NO_WARNINGS") 20 | set(C_FLAGS "${C_FLAGS} -DWIN32_LEAN_AND_MEAN") 21 | set(C_FLAGS "${C_FLAGS} -D_WINSOCK_DEPRECATED_NO_WARNINGS") 22 | set(C_FLAGS "${C_FLAGS} -DWINVER=0x0601 -D_WIN32_WINNT=0x0601") 23 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") 24 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") 25 | endif() 26 | 27 | if(CMAKE_COMPILER_IS_GNUCC) 28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-truncation=0") 29 | endif() 30 | 31 | set(DOTNET_ARGS "build" 32 | "${CMAKE_SOURCE_DIR}/bindings" 33 | "-c" 34 | "Release") 35 | 36 | set(BINDINGS_DLL "${CMAKE_SOURCE_DIR}/bindings/bin/Release/net5.0/Bindings.dll") 37 | 38 | add_custom_command(COMMAND ${DOTNET_EXECUTABLE} 39 | ARGS ${DOTNET_ARGS} 40 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bindings" 41 | OUTPUT ${BINDINGS_DLL} 42 | DEPENDS "${CMAKE_SOURCE_DIR}/bindings/Bindings.cs") 43 | 44 | yarc_bundle(NAME bindings 45 | RESOURCES ${BINDINGS_DLL} 46 | OUTPUT resources.c) 47 | 48 | include_directories(include) 49 | 50 | add_executable(native-host 51 | resources.c 52 | main.c) 53 | 54 | target_link_libraries(native-host 55 | ${CMAKE_DL_LIBS} Threads::Threads) 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell Native Host 2 | 3 | ## Getting Started 4 | 5 | Install [PowerShell 7.2.0-preview6 or later](https://github.com/PowerShell/PowerShell/releases) for the required `LoadAssemblyFromNativeMemory` function, then export the PowerShell base installation path: 6 | 7 | ``` 8 | $Env:PWSH_BASE_PATH="${Env:ProgramFiles}\PowerShell\7-preview" 9 | $Env:PWSH_BASE_PATH="/opt/microsoft/powershell/7-preview" 10 | ``` 11 | 12 | Build the PowerShell native host program: 13 | 14 | ``` 15 | mkdir build && cd build 16 | cmake .. 17 | cmake --build . 18 | .\native-host app 19 | ``` 20 | 21 | ``` 22 | mkdir build && cd build 23 | cmake -G "Visual Studio 16 2019" -A x64 .. 24 | cmake --build . --config Release 25 | .\Release\native-host.exe lib 26 | ``` 27 | 28 | The application native host loads pwsh.exe and launches it with its command-line interface, yet it will not spawn a subprocess: it literally loads the PowerShell application to execute it within the current process. This is useful to avoid leaking sensitive data in command-line parameters. 29 | 30 | The library native host loads the PowerShell SDK APIs and calls a few functions for testing. 31 | 32 | ## References 33 | 34 | * https://github.com/PowerShell/PowerShell/tree/master/docs/host-powershell 35 | * https://docs.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting 36 | * https://github.com/dotnet/runtime/blob/master/docs/design/features/native-hosting.md 37 | * https://github.com/dotnet/runtime/blob/master/docs/design/features/host-components.md 38 | * https://github.com/dotnet/runtime/blob/main/docs/design/features/host-error-codes.md 39 | * https://github.com/dotnet/samples/tree/master/core/hosting 40 | * https://github.com/dotnet/runtime/tree/master/src/coreclr/hosts 41 | * https://github.com/dotnet/runtime/tree/master/src/installer/corehost 42 | * https://github.com/dotnet/runtime/blob/master/docs/design/features/host-probing.md 43 | * https://github.com/dotnet/runtime/issues/35329 44 | * https://github.com/dotnet/runtime/issues/35465 45 | * https://github.com/dotnet/runtime/pull/36990 46 | * https://github.com/dotnet/docs/issues/16646 47 | * https://github.com/sanosdole/nodeclrhost 48 | * https://github.com/dotnet/runtime/issues/46652 49 | * https://github.com/PowerShell/PowerShell/issues/14641 50 | * https://github.com/PowerShell/PowerShell/pull/14652 51 | * https://keithbabinec.com/2020/02/15/how-to-run-powershell-core-scripts-from-net-core-applications/ 52 | -------------------------------------------------------------------------------- /bindings/Bindings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Management.Automation; 4 | 5 | namespace NativeHost 6 | { 7 | public static class Bindings 8 | { 9 | [UnmanagedCallersOnly] 10 | public static void RunCommand(IntPtr ptrCommand) 11 | { 12 | string command = Marshal.PtrToStringUTF8(ptrCommand); 13 | PowerShell ps = PowerShell.Create(); 14 | ps.AddScript(command); 15 | ps.Invoke(); 16 | } 17 | 18 | [UnmanagedCallersOnly] 19 | public static IntPtr PowerShell_Create() 20 | { 21 | // https://stackoverflow.com/a/32108252 22 | PowerShell ps = PowerShell.Create(); 23 | GCHandle gch = GCHandle.Alloc(ps, GCHandleType.Normal); 24 | IntPtr ptrHandle = GCHandle.ToIntPtr(gch); 25 | return ptrHandle; 26 | } 27 | 28 | [UnmanagedCallersOnly] 29 | public static void PowerShell_AddCommand(IntPtr ptrHandle, IntPtr ptrCommand) 30 | { 31 | GCHandle gch = GCHandle.FromIntPtr(ptrHandle); 32 | PowerShell ps = (PowerShell) gch.Target; 33 | string command = Marshal.PtrToStringUTF8(ptrCommand); 34 | ps.AddCommand(command); 35 | } 36 | 37 | [UnmanagedCallersOnly] 38 | public static void PowerShell_AddScript(IntPtr ptrHandle, IntPtr ptrScript) 39 | { 40 | GCHandle gch = GCHandle.FromIntPtr(ptrHandle); 41 | PowerShell ps = (PowerShell) gch.Target; 42 | string script = Marshal.PtrToStringUTF8(ptrScript); 43 | ps.AddScript(script); 44 | } 45 | 46 | [UnmanagedCallersOnly] 47 | public static void PowerShell_Invoke(IntPtr ptrHandle) 48 | { 49 | GCHandle gch = GCHandle.FromIntPtr(ptrHandle); 50 | PowerShell ps = (PowerShell) gch.Target; 51 | ps.Invoke(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /bindings/Bindings.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | true 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /cmake/yarc.cmake: -------------------------------------------------------------------------------- 1 | function(yarc_bundle) 2 | cmake_parse_arguments(YARC_BUNDLE "" "NAME;TARGET;OUTPUT" "RESOURCES" ${ARGN}) 3 | if(NOT YARC_BUNDLE_TARGET) 4 | set(YARC_BUNDLE_TARGET "yarc-${YARC_BUNDLE_NAME}-bundle") 5 | endif() 6 | if(YARC_BUNDLE_VERBOSE) 7 | list(APPEND YARC_OPTIONS "-Verbose") 8 | endif() 9 | if(YARC_BUNDLE_NAME) 10 | list(APPEND YARC_OPTIONS "-SymbolName;${YARC_BUNDLE_NAME}") 11 | endif() 12 | if(YARC_BUNDLE_OUTPUT) 13 | list(APPEND YARC_OPTIONS "-OutputFile;${YARC_BUNDLE_OUTPUT}") 14 | endif() 15 | foreach(YARC_BUNDLE_RESOURCE ${YARC_BUNDLE_RESOURCES}) 16 | set(YARC_BUNDLE_RESOURCE_ABS "${YARC_BUNDLE_RESOURCE}") 17 | if(NOT IS_ABSOLUTE ${YARC_BUNDLE_RESOURCE}) 18 | set(YARC_BUNDLE_RESOURCE_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${YARC_BUNDLE_RESOURCE}") 19 | endif() 20 | file(RELATIVE_PATH YARC_BUNDLE_RESOURCE_REL "${CMAKE_CURRENT_BINARY_DIR}" "${YARC_BUNDLE_RESOURCE_ABS}") 21 | list(APPEND YARC_BUNDLE_RESOURCES_ABS ${YARC_BUNDLE_RESOURCE_ABS}) 22 | list(APPEND YARC_BUNDLE_RESOURCES_REL ${YARC_BUNDLE_RESOURCE_REL}) 23 | endforeach() 24 | set(YARC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/yarc.ps1") 25 | set(PWSH_OPTIONS "-ExecutionPolicy;Unrestricted;-NoLogo") 26 | add_custom_command(COMMAND pwsh 27 | ARGS ${PWSH_OPTIONS} ${YARC_PATH} ${YARC_OPTIONS} -InputFile ${YARC_BUNDLE_RESOURCES_REL} 28 | OUTPUT ${YARC_BUNDLE_OUTPUT} 29 | DEPENDS ${YARC_BUNDLE_RESOURCES}) 30 | endfunction() -------------------------------------------------------------------------------- /cmake/yarc.ps1: -------------------------------------------------------------------------------- 1 | function Export-Resource { 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory=$true,Position=0)] 5 | [string] $InputFile, 6 | [Parameter(Mandatory=$true,Position=1)] 7 | [string] $OutputFile, 8 | [Parameter(Mandatory=$true,Position=2)] 9 | [string] $SymbolName 10 | ) 11 | 12 | $InputFile = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($InputFile) 13 | $OutputFile = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($OutputFile) 14 | 15 | $buffer = [System.IO.File]::ReadAllBytes($InputFile) 16 | $BufferSize = $buffer.Count 17 | 18 | $stream = [System.IO.File]::CreateText($OutputFile) 19 | $stream.WriteLine("const unsigned int ${SymbolName}_size = $BufferSize;") 20 | $stream.WriteLine("const unsigned char ${SymbolName}_data[$BufferSize] = {") 21 | 22 | for ($line = 0; $line -lt ([Math]::Floor($BufferSize / 16) - 1); $line++) { 23 | $slice = $buffer[($line * 16)..(($line * 16) + 15)] 24 | $row = (("0x{0:X2}, 0x{1:X2}, 0x{2:X2}, 0x{3:X2}, 0x{4:X2}, 0x{5:X2}, ") + 25 | ("0x{6:X2}, 0x{7:X2}, 0x{8:X2}, 0x{9:X2}, 0x{10:X2}, 0x{11:X2}, ") + 26 | ("0x{12:X2}, 0x{13:X2}, 0x{14:X2}, 0x{15:X2}, ")) -f $slice 27 | $stream.WriteLine($row) 28 | } 29 | $line++; 30 | 31 | $slice = $buffer[($line * 16)..($BufferSize - 1)] 32 | $row = "" 33 | foreach ($byte in $slice) { 34 | $row += "0x{0:X2}, " -f $byte 35 | } 36 | $row = $row.TrimEnd(", ") 37 | $stream.WriteLine($row) 38 | 39 | $stream.WriteLine("};") 40 | $stream.Close() 41 | } 42 | 43 | Export-Resource @args 44 | -------------------------------------------------------------------------------- /include/coreclr_delegates.h: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | #ifndef __CORECLR_DELEGATES_H__ 5 | #define __CORECLR_DELEGATES_H__ 6 | 7 | #include 8 | 9 | #if defined(_WIN32) 10 | #define CORECLR_DELEGATE_CALLTYPE __stdcall 11 | #ifdef _WCHAR_T_DEFINED 12 | typedef wchar_t char_t; 13 | #else 14 | typedef unsigned short char_t; 15 | #endif 16 | #else 17 | #define CORECLR_DELEGATE_CALLTYPE 18 | typedef char char_t; 19 | #endif 20 | 21 | #define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1) 22 | 23 | // Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer 24 | typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_and_get_function_pointer_fn)( 25 | const char_t *assembly_path /* Fully qualified path to assembly */, 26 | const char_t *type_name /* Assembly qualified type name */, 27 | const char_t *method_name /* Public static method name compatible with delegateType */, 28 | const char_t *delegate_type_name /* Assembly qualified delegate type name or null 29 | or UNMANAGEDCALLERSONLY_METHOD if the method is marked with 30 | the UnmanagedCallersOnlyAttribute. */, 31 | void *reserved /* Extensibility parameter (currently unused and must be 0) */, 32 | /*out*/ void **delegate /* Pointer where to store the function pointer result */); 33 | 34 | // Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default) 35 | typedef int (CORECLR_DELEGATE_CALLTYPE *component_entry_point_fn)(void *arg, int32_t arg_size_in_bytes); 36 | 37 | typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)( 38 | const char_t *type_name /* Assembly qualified type name */, 39 | const char_t *method_name /* Public static method name compatible with delegateType */, 40 | const char_t *delegate_type_name /* Assembly qualified delegate type name or null, 41 | or UNMANAGEDCALLERSONLY_METHOD if the method is marked with 42 | the UnmanagedCallersOnlyAttribute. */, 43 | void *load_context /* Extensibility parameter (currently unused and must be 0) */, 44 | void *reserved /* Extensibility parameter (currently unused and must be 0) */, 45 | /*out*/ void **delegate /* Pointer where to store the function pointer result */); 46 | 47 | #endif // __CORECLR_DELEGATES_H__ 48 | -------------------------------------------------------------------------------- /include/coreclrhost.h: -------------------------------------------------------------------------------- 1 | // Retrieved from https://github.com/dotnet/runtime/blob/master/src/coreclr/src/hosts/inc/coreclrhost.h 2 | 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT license. 5 | // See the LICENSE file in the project root for more information. 6 | 7 | // 8 | // APIs for hosting CoreCLR 9 | // 10 | 11 | #ifndef __CORECLR_HOST_H__ 12 | #define __CORECLR_HOST_H__ 13 | 14 | #if defined(_WIN32) && defined(_M_IX86) 15 | #define CORECLR_CALLING_CONVENTION __stdcall 16 | #else 17 | #define CORECLR_CALLING_CONVENTION 18 | #endif 19 | 20 | // For each hosting API, we define a function prototype and a function pointer 21 | // The prototype is useful for implicit linking against the dynamic coreclr 22 | // library and the pointer for explicit dynamic loading (dlopen, LoadLibrary) 23 | #define CORECLR_HOSTING_API(function, ...) \ 24 | typedef int (CORECLR_CALLING_CONVENTION *function##_fn)(__VA_ARGS__) 25 | 26 | // 27 | // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain 28 | // 29 | // Parameters: 30 | // exePath - Absolute path of the executable that invoked the ExecuteAssembly (the native host application) 31 | // appDomainFriendlyName - Friendly name of the app domain that will be created to execute the assembly 32 | // propertyCount - Number of properties (elements of the following two arguments) 33 | // propertyKeys - Keys of properties of the app domain 34 | // propertyValues - Values of properties of the app domain 35 | // hostHandle - Output parameter, handle of the created host 36 | // domainId - Output parameter, id of the created app domain 37 | // 38 | // Returns: 39 | // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed 40 | // 41 | CORECLR_HOSTING_API(coreclr_initialize, 42 | const char* exePath, 43 | const char* appDomainFriendlyName, 44 | int propertyCount, 45 | const char** propertyKeys, 46 | const char** propertyValues, 47 | void** hostHandle, 48 | unsigned int* domainId); 49 | 50 | // 51 | // Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host. 52 | // 53 | // Parameters: 54 | // hostHandle - Handle of the host 55 | // domainId - Id of the domain 56 | // 57 | // Returns: 58 | // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed 59 | // 60 | CORECLR_HOSTING_API(coreclr_shutdown, 61 | void* hostHandle, 62 | unsigned int domainId); 63 | 64 | // 65 | // Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host. 66 | // 67 | // Parameters: 68 | // hostHandle - Handle of the host 69 | // domainId - Id of the domain 70 | // latchedExitCode - Latched exit code after domain unloaded 71 | // 72 | // Returns: 73 | // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed 74 | // 75 | CORECLR_HOSTING_API(coreclr_shutdown_2, 76 | void* hostHandle, 77 | unsigned int domainId, 78 | int* latchedExitCode); 79 | 80 | // 81 | // Create a native callable function pointer for a managed method. 82 | // 83 | // Parameters: 84 | // hostHandle - Handle of the host 85 | // domainId - Id of the domain 86 | // entryPointAssemblyName - Name of the assembly which holds the custom entry point 87 | // entryPointTypeName - Name of the type which holds the custom entry point 88 | // entryPointMethodName - Name of the method which is the custom entry point 89 | // delegate - Output parameter, the function stores a native callable function pointer to the delegate at the specified address 90 | // 91 | // Returns: 92 | // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed 93 | // 94 | CORECLR_HOSTING_API(coreclr_create_delegate, 95 | void* hostHandle, 96 | unsigned int domainId, 97 | const char* entryPointAssemblyName, 98 | const char* entryPointTypeName, 99 | const char* entryPointMethodName, 100 | void** delegate); 101 | 102 | // 103 | // Execute a managed assembly with given arguments 104 | // 105 | // Parameters: 106 | // hostHandle - Handle of the host 107 | // domainId - Id of the domain 108 | // argc - Number of arguments passed to the executed assembly 109 | // argv - Array of arguments passed to the executed assembly 110 | // managedAssemblyPath - Path of the managed assembly to execute (or NULL if using a custom entrypoint). 111 | // exitCode - Exit code returned by the executed assembly 112 | // 113 | // Returns: 114 | // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed 115 | // 116 | CORECLR_HOSTING_API(coreclr_execute_assembly, 117 | void* hostHandle, 118 | unsigned int domainId, 119 | int argc, 120 | const char** argv, 121 | const char* managedAssemblyPath, 122 | unsigned int* exitCode); 123 | 124 | #undef CORECLR_HOSTING_API 125 | 126 | #endif // __CORECLR_HOST_H__ 127 | -------------------------------------------------------------------------------- /include/hostfxr.h: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | #ifndef __HOSTFXR_H__ 5 | #define __HOSTFXR_H__ 6 | 7 | #include 8 | #include 9 | 10 | #if defined(_WIN32) 11 | #define HOSTFXR_CALLTYPE __cdecl 12 | #ifdef _WCHAR_T_DEFINED 13 | typedef wchar_t char_t; 14 | #else 15 | typedef unsigned short char_t; 16 | #endif 17 | #else 18 | #define HOSTFXR_CALLTYPE 19 | typedef char char_t; 20 | #endif 21 | 22 | enum hostfxr_delegate_type 23 | { 24 | hdt_com_activation, 25 | hdt_load_in_memory_assembly, 26 | hdt_winrt_activation, 27 | hdt_com_register, 28 | hdt_com_unregister, 29 | hdt_load_assembly_and_get_function_pointer, 30 | hdt_get_function_pointer, 31 | }; 32 | 33 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv); 34 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_startupinfo_fn)( 35 | const int argc, 36 | const char_t **argv, 37 | const char_t *host_path, 38 | const char_t *dotnet_root, 39 | const char_t *app_path); 40 | typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)( 41 | const int argc, 42 | const char_t** argv, 43 | const char_t* host_path, 44 | const char_t* dotnet_root, 45 | const char_t* app_path, 46 | int64_t bundle_header_offset); 47 | 48 | typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message); 49 | 50 | // 51 | // Sets a callback which is to be used to write errors to. 52 | // 53 | // Parameters: 54 | // error_writer 55 | // A callback function which will be invoked every time an error is to be reported. 56 | // Or nullptr to unregister previously registered callback and return to the default behavior. 57 | // Return value: 58 | // The previously registered callback (which is now unregistered), or nullptr if no previous callback 59 | // was registered 60 | // 61 | // The error writer is registered per-thread, so the registration is thread-local. On each thread 62 | // only one callback can be registered. Subsequent registrations overwrite the previous ones. 63 | // 64 | // By default no callback is registered in which case the errors are written to stderr. 65 | // 66 | // Each call to the error writer is sort of like writing a single line (the EOL character is omitted). 67 | // Multiple calls to the error writer may occure for one failure. 68 | // 69 | // If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer 70 | // will be propagated to hostpolicy for the duration of the call. This means that errors from 71 | // both hostfxr and hostpolicy will be reporter through the same error writer. 72 | // 73 | typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE *hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer); 74 | 75 | typedef void* hostfxr_handle; 76 | struct hostfxr_initialize_parameters 77 | { 78 | size_t size; 79 | const char_t *host_path; 80 | const char_t *dotnet_root; 81 | }; 82 | 83 | // 84 | // Initializes the hosting components for a dotnet command line running an application 85 | // 86 | // Parameters: 87 | // argc 88 | // Number of argv arguments 89 | // argv 90 | // Command-line arguments for running an application (as if through the dotnet executable). 91 | // parameters 92 | // Optional. Additional parameters for initialization 93 | // host_context_handle 94 | // On success, this will be populated with an opaque value representing the initialized host context 95 | // 96 | // Return value: 97 | // Success - Hosting components were successfully initialized 98 | // HostInvalidState - Hosting components are already initialized 99 | // 100 | // This function parses the specified command-line arguments to determine the application to run. It will 101 | // then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and 102 | // dependencies and prepare everything needed to load the runtime. 103 | // 104 | // This function only supports arguments for running an application. It does not support SDK commands. 105 | // 106 | // This function does not load the runtime. 107 | // 108 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)( 109 | int argc, 110 | const char_t **argv, 111 | const struct hostfxr_initialize_parameters *parameters, 112 | /*out*/ hostfxr_handle *host_context_handle); 113 | 114 | // 115 | // Initializes the hosting components using a .runtimeconfig.json file 116 | // 117 | // Parameters: 118 | // runtime_config_path 119 | // Path to the .runtimeconfig.json file 120 | // parameters 121 | // Optional. Additional parameters for initialization 122 | // host_context_handle 123 | // On success, this will be populated with an opaque value representing the initialized host context 124 | // 125 | // Return value: 126 | // Success - Hosting components were successfully initialized 127 | // Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components 128 | // Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components 129 | // CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components 130 | // 131 | // This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed 132 | // to load the runtime. It will only process the .deps.json from frameworks (not any app/component that 133 | // may be next to the .runtimeconfig.json). 134 | // 135 | // This function does not load the runtime. 136 | // 137 | // If called when the runtime has already been loaded, this function will check if the specified runtime 138 | // config is compatible with the existing runtime. 139 | // 140 | // Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful 141 | // initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that 142 | // the difference in properties is acceptable. 143 | // 144 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_runtime_config_fn)( 145 | const char_t *runtime_config_path, 146 | const struct hostfxr_initialize_parameters *parameters, 147 | /*out*/ hostfxr_handle *host_context_handle); 148 | 149 | // 150 | // Gets the runtime property value for an initialized host context 151 | // 152 | // Parameters: 153 | // host_context_handle 154 | // Handle to the initialized host context 155 | // name 156 | // Runtime property name 157 | // value 158 | // Out parameter. Pointer to a buffer with the property value. 159 | // 160 | // Return value: 161 | // The error code result. 162 | // 163 | // The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only 164 | // guaranteed until any of the below occur: 165 | // - a 'run' method is called for the host context 166 | // - properties are changed via hostfxr_set_runtime_property_value 167 | // - the host context is closed via 'hostfxr_close' 168 | // 169 | // If host_context_handle is nullptr and an active host context exists, this function will get the 170 | // property value for the active host context. 171 | // 172 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_property_value_fn)( 173 | const hostfxr_handle host_context_handle, 174 | const char_t *name, 175 | /*out*/ const char_t **value); 176 | 177 | // 178 | // Sets the value of a runtime property for an initialized host context 179 | // 180 | // Parameters: 181 | // host_context_handle 182 | // Handle to the initialized host context 183 | // name 184 | // Runtime property name 185 | // value 186 | // Value to set 187 | // 188 | // Return value: 189 | // The error code result. 190 | // 191 | // Setting properties is only supported for the first host context, before the runtime has been loaded. 192 | // 193 | // If the property already exists in the host context, it will be overwritten. If value is nullptr, the 194 | // property will be removed. 195 | // 196 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_set_runtime_property_value_fn)( 197 | const hostfxr_handle host_context_handle, 198 | const char_t *name, 199 | const char_t *value); 200 | 201 | // 202 | // Gets all the runtime properties for an initialized host context 203 | // 204 | // Parameters: 205 | // host_context_handle 206 | // Handle to the initialized host context 207 | // count 208 | // [in] Size of the keys and values buffers 209 | // [out] Number of properties returned (size of keys/values buffers used). If the input value is too 210 | // small or keys/values is nullptr, this is populated with the number of available properties 211 | // keys 212 | // Array of pointers to buffers with runtime property keys 213 | // values 214 | // Array of pointers to buffers with runtime property values 215 | // 216 | // Return value: 217 | // The error code result. 218 | // 219 | // The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only 220 | // guaranteed until any of the below occur: 221 | // - a 'run' method is called for the host context 222 | // - properties are changed via hostfxr_set_runtime_property_value 223 | // - the host context is closed via 'hostfxr_close' 224 | // 225 | // If host_context_handle is nullptr and an active host context exists, this function will get the 226 | // properties for the active host context. 227 | // 228 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_properties_fn)( 229 | const hostfxr_handle host_context_handle, 230 | /*inout*/ size_t * count, 231 | /*out*/ const char_t **keys, 232 | /*out*/ const char_t **values); 233 | 234 | // 235 | // Load CoreCLR and run the application for an initialized host context 236 | // 237 | // Parameters: 238 | // host_context_handle 239 | // Handle to the initialized host context 240 | // 241 | // Return value: 242 | // If the app was successfully run, the exit code of the application. Otherwise, the error code result. 243 | // 244 | // The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line. 245 | // 246 | // This function will not return until the managed application exits. 247 | // 248 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_run_app_fn)(const hostfxr_handle host_context_handle); 249 | 250 | // 251 | // Gets a typed delegate from the currently loaded CoreCLR or from a newly created one. 252 | // 253 | // Parameters: 254 | // host_context_handle 255 | // Handle to the initialized host context 256 | // type 257 | // Type of runtime delegate requested 258 | // delegate 259 | // An out parameter that will be assigned the delegate. 260 | // 261 | // Return value: 262 | // The error code result. 263 | // 264 | // If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config, 265 | // then all delegate types are supported. 266 | // If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line, 267 | // then only the following delegate types are currently supported: 268 | // hdt_load_assembly_and_get_function_pointer 269 | // hdt_get_function_pointer 270 | // 271 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)( 272 | const hostfxr_handle host_context_handle, 273 | enum hostfxr_delegate_type type, 274 | /*out*/ void **delegate); 275 | 276 | // 277 | // Closes an initialized host context 278 | // 279 | // Parameters: 280 | // host_context_handle 281 | // Handle to the initialized host context 282 | // 283 | // Return value: 284 | // The error code result. 285 | // 286 | typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle); 287 | 288 | #endif //__HOSTFXR_H__ 289 | -------------------------------------------------------------------------------- /include/nethost.h: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | #ifndef __NETHOST_H__ 5 | #define __NETHOST_H__ 6 | 7 | #include 8 | 9 | #ifdef _WIN32 10 | #ifdef NETHOST_EXPORT 11 | #define NETHOST_API __declspec(dllexport) 12 | #else 13 | // Consuming the nethost as a static library 14 | // Shouldn't export attempt to dllimport. 15 | #ifdef NETHOST_USE_AS_STATIC 16 | #define NETHOST_API 17 | #else 18 | #define NETHOST_API __declspec(dllimport) 19 | #endif 20 | #endif 21 | 22 | #define NETHOST_CALLTYPE __stdcall 23 | #ifdef _WCHAR_T_DEFINED 24 | typedef wchar_t char_t; 25 | #else 26 | typedef unsigned short char_t; 27 | #endif 28 | #else 29 | #ifdef NETHOST_EXPORT 30 | #define NETHOST_API __attribute__((__visibility__("default"))) 31 | #else 32 | #define NETHOST_API 33 | #endif 34 | 35 | #define NETHOST_CALLTYPE 36 | typedef char char_t; 37 | #endif 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | // Parameters for get_hostfxr_path 44 | // 45 | // Fields: 46 | // size 47 | // Size of the struct. This is used for versioning. 48 | // 49 | // assembly_path 50 | // Path to the compenent's assembly. 51 | // If specified, hostfxr is located as if the assembly_path is the apphost 52 | // 53 | // dotnet_root 54 | // Path to directory containing the dotnet executable. 55 | // If specified, hostfxr is located as if an application is started using 56 | // 'dotnet app.dll', which means it will be searched for under the dotnet_root 57 | // path and the assembly_path is ignored. 58 | // 59 | struct get_hostfxr_parameters { 60 | size_t size; 61 | const char_t *assembly_path; 62 | const char_t *dotnet_root; 63 | }; 64 | 65 | // 66 | // Get the path to the hostfxr library 67 | // 68 | // Parameters: 69 | // buffer 70 | // Buffer that will be populated with the hostfxr path, including a null terminator. 71 | // 72 | // buffer_size 73 | // [in] Size of buffer in char_t units. 74 | // [out] Size of buffer used in char_t units. If the input value is too small 75 | // or buffer is nullptr, this is populated with the minimum required size 76 | // in char_t units for a buffer to hold the hostfxr path 77 | // 78 | // get_hostfxr_parameters 79 | // Optional. Parameters that modify the behaviour for locating the hostfxr library. 80 | // If nullptr, hostfxr is located using the enviroment variable or global registration 81 | // 82 | // Return value: 83 | // 0 on success, otherwise failure 84 | // 0x80008098 - buffer is too small (HostApiBufferTooSmall) 85 | // 86 | // Remarks: 87 | // The full search for the hostfxr library is done on every call. To minimize the need 88 | // to call this function multiple times, pass a large buffer (e.g. PATH_MAX). 89 | // 90 | NETHOST_API int NETHOST_CALLTYPE get_hostfxr_path( 91 | char_t * buffer, 92 | size_t * buffer_size, 93 | const struct get_hostfxr_parameters *parameters); 94 | 95 | #ifdef __cplusplus 96 | } // extern "C" 97 | #endif 98 | 99 | #endif // __NETHOST_H__ 100 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #include 14 | #endif 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #ifdef _WIN32 23 | #define PATH_SEPARATOR_CHR '\\' 24 | #define PATH_SEPARATOR_STR "\\" 25 | #define HOSTFXR_LIB_NAME "hostfxr.dll" 26 | #define CORECLR_LIB_NAME "coreclr.dll" 27 | #else 28 | #define PATH_SEPARATOR_CHR '/' 29 | #define PATH_SEPARATOR_STR "/" 30 | #define HOSTFXR_LIB_NAME "libhostfxr.so" 31 | #define CORECLR_LIB_NAME "libcoreclr.so" 32 | #endif 33 | 34 | #define HOSTFXR_MAX_PATH 1024 35 | 36 | static char g_PWSH_BASE_PATH[HOSTFXR_MAX_PATH]; 37 | 38 | int get_env(const char* name, char* value, int cch) 39 | { 40 | int status; 41 | 42 | int len; 43 | char* env; 44 | 45 | env = getenv(name); 46 | 47 | if (!env) 48 | return -1; 49 | 50 | len = (int) strlen(name); 51 | 52 | if (len < 1) 53 | return -1; 54 | 55 | status = len + 1; 56 | 57 | if (value && (cch > 0)) 58 | { 59 | if (cch >= (len + 1)) 60 | { 61 | strncpy(value, env, cch); 62 | value[cch - 1] = '\0'; 63 | status = len; 64 | } 65 | } 66 | 67 | return status; 68 | } 69 | 70 | static void* load_library(const char* path) 71 | { 72 | #ifdef _WIN32 73 | HMODULE hModule = LoadLibraryA(path); 74 | return (void*) hModule; 75 | #else 76 | void* handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 77 | return handle; 78 | #endif 79 | } 80 | 81 | static void* get_proc_address(void* handle, const char* name) 82 | { 83 | #ifdef _WIN32 84 | HMODULE hModule = (HMODULE) handle; 85 | void* symbol = GetProcAddress(hModule, name); 86 | return symbol; 87 | #else 88 | void* symbol = dlsym(handle, name); 89 | return symbol; 90 | #endif 91 | } 92 | 93 | static uint8_t* load_file(const char* filename, size_t* size) 94 | { 95 | FILE* fp = NULL; 96 | uint8_t* data = NULL; 97 | 98 | if (!filename || !size) 99 | return NULL; 100 | 101 | *size = 0; 102 | 103 | fp = fopen(filename, "rb"); 104 | 105 | if (!fp) 106 | return NULL; 107 | 108 | fseek(fp, 0, SEEK_END); 109 | *size = ftell(fp); 110 | fseek(fp, 0, SEEK_SET); 111 | 112 | data = malloc(*size + 1); 113 | 114 | if (!data) 115 | goto exit; 116 | 117 | if (fread(data, 1, *size, fp) != *size) 118 | { 119 | free(data); 120 | data = NULL; 121 | *size = 0; 122 | goto exit; 123 | } 124 | 125 | data[*size] = '\0'; 126 | 127 | exit: 128 | fclose(fp); 129 | return data; 130 | } 131 | 132 | #ifndef _WIN32 133 | extern void pthread_create(); 134 | 135 | void linker_dummy() 136 | { 137 | // force linking pthread library 138 | pthread_create(); 139 | } 140 | #endif 141 | 142 | #ifdef _WIN32 143 | WCHAR* convert_string_to_utf16(const char* lpMultiByteStr) 144 | { 145 | if (!lpMultiByteStr) 146 | return NULL; 147 | 148 | int cchWideChar = MultiByteToWideChar(CP_UTF8, 0, lpMultiByteStr, -1, NULL, 0); 149 | WCHAR* lpWideCharStr = (LPWSTR) calloc(cchWideChar + 1, sizeof(WCHAR)); 150 | MultiByteToWideChar(CP_UTF8, 0, lpMultiByteStr, -1, lpWideCharStr, cchWideChar); 151 | 152 | return lpWideCharStr; 153 | } 154 | #endif 155 | 156 | struct coreclr_context 157 | { 158 | coreclr_initialize_fn initialize; 159 | coreclr_shutdown_fn shutdown; 160 | coreclr_shutdown_2_fn shutdown_2; 161 | coreclr_create_delegate_fn create_delegate; 162 | coreclr_execute_assembly_fn execute_assembly; 163 | }; 164 | typedef struct coreclr_context CORECLR_CONTEXT; 165 | 166 | static CORECLR_CONTEXT g_CORECLR_CONTEXT; 167 | 168 | bool load_coreclr(CORECLR_CONTEXT* coreclr, const char* coreclr_path) 169 | { 170 | void* lib_handle = load_library(coreclr_path); 171 | 172 | memset(coreclr, 0, sizeof(CORECLR_CONTEXT)); 173 | 174 | if (!lib_handle) { 175 | printf("could not load %s\n", coreclr_path); 176 | } 177 | 178 | coreclr->initialize = (coreclr_initialize_fn) get_proc_address(lib_handle, "coreclr_initialize"); 179 | coreclr->shutdown = (coreclr_shutdown_fn) get_proc_address(lib_handle, "coreclr_shutdown"); 180 | coreclr->shutdown_2 = (coreclr_shutdown_2_fn) get_proc_address(lib_handle, "coreclr_shutdown_2"); 181 | coreclr->create_delegate = (coreclr_create_delegate_fn) get_proc_address(lib_handle, "coreclr_create_delegate"); 182 | coreclr->execute_assembly = (coreclr_execute_assembly_fn) get_proc_address(lib_handle, "coreclr_execute_assembly"); 183 | 184 | if (!coreclr->initialize || !coreclr->shutdown || !coreclr->shutdown_2 || 185 | !coreclr->create_delegate || !coreclr->execute_assembly) 186 | { 187 | printf("could not load CoreCLR functions\n"); 188 | return false; 189 | } 190 | 191 | return true; 192 | } 193 | 194 | struct hostfxr_context 195 | { 196 | hostfxr_initialize_for_dotnet_command_line_fn initialize_for_dotnet_command_line; 197 | hostfxr_initialize_for_runtime_config_fn initialize_for_runtime_config; 198 | hostfxr_get_runtime_property_value_fn get_runtime_property_value; 199 | hostfxr_set_runtime_property_value_fn set_runtime_property_value; 200 | hostfxr_get_runtime_properties_fn get_runtime_properties; 201 | hostfxr_run_app_fn run_app; 202 | hostfxr_get_runtime_delegate_fn get_runtime_delegate; 203 | hostfxr_close_fn close; 204 | 205 | load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer; 206 | get_function_pointer_fn get_function_pointer; 207 | hostfxr_handle context_handle; 208 | }; 209 | typedef struct hostfxr_context HOSTFXR_CONTEXT; 210 | 211 | static HOSTFXR_CONTEXT g_HOSTFXR_CONTEXT; 212 | 213 | struct hostfxr_init_params 214 | { 215 | size_t size; 216 | const char* host_path; 217 | const char* dotnet_root; 218 | }; 219 | typedef struct hostfxr_init_params HOSTFXR_INIT_PARAMS; 220 | 221 | static int32_t hostfxr_initialize_for_dotnet_command_line(int argc, const char** argv, 222 | const HOSTFXR_INIT_PARAMS* params, hostfxr_handle* host_context_handle) 223 | { 224 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 225 | #ifdef _WIN32 226 | int32_t status; 227 | int32_t index; 228 | WCHAR** argv_w = NULL; 229 | struct hostfxr_initialize_parameters params_w; 230 | struct hostfxr_initialize_parameters* p_params = NULL; 231 | 232 | if (params) { 233 | params_w.size = sizeof(params_w); 234 | params_w.host_path = convert_string_to_utf16(params->host_path); 235 | params_w.dotnet_root = convert_string_to_utf16(params->dotnet_root); 236 | p_params = ¶ms_w; 237 | } 238 | 239 | argv_w = (WCHAR**) calloc(argc, sizeof(WCHAR*)); 240 | 241 | if (!argv_w) 242 | return -1; 243 | 244 | for (index = 0; index < argc; index++) { 245 | argv_w[index] = convert_string_to_utf16(argv[index]); 246 | } 247 | 248 | status = hostfxr->initialize_for_dotnet_command_line(argc, argv_w, p_params, host_context_handle); 249 | 250 | for (index = 0; index < argc; index++) { 251 | free(argv_w[index]); 252 | } 253 | free(argv_w); 254 | 255 | if (params) { 256 | free((void*) params_w.host_path); 257 | free((void*) params_w.dotnet_root); 258 | } 259 | 260 | return status; 261 | #else 262 | return hostfxr->initialize_for_dotnet_command_line(argc, argv, 263 | (const struct hostfxr_initialize_parameters*) params, host_context_handle); 264 | #endif 265 | } 266 | 267 | int32_t hostfxr_initialize_for_runtime_config(const char* runtime_config_path, 268 | const HOSTFXR_INIT_PARAMS* params, hostfxr_handle* host_context_handle) 269 | { 270 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 271 | #ifdef _WIN32 272 | int32_t status; 273 | WCHAR* runtime_config_path_w = NULL; 274 | struct hostfxr_initialize_parameters params_w; 275 | struct hostfxr_initialize_parameters* p_params = NULL; 276 | 277 | runtime_config_path_w = convert_string_to_utf16(runtime_config_path); 278 | 279 | if (params) { 280 | params_w.size = sizeof(params_w); 281 | params_w.host_path = convert_string_to_utf16(params->host_path); 282 | params_w.dotnet_root = convert_string_to_utf16(params->dotnet_root); 283 | p_params = ¶ms_w; 284 | } 285 | 286 | status = hostfxr->initialize_for_runtime_config(runtime_config_path_w, p_params, host_context_handle); 287 | 288 | if (params) { 289 | free((void*) params_w.host_path); 290 | free((void*) params_w.dotnet_root); 291 | } 292 | 293 | free(runtime_config_path_w); 294 | 295 | return status; 296 | #else 297 | return hostfxr->initialize_for_runtime_config(runtime_config_path, 298 | (const struct hostfxr_initialize_parameters*) params, host_context_handle); 299 | #endif 300 | } 301 | 302 | #define UNMANAGEDCALLERSONLY_METHOD_A ((const char*)-1) 303 | 304 | int32_t hostfxr_load_assembly_and_get_function_pointer(const char* assembly_path, 305 | const char* type_name, const char* method_name, const char* delegate_type_name, 306 | void* reserved, void** delegate) 307 | { 308 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 309 | #ifdef _WIN32 310 | int32_t status; 311 | const WCHAR* assembly_path_w; 312 | const WCHAR* type_name_w; 313 | const WCHAR* method_name_w; 314 | const WCHAR* delegate_type_name_w; 315 | 316 | assembly_path_w = convert_string_to_utf16(assembly_path); 317 | type_name_w = convert_string_to_utf16(type_name); 318 | method_name_w = convert_string_to_utf16(method_name); 319 | 320 | if (delegate_type_name != UNMANAGEDCALLERSONLY_METHOD_A) { 321 | delegate_type_name_w = convert_string_to_utf16(delegate_type_name); 322 | } 323 | else { 324 | delegate_type_name_w = UNMANAGEDCALLERSONLY_METHOD; 325 | } 326 | 327 | status = hostfxr->load_assembly_and_get_function_pointer(assembly_path_w, 328 | type_name_w, method_name_w, delegate_type_name_w, 329 | reserved, delegate); 330 | 331 | free((void*) assembly_path_w); 332 | free((void*) type_name_w); 333 | free((void*) method_name_w); 334 | 335 | if (delegate_type_name != UNMANAGEDCALLERSONLY_METHOD_A) 336 | free((void*) delegate_type_name_w); 337 | 338 | return status; 339 | #else 340 | return hostfxr->load_assembly_and_get_function_pointer(assembly_path, 341 | type_name, method_name, delegate_type_name, 342 | reserved, delegate); 343 | #endif 344 | } 345 | 346 | int32_t hostfxr_get_function_pointer(const char* type_name, 347 | const char* method_name, const char* delegate_type_name, 348 | void* load_context, void* reserved, void** delegate) 349 | { 350 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 351 | #ifdef _WIN32 352 | int32_t status; 353 | const WCHAR* type_name_w; 354 | const WCHAR* method_name_w; 355 | const WCHAR* delegate_type_name_w; 356 | 357 | type_name_w = convert_string_to_utf16(type_name); 358 | method_name_w = convert_string_to_utf16(method_name); 359 | 360 | if (delegate_type_name != UNMANAGEDCALLERSONLY_METHOD_A) { 361 | delegate_type_name_w = convert_string_to_utf16(delegate_type_name); 362 | } 363 | else { 364 | delegate_type_name_w = UNMANAGEDCALLERSONLY_METHOD; 365 | } 366 | 367 | status = hostfxr->get_function_pointer(type_name_w, 368 | method_name_w, delegate_type_name_w, 369 | load_context, reserved, delegate); 370 | 371 | free((void*) type_name_w); 372 | free((void*) method_name_w); 373 | 374 | if (delegate_type_name != UNMANAGEDCALLERSONLY_METHOD_A) 375 | free((void*) delegate_type_name_w); 376 | 377 | return status; 378 | #else 379 | return hostfxr->get_function_pointer(type_name, 380 | method_name, delegate_type_name, 381 | load_context, reserved, delegate); 382 | #endif 383 | } 384 | 385 | typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)( 386 | const char_t *type_name /* Assembly qualified type name */, 387 | const char_t *method_name /* Public static method name compatible with delegateType */, 388 | const char_t *delegate_type_name /* Assembly qualified delegate type name or null, 389 | or UNMANAGEDCALLERSONLY_METHOD if the method is marked with 390 | the UnmanagedCallersOnlyAttribute. */, 391 | void *load_context /* Extensibility parameter (currently unused and must be 0) */, 392 | void *reserved /* Extensibility parameter (currently unused and must be 0) */, 393 | /*out*/ void **delegate /* Pointer where to store the function pointer result */); 394 | 395 | bool load_hostfxr(HOSTFXR_CONTEXT* hostfxr, const char* hostfxr_path) 396 | { 397 | void* lib_handle = load_library(hostfxr_path); 398 | 399 | memset(hostfxr, 0, sizeof(HOSTFXR_CONTEXT)); 400 | 401 | if (!lib_handle) { 402 | printf("could not load %s\n", hostfxr_path); 403 | } 404 | 405 | hostfxr->initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn) 406 | get_proc_address(lib_handle, "hostfxr_initialize_for_dotnet_command_line"); 407 | hostfxr->initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn) 408 | get_proc_address(lib_handle, "hostfxr_initialize_for_runtime_config"); 409 | hostfxr->get_runtime_property_value = (hostfxr_get_runtime_property_value_fn) 410 | get_proc_address(lib_handle, "hostfxr_get_runtime_property_value"); 411 | hostfxr->set_runtime_property_value = (hostfxr_set_runtime_property_value_fn) 412 | get_proc_address(lib_handle, "hostfxr_set_runtime_property_value"); 413 | hostfxr->get_runtime_properties = (hostfxr_get_runtime_properties_fn) 414 | get_proc_address(lib_handle, "hostfxr_get_runtime_properties"); 415 | hostfxr->run_app = (hostfxr_run_app_fn) 416 | get_proc_address(lib_handle, "hostfxr_run_app"); 417 | hostfxr->get_runtime_delegate = (hostfxr_get_runtime_delegate_fn) 418 | get_proc_address(lib_handle, "hostfxr_get_runtime_delegate"); 419 | hostfxr->close = (hostfxr_close_fn) 420 | get_proc_address(lib_handle, "hostfxr_close"); 421 | 422 | return true; 423 | } 424 | 425 | bool load_runtime(HOSTFXR_CONTEXT* hostfxr, const char* config_path) 426 | { 427 | hostfxr_handle ctx = NULL; 428 | load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = NULL; 429 | 430 | int rc = hostfxr_initialize_for_runtime_config(config_path, NULL, &ctx); 431 | 432 | if ((rc != 0) || (ctx == NULL)) { 433 | printf("initialize_for_runtime_config(%s) failure\n", config_path); 434 | return false; 435 | } 436 | 437 | rc = hostfxr->get_runtime_delegate(ctx, 438 | hdt_load_assembly_and_get_function_pointer, 439 | (void**) &load_assembly_and_get_function_pointer); 440 | 441 | if ((rc != 0) || (NULL == load_assembly_and_get_function_pointer)) { 442 | printf("get_runtime_delegate failure\n"); 443 | return false; 444 | } 445 | 446 | hostfxr->close(ctx); 447 | 448 | hostfxr->load_assembly_and_get_function_pointer = load_assembly_and_get_function_pointer; 449 | 450 | return true; 451 | } 452 | 453 | typedef int32_t (CORECLR_DELEGATE_CALLTYPE * fnLoadAssemblyFromNativeMemory)(uint8_t* bytes, int32_t size); 454 | 455 | static fnLoadAssemblyFromNativeMemory g_LoadAssemblyFromNativeMemory = NULL; 456 | 457 | bool load_assembly_helper(HOSTFXR_CONTEXT* hostfxr, const char* helper_path, const char* type_name) 458 | { 459 | int rc; 460 | 461 | rc = hostfxr_load_assembly_and_get_function_pointer(helper_path, 462 | type_name, "LoadAssemblyFromNativeMemory", 463 | UNMANAGEDCALLERSONLY_METHOD_A, NULL, (void**) &g_LoadAssemblyFromNativeMemory); 464 | 465 | if (rc != 0) { 466 | printf("load_assembly_and_get_function_pointer(LoadAssemblyFromNativeMemory): 0x%08X\n", rc); 467 | return false; 468 | } 469 | 470 | return true; 471 | } 472 | 473 | typedef void* hPowerShell; 474 | typedef hPowerShell (CORECLR_DELEGATE_CALLTYPE * fnPowerShell_Create)(void); 475 | typedef void (CORECLR_DELEGATE_CALLTYPE * fnPowerShell_AddScript)(hPowerShell handle, const char* script); 476 | typedef void (CORECLR_DELEGATE_CALLTYPE * fnPowerShell_Invoke)(hPowerShell handle); 477 | 478 | typedef struct 479 | { 480 | fnPowerShell_Create Create; 481 | fnPowerShell_AddScript AddScript; 482 | fnPowerShell_Invoke Invoke; 483 | } iPowerShell; 484 | 485 | extern const unsigned int bindings_size; 486 | extern unsigned char bindings_data[]; 487 | 488 | bool load_pwsh_sdk(HOSTFXR_CONTEXT* hostfxr, iPowerShell* iface) 489 | { 490 | int rc; 491 | size_t assembly_size = (size_t) bindings_size; 492 | uint8_t* assembly_data = (uint8_t*) &bindings_data; 493 | 494 | memset(iface, 0, sizeof(iPowerShell)); 495 | 496 | rc = g_LoadAssemblyFromNativeMemory(assembly_data, (int32_t) assembly_size); 497 | 498 | if (rc < 0) { 499 | printf("LoadAssemblyFromNativeMemory failure: %d\n", rc); 500 | return false; 501 | } 502 | 503 | rc = hostfxr_get_function_pointer( 504 | "NativeHost.Bindings, Bindings", "PowerShell_Create", 505 | UNMANAGEDCALLERSONLY_METHOD_A, NULL, NULL, (void**) &iface->Create); 506 | 507 | if (rc != 0) { 508 | printf("get_function_pointer failure: 0x%08X\n", rc); 509 | return false; 510 | } 511 | 512 | rc = hostfxr_get_function_pointer( 513 | "NativeHost.Bindings, Bindings", "PowerShell_AddScript", 514 | UNMANAGEDCALLERSONLY_METHOD_A, NULL, NULL, (void**) &iface->AddScript); 515 | 516 | rc = hostfxr_get_function_pointer( 517 | "NativeHost.Bindings, Bindings", "PowerShell_Invoke", 518 | UNMANAGEDCALLERSONLY_METHOD_A, NULL, NULL, (void**) &iface->Invoke); 519 | 520 | return true; 521 | } 522 | 523 | bool call_pwsh_sdk(HOSTFXR_CONTEXT* hostfxr) 524 | { 525 | iPowerShell iface; 526 | 527 | load_pwsh_sdk(hostfxr, &iface); 528 | 529 | hPowerShell handle = iface.Create(); 530 | iface.AddScript(handle, "$TempPath = [System.IO.Path]::GetTempPath();"); 531 | iface.AddScript(handle, "Set-Content -Path $(Join-Path $TempPath pwsh-date.txt) -Value \"Microsoft.PowerShell.SDK: $(Get-Date)\""); 532 | iface.Invoke(handle); 533 | 534 | return true; 535 | } 536 | 537 | bool load_command(HOSTFXR_CONTEXT* hostfxr, int argc, const char** argv, bool close_handle) 538 | { 539 | hostfxr_handle ctx = NULL; 540 | load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = NULL; 541 | get_function_pointer_fn get_function_pointer = NULL; 542 | 543 | int rc = hostfxr_initialize_for_dotnet_command_line(argc, argv, NULL, &ctx); 544 | 545 | if ((rc != 0) || (ctx == NULL)) { 546 | printf("hostfxr->initialize_for_dotnet_command_line() failure: 0x%08X\n", rc); 547 | return false; 548 | } 549 | 550 | rc = hostfxr->get_runtime_delegate(ctx, 551 | hdt_load_assembly_and_get_function_pointer, 552 | (void**) &load_assembly_and_get_function_pointer); 553 | 554 | if ((rc != 0) || (NULL == load_assembly_and_get_function_pointer)) { 555 | printf("get_runtime_delegate failure: 0x%08X\n", rc); 556 | return false; 557 | } 558 | 559 | rc = hostfxr->get_runtime_delegate(ctx, 560 | hdt_get_function_pointer, 561 | (void**) &get_function_pointer); 562 | 563 | if ((rc != 0) || (NULL == get_function_pointer)) { 564 | printf("get_runtime_delegate failure: 0x%08X\n", rc); 565 | return false; 566 | } 567 | 568 | hostfxr->context_handle = ctx; 569 | 570 | if (close_handle) { 571 | hostfxr->close(ctx); 572 | } 573 | 574 | hostfxr->load_assembly_and_get_function_pointer = load_assembly_and_get_function_pointer; 575 | hostfxr->get_function_pointer = get_function_pointer; 576 | 577 | return true; 578 | } 579 | 580 | bool run_pwsh_app() 581 | { 582 | char base_path[HOSTFXR_MAX_PATH]; 583 | char hostfxr_path[HOSTFXR_MAX_PATH]; 584 | char runtime_config_path[HOSTFXR_MAX_PATH]; 585 | char assembly_path[HOSTFXR_MAX_PATH]; 586 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 587 | 588 | strncpy(base_path, g_PWSH_BASE_PATH, HOSTFXR_MAX_PATH); 589 | snprintf(hostfxr_path, HOSTFXR_MAX_PATH, "%s%s%s", base_path, PATH_SEPARATOR_STR, HOSTFXR_LIB_NAME); 590 | 591 | if (!load_hostfxr(hostfxr, hostfxr_path)) { 592 | printf("failed to load hostfxr!\n"); 593 | return false; 594 | } 595 | 596 | snprintf(runtime_config_path, HOSTFXR_MAX_PATH, "%s%s%s.runtimeconfig.json", 597 | base_path, PATH_SEPARATOR_STR, "pwsh"); 598 | snprintf(assembly_path, HOSTFXR_MAX_PATH, "%s%s%s.dll", base_path, PATH_SEPARATOR_STR, "pwsh"); 599 | 600 | char* command_args[] = { 601 | assembly_path, 602 | "-NoLogo", 603 | "-Command", 604 | "Write-Host 'Hello PowerShell Host'" 605 | }; 606 | int command_argc = sizeof(command_args) / sizeof(char*); 607 | 608 | if (!load_command(hostfxr, command_argc, (const char**) command_args, false)) { 609 | printf("failed to load runtime!\n"); 610 | return false; 611 | } 612 | 613 | hostfxr->run_app(hostfxr->context_handle); 614 | 615 | return true; 616 | } 617 | 618 | bool run_pwsh_lib() 619 | { 620 | char base_path[HOSTFXR_MAX_PATH]; 621 | char hostfxr_path[HOSTFXR_MAX_PATH]; 622 | char coreclr_path[HOSTFXR_MAX_PATH]; 623 | char runtime_config_path[HOSTFXR_MAX_PATH]; 624 | char assembly_path[HOSTFXR_MAX_PATH]; 625 | HOSTFXR_CONTEXT* hostfxr = &g_HOSTFXR_CONTEXT; 626 | CORECLR_CONTEXT* coreclr = &g_CORECLR_CONTEXT; 627 | 628 | strncpy(base_path, g_PWSH_BASE_PATH, HOSTFXR_MAX_PATH); 629 | snprintf(hostfxr_path, HOSTFXR_MAX_PATH, "%s%s%s", base_path, PATH_SEPARATOR_STR, HOSTFXR_LIB_NAME); 630 | snprintf(coreclr_path, HOSTFXR_MAX_PATH, "%s%s%s", base_path, PATH_SEPARATOR_STR, CORECLR_LIB_NAME); 631 | 632 | if (!load_hostfxr(hostfxr, hostfxr_path)) { 633 | printf("failed to load hostfxr!\n"); 634 | return false; 635 | } 636 | 637 | if (!load_coreclr(coreclr, coreclr_path)) { 638 | printf("failed to load coreclr!\n"); 639 | return false; 640 | } 641 | 642 | snprintf(runtime_config_path, HOSTFXR_MAX_PATH, "%s%s%s.runtimeconfig.json", 643 | base_path, PATH_SEPARATOR_STR, "pwsh"); 644 | snprintf(assembly_path, HOSTFXR_MAX_PATH, "%s%s%s.dll", base_path, PATH_SEPARATOR_STR, "pwsh"); 645 | 646 | printf("loading %s\n", runtime_config_path); 647 | 648 | char* command_args[] = { 649 | assembly_path 650 | }; 651 | int command_argc = sizeof(command_args) / sizeof(char*); 652 | 653 | if (!load_command(hostfxr, command_argc, (const char**) command_args, false)) { 654 | printf("failed to load runtime!\n"); 655 | return false; 656 | } 657 | 658 | char helper_assembly_path[HOSTFXR_MAX_PATH]; 659 | 660 | snprintf(helper_assembly_path, HOSTFXR_MAX_PATH, "%s%sSystem.Management.Automation.dll", base_path, PATH_SEPARATOR_STR); 661 | if (!load_assembly_helper(hostfxr, helper_assembly_path, 662 | "System.Management.Automation.PowerShellUnsafeAssemblyLoad, System.Management.Automation")) { 663 | printf("failed to load PowerShellUnsafeAssemblyLoad helper function!\n"); 664 | return false; 665 | } 666 | 667 | call_pwsh_sdk(hostfxr); 668 | 669 | return true; 670 | } 671 | 672 | bool detect_pwsh() 673 | { 674 | // TODO: proper detect PowerShell installation path 675 | 676 | if (get_env("PWSH_BASE_PATH", g_PWSH_BASE_PATH, HOSTFXR_MAX_PATH) < 1) { 677 | #ifdef _WIN32 678 | strncpy(g_PWSH_BASE_PATH, "C:\\Program Files\\PowerShell\\7-preview", HOSTFXR_MAX_PATH); 679 | #else 680 | strncpy(g_PWSH_BASE_PATH, "/opt/microsoft/powershell/7-preview", HOSTFXR_MAX_PATH); 681 | #endif 682 | printf("Set PWSH_BASE_PATH environment variable to point to PowerShell installation path\n"); 683 | printf("using hardcoded PowerShell installation path: \"%s\"\n", g_PWSH_BASE_PATH); 684 | } 685 | 686 | return true; 687 | } 688 | 689 | int main(int argc, char** argv) 690 | { 691 | detect_pwsh(); 692 | 693 | if (argc > 1) { 694 | if (!strcmp(argv[1], "app")) { 695 | run_pwsh_app(); 696 | } else if (!strcmp(argv[1], "lib")) { 697 | run_pwsh_lib(); 698 | } 699 | } else { 700 | run_pwsh_app(); 701 | } 702 | 703 | return 0; 704 | } 705 | --------------------------------------------------------------------------------