尝试通过互操作将c++转换为c#
本文关键字:c++ 转换 互操作 | 更新日期: 2023-09-27 18:02:56
我有一个在c++中调用EGL的程序。我想在c#中做同样的调用,但是c#中似乎没有相应的概念。
当执行上下文进入c++ EGL代码时,我得到一个读/写访问拒绝错误。
这是我试图转换为c#的c++程序中的代码:
PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), somethingOtherThanAWindow);
mEglSurface = eglCreateWindowSurface(mEglDisplay, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes));
我有一个c#类,它将c# EGL调用转换为c++调用。我相信c++是非托管的,尽管我不知道如何确切地告诉你。
c#类是这样的:
public static IntPtr CreateWindowSurface(IntPtr dpy, IntPtr config, IntPtr win, int[] attrib_list)
{
IntPtr retValue;
unsafe {
fixed (int* p_attrib_list = attrib_list)
{
retValue = Delegates.peglCreateWindowSurface(dpy, config, win, p_attrib_list);
}
}
return (retValue);
}
更多的代码可以在这里看到:https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/Egl.VERSION_1_0.cs#L751
你可能会注意到这个方法有一个IntPtr win
——这是我传递PropertySet
的地方。通常我认为这将是一个System.Windows.Forms.Control
,但一些检查是在c++ EGL代码中完成的,看看它是否是,或者如果它是一个PropertySet
。
被调用的c++方法是:
EGLSurface EGLAPIENTRY CreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)
更多内容可在此查看:https://github.com/Microsoft/angle/blob/ms-holographic-experimental/src/libGLESv2/entry_points_egl.cpp#L241
正如你所看到的,c++方法期待一个EGLNativeWindowType。我不太确定IInspectable和PropertSet之间的关系是什么——这似乎很奇怪,它可以被强制转换。
EGLNativeWindowType有以下类型定义:
typedef HWND EGLNativeWindowType;
这意味着它必须是某种窗口句柄。我不明白PropertySet怎么可能是一个窗口句柄。
我怀疑主要的问题是选择正确的对象类型来传递给c# EGL实现。PropertySet似乎是正确的选择,但是reinterpret_cast真的让我很困惑。
谁能告诉我这是怎么回事?通常我认为这将是一个System.Windows.Forms.Control…
这是一个令人痛苦的错误假设。要理解打字需要写三本书,在一个SO的答案中很难做到。如果你真的打算在Winforms应用程序中这样做,那么现在就停止,这是永远行不通的。
OpenGL使用非常松散类型,它们的api函数的参数只不过是void*
,一个原始指针。这使得它非常灵活,但是真正重要的是指针实际指向什么。如果客户端程序和视频适配器接口在最轻微的方式上不一致,那么你的程序将构建得很好,但会在运行时以完全无法诊断的方式崩溃和烧录。DirectX是微软放弃OpenGL并决定创建自己的OpenGL的一个主要原因。
也使用指针,但它们是更智能的类型,它们支持在运行时发现类型。它们是IUnknown
指针,它的QueryInterface()方法允许发现一个对象是否支持特定的预期接口。你看到这里使用的风格是完全相同的指针,IInspectable
是一个比IUnknown稍微聪明一点的版本,并且是所有WinRT类实现的基本接口。你真的必须传递一个IInspectable*,因为这是角端口所期望的。
你通常会期望你可以传递一个ICoreWindow
接口指针,然后完成它,这是WinRT窗口的接口。然而,渲染器需要比ICoreWindow更多的信息。不太确定为什么,我认为这与WinRT的分辨率独立性有关。它还需要知道表面尺寸和比例因子。
问题是,OpenGL没有办法传递那个信息。所以微软程序员使用了一个非常狡猾的hack,而不是添加一个函数来传递这个信息,他使用了传递任何类型的IInspectable*的能力,他传递了一个IMap<String^, IInspectable*>
指针。基本上是一个属性包,CoreWindowNativeWindow.cpp在ANGLE端口中再次在其CoreWindowNativeWindow::initialize()函数中从包中挖出属性。
PropertySet是c++语言投影中实现IMap<K, V>
的一个具体类。请注意,它是特定于c++的,在c#中您将使用Dictionary<string, IntPtr>
代替。CLR内建的语言投影会自动将托管字典映射到本机IMap接口。
哦,太好了,更多的IntPtrs。IInspectable*完全隐藏在你在c#中使用的语言投影中,这并不容易。我有98%的把握可以使用Marshal.GetIUnknownForObject()来获得一个可以工作的指针,即使它的风格是错误的。由于c++代码做正确的事情并使用QueryInterface:),因此必须在之后调用Marshal.Release()来清理,不这样做会导致内存泄漏。
一定要注意你做错事的强烈暗示。我想你是这样的,微软提供这个ANGLE分叉只有一个原因。他们试图让公司更容易将iOS游戏移植到WinRT/UWP。有必要让商店里充满顾客喜欢的游戏。角度移植只是为了便于从objective - C或c++中开始使用,这是用于编写这些游戏的语言。
他们本可以使使用Javascript或c#等语言的库变得容易得多,但他们没有这样做,因为这是不必要的。如果你必须将使用OpenGL的c++代码翻译成c#,那么当你使用DirectX时,你很可能会好得多。预计其他功能将出现更多此类映射问题,并对实验性HoloLens端口感到厌倦。
我认为参数类型肯定是错误的。
对于一个完整的示例,您应该阅读https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/NativeDeviceContext.cs中的DeviceContext实现。你还应该看到这段代码被调用的地方,这样你就得到了初始化EGL所需的实际调用序列:—工厂方式:https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/DeviceContextFactory.cs-控制集成:https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/GlControl.cs
如您所见,句柄是Control。处理财产。可能要传递的实际值取决于当前实现EGL的操作系统,但它应该是承载绘图结果的窗口(或控件)的句柄。或者,您可以检查实际的EGL方法实现,并遵循参数使用直到实际的DirectX调用,就像我当时所做的那样。