将 SAFEARRAY 从 C++ 返回到 C#

2024-04-01

我有一个 C++ 方法,可以创建、填充并返回 SAFEARRAY:

SAFEARRAY* TestClass::GetResult(long& size) 
{
    return GetSafeArrayList(size);
}

How should I export that function in a DLL so that c# could take it
How should I write c# method signature?

我在 C++ 中有一些类似的东西:

extern "C" __declspec(dllexport) void GetResult(SAFEARRAY*& data, long& size)
{
    size = 0;
    data = handle->GetResult(size);
}

这是正确的,不是吗?

感谢帮助!

EDIT:

c# call:

public static extern void GetResult(IntPtr handle, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_USERDEFINED)] TestStruct[] data, ref int size);

使用 a 的完整示例SAFEARRAY(int)C#->C++->C#(因此数组是用 C# 中的一些数据初始化的,传递给 C++,在那里修改并返回给 C#)。

C++:

// For the various _t classes for handling BSTR and IUnknown
#include <comdef.h>

struct ManagedUDT
{
    BSTR m_str01;
    int m_int01;

    ~ManagedUDT()
    {
        ::SysFreeString(m_str01);
        m_str01 = NULL;
    }
};

extern "C" __declspec(dllexport) void GetResult(SAFEARRAY*& data)
{
    if (data != NULL)
    {
        // Begin print content of SAFEARRAY
        VARTYPE vt;
        HRESULT hr = SafeArrayGetVartype(data, &vt);

        if (SUCCEEDED(hr))
        {
            // To make this code simple, we print only
            // SAFEARRAY(VT_I4)
            if (vt == VT_I4)
            {
                int *pVals;
                hr = SafeArrayAccessData(data, (void**)&pVals); // direct access to SA memory

                if (SUCCEEDED(hr))
                {
                    long lowerBound, upperBound;  // get array bounds
                    SafeArrayGetLBound(data, 1, &lowerBound);
                    SafeArrayGetUBound(data, 1, &upperBound);

                    long cnt_elements = upperBound - lowerBound + 1;

                    for (int i = 0; i < cnt_elements; i++)  // iterate through returned values
                    {
                        int val = pVals[i];
                        printf("C++: %d\n", val);
                    }

                    SafeArrayUnaccessData(data);
                }
                else
                {
                    // Error
                }
            }
        }
        else
        {
            // Error
        }
        // End print content of SAFEARRAY

        // Delete the SAFEARRAY if already present
        SafeArrayDestroy(data);
        data = NULL;
    }

    {
        // Creation of a new SAFEARRAY
        SAFEARRAYBOUND bounds;
        bounds.lLbound = 0;
        bounds.cElements = 10;

        data = SafeArrayCreate(VT_I4, 1, &bounds);
        int *pVals;

        HRESULT hr = SafeArrayAccessData(data, (void**)&pVals); // direct access to SA memory

        if (SUCCEEDED(hr))
        {
            for (ULONG i = 0; i < bounds.cElements; i++)
            {
                pVals[i] = i + 100;
            }
        }
        else
        {
            // Error
        }
    }
}

C#

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void GetResult([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4)] ref int[] ar);

and

var data = new int[] { 1, 2, 3, 4, 5 };
GetResult(ref data);

if (data != null)
{
    for (int i = 0; i < data.Length; i++)
    {
        Console.WriteLine("C#: {0}", data[i]);
    }
}
else
{
    Console.WriteLine("C#: data is null");
}

代码部分取自https://stackoverflow.com/a/12484259/613130 https://stackoverflow.com/a/12484259/613130 and https://stackoverflow.com/a/3735438/613130 https://stackoverflow.com/a/3735438/613130


SAFEARRAY(VT_RECORD)

这是可行的...非常难...但是可行。请不要这样做。你怎么恨这个世界都不足以做到这一点。我真希望你不要!

C++:

// For the _com_util
#include <comdef.h>

