是否有可能调用(p/调用)c#方法从c++当你有本地文件引用Blazor?



我对c++和c编程语言有非常基本的了解。在我看了Steve Sanderson和Matthew Leibowitz关于ASP的视频之后。. NET Community Standup.

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

我决定做一个实验项目,尝试一下在那里描述的概念。所以我想做的是将字节数组(视频文件)传递给本机代码,并从内部取出所有帧并将输出发送回c#代码,以便我可以在skiassharp canvas中使用它们。

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

c代码

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

这只是为了测试的目的,通常我想做的是这样的

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

c#端

[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项目

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

一切都很好,但是在运行时,我得到了这个错误。

Assertion at/__w/1/s/src/mono/mono/metadata/loader.c:1806, condition .c" not met 573846 @ dotnet. bnoeqi2key .js:1430$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

第一个问题是,如果有可能调用(调用)c#方法从c/c++本地代码,如果它是可能的,我怎么能做到在blazor项目?谢谢你的阅读。

PS:我不认为[UnmanagedFunctionPointer(CallingConvention.Cdecl)]和[MarshalAs(UnmanagedType.FunctionPtr)]属性是必要的。但是所有经典的从文件导入的例子都这样做了。但是当你想要使用__stdcall或__declspec(dllexport)时,emscripten总是会给出警告。这些属性是必要的blazor web组装项目?

最后经过几次测试,我找到了解决方案。希望这能对其他人有所帮助。我在。net运行时问题页面上发现了类似的问题。https://github.com/dotnet/runtime/issues/60824

当你想传递函数指针到本机库时,是一个很好的检查表。我从线程引用

要使用本机代码,请确保:

  1. 安装wasm-tools工作负载
  2. 将属性WasmBuildNative设置为true进行构建。(即:dotnet使用-p:WasmBuildNative=true运行build和dotnet,或者将其设置在.csproj)
  3. 你必须给传递给c++的托管方法贴上标签[UnmanagedCallersOnly]属性。
  4. 带有UnmanagedCallersOnly属性的方法必须是静态的。这意味着在您的razor页面上调用实例方法将需要将实例的GCHandle传递给c++,然后传递它回归原生。或者使用其他方法来识别实例
  5. 带有[DllImport]标记的方法必须使用c# 9.0函数
  6. 但是目前使用c#函数指针类型有一个问题在WebAssembly的[DllImport]方法中。我们需要使用IntPtr in在托管端而不是委托上的方法签名* unmanaged<int,>

对我来说,关键的部分是第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);
}
}

相关内容

最新更新