首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >如何从DLL设置委托/回调(C和C#之间的互操作)

如何从DLL设置委托/回调(C和C#之间的互操作)
EN

Stack Overflow用户
提问于 2015-10-04 21:46:01
回答 3查看 811关注 0票数 0

我有一个静态库(*.a表示iOS),它包含需要分配给C#回调的一些函数。代码在没有回调的情况下运行良好,但是当我将委托添加到结构时,它会失败,但会出现以下错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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#:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
            }
        }        
    }
...
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-10-05 08:15:38

以下是基于@Josh Peterson的建议的工作代码。C代码是相同的。只改变了C#的一面。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[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; 

            }
        }
票数 0
EN

Stack Overflow用户

发布于 2015-10-04 23:04:28

来自MSDN

可以将此属性应用于类或结构。公共语言运行库控制托管内存中类或结构的数据字段的物理布局。但是,如果要将类型传递给非托管代码,则可以使用StructLayoutAttribute属性控制该类型的非托管布局。使用带有LayoutKind.Sequential的属性强制按照成员出现的顺序排列成员。对于,LayoutKind.Sequential控制托管内存中的布局和非托管内存中的布局。对于非闪存类型,当类或结构封送到非托管代码时,它控制布局,但不控制托管内存中的布局。使用属性和LayoutKind.Explicit来控制每个数据成员的精确位置。这会影响托管和非托管布局,无论是闪存类型还是非闪烁类型。使用LayoutKind.Explicit需要使用FieldOffsetAttribute属性来指示类型中每个字段的位置。 默认情况下,C#、Visual和C++编译器将顺序布局值应用于结构。对于类,必须显式应用LayoutKind.Sequential值。Tlbimp.exe (类型库导入器)还应用StructLayoutAttribute属性;它总是在导入类型库时应用LayoutKind.Sequential值。

我想你的代码一定是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}
票数 0
EN

Stack Overflow用户

发布于 2015-10-05 03:39:37

这里的问题是,委托不是闪烁类型(在此页面上搜索“委托”,因此当您将委托添加到结构中时就会发生此错误。

错误的第一部分:

指定的结构必须是可闪现的,或者具有布局信息。

这才是最重要的。错误的“布局信息”部分可以忽略。

最好的选择可能是将委托的out参数作为单独的参数传递给AigooRegisterPhone,或者在TELEPHONE结构中使用IntPtr而不是basic_callback类型。在后一种情况下,可以调用Marshal.GetDelegateForFunctionPointer从本机函数指针获取C#委托。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32941987

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文