Dynamic DLL Loader

JSON 설정 파일을 읽어 DLL을 동적으로 로드하고 Form을 여는 프로젝트

폴더 구조

work/
├── .vscode/
│   ├── launch.json          # F5 실행 설정
│   └── tasks.json           # 빌드 태스크
├── build.bat                # 더블클릭으로 빌드+실행
├── DynamicFormLib/          # DLL 라이브러리
│   ├── DynamicFormLib.csproj
│   ├── Form1.cs             # Title, Width, Height, Message 파라미터
│   └── Form2.cs             # Title, Items(string[]), SelectedIndex 파라미터
└── MainApp/                 # 메인 앱
    ├── MainApp.csproj
    ├── MainForm.cs          # JSON 읽기 + DLL 동적 로드
    ├── Program.cs
    └── config.json          # DLL 호출 설정

동작 원리

config.json 읽기
       ↓
Assembly.LoadFrom() → DLL 로드
       ↓
Activator.CreateInstance() → Form 인스턴스 생성
       ↓
리플렉션(Property.SetValue) → 파라미터 전달
       ↓
Form.Show() → 새 폼 열기

config.json 형식

{
  "dlls": [
    {
      "name": "Form1",
      "path": "DynamicFormLib.dll",
      "typeName": "DynamicFormLib.Form1",
      "parameters": [
        { "name": "Title", "type": "string", "value": "내 폼" },
        { "name": "Width", "type": "int", "value": "800" }
      ]
    }
  ]
}
필드 설명
name DLL 표시 이름
path DLL 파일 경로 (실행파일 기준 상대경로)
typeName 네임스페이스.클래스명
parameters Form Property에 전달할 파라미터 목록

지원 파라미터 타입

type 설명 예시
string 문자열 "Hello"
int 정수 "800"
bool 불리언 "true"
double 실수 "3.14"
string[] 문자열 배열 (쉼표 구분) "A,B,C"

실행 방법

VSCode

  1. ds_dl2 폴더를 VSCode로 열기
  2. F5 또는 Ctrl+Shift+B로 빌드 및 실행

BAT 파일

build.bat 더블클릭

명령줄

dotnet build MainApp/MainApp.csproj -c Debug
cd MainApp/bin/Debug/net10.0-windows
MainApp.exe

새로운 DLL 추가 방법

  1. DynamicFormLib에 새 Form 클래스 생성
  2. config.json에 DLL 정보와 파라미터 추가
  3. 빌드 후 실행

새 Form 추가 예시

namespace DynamicFormLib;

public partial class Form3 : Form
{
    public string Title { get; set; } = "Form3";
    public int Count { get; set; } = 0;

    public Form3()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        SuspendLayout();
        // 폼 컨트롤 설정...
        Text = Title;
        ResumeLayout(false);
    }
}
{
  "name": "Form3",
  "path": "DynamicFormLib.dll",
  "typeName": "DynamicFormLib.Form3",
  "parameters": [
    { "name": "Title", "type": "string", "value": "Form3" },
    { "name": "Count", "type": "int", "value": "10" }
  ]
}

핵심 API

API 용도
Assembly.LoadFrom(path) DLL 파일을 메모리에 로드
assembly.GetType(name) 네임스페이스.클래스명으로 Type 조회
Activator.CreateInstance(type) Type의 인스턴스 생성
type.GetProperty(name) Property 이름으로 PropertyInfo 조회
prop.SetValue(instance, value) Property에 값 설정

