├── .gitignore
├── README.md
├── efi-no-runtime
├── README.md
├── build.cmd
├── efinoruntime.csproj
└── zerosharp.cs
├── no-runtime
├── build.cmd
├── noruntime.csproj
└── zerosharp.cs
└── with-runtime
├── Test.CoreLib.dll
├── Test.CoreLib.pdb
├── withruntime.csproj
└── zerosharp.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | *.exe
5 | *.ilexe
6 | *.pdb
7 | *.obj
8 | *.map
9 |
10 | !Test.CoreLib.pdb
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C# for systems programming
2 |
3 | These samples show how to compile C# to native code using the .NET Native AOT technology ([NativeAOT](https://github.com/dotnet/runtimelab/tree/feature/NativeAOT), also known as CoreRT previously).
4 |
5 | The samples are for people who would like to use C#, but don't want to be bound by the choices of the base class libraries that normally come with C# (in the form that it's bundled in .NET). If you just want to native compile your .NET apps, go to the [NativeAOT](https://github.com/dotnet/runtimelab/tree/feature/NativeAOT) repo/branch instead. Nothing to see for you in this repo.
6 |
7 | `no-runtime` is a rather pointless sample that demonstrates how to write code in C# that is directly runnable without a runtime. C# has value types and you can p/invoke into an unmanaged memory allocator, so you can do things with this, but you're so severily limited it's rather pointless. But Hello world ends up being about 8 kB native EXE with no dependencies, so that's rather cool.
8 |
9 | `with-runtime` is something that can be actually useful. This includes the full managed and unmanaged runtime - GC, exception handling, and interface dispatch all work. Test.CoreLib used as the class library here is the same Test.CoreLib that you can find in the NativeAOT repo. Don't look for things like `Object.ToString()` because being compatible with .NET is not the point. This sample comes down to about 400 kB, most of which is the C runtime library.
10 |
11 | `efi-no-runtime` is an EFI boot application that lets you run C# on bare metal, without an OS. Similar restrictions to the `no-runtime` sample apply. Making a version of this sample with a runtime would require some porting work on the runtime side.
12 |
13 | ## Building the samples
14 |
15 | [.NET 7 SDK](https://dotnet.microsoft.com/download) is a prerequisite for building these on all platforms.
16 |
17 | In addition to the .NET 7 SDK, these are needed:
18 | * On Windows: Visual Studio 2022 **with** C++ development support and a Windows SDK
19 | * On Linux: clang
20 | * On macOS (untested): XCode
21 |
22 | Once you made sure you have the prerequisites, enter the appropriate sample directory and type:
23 |
24 | ```bash
25 | $ dotnet publish -c Release
26 | ```
27 |
28 | Some samples also come with a shell script (*.cmd) that pieces together all the tools and avoid MSBuild or dotnet. You need to make sure you have environment set up before running the script. Look at the script for details. The script is redundant with the *.csproj project files.
29 |
--------------------------------------------------------------------------------
/efi-no-runtime/README.md:
--------------------------------------------------------------------------------
1 | # EFI boot application in C#
2 |
3 | This sample is a EFI boot application written in C# that displays Hello World. It runs without an OS, on x64 bare metal hardware.
4 |
5 |
6 |
7 | ## Building the program
8 |
9 | Refer to the general instructions at the root of the repo.
10 |
11 | ## Booting the generated program
12 |
13 | Note: producing VHDX requires running from an elevated command prompt. It will not work without elevation.
14 |
15 | Running `build.cmd` should produce a BOOTX64.EFI file in the current directory. There are multiple ways to run this. QEMU with an EFI firmware should work. I use Hyper-V.
16 |
17 | Running `build.cmd vhd` will produce a VHDX file for you that you can run on Hyper-V directly. You need to create a new Gen 2 virtual machine in Hyper-V and attach the generated disk. Make sure to turn off Secure boot in the virtual machine: the EFI image is not signed.
18 |
19 | Similarly, adding `-p:BuildVHDX=true` to the `dotnet publish` line (when using the `*.csproj` project) will produce a bootable VHDX.
20 |
21 | ### Booting in VirtualBox
22 |
23 | To boot it in VirtualBox, we need to get a `vdi` file. First, create the vhdx like explained above (for example, `build.cmd vhd`). Then, you can use VB's built-in tool to convert the `vhdx` file into a `vdi` one:
24 | ```
25 | VBoxManage.exe clonemedium disk zerosharp.vhdx zerosharp.vdi
26 | ```
27 |
28 | Now, go to VirtualBox, create an empty OS, load the `vdi` file as a hard drive, and it should work.
29 |
--------------------------------------------------------------------------------
/efi-no-runtime/build.cmd:
--------------------------------------------------------------------------------
1 | ::
2 | :: "Manual" build script that bypasses MSBuild and directly invokes the necessary tools.
3 | :: Good to show how things get hooked up together, but redundant with the project file.
4 | ::
5 | :: The tools are:
6 | ::
7 | :: * CSC, the C# compiler
8 | :: Opening a "x64 Native Tools Command Prompt for VS 2019" will place csc.exe on your PATH.
9 | :: * ILC, the Native AOT compiler
10 | :: If you use the project file to build this sample at least once, you can find ILC
11 | :: in your NuGet cache. It will be somewhere like
12 | :: C:\Users\username\.nuget\packages\runtime.win-x64.microsoft.dotnet.ilcompiler\7.0.0-alpha.1.21430.2
13 | :: * Linker
14 | :: This is the platform linker. "x64 Native Tools Command Prompt for VS 2019" will place
15 | :: the linker on your PATH.
16 | ::
17 |
18 | @set ILCPATH=%DROPPATH%\tools
19 | @if not exist %ILCPATH%\ilc.exe (
20 | echo The DROPPATH environment variable not set.
21 | exit /B
22 | )
23 | @where csc >nul 2>&1
24 | @if ERRORLEVEL 1 (
25 | echo CSC not on the PATH.
26 | exit /B
27 | )
28 |
29 | @set VHD=%CD%\zerosharp.vhdx
30 | @set VHD_SCRIPT=%CD%\diskpart.txt
31 | @del %VHD% >nul 2>&1
32 | @del %VHD_SCRIPT% >nul 2>&1
33 | @del zerosharp.ilexe >nul 2>&1
34 | @del zerosharp.obj >nul 2>&1
35 | @del zerosharp.map >nul 2>&1
36 | @del zerosharp.pdb >nul 2>&1
37 | @del BOOTX64.EFI >nul 2>&1
38 |
39 | @if "%1" == "clean" exit /B
40 |
41 | csc /nologo /debug:embedded /noconfig /nostdlib /runtimemetadataversion:v4.0.30319 zerosharp.cs /out:zerosharp.ilexe /langversion:latest /unsafe
42 | %ILCPATH%\ilc zerosharp.ilexe -o zerosharp.obj --systemmodule zerosharp --map zerosharp.map -O
43 | link /nologo /subsystem:EFI_APPLICATION zerosharp.obj /entry:EfiMain /incremental:no /out:BOOTX64.EFI
44 |
45 | @rem Build a VHD if requested
46 |
47 | @if not "%1" == "vhd" exit /B
48 |
49 | @(
50 | echo create vdisk file=%VHD% maximum=500
51 | echo select vdisk file=%VHD%
52 | echo attach vdisk
53 | echo convert gpt
54 | echo create partition efi size=100
55 | echo format quick fs=fat32 label="System"
56 | echo assign letter="X"
57 | echo exit
58 | )>%VHD_SCRIPT%
59 |
60 | diskpart /s %VHD_SCRIPT%
61 |
62 | xcopy BOOTX64.EFI X:\EFI\BOOT\
63 |
64 | @(
65 | echo select vdisk file=%VHD%
66 | echo select partition 2
67 | echo remove letter=X
68 | echo detach vdisk
69 | echo exit
70 | )>%VHD_SCRIPT%
71 |
72 | diskpart /s %VHD_SCRIPT%
73 |
--------------------------------------------------------------------------------
/efi-no-runtime/efinoruntime.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | true
7 |
8 | true
9 | true
10 | v4.0.30319
11 | false
12 | false
13 |
14 | efinoruntime
15 | EfiMain
16 | EFI_APPLICATION
17 |
18 | true
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
34 |
35 | $([System.IO.Path]::GetFullPath('$(PublishDir)'))zerosharp.vhdx
36 |
37 |
38 | create vdisk file=$(VHDFilePath) maximum=500
39 | select vdisk file=$(VHDFilePath)
40 | attach vdisk
41 | convert gpt
42 | create partition efi size=100
43 | format quick fs=fat32 label="System"
44 | assign letter=X
45 |
46 |
47 | select vdisk file=$(VHDFilePath)
48 | select partition 2
49 | remove letter=X
50 | detach vdisk
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/efi-no-runtime/zerosharp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | #region A couple very basic things
5 | namespace System
6 | {
7 | public class Object
8 | {
9 | #pragma warning disable 169
10 | // The layout of object is a contract with the compiler.
11 | private IntPtr m_pMethodTable;
12 | #pragma warning restore 169
13 | }
14 | public struct Void { }
15 |
16 | // The layout of primitive types is special cased because it would be recursive.
17 | // These really don't need any fields to work.
18 | public struct Boolean { }
19 | public struct Char { }
20 | public struct SByte { }
21 | public struct Byte { }
22 | public struct Int16 { }
23 | public struct UInt16 { }
24 | public struct Int32 { }
25 | public struct UInt32 { }
26 | public struct Int64 { }
27 | public struct UInt64 { }
28 | public struct IntPtr { }
29 | public struct UIntPtr { }
30 | public struct Single { }
31 | public struct Double { }
32 |
33 | public abstract class ValueType { }
34 | public abstract class Enum : ValueType { }
35 |
36 | public struct Nullable where T : struct { }
37 |
38 | public sealed class String { public readonly int Length; }
39 | public abstract class Array { }
40 | public abstract class Delegate { }
41 | public abstract class MulticastDelegate : Delegate { }
42 |
43 | public struct RuntimeTypeHandle { }
44 | public struct RuntimeMethodHandle { }
45 | public struct RuntimeFieldHandle { }
46 |
47 | public class Attribute { }
48 |
49 | public enum AttributeTargets { }
50 |
51 | public sealed class AttributeUsageAttribute : Attribute
52 | {
53 | public AttributeUsageAttribute(AttributeTargets validOn) { }
54 | public bool AllowMultiple { get; set; }
55 | public bool Inherited { get; set; }
56 | }
57 |
58 | public class AppContext
59 | {
60 | public static void SetData(string s, object o) { }
61 | }
62 |
63 | namespace Runtime.CompilerServices
64 | {
65 | public class RuntimeHelpers
66 | {
67 | public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int);
68 | }
69 |
70 | public static class RuntimeFeature
71 | {
72 | public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
73 | }
74 | }
75 | }
76 |
77 | namespace System.Runtime.InteropServices
78 | {
79 | public class UnmanagedType { }
80 |
81 | sealed class StructLayoutAttribute : Attribute
82 | {
83 | public StructLayoutAttribute(LayoutKind layoutKind)
84 | {
85 | }
86 | }
87 |
88 | internal enum LayoutKind
89 | {
90 | Sequential = 0, // 0x00000008,
91 | Explicit = 2, // 0x00000010,
92 | Auto = 3, // 0x00000000,
93 | }
94 |
95 | internal enum CharSet
96 | {
97 | None = 1, // User didn't specify how to marshal strings.
98 | Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars.
99 | Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars.
100 | Auto = 4, // Marshal Strings in the right way for the target system.
101 | }
102 | }
103 | #endregion
104 |
105 | #region Things needed by ILC
106 | namespace System
107 | {
108 | namespace Runtime
109 | {
110 | internal sealed class RuntimeExportAttribute : Attribute
111 | {
112 | public RuntimeExportAttribute(string entry) { }
113 | }
114 | }
115 |
116 | class Array : Array { }
117 | }
118 |
119 | namespace Internal.Runtime.CompilerHelpers
120 | {
121 | using System.Runtime;
122 |
123 | // A class that the compiler looks for that has helpers to initialize the
124 | // process. The compiler can gracefully handle the helpers not being present,
125 | // but the class itself being absent is unhandled. Let's add an empty class.
126 | class StartupCodeHelpers
127 | {
128 | // A couple symbols the generated code will need we park them in this class
129 | // for no particular reason. These aid in transitioning to/from managed code.
130 | // Since we don't have a GC, the transition is a no-op.
131 | [RuntimeExport("RhpReversePInvoke")]
132 | static void RhpReversePInvoke(IntPtr frame) { }
133 | [RuntimeExport("RhpReversePInvokeReturn")]
134 | static void RhpReversePInvokeReturn(IntPtr frame) { }
135 | [RuntimeExport("RhpPInvoke")]
136 | static void RhpPInvoke(IntPtr frame) { }
137 | [RuntimeExport("RhpPInvokeReturn")]
138 | static void RhpPInvokeReturn(IntPtr frame) { }
139 |
140 | [RuntimeExport("RhpFallbackFailFast")]
141 | static void RhpFallbackFailFast() { while (true) ; }
142 | }
143 | }
144 | #endregion
145 |
146 | [StructLayout(LayoutKind.Sequential)]
147 | struct EFI_HANDLE
148 | {
149 | private IntPtr _handle;
150 | }
151 |
152 | [StructLayout(LayoutKind.Sequential)]
153 | unsafe readonly struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
154 | {
155 | private readonly IntPtr _pad;
156 |
157 | public readonly delegate* unmanaged OutputString;
158 | }
159 |
160 | [StructLayout(LayoutKind.Sequential)]
161 | readonly struct EFI_TABLE_HEADER
162 | {
163 | public readonly ulong Signature;
164 | public readonly uint Revision;
165 | public readonly uint HeaderSize;
166 | public readonly uint Crc32;
167 | public readonly uint Reserved;
168 | }
169 |
170 | [StructLayout(LayoutKind.Sequential)]
171 | unsafe readonly struct EFI_SYSTEM_TABLE
172 | {
173 | public readonly EFI_TABLE_HEADER Hdr;
174 | public readonly char* FirmwareVendor;
175 | public readonly uint FirmwareRevision;
176 | public readonly EFI_HANDLE ConsoleInHandle;
177 | public readonly void* ConIn;
178 | public readonly EFI_HANDLE ConsoleOutHandle;
179 | public readonly EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* ConOut;
180 | }
181 |
182 | unsafe class Program
183 | {
184 | static void Main() { }
185 |
186 | [System.Runtime.RuntimeExport("EfiMain")]
187 | static long EfiMain(IntPtr imageHandle, EFI_SYSTEM_TABLE* systemTable)
188 | {
189 | string hello = "Hello world!";
190 | fixed (char* pHello = hello)
191 | {
192 | systemTable->ConOut->OutputString(systemTable->ConOut, pHello);
193 | }
194 |
195 | while (true) ;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/no-runtime/build.cmd:
--------------------------------------------------------------------------------
1 | ::
2 | :: "Manual" build script that bypasses MSBuild and directly invokes the necessary tools.
3 | :: Good to show how things get hooked up together, but redundant with the project file.
4 | ::
5 | :: The tools are:
6 | ::
7 | :: * CSC, the C# compiler
8 | :: Opening a "x64 Native Tools Command Prompt for VS 2019" will place csc.exe on your PATH.
9 | :: * ILC, the Native AOT compiler
10 | :: If you use the project file to build this sample at least once, you can find ILC
11 | :: in your NuGet cache. It will be somewhere like
12 | :: C:\Users\username\.nuget\packages\runtime.win-x64.microsoft.dotnet.ilcompiler\7.0.0-alpha.1.21430.2
13 | :: * Linker
14 | :: This is the platform linker. "x64 Native Tools Command Prompt for VS 2019" will place
15 | :: the linker on your PATH.
16 | ::
17 |
18 | @set ILCPATH=%DROPPATH%\tools
19 | @if not exist %ILCPATH%\ilc.exe (
20 | echo The DROPPATH environment variable not set.
21 | exit /B
22 | )
23 | @where csc >nul 2>&1
24 | @if ERRORLEVEL 1 (
25 | echo CSC not on the PATH.
26 | exit /B
27 | )
28 |
29 | @del zerosharp.ilexe >nul 2>&1
30 | @del zerosharp.obj >nul 2>&1
31 | @del zerosharp.exe >nul 2>&1
32 | @del zerosharp.map >nul 2>&1
33 | @del zerosharp.pdb >nul 2>&1
34 |
35 | @if "%1" == "clean" exit /B
36 |
37 | csc /define:WINDOWS /debug:embedded /noconfig /nostdlib /runtimemetadataversion:v4.0.30319 zerosharp.cs /out:zerosharp.ilexe /langversion:latest /unsafe || goto Error
38 | %ILCPATH%\ilc zerosharp.ilexe -g -o zerosharp.obj --systemmodule zerosharp --map zerosharp.map -O --directpinvoke:kernel32 || goto Error
39 | link /debug /subsystem:console zerosharp.obj /entry:__managed__Main kernel32.lib /incremental:no || goto Error
40 |
41 | @goto :EOF
42 |
43 | :Error
44 | @echo Tool failed.
45 | exit /B 1
46 |
--------------------------------------------------------------------------------
/no-runtime/noruntime.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | true
7 |
8 | true
9 | true
10 | v4.0.30319
11 | false
12 | false
13 |
14 | noruntime
15 | __managed__Main
16 |
17 | true
18 |
19 | $(DefineConstants);WINDOWS
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/no-runtime/zerosharp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime;
3 | using System.Runtime.InteropServices;
4 |
5 | #region A couple very basic things
6 | namespace System
7 | {
8 | public class Object
9 | {
10 | #pragma warning disable 169
11 | // The layout of object is a contract with the compiler.
12 | private IntPtr m_pMethodTable;
13 | #pragma warning restore 169
14 | }
15 | public struct Void { }
16 |
17 | // The layout of primitive types is special cased because it would be recursive.
18 | // These really don't need any fields to work.
19 | public struct Boolean { }
20 | public struct Char { }
21 | public struct SByte { }
22 | public struct Byte { }
23 | public struct Int16 { }
24 | public struct UInt16 { }
25 | public struct Int32 { }
26 | public struct UInt32 { }
27 | public struct Int64 { }
28 | public struct UInt64 { }
29 | public struct IntPtr { }
30 | public struct UIntPtr { }
31 | public struct Single { }
32 | public struct Double { }
33 |
34 | public abstract class ValueType { }
35 | public abstract class Enum : ValueType { }
36 |
37 | public struct Nullable where T : struct { }
38 |
39 | public sealed class String { public readonly int Length; }
40 | public abstract class Array { }
41 | public abstract class Delegate { }
42 | public abstract class MulticastDelegate : Delegate { }
43 |
44 | public struct RuntimeTypeHandle { }
45 | public struct RuntimeMethodHandle { }
46 | public struct RuntimeFieldHandle { }
47 |
48 | public class Attribute { }
49 |
50 | public enum AttributeTargets { }
51 |
52 | public sealed class AttributeUsageAttribute : Attribute
53 | {
54 | public AttributeUsageAttribute(AttributeTargets validOn) { }
55 | public bool AllowMultiple { get; set; }
56 | public bool Inherited { get; set; }
57 | }
58 |
59 | public class AppContext
60 | {
61 | public static void SetData(string s, object o) { }
62 | }
63 |
64 | namespace Runtime.CompilerServices
65 | {
66 | public class RuntimeHelpers
67 | {
68 | public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int);
69 | }
70 | }
71 | }
72 | namespace System.Runtime.InteropServices
73 | {
74 | public sealed class DllImportAttribute : Attribute
75 | {
76 | public DllImportAttribute(string dllName) { }
77 | }
78 | }
79 | #endregion
80 |
81 | #region Things needed by ILC
82 | namespace System
83 | {
84 | namespace Runtime
85 | {
86 | internal sealed class RuntimeExportAttribute : Attribute
87 | {
88 | public RuntimeExportAttribute(string entry) { }
89 | }
90 | }
91 |
92 | class Array : Array { }
93 | }
94 |
95 | namespace Internal.Runtime.CompilerHelpers
96 | {
97 | // A class that the compiler looks for that has helpers to initialize the
98 | // process. The compiler can gracefully handle the helpers not being present,
99 | // but the class itself being absent is unhandled. Let's add an empty class.
100 | class StartupCodeHelpers
101 | {
102 | // A couple symbols the generated code will need we park them in this class
103 | // for no particular reason. These aid in transitioning to/from managed code.
104 | // Since we don't have a GC, the transition is a no-op.
105 | [RuntimeExport("RhpReversePInvoke")]
106 | static void RhpReversePInvoke(IntPtr frame) { }
107 | [RuntimeExport("RhpReversePInvokeReturn")]
108 | static void RhpReversePInvokeReturn(IntPtr frame) { }
109 | [RuntimeExport("RhpPInvoke")]
110 | static void RhpPInvoke(IntPtr frame) { }
111 | [RuntimeExport("RhpPInvokeReturn")]
112 | static void RhpPInvokeReturn(IntPtr frame) { }
113 |
114 | [RuntimeExport("RhpFallbackFailFast")]
115 | static void RhpFallbackFailFast() { while (true) ; }
116 | }
117 | }
118 | #endregion
119 |
120 | unsafe class Program
121 | {
122 | [DllImport("libc")]
123 | static extern int printf(byte* fmt);
124 |
125 | [DllImport("kernel32")]
126 | static extern IntPtr GetStdHandle(int nStdHandle);
127 |
128 | [DllImport("kernel32")]
129 | static extern IntPtr WriteConsoleW(IntPtr hConsole, void* lpBuffer, int charsToWrite, out int charsWritten, void* reserved);
130 |
131 | #if !WINDOWS
132 | // Export this as "main" so that we can link with the C runtime library properly.
133 | // If the C runtime library is not initialized we can't even printf.
134 | // This is not needed on Windows because we don't call the C runtime.
135 | [RuntimeExport("main")]
136 | #endif
137 | static int Main()
138 | {
139 | string hello = "Hello world!\n";
140 | fixed (char* pHello = hello)
141 | {
142 | #if WINDOWS
143 | WriteConsoleW(GetStdHandle(-11), pHello, hello.Length, out int _, null);
144 | #else
145 | // Once C# has support for UTF-8 string literals, this can be simplified.
146 | // https://github.com/dotnet/csharplang/issues/2911
147 | // Since we don't have that, convert from UTF-16 to ASCII.
148 | byte* pHelloASCII = stackalloc byte[hello.Length + 1];
149 | for (int i = 0; i < hello.Length; i++)
150 | pHelloASCII[i] = (byte)pHello[i];
151 |
152 | printf(pHelloASCII);
153 | #endif
154 | }
155 |
156 | return 42;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/with-runtime/Test.CoreLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MichalStrehovsky/zerosharp/33cb5b6a663da1918767a2c624afcd0b16e1da43/with-runtime/Test.CoreLib.dll
--------------------------------------------------------------------------------
/with-runtime/Test.CoreLib.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MichalStrehovsky/zerosharp/33cb5b6a663da1918767a2c624afcd0b16e1da43/with-runtime/Test.CoreLib.pdb
--------------------------------------------------------------------------------
/with-runtime/withruntime.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | true
7 |
8 | true
9 | true
10 | v4.0.30319
11 | false
12 | false
13 |
14 | Test.CoreLib
15 |
16 | true
17 |
18 | 10
19 |
20 |
21 |
22 |
23 | Test.CoreLib.dll
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/with-runtime/zerosharp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | static unsafe class Console
5 | {
6 | [DllImport("kernel32")]
7 | static extern IntPtr GetStdHandle(int nStdHandle);
8 |
9 | [DllImport("kernel32")]
10 | static extern IntPtr WriteConsoleW(IntPtr hConsole, void* lpBuffer, int charsToWrite, out int charsWritten, void* reserved);
11 |
12 | public static void WriteLine(string s)
13 | {
14 | IntPtr stdInputHandle = GetStdHandle(-11);
15 | int charsWritten;
16 |
17 | fixed (char* c = s)
18 | {
19 | WriteConsoleW(stdInputHandle, c, s.Length, out charsWritten, null);
20 | }
21 |
22 | char newLine = '\n';
23 | WriteConsoleW(stdInputHandle, &newLine, 1, out charsWritten, null);
24 | }
25 | }
26 |
27 | class MyException : Exception { }
28 |
29 | interface IFooer
30 | {
31 | void Foo();
32 | }
33 |
34 | struct Fooer : IFooer
35 | {
36 | public void Foo() => Console.WriteLine("Foo");
37 | }
38 |
39 | class Program
40 | {
41 | static int Main()
42 | {
43 | try
44 | {
45 | throw new MyException();
46 | Console.WriteLine("Exception not thrown!");
47 | }
48 | catch
49 | {
50 | Console.WriteLine("Exception caught");
51 | }
52 |
53 | IFooer fooer = (IFooer)new Fooer();
54 | fooer.Foo();
55 |
56 | return 42;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------