extern "C"
{
    __declspec(dllexport) void GetResultSafeArray(SAFEARRAY *&psa)
    {
        // All the various hr results should be checked!
        HRESULT hr;

        // Begin sanity checks
        if (psa == NULL)
        {
            // Error
        }

        VARTYPE pvt;
        hr = ::SafeArrayGetVartype(psa, &pvt);

        if (pvt != VT_RECORD)
        {
            // Error
        }

        UINT size;
        size = ::SafeArrayGetElemsize(psa);

        if (size != sizeof(ManagedUDT))
        {
            // Error
        }

        // From tests done, it seems SafeArrayGetRecordInfo does a AddRef
        _com_ptr_t<_com_IIID<IRecordInfo, NULL> > prinfo;
        // The_com_ptr_t<>::operator& is overloaded
        hr = ::SafeArrayGetRecordInfo(psa, &prinfo);

        // From tests done, it seems GetName returns a new instance of the
        // BSTR
        // It is ok to use _bstr_t.GetAddress() here, see its description
        _bstr_t name1;
        hr = prinfo->GetName(name1.GetAddress());

        const _bstr_t name2 = _bstr_t(L"ManagedUDT");

        if (name1 != name2)
        {
            // Error
        }

        // End sanity checks

        long lowerBound, upperBound;  // get array bounds
        hr = ::SafeArrayGetLBound(psa, 1, &lowerBound);
        hr = ::SafeArrayGetUBound(psa, 1, &upperBound);
        long cnt_elements = upperBound - lowerBound + 1;

        // Begin print
        ManagedUDT *pVals;
        hr = ::SafeArrayAccessData(psa, (void**)&pVals);

        printf("C++:\n");

        for (int i = 0; i < cnt_elements; ++i)
        {
            ManagedUDT *pVal = pVals + i;

            // If you are using a recent VisualC++, you can
            // #include <memory>, and then
            //std::unique_ptr<char[]> pstr(_com_util::ConvertBSTRToString(pVal->m_str01));
            // and you don't need the char *pstr line and the delete[]
            // line
            char *pstr = _com_util::ConvertBSTRToString(pVal->m_str01);
            printf("%s, %d\n", pstr, pVal->m_int01);
            delete[] pstr;
        }

        hr = ::SafeArrayUnaccessData(psa);
        // End print

        // Begin free
        SAFEARRAYBOUND sab;
        sab.lLbound = 0;
        sab.cElements = 0;

        // SafeArrayRedim will call IRecordInfo::RecordClear
        hr = ::SafeArrayRedim(psa, &sab);
        // End Free

        // Begin create
        int numElements = 10;
        sab.cElements = numElements;
        hr = ::SafeArrayRedim(psa, &sab);

        hr = ::SafeArrayAccessData(psa, (void**)&pVals);

        for (int i = 0; i < numElements; i++)
        {
            ManagedUDT *pVal = pVals + i;

            char pstr[100];
            sprintf(pstr, "Element #%d", i);
            pVal->m_str01 = _com_util::ConvertStringToBSTR(pstr);

            pVal->m_int01 = 100 + i;
        }

        hr = ::SafeArrayUnaccessData(psa);
        // End create
    }

    __declspec(dllexport) void GetResultSafeArrayOut(SAFEARRAY *&psa, ITypeInfo *itypeinfo)
    {
        // All the various hr results should be checked!
        HRESULT hr;

        // Begin sanity checks
        if (psa != NULL)
        {
            // Begin free
            // SafeArrayDestroy will call IRecordInfo::RecordClear
            // if necessary
            hr = ::SafeArrayDestroy(psa);
            // End Free
        }

        // Begin create
        int numElements = 10;

        SAFEARRAYBOUND sab;
        sab.lLbound = 0;
        sab.cElements = numElements;

        // The_com_ptr_t<>::operator& is overloaded
        _com_ptr_t<_com_IIID<IRecordInfo, NULL> > prinfo;
        hr = ::GetRecordInfoFromTypeInfo(itypeinfo, &prinfo);

        psa = ::SafeArrayCreateVectorEx(VT_RECORD, 0, numElements, prinfo);

        ManagedUDT *pVals;
        hr = ::SafeArrayAccessData(psa, (void**)&pVals);

        for (int i = 0; i < numElements; i++)
        {
            ManagedUDT *pVal = pVals + i;

            char pstr[100];
            sprintf(pstr, "Element #%d", i);
            pVal->m_str01 = _com_util::ConvertStringToBSTR(pstr);

            pVal->m_int01 = 100 + i;
        }

        hr = ::SafeArrayUnaccessData(psa);
        // End create
    }
}

C#:

[ComVisible(true)]
[Guid("BBFE1092-A90C-4b6d-B279-CBA28B9EDDFA")]
[StructLayout(LayoutKind.Sequential)]
public struct ManagedUDT
{
    [MarshalAs(UnmanagedType.BStr)]
    public string m_str01;
    public Int32 m_int01;
}

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResultSafeArray([MarshalAs(UnmanagedType.SafeArray)] ref ManagedUDT[] array);

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResultSafeArrayOut([MarshalAs(UnmanagedType.SafeArray)] out ManagedUDT[] array, IntPtr itypeinfo);

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "GetResultSafeArrayOut")]
static extern void GetResultSafeArrayRef([MarshalAs(UnmanagedType.SafeArray)] ref ManagedUDT[] array, IntPtr itypeinfo);

