C#内存加载dll和EXE是不是差不多,主要是EXE有入口点

C#内存加载dll和EXE是不是差不多,主要是EXE有入口点?

是的!在 C# 中,内存加载 DLL 和 EXE(仅限 .NET 托管程序集)的核心逻辑几乎完全一致,唯一的关键差异确实集中在「EXE 有入口点(EntryPoint)」,但本质上二者都是 CLR 可加载的托管程序集,加载和反射调用的底层规则完全相同。

下面我们从「核心共性」「关键差异」「代码示例」三个维度讲清楚,帮你彻底理解:

一、核心共性(加载 DLL/EXE 完全一致)

无论加载的是 .NET DLL 还是 EXE,只要是托管程序集,内存加载的底层逻辑、反射调用方法的规则完全相同:

  1. 加载方式一致 :都通过 Assembly.Load(byte[]) 从内存字节数组加载(而非 LoadFile/LoadFrom 读文件),避免磁盘依赖;
  2. 反射调用一致 :加载后都通过 Type.GetMethod/MethodInfo.Invoke 调用方法(无论是否静态、有无参数 / 返回值);
  3. 依赖处理一致 :若程序集依赖其他 DLL/EXE,都需通过 AppDomain.CurrentDomain.AssemblyResolve 事件处理内存加载依赖;
  4. 类型访问一致:都能访问程序集中的公开类、方法、属性(只要权限允许)。

简单说:CLR 并不区分「DLL」和「EXE」扩展名,只认「是否是符合 .NET 规范的托管程序集」------ EXE 只是「带入口点的 DLL」,DLL 是「无入口点的 EXE」。

二、关键差异(仅 2 点,核心是入口点)

