├── .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 |
--------------------------------------------------------------------------------