是否可以从C++当您有针对性的本机文件参考时?

发布于 2025-02-06 17:17:02 字数 2745 浏览 1 评论 0原文

我对C ++和C编程语言有非常基本的知识。 在我看到了史蒂夫·桑德森(Steve Sanderson)和马修·莱博维茨(Matthew Leibowitz)的一些视频之后。

https://wwww.youtube.com/watch? 2957S https://www.youtube.com/watch?v=8gwsu3oaMV8

我决定去做一个实验项目,并尝试一些在那儿描述的概念。因此,我要做的是将字节数组(视频文件)传递到本机代码,然后从内部取出所有帧,然后将输出发送回C#代码,以便我可以在Skiasharp Canvas中使用它们。

我设法将字节数组传递给本机代码,但是我无法从本机C代码调用C#方法。我尝试过反向p /调用,如果那是所谓的:)我试图将一个代表通过这样的c代码

c code < / strong>

extern "C"
{
void test_class_callmycallback(
      void callback(int,int))
  {
    callback(3,5);
  }
}

这只是我想做的目的,我想做的是这样的STH。

void test_class_renderpicture(void *obj, void (*func)(uint8_t *, int))
  {
    ((TestClass *)obj)->getBytesWithCallback(func);
  }

以及在C#side

        [DllImport("Test")]
        static extern void test_class_callmycallback([MarshalAs(UnmanagedType.FunctionPtr)] CallbackDelegate func);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void CallbackDelegate(int arg1, int arg2);

        public void RenderPicture()
        {
            var managedDel = new CallbackDelegate(Multiply);

             test_class_callmycallback(Multiply);
            //RenderPictureDelegate managedDelegate = new RenderPictureDelegate(RenderPictureCallback);
            //test_class_renderpicture(this.handle, managedDelegate);
        }
        public void Multiply(int a,int b)
        {
            var c=a * b; 
        }

,我将此命令用于emscripten编译。

emcc myfile.cpp -shared -o test.o

并将其添加到我的Blazor Project

&lt; itemgroup&gt; &lt; antibalfilereference包括=“ test.o” /&gt; &lt;/itemGroup&gt;

所有内容都可以纠正,但是在运行时,我遇到了此错误。

断言在/__w/1/s/src/mono/mono/mono/metadata/loader.c:1806,条件 `'没有满足573846 @ dotnet..bnoeqi2kuy.js:1430 _emscripten_asm_const_int @ dotnet..bnoeqi2kuy.js:5673 $ wasm_trace_logger @ 02E96B9A:0x228B7AA $ eglib_log_adapter @ 02E96B9A:0xc102f $ MONOEG_G_LOGSTR @ 02E96B9A:0x20754f $ MONOEG_G_LOGV_NOFREE @ 02E96B9A:0x2074f3 $ MONOEG_ASSERTION_MESSAGE @ 02E96B9A:0x2075D0 $ mono_assertion_message @ 02E96B9A:0x207619

第一个问题是是否可以从C/C ++本机代码中调用(Indoke)C#方法,如果有可能,我该如何在Blazor Project中进行?谢谢您阅读。

PS:我不认为[unmanagedFunctionPointer(calleConvention.cdecl)]和[Marshalas(unmanagedtype.functionptr)]属性是必要的。但是,文件中的所有经典dllimport的示例都这样做。但是,每当您想使用__stdcall或__declSpec(dllexport)时,Emscripten总是会发出警告。这些属性是否需要用于Blazor Web组装项目?

I have a very basic knowledge of c++ and c programming languages.
After I have seen some videos from Steve Sanderson and Matthew Leibowitz on ASP.NET Community Standup.

https://www.youtube.com/watch?v=lVWQkpcVEWQ&t=2957s
https://www.youtube.com/watch?v=8gwSU3oaMV8

I decided to do an experimental project and try a little about the concepts that have been described over there. So what I want to do is to pass byte array(video file) to native code and take out all the frames from inside and send the output back to c# code so I can use them in skiasharp canvas.

I have managed to pass byte array to native code, but I couldn't manage to call c# method from native c code. I have tried reverse p / invoke, if that is what is called :)I am trying to pass a delegate to c code like this

c code

extern "C"
{
void test_class_callmycallback(
      void callback(int,int))
  {
    callback(3,5);
  }
}

this is just for testing purposes normally what I want to do is sth like this.

void test_class_renderpicture(void *obj, void (*func)(uint8_t *, int))
  {
    ((TestClass *)obj)->getBytesWithCallback(func);
  }

And on the c# side

        [DllImport("Test")]
        static extern void test_class_callmycallback([MarshalAs(UnmanagedType.FunctionPtr)] CallbackDelegate func);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void CallbackDelegate(int arg1, int arg2);

        public void RenderPicture()
        {
            var managedDel = new CallbackDelegate(Multiply);

             test_class_callmycallback(Multiply);
            //RenderPictureDelegate managedDelegate = new RenderPictureDelegate(RenderPictureCallback);
            //test_class_renderpicture(this.handle, managedDelegate);
        }
        public void Multiply(int a,int b)
        {
            var c=a * b; 
        }

and I use this command for emscripten compilation.

emcc myfile.cpp -shared -o Test.o

and added this to my blazor project

<ItemGroup> <NativeFileReference Include="Test.o" /> </ItemGroup>

everything compiles fine, but on the runtime, I got this error.

Assertion at /__w/1/s/src/mono/mono/metadata/loader.c:1806, condition
`' not met 573846 @ dotnet..bnoeqi2kuy.js:1430
_emscripten_asm_const_int @ dotnet..bnoeqi2kuy.js:5673 $wasm_trace_logger @ 02e96b9a:0x228b7a
$eglib_log_adapter @ 02e96b9a:0xc102f
$monoeg_g_logstr @ 02e96b9a:0x20754f
$monoeg_g_logv_nofree @ 02e96b9a:0x2074f3
$monoeg_assertion_message @ 02e96b9a:0x2075d0
$mono_assertion_message @ 02e96b9a:0x207619

