Rehber C# Runtime'da dinamik olarak kod nasıl çalıştırılır?

Uygulama çalışırken server'dan veya herhangi bir yerden indirdiğimiz .cs dosyasındaki kodu runtime'da çalıştırıp kullanıcılara zoraki güncelleme getirmeden hafif değişiklikler nasıl yapılır diye araştırırken aklıma runtime'da dinamik olarak kod çalıştırabilmemize yarayan kütüphaneler geldi. Daha önce rastlamıştım fakat öğrenmek için vaktim yoktu ve ihtiyacımda o anlık yoktu. Şimdi ise ihtiyacım olduğu için yaptım ve sizlerle paylaşmak istiyorum.
C# projemizi oluşturuyoruz. Ben size göstermek amaçlı WinUI 3 .NET 9 açtım.
Öncelikle "Microsoft.CodeAnalysis.CSharp.Scripting" ve "Microsoft.CodeAnalysis.Scripting" kütüphanelerini ekleyelim:
1755871826870.png

Runtime'da ekleyeceğimiz kodun kullanılacaklar listesi:
C#:
private static readonly Assembly[] PredefinedAssemblies =
[
    typeof(object).Assembly,
    typeof(Uri).Assembly,
    typeof(Enumerable).Assembly,
    typeof(Application).Assembly,
    typeof(Button).Assembly,
    typeof(Debug).Assembly,
    Assembly.GetExecutingAssembly()
];
private static readonly string[] PredefinedImports =
[
    "System",
    "System.Linq",
    "System.Diagnostics",
    "Microsoft.UI.Xaml",
    "Microsoft.UI.Xaml.Controls"
];
Ben rastgele ekledim. Siz kendinize göre eklersiniz.
Önceki rehberde gösterdiğim gibi not defteri çalışınca çalıştırmak için:
C#:
using ManagementEventWatcher watcher = new(
    new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName='notepad.exe'"));
watcher.EventArrived += (_, ev) =>
{
    var name = (string)ev.NewEvent.Properties["ProcessName"].Value;
    var pid = (uint)ev.NewEvent.Properties["ProcessID"].Value;
    Debug.WriteLine($"[{DateTime.Now:HH:mm:ss}] [{name}] başladı. PID={pid}");
};
watcher.Start();
Sayfa yüklenince çalışması için:
XML:
<Window
    x:Class="MSClientWepApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MSClientWepApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Title="MSClientWepApp">
    <Window.SystemBackdrop>
        <MicaBackdrop />
    </Window.SystemBackdrop>
    <Grid Loaded="Grid_Loaded"> <!-- BURASI -->

    </Grid>
</Window>
C# tarafı:
C#:
private void Grid_Loaded(object __, RoutedEventArgs ___) => CodeHelperAsync();
1755873566119.png

Kodun tam hâli:
C#:
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.UI.Xaml.Controls;
using System.Diagnostics;
using Microsoft.UI.Xaml;
using System.Management;
using System.Reflection;
namespace MSClientWepApp;
public sealed partial class MainWindow : Window
{
    private static readonly ScriptGlobals _scriptGlobals = new();
    private static readonly Assembly[] PredefinedAssemblies =
    [
        typeof(object).Assembly,
        typeof(Uri).Assembly,
        typeof(Enumerable).Assembly,
        typeof(Application).Assembly,
        typeof(Button).Assembly,
        typeof(Debug).Assembly,
        Assembly.GetExecutingAssembly()
    ];
    private static readonly string[] PredefinedImports =
    [
        "System",
        "System.Linq",
        "System.Diagnostics",
        "Microsoft.UI.Xaml",
        "Microsoft.UI.Xaml.Controls"
    ];
    public MainWindow()
    {
        InitializeComponent();
        Activate();
        using ManagementEventWatcher watcher = new(
            new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName='notepad.exe'"));
        watcher.EventArrived += (_, ev) =>
        {
            var name = (string)ev.NewEvent.Properties["ProcessName"].Value;
            var pid = (uint)ev.NewEvent.Properties["ProcessID"].Value;
            Debug.WriteLine($"[{DateTime.Now:HH:mm:ss}] [{name}] başladı. PID={pid}");
        };
        watcher.Start();
    }
    private static async void CodeHelperAsync()
    {
        string code = await GetCodeAsync();
        var options = ScriptOptions.Default
            .WithImports(PredefinedImports)
            .WithReferences(PredefinedAssemblies);
        try
        {
            await CSharpScript.EvaluateAsync(code, options, globals: _scriptGlobals, globalsType: typeof(ScriptGlobals));
        }
        catch (CompilationErrorException e)
        {
            Debug.WriteLine("Derleme hatası: " + string.Join(Environment.NewLine, e.Diagnostics));
        }
    }
    private static async Task<string> GetCodeAsync() => await File.ReadAllTextAsync("Script.ini");
    private void Grid_Loaded(object __, RoutedEventArgs ___) => CodeHelperAsync();
}
public class ScriptGlobals
{
    public static void ShowMessage(string text) =>
    mainWindow.DispatcherQueue.TryEnqueue(async () =>
    {
        ContentDialog dialog = new()
        {
            Title = "Script Çıktısı",
            Content = text,
            CloseButtonText = "Tamam",
            XamlRoot = mainWindow.Content.XamlRoot
        };
        await dialog.ShowAsync();
    });
    public static void Log(string message) => Debug.WriteLine($"[Script Log] {message}");
}


Önemli not: Burada gösterilen kodları kötü niyetle kullanmayınız. Sorumlulukları red ediyorum.

Yakında çıkacak Windoc uygulamamı takip etmek için: Bilgisayarın sağlığını ve performansını kontrol eden uygulamam Windoc
 
Diyelim ki runtime da heap'te tutulan bir X objesi var.
Yine runtime'da gittim ben bu class'in yapisini degistirdim, eskiden integer olan kismi string yaptim.

Ne olacak bu durumda?
Exception fırlatıcak. Debug.Writeline ile yakalıyoruz kodda.
Biraz hassas yapı olsa da işe yarar.
Örneğin uygulamayı kullanan kişiye uzaktan uygulamayı silmesini sağlayabilirsin. Bu verilebilecek en küçük örnek tabii ki.
 

Technopat Haberler

Yeni konular

Geri
Yukarı