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

相关推荐
蓝速科技12 分钟前
桌面双屏翻译机量产调试:三类场景兼容性破局方案丨蓝速科技
运维·数据库·人工智能·科技·技术分享
达梦数据39 分钟前
DMDRS空间数据类型说明:Oracle、DM8、MySQL空间数据类型映射
数据库·mysql·oracle
白远山43 分钟前
24小时自助健身房系统开发实战:从需求分析到完整指南
java·开发语言·数据库·数据挖掘·需求分析
达梦数据1 小时前
DMDRS服务与模块的运行步骤
数据库
让学习成为一种生活方式2 小时前
PMGD:植物线粒体基因组数据库--Journal of Integrative Plant Biology
数据库
达梦数据2 小时前
达梦数据复制软件DMDRS搭建部署示例:源数据库DM8到目标数据库DM8一对一数据同步
数据库
IT_Octopus3 小时前
从一个应用开发者的角度,搞懂大数据查询的完整链路
java·大数据·数据库
Htr_3 小时前
Creem 2.0 使用指南:面向 AI 构建时代的资金平台
android·数据库·人工智能·ui·photoshop
我是大猴子3 小时前
reids的数据结构的应用场景
数据库
tang_04273 小时前
【Hi.Ltd 专题】第7期:Threading 采集线程、锁与 LRU 缓存
经验分享·c#·hi.ltd系列