如何在Inno Setup Pascal脚本中处理.NET导出函数返回的字符串
解决Unicode版本Inno Setup调用.NET导出函数返回字符串的问题
最优方案:缓冲区填充模式(无指针、无内存泄漏)
Unicode版本Inno Setup v6.3.3默认使用WideString(UTF-16编码),直接返回托管字符串或非托管指针容易引发内存访问错误或泄漏,缓冲区填充模式是最安全可靠的实现方式:由Inno分配内存缓冲区,.NET函数将结果写入缓冲区,同时返回字符串实际长度。
.NET端(C# 示例,基于DllExport)
using System; using System.Runtime.InteropServices; using RGiesecke.DllExport; public class InnoHelper { // 导出函数:填充版本字符串到缓冲区,返回实际需要的缓冲区长度(含终止符) [DllExport("GetInnoHelperVersion", CallingConvention = CallingConvention.StdCall)] public static int GetInnoHelperVersion(IntPtr buffer, int bufferSize) { string version = "1.0.0.0"; // 替换为你的实际版本字符串 // 转换为UTF-16字节数组(匹配Inno的WideString编码),添加字符串终止符 byte[] versionBytes = System.Text.Encoding.Unicode.GetBytes(version + "\0"); int requiredLength = versionBytes.Length; // 缓冲区有效且大小足够时,写入内容 if (buffer != IntPtr.Zero && bufferSize >= requiredLength) { Marshal.Copy(versionBytes, 0, buffer, requiredLength); } // 返回所需缓冲区长度,供Inno分配内存使用 return requiredLength; } }
Inno Setup端(Pascal脚本)
[Code] // 声明导出函数 function GetInnoHelperVersion(buffer: String; var bufferSize: Integer): Integer; external 'GetInnoHelperVersion@files:InnoHelper.dll stdcall'; procedure InitializeWizard; var VersionStr: String; RequiredSize: Integer; begin // 第一步:获取所需缓冲区的字节长度 RequiredSize := GetInnoHelperVersion('', 0); // 第二步:分配对应大小的字符串缓冲区(WideString每个字符占2字节,除以2得到字符数) SetLength(VersionStr, RequiredSize div 2); // 第三步:填充缓冲区并获取版本字符串 GetInnoHelperVersion(VersionStr, RequiredSize); // 输出结果(按需使用) MsgBox('Helper版本: ' + VersionStr, mbInformation, MB_OK); end;
之前方法出错的原因
- 直接返回字符串(GetInnoHelperVersionStr):.NET的托管字符串内存由CLR管理,Inno作为非托管程序无法正确访问这块内存,触发内存访问违规。
- 返回指针(GetInnoHelperVersionPtrA/W):若用
Marshal.AllocHGlobal分配非托管内存,Inno读取后无法直接释放该内存(权限不兼容),必须额外导出释放函数,否则会造成内存泄漏;乱码则是因为ANSI编码(PtrA)与Inno默认的UTF-16编码不匹配。
可选方案:带内存释放的指针模式(不推荐,仅作兼容参考)
如果必须使用指针返回字符串,需额外导出内存释放函数:
.NET端补充代码
[DllExport("FreeInnoHelperString", CallingConvention = CallingConvention.StdCall)] public static void FreeInnoHelperString(IntPtr ptr) { Marshal.FreeHGlobal(ptr); } [DllExport("GetInnoHelperVersionPtrW", CallingConvention = CallingConvention.StdCall)] public static IntPtr GetInnoHelperVersionPtrW() { string version = "1.0.0.0"; // 分配非托管UTF-16字符串内存 return Marshal.StringToHGlobalUni(version); }
Inno Setup端对应代码
[Code] function GetInnoHelperVersionPtrW: Integer; external 'GetInnoHelperVersionPtrW@files:InnoHelper.dll stdcall'; procedure FreeInnoHelperString(ptr: Integer); external 'FreeInnoHelperString@files:InnoHelper.dll stdcall'; procedure InitializeWizard; var VersionPtr: Integer; VersionStr: String; begin VersionPtr := GetInnoHelperVersionPtrW; try VersionStr := WideString(VersionPtr); MsgBox('Helper版本: ' + VersionStr, mbInformation, MB_OK); finally FreeInnoHelperString(VersionPtr); // 必须调用释放,否则内存泄漏 end; end;
内容的提问来源于stack exchange,提问作者ElektroStudios
相关产品推荐
相关产品推荐