and

var arr = new[]
{
    new ManagedUDT { m_str01 = "Foo", m_int01 = 1},
    new ManagedUDT { m_str01 = "Bar", m_int01 = 2},
};

{
    Console.WriteLine("C#:");

    for (int i = 0; i < arr.Length; i++)
    {
        Console.WriteLine("{0}, {1}", arr[i].m_str01, arr[i].m_int01);
    }
}

{
    Console.WriteLine();

    var arr2 = (ManagedUDT[])arr.Clone();

    GetResultSafeArray(ref arr2);

    Console.WriteLine();

    Console.WriteLine("C#:");

    for (int i = 0; i < arr2.Length; i++)
    {
        Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
    }
}

{
    Console.WriteLine();

    ManagedUDT[] arr2;

    IntPtr itypeinfo = Marshal.GetITypeInfoForType(typeof(ManagedUDT));
    GetResultSafeArrayOut(out arr2, itypeinfo);

    Console.WriteLine();

    Console.WriteLine("C#:");

    for (int i = 0; i < arr2.Length; i++)
    {
        Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
    }
}

{
    Console.WriteLine();

    var arr2 = (ManagedUDT[])arr.Clone();

    IntPtr itypeinfo = Marshal.GetITypeInfoForType(typeof(ManagedUDT));
    GetResultSafeArrayRef(ref arr2, itypeinfo);

    Console.WriteLine();

    Console.WriteLine("C#:");

    for (int i = 0; i < arr2.Length; i++)
    {
        Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
    }
}

有一个很大的警告GetResultSafeArray:您必须从 C# 传递至少一个空数组(例如new ManagedUDT[0])。这是因为要创建一个SAFEARRAY(ManagedUDT)从 C++ 中的任何东西开始,你都需要一个IRecordInfo目的。我不知道如何从 C++ 中检索它。如果您已经有SAFEARRAY(ManagedUDT)那么显然它有IRecordInfo已经设置好了,所以没有问题。在给出的示例中,在 C++ 中,首先进行一些健全性检查,然后打印传递的数组,然后清空它,然后重新填充它。这GetResultSafeArrayOut/GetResultSafeArrayRef“cheat”:他们从 C# 收到ITypeInfo指针(在 C# 中很容易检索,使用Marshal.GetITypeInfoForType()),并且 C++ 可以从中检索IRecordInfo界面。