필요 조건

  • .NET 10 SDK
  • VSCode (C# Dev Tools 확장 권장)

 

소스

 

MainApp/MainForm.cs

using System.Reflection;
using System.Text.Json;

namespace MainApp;

public partial class MainForm : Form
{
    private ListBox dllList = new();
    private Button btnLoad = new();
    private Button btnRefresh = new();
    private Label statusLabel = new();
    private List<DllConfig> dllConfigs = new();

    public MainForm()
    {
        InitializeComponent();
        LoadJsonConfig();
    }

    private void InitializeComponent()
    {
        SuspendLayout();

        Label lblTitle = new Label
        {
            Text = "DLL List:",
            AutoSize = true,
            Location = new Point(20, 20),
            Font = new Font("Malgun Gothic", 10, FontStyle.Bold)
        };

        dllList = new ListBox
        {
            Location = new Point(20, 50),
            Size = new Size(540, 250)
        };

        btnRefresh = new Button
        {
            Text = "Refresh",
            Location = new Point(20, 320),
            Size = new Size(100, 35)
        };
        btnRefresh.Click += BtnRefresh_Click;

        btnLoad = new Button
        {
            Text = "Run Selected DLL",
            Location = new Point(140, 320),
            Size = new Size(150, 35)
        };
        btnLoad.Click += BtnLoad_Click;

        statusLabel = new Label
        {
            Text = "Status: Ready",
            AutoSize = true,
            Location = new Point(20, 370),
            ForeColor = Color.Gray
        };

        Controls.Add(lblTitle);
        Controls.Add(dllList);
        Controls.Add(btnRefresh);
        Controls.Add(btnLoad);
        Controls.Add(statusLabel);

        Text = "Dynamic DLL Loader";
        Width = 600;
        Height = 430;

        ResumeLayout(false);
        PerformLayout();
    }

    private void LoadJsonConfig()
    {
        try
        {
            string jsonPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");

            if (!File.Exists(jsonPath))
            {
                statusLabel.Text = "Status: config.json not found";
                statusLabel.ForeColor = Color.Red;
                return;
            }

            string json = File.ReadAllText(jsonPath);
            var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
            var config = JsonSerializer.Deserialize<ConfigRoot>(json, options);

            if (config?.Dlls != null)
            {
                dllConfigs = config.Dlls;
                RefreshDllList();
                statusLabel.Text = $"Status: {dllConfigs.Count} DLL(s) loaded";
                statusLabel.ForeColor = Color.Green;
            }
        }
        catch (Exception ex)
        {
            statusLabel.Text = $"Status: JSON load error - {ex.Message}";
            statusLabel.ForeColor = Color.Red;
        }
    }

    private void RefreshDllList()
    {
        dllList.Items.Clear();
        foreach (var config in dllConfigs)
        {
            dllList.Items.Add($"[{config.Name}] {config.TypeName}");
        }
    }

    private void BtnRefresh_Click(object? sender, EventArgs e)
    {
        LoadJsonConfig();
    }

    private void BtnLoad_Click(object? sender, EventArgs e)
    {
        if (dllList.SelectedIndex < 0)
        {
            MessageBox.Show("Please select a DLL.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        try
        {
            var selectedConfig = dllConfigs[dllList.SelectedIndex];
            LoadAndShowForm(selectedConfig);
            statusLabel.Text = $"Status: {selectedConfig.Name} running";
            statusLabel.ForeColor = Color.Green;
        }
        catch (Exception ex)
        {
            statusLabel.Text = $"Status: DLL load error - {ex.Message}";
            statusLabel.ForeColor = Color.Red;
            MessageBox.Show($"Error loading DLL:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void LoadAndShowForm(DllConfig config)
    {
        string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.Path);

        if (!File.Exists(dllPath))
        {
            throw new FileNotFoundException($"DLL not found: {dllPath}");
        }

        Assembly assembly = Assembly.LoadFrom(dllPath);
        Type? type = assembly.GetType(config.TypeName);

        if (type == null)
        {
            throw new TypeLoadException($"Type not found: {config.TypeName}");
        }

        object instance = Activator.CreateInstance(type)!;

        if (config.Parameters != null)
        {
            foreach (var param in config.Parameters)
            {
                PropertyInfo? prop = type.GetProperty(param.Name);

                if (prop != null && prop.CanWrite)
                {
                    object? value = ConvertParameter(param.Value, prop.PropertyType);
                    prop.SetValue(instance, value);
                }
            }
        }

        if (instance is Form form)
        {
            form.Show();
        }
        else
        {
            throw new InvalidCastException($"Instance is not a Form: {type.Name}");
        }
    }

    private object? ConvertParameter(string value, Type targetType)
    {
        if (targetType == typeof(string))
            return value;
        if (targetType == typeof(int))
            return int.Parse(value);
        if (targetType == typeof(bool))
            return bool.Parse(value);
        if (targetType == typeof(double))
            return double.Parse(value);
        if (targetType == typeof(string[]))
            return value.Split(',');
        return Convert.ChangeType(value, targetType);
    }
}

public class ConfigRoot
{
    public List<DllConfig> Dlls { get; set; } = new();
}

public class DllConfig
{
    public string Name { get; set; } = "";
    public string Path { get; set; } = "";
    public string TypeName { get; set; } = "";
    public List<Parameter>? Parameters { get; set; }
}

public class Parameter
{
    public string Name { get; set; } = "";
    public string Type { get; set; } = "";
    public string Value { get; set; } = "";
}

+ Recent posts