[英]How to setup delegate/callback from DLL (interop between C and C#)
我有一个静态库(对于iOS为* .a),其中包含一些我需要分配给C#回调的函数。 该代码可以在没有回调的情况下正常运行,但是当我将委托添加到结构中时,它将失败并显示以下错误:
ArgumentException: The specified structure must be blittable or have
layout information. Parameter name: structure at
FMOD_Listener.LoadPlugins () [0x00000] in <filename unknown>:0 at
FMOD_Listener.Initialize () [0x00000] in <filename unknown>:0
(Filename: currently not available on il2cpp Line: -1)
这是本机代码(C):
extern "C" {
typedef void (F_CALLBACK *basic_callback) (int *value1);
typedef struct telephone
{
int area_code;
int number;
basic_callback basic_callbck;
} TELEPHONE;
F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone);
void F_CALLBACK aigoo_basic_callback(int *value1)
{
*value1 = *value1 * 10 ;
}
F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone)
{
TELEPHONE* myPhone = new TELEPHONE ();
myPhone->area_code = 929;
myPhone->number = 823;
myPhone->basic_callbck = aigoo_basic_callback;
*telephone = myPhone;
}
}
这是托管端C#:
public delegate void basic_callback (ref int value1);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TELEPHONE
{
public int area_code;
public int number;
public basic_callback basic_callbck;
}
public class FMODPlugInHandler {
[DllImport ("__Internal")]
public static extern void AigooRegisterPhone(out IntPtr TelephonePtr);
}
public class FMOD_Listener : MonoBehaviour
{
...
void LoadPlugins()
{
int plugin_result = 0;
if (Application.platform == RuntimePlatform.IPhonePlayer) {
IntPtr PhoneIntPtr;
FMODPlugInHandler.AigooRegisterPhone(out PhoneIntPtr);
plugin_result = 823823823;
myLog = "plugin_result = " + plugin_result + " PhoneIntPtr: " + PhoneIntPtr;
if (PhoneIntPtr != IntPtr.Zero){
TELEPHONE MyPhone = (TELEPHONE)Marshal.PtrToStructure(PhoneIntPtr, typeof(TELEPHONE));
plugin_result = 123456;
myLog = "result = " + plugin_result + " number: " + MyPhone.number ;
int int_cs = 2;
plugin_result = MyPhone.basic_callbck(ref int_cs);
myLog = "result = " + plugin_result + " number: " + MyPhone.number + " int_cs: " + int_cs;
}
}
}
...
}
从MSDN
您可以将此属性应用于类或结构。 公共语言运行时控制托管内存中类或结构的数据字段的物理布局。 但是,如果要将类型传递给非托管代码,则可以使用StructLayoutAttribute属性来控制类型的非托管布局。 将该属性与LayoutKind.Sequential一起使用,以强制成员按它们出现的顺序顺序进行布局。 对于,LayoutKind.Sequential同时控制托管内存中的布局和非托管内存中的布局。 对于不可拆分的类型,当类或结构封送为非托管代码时,它控制布局,但不控制托管内存中的布局。 将该属性与LayoutKind.Explicit一起使用,以控制每个数据成员的精确位置。 这会影响可托管和不可托管类型的托管布局和非托管布局 。 使用LayoutKind.Explicit要求您使用FieldOffsetAttribute属性指示类型中每个字段的位置。
默认情况下,C#,Visual Basic和C ++编译器将顺序布局值应用于结构。 对于类,必须显式应用LayoutKind.Sequential值。 Tlbimp.exe(类型库导入器)还应用了StructLayoutAttribute属性; 导入类型库时,它始终应用LayoutKind.Sequential值。
我认为您的代码必须是
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct TELEPHONE
{
[FieldOffset(0)]
public int area_code;
[FieldOffset(4)]
public int number;
[FieldOffset(8)]
public basic_callback basic_callbck;
}
这里的问题是委托不是可复制的类型(在此页面上搜索“ delegate”),因此,当您将委托添加到结构中时,就会开始发生此错误。
错误的第一部分:
指定的结构必须是可漂白的或具有布局信息。
这里重要的是 错误的“布局信息”部分可以忽略。
最好的选择可能是将委托的out参数作为单独的参数传递给AigooRegisterPhone
或者在TELEPHONE
结构中使用IntPtr
而不是basic_callback
类型。 在后一种情况下,可以调用Marshal.GetDelegateForFunctionPointer
以从本机函数指针获取C#委托。
这是基于@Josh Peterson的建议的工作代码。 C代码是相同的。 仅更改了C#端。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TELEPHONE
{
public int area_code;
public int number;
public **IntPtr** basic_callbck_intptr;
}
...
public class FMOD_Listener : MonoBehaviour
{
...
void LoadPlugins()
{
int plugin_result = 0;
if (Application.platform == RuntimePlatform.IPhonePlayer) {
IntPtr PhoneIntPtr;
FMODPlugInHandler.AigooRegisterPhone(out PhoneIntPtr);
plugin_result = 823823823;
myLog = "plugin_result = " + plugin_result + " PhoneIntPtr: " + PhoneIntPtr;
if (PhoneIntPtr != IntPtr.Zero){
TELEPHONE MyPhone = (TELEPHONE)Marshal.PtrToStructure(PhoneIntPtr, typeof(TELEPHONE));
plugin_result = 123456;
myLog = "result = " + plugin_result + " number: " + MyPhone.number ;
int int_cs = 2;
IntPtr basic_callbck_intptr = MyPhone.basic_callbck_intptr;
basic_callback basic_callbck = Marshal.GetDelegateForFunctionPointer(basic_callbck_intptr, typeof(basic_callback)) as basic_callback;
basic_callbck(ref int_cs);
myLog = "result = " + plugin_result + " number: " + MyPhone.number + " int_cs: " + int_cs;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.