前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity3D (Mono/IL2CPP) 中 P/Invoke 平台调用代码应该如何传委托

Unity3D (Mono/IL2CPP) 中 P/Invoke 平台调用代码应该如何传委托

作者头像
walterlv
发布2023-10-22 11:27:23
4950
发布2023-10-22 11:27:23
举报

IL2CPP does not support marshaling delegates that point to instance methods to native code. 你可能平时在 .NET Core / Framework 的代码中写得很正常的托管代码的委托调用,在 Unity3D 中变得不可行。

本文举个例子,并且将其改正。

举例:查找所有可见窗口

本文的例子会使用到 NuGet 包 Lsj.Util.Win32,这是个非常棒的 Win32 调用的 API 包装,可以免去大量自己可能写不对的 [DllImport]

引入命名空间:

1 2

using Lsj.Util.Win32; using Lsj.Util.Win32.BaseTypes;

然后查找所有的可见窗口。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public static IReadOnlyList<HWND> FindVisibleWindows() { var found = new List<HWND>(); User32.EnumWindows(OnWindowEnum, IntPtr.Zero); return found; BOOL OnWindowEnum(HWND hWnd, LPARAM lparam) { if (User32.GetParent(hWnd) == IntPtr.Zero && User32.IsWindowVisible(hWnd)) { found.Add(HWND); } return true; } }

Mono / IL2CPP

Unity 编译的时候可以选择脚本后端是 Mono 还是 IL2CPP。不幸的是,没有 .NET Core 或者未来的 .NET 5/6,因此很多 .NET Core 的特性不能用。

关于脚本后端的选择,可以参见我的另一篇博客:

在编译时不会有什么问题,但是在运行时会发生异常(如果你去捕捉,或者用 VS 调试就可以看到):

1 2 3 4 5

NotSupportedException: IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: Win32WindowExtensions+<>c__DisplayClass0_0::<FindVisibleWindows>g__OnWindowEnum|0 at Lsj.Util.Win32.User32.EnumWindows (Lsj.Util.Win32.User32+WNDENUMPROC lpEnumFunc, Lsj.Util.Win32.BaseTypes.LPARAM lParam) at Win32WindowExtensions.FindVisibleWindows ()

“IL2CPP 不支持封送实例方法到本机代码”。

修正代码

Mono/IL2CPP 要求封送到本机的代码必须是静态方法,且必须标 MonoPInvokeCallback 特性。

因此,我们不得不把上面的代码改成这样:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

using System; using System.Collections.Generic; using System.Text; using AOT; using Lsj.Util.Win32; using Lsj.Util.Win32.BaseTypes; public class WindowsEnumerator { private static List<HWND> _currentList; public IReadOnlyList<HWND> FindVisibleWindows() { _currentList = new List<HWND>(); User32.EnumWindows(OnWindowEnum, IntPtr.Zero); return _currentList; } [MonoPInvokeCallback(typeof(User32.WNDENUMPROC))] static BOOL OnWindowEnum(HWND hWnd, LPARAM lparam) { if (User32.GetParent(hWnd) == IntPtr.Zero && User32.IsWindowVisible(hWnd)) { _currentList?.Add(HWND); } return true; } }

当然上述代码不是线程安全的。所以如果你希望在多线程环境下使用,请自行修改为线程安全的版本。

参考资料

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/unity3d-marshal-callback-must-be-static.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 举例:查找所有可见窗口
  • Mono / IL2CPP
  • 修正代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档