维度 .NET DLL .NET EXE
入口点(EntryPoint) 无默认入口点(除非手动定义) 必有入口点(Main 方法,可通过 Assembly.EntryPoint 获取)
编译 / 运行特性 无法直接独立运行(需宿主程序) 可直接双击运行(CLR 自动调用入口点)
反射调用重点 找「自定义业务方法」(如 DoWork() 可调用「入口点 Main」或「自定义业务方法」

补充:EXE 的入口点也可以是无参 / 有参、有返回值 / 无返回值(如 int Main(string[] args)),和 DLL 自定义方法的调用规则完全一致。

三、C# 代码示例(对比 DLL/EXE 内存加载)

1. 通用内存加载方法(DLL/EXE 共用)csharp
cs 复制代码
using System;
using System.Reflection;
using System.IO;

public static class AssemblyLoader
{
    // 核心:从字节数组加载托管程序集(DLL/EXE 通用)
    public static Assembly LoadFromMemory(byte[] assemblyBytes)
    {
        // 内存加载,不落地磁盘(区别于 LoadFile/LoadFrom)
        return Assembly.Load(assemblyBytes);
    }

    // 辅助:从文件读取字节(实际可从网络/加密存储获取)
    public static byte[] GetAssemblyBytes(string filePath)
    {
        return File.ReadAllBytes(filePath);
    }
}
2. 内存加载 DLL(调用自定义方法)

假设 DLL 中有如下代码:

cs 复制代码
// 被加载的 DLL 代码
namespace MyDll
{
    public class Calculator
    {
        // 自定义有返回值方法
        public static int Add(int a, int b)
        {
            return a + b;
        }
    }
}

内存加载并调用:

csharp

运行

cs 复制代码
public static void CallDllFromMemory()
{
    try
    {
        // 1. 读取 DLL 字节(内存加载核心)
        byte[] dllBytes = AssemblyLoader.GetAssemblyBytes(@"C:\MyDll.dll");
        Assembly assembly = AssemblyLoader.LoadFromMemory(dllBytes);

        // 2. 找 DLL 中的类型和方法(重点:自定义业务方法)
        Type calcType = assembly.GetType("MyDll.Calculator");
        if (calcType == null) throw new Exception("未找到 Calculator 类型");

        MethodInfo addMethod = calcType.GetMethod("Add", 
            BindingFlags.Public | BindingFlags.Static,
            null,
            new[] { typeof(int), typeof(int) },
            null);

        // 3. 调用方法并获取返回值
        object result = addMethod.Invoke(null, new object[] { 100, 200 });
        Console.WriteLine($"DLL 方法返回值:{result}"); // 输出 300
    }
    catch (Exception ex)
    {
        Console.WriteLine("DLL 调用失败:" + ex);
    }
}
3. 内存加载 EXE(调用入口点 + 自定义方法)假设 EXE 中有如下代码:
cs 复制代码
// 被加载的 EXE 代码
namespace MyExe
{
    class Program
    {
        // EXE 入口点(有参数、有返回值)
        static int Main(string[] args)
        {
            return args?.Length ?? -1; // 返回参数个数,无参数返回 -1
        }
    }

    // EXE 中的自定义公开方法(和 DLL 一样可调用)
    public class ExeHelper
    {
        public static string GetInfo()
        {
            return "EXE 自定义方法:" + DateTime.Now;
        }
    }
}

内存加载并调用:

cs 复制代码
public static void CallExeFromMemory()
{
    try
    {
        // 1. 读取 EXE 字节(和 DLL 加载逻辑完全一致)
        byte[] exeBytes = AssemblyLoader.GetAssemblyBytes(@"C:\MyExe.exe");
        Assembly assembly = AssemblyLoader.LoadFromMemory(exeBytes);

        // ========== 场景1:调用 EXE 入口点(Main) ==========
        MethodInfo entryMethod = assembly.EntryPoint; // 核心:获取 EXE 入口点
        if (entryMethod == null) throw new Exception("未找到 EXE 入口点");

        // 调用入口点并获取返回值(和 DLL 方法调用规则一致)
        object entryResult = entryMethod.Invoke(null, new object[] { new string[] { "param1", "param2" } });
        Console.WriteLine($"EXE 入口点返回值:{entryResult}"); // 输出 2

        // ========== 场景2:调用 EXE 自定义方法(和 DLL 完全一致) ==========
        Type helperType = assembly.GetType("MyExe.ExeHelper");
        MethodInfo infoMethod = helperType.GetMethod("GetInfo");
        object infoResult = infoMethod.Invoke(null, null);
        Console.WriteLine(infoResult); // 输出 EXE 自定义方法:2025-xx-xx...
    }
    catch (Exception ex)
    {
        Console.WriteLine("EXE 调用失败:" + ex);
    }
}

四、关键总结

  1. 核心结论 :C# 内存加载托管 DLL/EXE 几乎无差异,唯一区别是 EXE 多了「入口点 Main」,但调用入口点的逻辑和调用 DLL 自定义方法完全一致;
  2. 本质逻辑:CLR 加载的是「托管程序集」,扩展名(DLL/EXE)仅影响「是否可独立运行」,不影响内存加载和反射调用;
  3. 注意事项
    • 仅适用于 .NET 托管程序集(非托管 DLL/EXE 需用 LoadLibrary/P/Invoke);
    • EXE 调用入口点时,需匹配 Main 的参数 / 返回值类型(如 string[] argsint 返回值);
    • 依赖处理:若 EXE/DLL 依赖其他程序集,需通过 AssemblyResolve 事件内存加载依赖项。

简单记:EXE = DLL + 入口点,内存加载和反射调用的核心逻辑完全复用。

相关推荐
知识分享小能手几秒前
edis入门学习教程,从入门到精通,Redis编程开发知识点详解(4)
数据库·redis·学习
qq_334903155 分钟前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
wutang0ka7 分钟前
高频 SQL 50题 197.上升的温度
数据库·sql
薛定谔的悦11 分钟前
嵌入式 OTA(远程固件升级)(二)
服务器·数据库·能源·储能·ota
V1ncent Chen12 分钟前
SQL大师之路 14 子查询
数据库·sql·mysql·数据分析
LinuxGeek102434 分钟前
Windows的GPU版编译好的gromacs 2026.1添加hwloc和plumed支持版
windows·量子计算
奇点爆破XC36 分钟前
统计数据库当前数据容量
数据库
Leon-Ning Liu39 分钟前
OGG同步Oracle到Kafka
数据库·oracle·kafka
远方160943 分钟前
117-Oracle 26ai FILTER(过滤)子句新特性
大数据·数据库·sql·oracle·database
Maverick0643 分钟前
Oracle 归档日志(Archive Log)操作手册
数据库·oracle