You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中获取对象属性的内存地址?Celeste游戏Mod开发实践

如何在Celeste中获取房间名称的稳定内存地址?

问题分析

你尝试获取Celeste中房间名称(string类型)的内存地址以创建静态指针,但两次尝试都出现异常:

  1. GCHandle方法:得到极小且变化的数值——这是因为GCHandle.ToIntPtr()返回的是CLR内部的句柄标识,并非string对象的实际内存地址。GCHandle的句柄是CLR追踪托管对象的索引,不是指向内存的指针。
  2. fixed方法:地址不稳定——这是因为fixed(string* pointer = &level.Session.LevelData.Name)固定的是**string类型的引用变量**的内存位置,而非string对象实例本身的地址。当CLR对string对象进行GC移动时,引用变量的值会更新;不同会话中引用变量的存储位置也可能不同,导致地址波动。

关于ChatGPT的说法:不完全准确

C#可以获取对象属性的指针,但需要区分两种指针:

  • 对于值类型属性:可直接获取属性实例的指针。
  • 对于引用类型属性:默认获取的是引用变量的指针(存储对象引用的内存地址),而非对象实例的内存地址。但通过正确方式,仍能获取引用类型对象实例的指针。

可行的获取方法

1. 正确使用GCHandle获取string对象实例的地址

通过GCHandle.Alloc固定对象,再用AddrOfPinnedObject()获取对象的实际内存地址:

private void Level_OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
{
    unsafe
    {
        string roomName = level.Session.LevelData.Name;
        // 固定string对象,阻止GC移动它
        GCHandle handle = GCHandle.Alloc(roomName, GCHandleType.Pinned);
        // 获取对象实例的内存地址
        IntPtr objectAddress = handle.AddrOfPinnedObject();
        Logger.Log(LogLevel.Verbose, "AddressLogger", $"房间名称对象地址: {(long)objectAddress:X}");
        // 必须释放句柄,避免内存泄漏
        handle.Free();
    }
}

注意:固定的对象在handle.Free()前不会被GC移动,地址暂时稳定,但跨会话无法保证固定——CLR内存布局是动态的,每次启动游戏时托管对象的加载位置都可能不同。

2. 针对静态指针的替代方案

由于托管对象地址受GC影响,无法作为跨会话的静态指针。若要创建静态指针,需:

  • 用内存扫描工具(如Cheat Engine)配合Mod日志,找到游戏原生内存中对应房间名称的存储位置(你提到内存中并非以字符串形式存储,可能是游戏自定义编码或字符串池)。
  • 追踪该原生地址的静态指针链,跳过托管层,直接访问游戏原生内存数据。

总结

  • 你之前的两种方法都未正确获取string对象实例的内存地址,前者混淆了句柄和指针,后者获取的是引用变量的地址。
  • C#可以获取托管对象实例的指针,但托管地址无法跨会话稳定,不适合作为静态指针。
  • 若要创建静态指针,应转向游戏原生内存层寻找对应数据的位置。

内容的提问来源于stack exchange,提问作者Ben Arkonovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 20:10:25