C#调用FSR的ffxQuery获取版本ID异常问题求助
问题排查与解决方案
核心问题出在C#与C++互操作时的结构体内存布局、调用约定或指针处理不匹配,以下是具体排查步骤和修正代码:
常见错误点及修正
1. 结构体内存布局未显式指定
C#默认的结构体布局可能与C++的顺序布局不一致,导致VersionId字段偏移错误,读取到无效值。
修正后的结构体定义:
public enum FfxQueryType { FFX_QUERY_VERSION_COUNT = 0, FFX_QUERY_VERSION_INFO = 1 } // 强制顺序布局,与C++结构体对齐 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FfxQueryVersionInfo { public uint VersionId; // 对应C++的uint32_t,必须用无符号整数 public IntPtr VersionName; // 对应C++的const char*,用IntPtr手动转字符串 }
2. 函数调用约定不匹配
AMD FSR的ffxQuery函数默认使用__cdecl调用约定,若C#未指定,会导致栈不平衡、参数传递错误。
修正后的委托定义:
// 显式指定Cdecl调用约定,与C++函数匹配 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate uint FfxQueryDelegate(FfxQueryType queryType, IntPtr data, uint dataSize);
3. 内存分配与指针处理错误
获取版本信息时,需为结构体数组分配足够内存,且正确遍历每个结构体的内存地址。
完整调用代码:
using System; using System.Runtime.InteropServices; class FsrVersionReader { // P/Invoke 动态加载DLL的函数 [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr hModule); public enum FfxQueryType { FFX_QUERY_VERSION_COUNT = 0, FFX_QUERY_VERSION_INFO = 1 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FfxQueryVersionInfo { public uint VersionId; public IntPtr VersionName; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate uint FfxQueryDelegate(FfxQueryType queryType, IntPtr data, uint dataSize); public static void ReadFsrVersions(string dllPath) { IntPtr dllHandle = LoadLibrary(dllPath); if (dllHandle == IntPtr.Zero) { Console.WriteLine("Failed to load DLL"); return; } try { IntPtr ffxQueryPtr = GetProcAddress(dllHandle, "ffxQuery"); if (ffxQueryPtr == IntPtr.Zero) { Console.WriteLine("Failed to get ffxQuery address"); return; } FfxQueryDelegate ffxQuery = Marshal.GetDelegateForFunctionPointer<FfxQueryDelegate>(ffxQueryPtr); // 获取版本数量 uint versionCount = ffxQuery(FfxQueryType.FFX_QUERY_VERSION_COUNT, IntPtr.Zero, 0); Console.WriteLine($"Version count: {versionCount}"); if (versionCount == 0) return; // 分配内存存储版本信息数组 uint structSize = (uint)Marshal.SizeOf(typeof(FfxQueryVersionInfo)); IntPtr versionInfoPtr = Marshal.AllocHGlobal((int)(structSize * versionCount)); try { // 获取版本信息 uint result = ffxQuery(FfxQueryType.FFX_QUERY_VERSION_INFO, versionInfoPtr, structSize * versionCount); if (result != versionCount) { Console.WriteLine("Failed to retrieve all version info"); return; } // 遍历并输出每个版本 for (int i = 0; i < versionCount; i++) { IntPtr currentPtr = IntPtr.Add(versionInfoPtr, (int)(structSize * i)); FfxQueryVersionInfo info = Marshal.PtrToStructure<FfxQueryVersionInfo>(currentPtr); string versionName = Marshal.PtrToStringAnsi(info.VersionName); Console.WriteLine($"Version {i+1}: ID = {info.VersionId}, Name = {versionName}"); } } finally { Marshal.FreeHGlobal(versionInfoPtr); } } finally { FreeLibrary(dllHandle); } } // 测试调用 static void Main() { ReadFsrVersions("amd_fidelityfx_dx12.dll"); } }
额外排查步骤
- 验证函数名:用VS的
dumpbin /exports amd_fidelityfx_dx12.dll命令查看DLL导出表,确认ffxQuery的名字是否正确(若存在 mangled 名称,需替换为实际导出名)。 - 检查结构体大小:对比C++中
sizeof(FfxQueryVersionInfo)与C#中Marshal.SizeOf(typeof(FfxQueryVersionInfo))的返回值,确保一致(64位系统应为16字节,32位为8字节)。 - 调试内存:在调试器中查看
versionInfoPtr指向的内存,确认是否有非0的VersionId数值(FSR 3.1.3的ID通常为0x03010300或类似编码值)。
内容的提问来源于stack exchange,提问作者Brad Moore
相关产品推荐
相关产品推荐