一些注意事项:

  • 我写了 Ansi-charset-C++。通常我自己总是编写支持 Unicode 的 C++(或直接编写 Unicode-C++,因为所有 Windows NT 都支持 Unicode),但我注意到我是一个例外......所以在代码的各个部分都有转换BSTR->Ansi->BSTR。

  • 我正在检索HRESULT所有函数调用。应检查它们并处理故障。

  • C++/COM 中最复杂的事情是知道何时释放某些东西......一般来说永远免费/Release()一切! (be it BSTR/IUnknown派生接口,...)

  • 除非有错误,否则不支持此代码。将其视为概念证明。出于好奇,我已经花了好几个小时在上面。你打破它,你修复它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 SAFEARRAY 从 C++ 返回到 C# 的相关文章

  • C++ 维护子类对象的混合集合

    如果我在这里错过了一个相当基本的概念 我很抱歉 但我正在尝试弄清楚如何维护多个类类型的集合 所有类类型都派生自同一个父类 并且在检索它们时仍然可以访问它们的特定于子类的方法从集合中 作为上下文 我有一个基类 BaseClass 和许多类 例
  • 为什么在连接两个字符串时 Python 比 C 更快?

    目前我想比较 Python 和 C 用来处理字符串的速度 我认为 C 应该比 Python 提供更好的性能 然而 我得到了完全相反的结果 这是 C 程序 include
  • CLR 2.0 与 4.0 性能比较?

    如果在 CLR 4 0 下运行 为 CLR 2 0 编译的 NET 程序会运行得更快吗 应用程序配置
  • 适合初学者的良好调试器教程[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有谁知道一个好的初学者教程 在 C 中使用调试器 我感觉自己好像错过了很多 我知道怎么做 单步执行代码并查看局部变量 虽然这常常给我带来问
  • 以编程方式读取 SQL Server 查询计划建议的 SQL 特定执行的索引?

    如果我在 SSMS 中运行此命令 set showplan xml on GO exec some procedure arg1 arg2 arg3 GO set showplan xml off GO 我获得查询执行中涉及的完整调用堆栈的
  • IdentityServer 4 对它的工作原理感到困惑

    我阅读和观看了很多有关 Identity Server 4 的内容 但我仍然对它有点困惑 因为似乎有很多移动部件 我现在明白这是一个单独的项目 它处理用户身份验证 我仍然不明白的是用户如何注册它 谁存储用户名 密码 我打算进行此设置 Rea
  • 当一组凭据下的计划任务启动的进程在另一组凭据下运行另一个程序时,Windows 是否有限制

    所以我有一个简单的例子 其中我有应用程序 A 它对用户 X 本地管理员 有一些硬编码的凭据 然后它使用硬编码的绝对路径启动带有这些凭据的应用程序 B A 和 B 以及 dotnet 控制台应用程序 但是它们不与控制台交互 只是将信息写入文件
  • 从同一个类中的另一个构造函数调用构造函数

    我有一个带有两个构造函数的类 C 这是代码片段 public class FooBar public FooBar string s constructor 1 some functionality public FooBar int i
  • 使用可变参数包类型扩展的 C++ 函数调用者包装器

    我绑定了一些 API 并且绑定了一些函数签名 如下所示 static bool WrapperFunction JSContext cx unsigned argc JS Value vp 我尝试将对象和函数包装在 SpiderMonkey
  • unordered_map 中字符串的 C++ 哈希函数

    看起来 C 标准库中没有字符串的哈希函数 这是真的 在任何 c 编译器上使用字符串作为 unordered map 中的键的工作示例是什么 C STL提供模板专业化 http en cppreference com w cpp string
  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • 告诉 Nancy 将枚举序列化为字符串

    Nancy 默认情况下在生成 JSON 响应时将枚举序列化为整数 我需要将枚举序列化为字符串 有一种方法可以通过创建来自定义 Nancy 的 JSON 序列化JavaScript 原始转换器 https github com NancyFx
  • 使用valgrind进行GDB远程调试

    如果我使用远程调试gdb我连接到gdbserver using target remote host 2345 如果我使用 valgrind 和 gdb 调试内存错误 以中断无效内存访问 我会使用 target remote vgdb 启动
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • OpenGL:仅获取模板缓冲区而没有深度缓冲区?

    我想获取一个模板缓冲区 但如果可能的话 不要承受附加深度缓冲区的开销 因为我不会使用它 我发现的大多数资源表明 虽然模板缓冲区是可选的 例如 排除它以利于获得更高的深度缓冲区精度 但我还没有看到任何请求并成功获取仅 8 位模板缓冲区的代码
  • 将数组作为参数传递

    如果我们修改作为方法内参数传递的数组的内容 则修改是在参数的副本而不是原始参数上完成的 因此结果不可见 当我们调用具有引用类型参数的方法时 会发生什么过程 这是我想问的代码示例 using System namespace Value Re
  • 实体框架中的“it”是什么

    如果以前有人问过这个问题 请原谅我 但我的任何搜索中都没有出现 它 我有两个数据库表 Person 和 Employee 对每个类型的表进行建模 例如 Employee is a Person 在我的 edmx 设计器中 我定义了一个实体
  • 可访问性不一致:参数类型的可访问性低于方法

    我试图在两个表单之间传递一个对象 基本上是对当前登录用户的引用 目前 我在登录表单中有一些类似的内容 private ACTInterface oActInterface public void button1 Click object s
  • 为boost python编译的.so找不到模块

    我正在尝试将 C 代码包装到 python 中 只需一个类即可导出两个函数 我编译为map so 当我尝试时import map得到像噪音一样的错误 Traceback most recent call last File
  • 不区分大小写的字符串比较 C++ [重复]

    这个问题在这里已经有答案了 我知道有一些方法可以进行忽略大小写的比较 其中涉及遍历字符串或一个good one https stackoverflow com questions 11635 case insensitive string

随机推荐

  • Visual Studio 2019 - 在 WebApp 中添加 WebJob 项目

    在我的解决方案中 我有一个 aspnet core 2 2 WebApp 我想添加一个现有项目 例如 webJob 但没有选项 从现有项目添加 WebJobs 为什么 使用相同的过程 我在其他 Net 应用程序上添加 WebJobs 此选项
  • 互斥体和 Windows Phone 8.1

    这是我的问题 Windows Phone 8 1 Visual Studio 2013 版本 4 我有一个主项目和一个每 30 分钟运行一次的后台项目 我想在两者之间传递数据 我想确保对 Windows Storage Applicatio
  • 如何在 android layout.xml 文件中创建 2 行按钮

    我尝试在 android layout xml 文件中创建 2 行按钮 第一行左对齐 第二行居中对齐 这就是我所做的 但我最终得到了一排按钮 你能告诉我我做错了什么吗 enter code here
  • 查找“IN”子句中与记录不匹配的值

    我有一个dept表与eng and finance记录 它不包含以下记录foo bar or baz 我有一个休眠查询 它返回与我要查找的内容匹配的记录 看起来像 select from dept where dept name in en
  • 布局权重在 ScrollView 中不起作用

    我想将布局权重分配给一个中的多个项目LinearLayout里面的一个ScrollView 但是 那ScrollView忽略LinearLayout weightSum 我的目标是将布局划分为权重 2 1 1 总和为 4 但这在 a 内部无
  • 在 R 中访问多个艺术家的 Spotify API

    我在 Spotify 的开发者应用程序部分创建了客户端 ID 和密钥 我正在参考这份文件 并希望通过选择多名艺术家来扩展它 https www r bloggers com the eurovision 2016 song contest
  • 使用指针交换对象

    我正在尝试为使用 void 指针交换对象的作业问题交换对象 我的函数的声明必须是 void swap void a void b size t size 我并不是在寻找如何执行此操作的确切代码 这样我就可以自己弄清楚 但我不确定我是否理解正
  • 找出3个字母的单词

    我的 Python 代码如下 import re string what are you doing you i just said hello guys regexValue re compile r s w w w s mo regex
  • R光栅绘制图像,画一个圆并掩盖圆外的像素

    下面的代码绘制一个图像 然后在该图像上绘制圆圈 我想让该圆圈之外的所有像素都变成黑色 我怎么能这么做呢 library raster library plotrix r1 lt brick system file external rlog
  • 无法安装 XML 包

    我无法安装最新的 XML 包 V3 9 4 我通过键入以下内容在 RGUI 中手动安装它 install packages LOCALDIR XML 3 6 0 tar gz repos NULL type source 但收到一些错误消息
  • Vaadin 7 在执行后台线程后不刷新 UI(仅当需要超过 5 分钟时)

    我正在使用 Vaadin 7 在我的应用程序中 有一个表格列表 用户可以选择一个或多个项目来详细说明 如果单击执行按钮 UI 组件将被禁用并启动两个线程 第一个线程执行繁重的计算 第二个线程更新 UI 上的进度指示器 使用 lock 和un
  • jest.mock express-jwt 受保护路由的中间件行为

    所以现在我有这样的东西 这不起作用 import app from src app beforeAll gt jest mock src middleware auth gt req Request res Response next Ne
  • 为什么在 initWIthCoder:(NSCoder *)aDecoder 中调用时未设置我的 UIView 图层属性?

    我构建了一个自定义 View 类 它是 UIView 的子类 该视图通过 ViewController 中的 nib 文件加载 NSBundle main loadNibNamed MyCustomView owner self optio
  • CUnit - “模拟”libc 函数

    我正在使用 CUnit 进行项目单元测试 我需要测试是否使用正确的参数调用 libc 函数以及是否以正确的方式处理它们的返回值 例如 如果我调用 bind 函数 我想检查我传递的 af 参数并断言如果这是错误的 并且我还想模拟它的返回值并断
  • Google OAuth2 授权 OAuth 令牌错误:redirect_uri_mismatch

    我正在根据此网页创建并授权 OAuth 令牌 https code google com p google mail oauth2 tools wiki OAuth2DotPyRunThrough https code google com
  • 找到最小正值

    从固定数量 在本例中为 3 个值中找到最小非零正值或在没有正问题时返回 0 的最佳算法是什么 我的天真的方法如下 在Delphi中 但请随意使用您喜欢的任何方法 但我认为有一种更优雅的方法 value1Temp MaxInt value2T
  • 将多个源 ArrayList 同步到单个目标列表

    需要从两个不同的表加载项目列表 ArrayList 由于 Hibernate 映射复杂 它们无法作为一个 ArrayList 加载 所以它们是由类定义中的两个不同的Hibernate包分别加载的 当我使用它们时 我想合并成一个列表 所以我正
  • 仅在调试模式下使用特定的 minSdkVersion

    如何仅在调试模式下使用特定的 minSdkVersion 我想使用 minSdkVersion 21 进行调试模式 但使用 minSdkVersion 15 进行发布 我不想为此使用香料 因为会带来麻烦 我认为可能是这样的 但不起作用 de
  • 你能在 Perl 中强制刷新输出吗?

    我在 Perl 中有以下两行 print Warning this will overwrite existing files Continue y N n my input
  • 将 SAFEARRAY 从 C++ 返回到 C#

    我有一个 C 方法 可以创建 填充并返回 SAFEARRAY SAFEARRAY TestClass GetResult long size return GetSafeArrayList size How should I export