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 + 入口点,内存加载和反射调用的核心逻辑完全复用。

相关推荐
地狱为王5 小时前
Windows下编译coturn
windows
小二·5 小时前
MyBatis基础入门《十二》批量操作优化:高效插入/更新万级数据,告别慢 SQL!
数据库·sql·mybatis
何中应5 小时前
【面试题-6】MySQL
数据库·后端·mysql·面试题
路遥_135 小时前
银河麒麟 V10 安装部署瀚高数据库 HighGoDB 4.5 全流程(统信UOS Server 20同理)
数据库
TDengine (老段)5 小时前
从关系型数据库到时序数据库的思维转变
大数据·数据库·mysql·时序数据库·tdengine·涛思数据·非关系型数据库
老兵发新帖5 小时前
ubuntu网络管理功能分析
数据库·ubuntu·php
2301_768350235 小时前
MySQL服务配置与管理
数据库·mysql
+VX:Fegn08955 小时前
计算机毕业设计|基于springboot + vue旅游信息推荐系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计·旅游
百锦再6 小时前
国产数据库的平替亮点——关系型数据库架构适配
android·java·前端·数据库·sql·算法·数据库架构