【问题标题】:Attempting to convert C++ into C# with interop尝试使用互操作将 C++ 转换为 C#
【发布时间】:2016-08-26 04:42:08
【问题描述】:

我有一个在 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 真的让我失望。

谁能帮我看看这个?

【问题讨论】:

  • 我只能说你的代码看起来很可疑......我认为你不能在托管句柄上使用reinterpret_cast 将其转换为本机接口指针。
  • 另外,鉴于 EGL 似乎是本机库,您可能应该在 C++/CLI(混合模式)中进行所有本机/托管转换,并且只在 C# 中使用托管代码。
  • @Phil1970 C++ - reinterpret_cast 的东西 - 工作正常。如果需要管理 EGL 才能使其正常工作,那么情况可能就是这样。 (我正在使用 ANGLE,这是一个 OpenGL 到 DX11 库,在我看来这意味着它很有可能被管理。)
  • @Phil1970 我的主要问题是内存访问异常。我认为这是因为一些托管 -> 本机进程,但我可能错了。
  • 这很可疑,因为我找不到任何从经理引用到本机界面的 reinterpret_cast 示例。您确定不会错过pin_ptr 的电话吗?正如您应该知道的,托管引用与指针不同,并且可以在内存中移动,所以对我来说,进行这种强制转换看起来非常危险,就好像垃圾收集器决定在那一刻运行本机指针可能变得无效并导致您的应用程序崩溃。

标签: c# .net opengl c++-cli interop


【解决方案1】:

通常我相信这将是一个 System.Windows.Forms.Control...

这是一个非常错误的假设。理解打字需要写三本书,在 SO 答案中很难做到。如果您真的打算从 Winforms 应用程序中执行此操作,请立即停止,那将永远行不通。

OpenGL 使用 very 松散类型,其 api 函数的参数只不过是 void*,一个原始指针。这使得它非常灵活,但指针实际指向的内容真的很重要。如果客户端程序和视频适配器接口在最轻微的方式上没有达成一致,那么您的程序将构建得很好,但会在运行时以完全无法诊断的方式崩溃和烧毁。微软放弃 OpenGL 并决定创建自己的一个主要原因,DirectX 就是结果。

它也使用指针,但它们是更智能的类型,它们支持运行时的类型发现。它们是IUnknown 指针,它的 QueryInterface() 方法允许查明对象是否支持特定的预期接口。您在此处看到的风格与指针完全相同,IInspectable 是比 IUnknown 稍微智能的版本,并且是所有 WinRT 类实现的基本接口。您确实确实必须通过 IInspectable*,因为这是 ANGLE 端口所期望的。

您通常期望您可以只传递一个ICoreWindow 接口指针并完成它,这就是窗口的WinRT 接口。然而,渲染器需要的信息不仅仅是 ICoreWindow。不完全确定为什么,我认为这与 WinRT 中的分辨率独立性有关。它需要知道表面尺寸和比例因子。

问题是,OpenGL 没有办法传递该信息。所以微软的程序员使用了一个非常狡猾的技巧,而不是添加一个函数来传递这个信息,他ab/使用了传递任何类型的IInspectable*的能力,他传递了一个IMap&lt;String^, IInspectable*&gt;指针。基本上是一个属性包,ANGLE 端口中的 CoreWindowNativeWindow.cpp 在其 CoreWindowNativeWindow::initialize() 函数中再次从包中挖掘属性。

PropertySet 是 C++ 语言投影中的一个具体类,它实现了IMap&lt;K, V&gt;。请注意它是特定于 C++ 的,在 C# 中,您将使用 Dictionary&lt;string, IntPtr&gt; 代替。 CLR 中内置的语言投影会自动将托管字典映射到本机 IMap 接口。

天哪,更多的 IntPtrs。 IInspectable* 完全隐藏在您在 C# 中使用的语言投影中,这并不容易。我 98% 确信您可以使用 Marshal.GetIUnknownForObject() 来获得一个有效的指针,即使它是错误的风格。由于 C++ 代码做正确的事并使用 QueryInterface :) 之后您必须调用 Marshal.Release() 进行清理,否则会导致内存泄漏。

请注意,您在做错事时有强烈的暗示。我认为您是,Microsoft 仅出于 一个 的原因提供了这个 ANGLE 分支。他们试图让公司轻松地将他们的 iOS 游戏移植到 WinRT/UWP。有必要用顾客喜欢的游戏填满商店。 ANGLE 移植只是为了易于使用以 ObjectiveC 或 C++(用于编写这些游戏的语言)开头的代码。

他们本可以让使用 Javascript 或 C# 等语言的库变得更加容易,但他们没有这样做,因为这不是必需的。如果您必须将使用 OpenGL 的一公尺 C++ 代码转换为 C#,那么当您使用 DirectX 时,您很可能会好多更好。预计其他功能会出现更多此类映射问题,并且厌倦了实验性 HoloLens 端口。

【讨论】:

  • 这是一个很棒的回复,谢谢。我做这一切的真正原因是我可以将 WebGL 调用从 JS 接口转换为 DirectX 调用。实际上,如果我足够了解 DirectX,我只会在 DirectX 中编写 WebGL 界面,但我不是。 Web 上至少有一个使用 OpenGL 作为 WebGL 后端的演示,所以我将使用它作为参考。
  • 我在使用IntPtr surfaceCreationPropertiesPointer = Marshal.GetIUnknownForObject(surfaceCreationProperties); 时遇到DXGI_SWAP_CHAIN_DESC.OutputWindow is not a valid window handle. 错误——我仍然认为您的答案是最好的,并且最适用于我提供的信息。不知道是否有人能提供比这更好的答案。
【解决方案2】:

我认为参数类型肯定是错误的。

有关完整示例,您应该阅读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.Handle 属性。可能要传递的实际值取决于当前实现 EGL 的操作系统,但它应该是托管绘图结果的窗口(或控件)的句柄。


或者,您可以检查实际的 EGL 方法实现,并按照参数使用直到实际的 DirectX 调用,就像我当时所做的那样。

【讨论】:

  • 嗨卢卡,感谢您的回复!所以我认为这个问题比你上面概述的要复杂一些,因为我试图传递给 C++ 的对象(句柄)基本上需要以未经检查的方式转换为 IInspectable (C# 没有t 支持。)这样做的原因是 ANGLE 的 HoloLens 分支支持非 HWND“句柄”,只要它们符合要求(在这种情况下,它必须是转换为 IInspectable 的 PropertySet。)我会考虑如果它更容易的话,在 C++ 领域中这样做。
  • 抱歉,我没有注意到您引用的 ANGLE 分支。但是,我怀疑参数语义会发生变化;由于基础类型是 IntPtr,参数可以指向任何不透明结构,并且值将按原样传递给链接的 ANGLE 实现。也许我会在假期回来时检查一下! ?
  • fyi,所有链接都失效了
猜你喜欢
  • 2018-12-15
  • 1970-01-01
  • 1970-01-01
  • 2017-09-27
  • 2023-04-10
  • 2011-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多