The first question is if it is possible to call(invoke) c# method from c/c++ native code, and if it is possible how can I do it in blazor project? Thank you for reading it.

PS: I don't think [UnmanagedFunctionPointer(CallingConvention.Cdecl)] and [MarshalAs(UnmanagedType.FunctionPtr)] attributes are necessary. But all the examples for classical dllimport from file do that. But emscripten always gives a warning whenever you want to use __stdcall or __declspec(dllexport). Are those attributes necessary for blazor web assembly project?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

摘星┃星的人 2025-02-13 17:17:02

最后,
我在几次测试后发现了解决方案。
希望这可以帮助别人。
我在dotnet运行时问题页面上找到了类似的问题。

https://github.com/dotnet/runtime/runtime/issues/60824 当您想将功能指针传递给本机库时,良好的清单。我在线程中引用

使用本机代码确保:

  1. 您安装WASM-Tools工作量
  2. 您将使用属性wasmbuildnative设置为true。 (即:dotnet
    用-p:wasmbuildnative = true或将其设置在您的
    .csproj)
  3. 您必须标记通过一个传递给C ++的托管方法
    [UnmanagedCallsery]属性。
  4. 用未管理的属性标记的方法必须为
    静止的。这意味着在剃须刀页面上调用实例方法
    将需要将gchandle通过该实例进行C ++,然后通过
    它回到本地。或使用其他一些方法来识别实例
    剃须刀页面。
  5. [dllimport]标记的方法必须使用C#9.0函数
    指针而不是回调参数的委托类型。
  6. 但是当前使用C#功能指针类型的问题
    在WebAssembly上的[dllimport]方法中。因此,我们必须在
    托管侧而不是委托的方法签名
    *未经管理的&lt; int,void&gt;

对我来说,关键零件是第3,4和第6号。因此,最后我设法使用此代码进行了修复。

C ++方面

typedef void (*TestCallBack)(int args1, int args2);

class TestClass
{       // The class
public: // Access specifier
        // Attribute (int variable)
  TestClass(int value) { this->value = value; }
  int getValue() { 
    return this->value; 
  }
  void setBytes(uint8_t *fileBytes, int length)
  {
    this->file_bytes = fileBytes;
    this->file_length = length;
  }

  uint8_t *getBytes()
  {
    return this->file_bytes;
  }
  void setCallback(TestCallBack callback)
  {
    this->callback = callback;
    callback(1,2);
  }

private:
  int value;
  uint8_t *file_bytes;
  int file_length;
  TestCallBack callback;
  void *callbackState;
};

extern "C"
{
  void *test_class_new(int value) { return new TestClass(value); }
  void test_class_delete(void *obj) { delete (TestClass *)obj; }
  int test_class_getvalue(void *obj) { return ((TestClass *)obj)->getValue(); }
  void test_class_setbytes(void *obj, uint8_t *file_bytes, int file_length)
  {
    ((TestClass *)obj)->setBytes(file_bytes, file_length);
  }
  uint8_t *test_class_getbytes(void *obj) { return ((TestClass *)obj)->getBytes(); }
  void test_class_setcallback(
      void *obj, TestCallBack callback)
  {
    ((TestClass *)obj)->setCallback(callback);
  }
}

在C#上的

 public class TestClassRef : IDisposable
    {
        nint handle;

        [DllImport("Test")]
        extern static nint test_class_new(int value);

        [DllImport("Test")]
        extern static void test_class_delete(nint obj);

        [DllImport("Test")]
        extern static int test_class_getvalue(nint obj);

        [DllImport("Test")]
        extern static void test_class_setbytes(nint obj, IntPtr file_bytes, int file_length);

        [DllImport("Test")]
        extern static void test_class_setcallback(nint obj, IntPtr callback);



        public TestClassRef(int value)
        {
            this.handle = test_class_new(value);
        }
        public int Value => test_class_getvalue(this.handle);
        IntPtr unmanagedFileBytes = default(IntPtr);
        public void SetBytes(byte[] bytes)
        {
            this.unmanagedFileBytes = Marshal.AllocHGlobal(bytes.Length);
            Marshal.Copy(bytes, 0, unmanagedFileBytes, bytes.Length);
            test_class_setbytes(this.handle, unmanagedFileBytes, bytes.Length);
        }
        public void SetCallback()
        {

            unsafe
            {
                delegate * unmanaged<int,int, void> fn = &Multiply;
                test_class_setcallback(this.handle, (IntPtr)fn);
            }
        }
        [UnmanagedCallersOnly]
        private static void Multiply(int a, int b)
        {
            var c = a * b;
        }
        public void CleanBytes()
        {
            if (this.unmanagedFileBytes != default(IntPtr))
            {
                Marshal.FreeHGlobal(unmanagedFileBytes);
            }

        }
        public void Dispose()
        {
            CleanBytes();
            test_class_delete(this.handle);
        }
    }

