C#调用System.Windows.Forms时出现类型加载异常问题咨询
这个问题我之前帮好几个Unity开发者解决过,本质是System.Windows.Forms是Windows桌面专属的UI组件,而Unity默认的运行时环境(不管是旧版的Mono还是新版的.NET后端)并没有默认包含这个库,再加上Unity的线程模型和WinForms不兼容,才会触发类型加载异常。下面给你几个靠谱的解决方向:
方案1:改用Unity原生/跨平台文件对话框(推荐)
这是最稳妥的方式,既能避免平台兼容性问题,又符合Unity的运行逻辑。
编辑器环境下(开发阶段用)
用Unity自带的EditorUtility.SaveFilePanel,完全适配编辑器环境:
using UnityEditor; using UnityEngine; using System.IO; namespace BuildShare { public static class BuildSaveHandler { public static void ExportSaveToFile(Build.BuildSave save) { // 弹出Unity原生的保存对话框 string savePath = EditorUtility.SaveFilePanel( "导出存档文件", Application.persistentDataPath, // 默认打开存档目录 $"{save.saveName}.txt", "txt"); if (!string.IsNullOrEmpty(savePath)) { using (StreamWriter writer = new StreamWriter(savePath)) { // 这里替换成你的存档写入逻辑 writer.WriteLine(save.ToString()); } } } } }
运行时环境(发布后用)
如果是跨平台发布,推荐用第三方跨平台文件对话框插件(Asset Store有很多免费的);如果只针对Windows平台,可以用Unity自带的UnityEngine.Windows.Dialogs:
using UnityEngine; using UnityEngine.Windows.Dialogs; using System.IO; namespace BuildShare { public static class BuildSaveHandler { public static void ExportSaveToFile(Build.BuildSave save) { FilePicker filePicker = new FilePicker(); filePicker.mode = FilePickerMode.Save; filePicker.filter = "Text files (*.txt)|*.txt"; filePicker.defaultFilename = $"{save.saveName}.txt"; filePicker.Show(result => { if (result == FilePickerResult.OK && !string.IsNullOrEmpty(filePicker.filePath)) { using (StreamWriter writer = new StreamWriter(filePicker.filePath)) { // 写入存档内容 writer.WriteLine(save.ToString()); } } }); } } }
方案2:强行使用System.Windows.Forms(仅限Windows平台)
如果你一定要用WinForms的对话框,只能针对Windows平台发布,步骤如下:
修改Unity的API兼容级别
打开Player Settings -> Other Settings -> Configuration,把Api Compatibility Level设置为**.NET Framework 4.x**(不要选.NET Core/.NET)。添加System.Windows.Forms.dll引用
找到你Unity安装目录下的对应dll,比如路径大概是:Unity\Hub\Editor\[你的Unity版本]\Editor\Data\MonoBleedingEdge\lib\mono\4.7.1-api\System.Windows.Forms.dll,把它复制到项目的Plugins文件夹里。修复线程兼容问题
WinForms需要在STA线程运行,而Unity主线程是MTA,所以要单独开线程调用对话框:
using System; using System.Threading; using System.Windows.Forms; using UnityEngine; using System.IO; namespace BuildShare { public static class BuildSaveHandler { public static void ExportSaveToFile(Build.BuildSave save) { string savePath = null; // 创建STA线程处理WinForms对话框 Thread staThread = new Thread(() => { SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.FileName = $"{save.saveName}.txt"; saveDialog.Filter = "Text File | *.txt"; if (saveDialog.ShowDialog() == DialogResult.OK) { savePath = saveDialog.FileName; } }); staThread.SetApartmentState(ApartmentState.STA); staThread.Start(); staThread.Join(); // 等待线程结束,获取保存路径 if (!string.IsNullOrEmpty(savePath)) { using (StreamWriter writer = new StreamWriter(savePath)) { // 写入存档内容 writer.WriteLine(save.ToString()); } } } } }
- 添加平台判断
为了避免跨平台发布报错,用预处理指令包裹WinForms相关代码:
#if UNITY_STANDALONE_WIN // 这里放WinForms相关的代码 #endif
额外注意事项
- 不要在Unity主线程直接调用WinForms UI,线程模型不兼容会导致崩溃或异常;
- 编辑器下尽量用Unity原生的对话框,WinForms窗口可能会出现焦点丢失、无法关闭的问题;
- 如果选择方案2,你的项目只能在Windows平台运行,跨平台发布时会报错。
内容的提问来源于stack exchange,提问作者Maximillion Billiony

