微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 CLI/C++ 中保存不透明句柄的 List<>?

如何解决如何在 CLI/C++ 中保存不透明句柄的 List<>?

我正在为 C 库编写 CLI/C++ 包装器,以便在 C# 中使用它。必须说,我只能访问 C 头文件和 C 库的 .lib,而不能访问源代码

我试图包装的一些函数正在返回不透明的句柄,例如:

typedef struct SanEvent_s *SanEvent;
typedef struct SanValue_s *SanValue;

在 C# 端返回这种类型的对象对我来说似乎很麻烦,因为我不知道结构的实现(我尝试在 C++ 包装器中返回 SanEvent 类型,但在 C# 端该类型不可访问由于“保护级别”或它所说的任何内容)。因此,我目前的计划是编写一些辅助函数,而不是只返回一个整数,该整数表示例如列表中的 San Event 或其他东西。该列表将保存在托管 C++ 包装器中,我可以在其中实际管理 San Event 类型。 我的问题是,我真的不知道如何使用这种类型的 type

这个:

using System::Collections::Generic::List;
namespace Wrapper {
    public ref class Analytics
    {
    private:
        static List<SanEvent^>^ events = gcnew List<SanEvent^>();
    }
}

给我的错误句柄来处理,指针,或引用是不允许的

右手边也抱怨预期类型说明符 + 与上面相同的错误

谁能给我一些关于如何巧妙有效地解决这个问题的提示?我的 List 实现并非一成不变,我愿意接受更好的建议。

解决方法

让我们想象以下 SanEvent 声明

struct SanEvent_s
{
    int test;
};
typedef SanEvent_s *SanEvent;

并遵循 C++ API 来处理此类事件:

SanEvent GetEvent()
{
    auto e = new SanEvent_s();
    e->test=42;
    return e;   
}

int UseEvent(SanEvent pEvent)
{
    return pEvent->test;
}

所有这些代码都包含在静态库项目中(完全原生,没有 CLR)。

然后我们有 C++/CLI 项目来包装这个静态库。 这里我们有事件本身的包装器:

#include "./../CppLib/SanEvent_s.h"
public ref class SanEventWrapper: Microsoft::Win32::SafeHandles::SafeHandleZeroOrMinusOneIsInvalid
{
public:
    static SanEventWrapper^ GetWrapper()
    {
        return gcnew SanEventWrapper(GetEvent());
    }

internal:
    SanEventWrapper(SanEvent event):SafeHandleZeroOrMinusOneIsInvalid(true)
    {
        this->e = event;
        this->handle = System::IntPtr(event);
    }

    int UseWrapper()
    {
        return ::UseEvent(this->e);
    }

protected:
    bool ReleaseHandle() override
    {
        //todo: release wrapped event
        return true;
    }

private:
    SanEvent e;
};

另一个使用这种包装器的类

public ref class SanEventConsumer
{
public:
    int ConsumeEvent(SanEventWrapper^ wrapper)
    {
        return wrapper->UseWrapper();
    }
};

最后,如何使用 C# 中的所有这些:

        var wrapper = SanEventWrapper.GetWrapper();
        var consumer = new SanEventConsumer();
        var res = consumer.ConsumeEvent(wrapper);
        Console.WriteLine(res);

这应该打印 42;

注意事项: 备注:

,

这里有一些类似于上面@Serg 的内容,但明确表示您在 C# 世界中没有想法,对象内部是什么。

所以如果你有一个用 VS 制作的 C++/CLI 库,你会在 .h 文件中得到它:

#pragma once

#include <cstdint>

using namespace System;

namespace CppCliLibrary {
    public ref class Class1
    {
    public:
        static IntPtr getOpaqueInstance(int32_t argument);
        static void useOpaqueInstance(IntPtr obj);
        static void freeOpaqueInstance(IntPtr obj);
    };
}

和上面一样,使用 IntPtr 来表示指向“whatever”的指针。对应的.cpp文件是这样的:

#include "pch.h"

#include "CppCliLibrary.h"
#include <string>
#include <iostream>

namespace CppCliLibrary
{
    class OpaqueCppClass
    {
    public:
        OpaqueCppClass(int32_t arg)
            : m_int(arg) { }
        int32_t m_int;
    };
}

IntPtr CppCliLibrary::Class1::getOpaqueInstance(int32_t argument)
{
    return IntPtr(new OpaqueCppClass(argument));
}

void CppCliLibrary::Class1::useOpaqueInstance(IntPtr obj)
{
    CppCliLibrary::OpaqueCppClass* deref = reinterpret_cast<CppCliLibrary::OpaqueCppClass *>(obj.ToPointer());
    std::cout << "Contents of class are: " << deref->m_int << std::endl;
}

void CppCliLibrary::Class1::freeOpaqueInstance(IntPtr obj)
{
    CppCliLibrary::OpaqueCppClass* deref = reinterpret_cast<CppCliLibrary::OpaqueCppClass*>(obj.ToPointer());
    std::cout << "Deleting class with contents: " << deref->m_int << std::endl;
    delete deref;
}

然后在 C# 文件中你有这个:

namespace CsCoreConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            // Get an instance
            var instance = CppCliLibrary.Class1.getOpaqueInstance(52);
            // Use it
            Console.WriteLine("Got an instance we're using");
            CppCliLibrary.Class1.useOpaqueInstance(instance);
            Console.WriteLine("Freeing it");
            CppCliLibrary.Class1.freeOpaqueInstance(instance);

            // Add a bunch to a list
            List<IntPtr> opaqueInstances = new List<IntPtr>();
            for(int i = 0; i < 5; i++)
            {
                opaqueInstances.Add(CppCliLibrary.Class1.getOpaqueInstance(i * 10));
            }

            // Use them all
            foreach(var cur in opaqueInstances)
            {
                CppCliLibrary.Class1.useOpaqueInstance(cur);
            }

            // Delete them all
            foreach (var cur in opaqueInstances)
            {
                CppCliLibrary.Class1.freeOpaqueInstance(cur);
            }

        }
    }
}

当然,C# 项目需要引用 C++/CLI 项目,但您可以在这里了解这个想法。 C++/CLI 是 IntPtr 的工厂(仅此而已),它也可以使用,因为对 C# 来说它是不透明的。 C# 只知道 IntPtr

Serg 的想法是以类型安全的方式对其进行更多包装。当然,这可以工作,但这是“更原始”的变体,如果您想“直接”将其放入 List<>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。