Finally,
I have found out the solution after couple of tests.
hope this can help to someone else.
I have found the similar issue on dotnet runtime issues page.
https://github.com/dotnet/runtime/issues/60824

here is a good checklist when you want to pass function pointer to native library. I am quoting from the thread

To use native code make sure that:

  1. You install the wasm-tools workload
  2. You build with the property WasmBuildNative set to true. (ie: dotnet
    build and dotnet run with -p:WasmBuildNative=true or set it in your
    .csproj)
  3. You must label managed methods that are passed to C++ with a
    [UnmanagedCallersOnly] attribute.
  4. The method marked with the UnmanagedCallersOnly attribute must be
    static. That means to call an instance method on a razor page you
    will need to pass a GCHandle for the instance to C++, and then pass
    it back to native. Or use some other method to identify the instance
    of the Razor page.
  5. The method marked with [DllImport] must use a C# 9.0 function
    pointer rather than a delegate type for the callback argument.
  6. But there's currently an issue with using C# function pointer types
    in [DllImport] methods on WebAssembly. So we have to use IntPtr in
    the method signature on the managed side instead of delegate
    *unmanaged<int, void>

for me the critical parts were number 3,4 and number 6. So at the end i have managed to fix it with this code.

on c++ side

typedef void (*TestCallBack)(int args1, int args2);

class TestClass
{       // The class
public: // Access specifier
        // Attribute (int variable)
  TestClass(int value) { this->value = value; }
  int getValue() { 
    return this->value; 
  }
  void setBytes(uint8_t *fileBytes, int length)
  {
    this->file_bytes = fileBytes;
    this->file_length = length;
  }

  uint8_t *getBytes()
  {
    return this->file_bytes;
  }
  void setCallback(TestCallBack callback)
  {
    this->callback = callback;
    callback(1,2);
  }

private:
  int value;
  uint8_t *file_bytes;
  int file_length;
  TestCallBack callback;
  void *callbackState;
};

extern "C"
{
  void *test_class_new(int value) { return new TestClass(value); }
  void test_class_delete(void *obj) { delete (TestClass *)obj; }
  int test_class_getvalue(void *obj) { return ((TestClass *)obj)->getValue(); }
  void test_class_setbytes(void *obj, uint8_t *file_bytes, int file_length)
  {
    ((TestClass *)obj)->setBytes(file_bytes, file_length);
  }
  uint8_t *test_class_getbytes(void *obj) { return ((TestClass *)obj)->getBytes(); }
  void test_class_setcallback(
      void *obj, TestCallBack callback)
  {
    ((TestClass *)obj)->setCallback(callback);
  }
}

on c#

 public class TestClassRef : IDisposable
    {
        nint handle;

        [DllImport("Test")]
        extern static nint test_class_new(int value);

        [DllImport("Test")]
        extern static void test_class_delete(nint obj);

        [DllImport("Test")]
        extern static int test_class_getvalue(nint obj);

        [DllImport("Test")]
        extern static void test_class_setbytes(nint obj, IntPtr file_bytes, int file_length);

        [DllImport("Test")]
        extern static void test_class_setcallback(nint obj, IntPtr callback);



        public TestClassRef(int value)
        {
            this.handle = test_class_new(value);
        }
        public int Value => test_class_getvalue(this.handle);
        IntPtr unmanagedFileBytes = default(IntPtr);
        public void SetBytes(byte[] bytes)
        {
            this.unmanagedFileBytes = Marshal.AllocHGlobal(bytes.Length);
            Marshal.Copy(bytes, 0, unmanagedFileBytes, bytes.Length);
            test_class_setbytes(this.handle, unmanagedFileBytes, bytes.Length);
        }
        public void SetCallback()
        {

            unsafe
            {
                delegate * unmanaged<int,int, void> fn = &Multiply;
                test_class_setcallback(this.handle, (IntPtr)fn);
            }
        }
        [UnmanagedCallersOnly]
        private static void Multiply(int a, int b)
        {
            var c = a * b;
        }
        public void CleanBytes()
        {
            if (this.unmanagedFileBytes != default(IntPtr))
            {
                Marshal.FreeHGlobal(unmanagedFileBytes);
            }

        }
        public void Dispose()
        {
            CleanBytes();
            test_class_delete(this.handle);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文