如何将自定义结构传递给 C++(非 CLI)中的 _variant_t?

Posted

技术标签:

【中文标题】如何将自定义结构传递给 C++(非 CLI)中的 _variant_t?【英文标题】:How to pass a custom struct into a _variant_t in C++ (non-CLI)? 【发布时间】:2011-08-02 13:23:17 【问题描述】:

我正在尝试传递一个结构 e。 g.:

struct SVec3

public:
    float X;
    float Y;
    float Z;
;

到 _variant_t 中,将其存储在 SAFEARRAY 中。我的方法是首先创建一个实例:

SVec3 rot;
rot.X = 0.1f;
rot.Y = 0.65f;
rot.Z = 0.01f;

然后我通过引用将它放入 _variant_t

_variant_t var((IUnknown*)&rot, true);

并将其存储在 SAFEARRAY 中:

LONG index = 0;
SAFEARRAY* psaArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
SafeArrayPutElement(psaArgs, &index, &var);  // This throws a memory access exception.

那么我的错误是什么?还有其他方法吗?如果是这样,我应该如何将这些结构的 SAFEARRAY 传递给 SAFEARRAY?有没有通用的方法?或者我是否缺少像 Recorddescription 这样的东西,因为这样 SAFEARRAY 不包含数据,它包含指向数据的指针。但是如何将数据存储在 SAFEARRAY 中?

问候尼姆

【问题讨论】:

我还没有尝试过,但是查看MSDN 看起来您可能希望类型为VT_ARRAY | VT_R4 这里不能绕过 VT_RECORD 和 IRecordInfo。已经有足够的链接汤,但规范的 MSDN 库页面在这里:msdn.microsoft.com/en-us/library/ms221453%28v=VS.85%29 为什么不将它存储为 3 个元素的 SAFEARRAY,每个元素都是一个浮点数? 【参考方案1】:

您正在将 SVec3 指针转换为指向 IUnknown 接口的指针。您的数据结构是一种简单的数据类型,而不是实现 IUnknown 的成熟类。因此,一旦 Windows 尝试调用 IUnknown 上的方法,您的应用程序就会中断。

一些快速的谷歌搜索揭示了一些可能对变体和用户定义类型有用的链接:

http://social.msdn.microsoft.com/Forums/en/vclanguage/thread/38dca037-f6da-43dc-8fa3-0358638681ce http://msdn.microsoft.com/en-us/library/ms221039(VS.85).aspx http://msdn.microsoft.com/en-us/library/ms221210(VS.85).aspx 特别适合您:传递 UDT 的 Safearray:http://msdn.microsoft.com/en-us/library/ms221212(VS.85).aspx http://msdn.microsoft.com/en-us/library/ms221543(VS.85).aspx

您必须首先将 UDT 放入类型库中...

【讨论】:

这个链接我已经看了几个小时了。 主题“传递 UDT 的 Safearray”似乎对您的应用程序特别有用……您尝试过吗? 我不知道它是什么语法,但我的编译器说“不”。看起来像 CLI 或类似的东西。特别是我在谈论 typedef [uuid(D8B3861A-74C6-11d2-A0D6-00C04FB17CDB)] struct tagStudentStruct .... 那部分是接口描述语言,而不是 C++。见msdn.microsoft.com/en-us/library/aa367091(VS.85).aspx。基本上,您使用不同的编译器 (MIDL) 将其编译为类型库,您将其作为 Windows 资源包含在程序的 EXE 中。您可以使用 OLEVIEW.EXE 查看安装在您计算机上的各种库的 IDL,以了解情况。 MIDL还可以输出你可以使用的C/C++头文件。 如果这完全超出您的想象,您最好使用 3 个单独的数组:X 数组、Y 数组和 Z 数组。然后您可以使用原始类型数组 ( float) 而不是类型库中的用户定义类型。【参考方案2】:

漫长的话题,漫长的一天。我不想将我的程序集注册为 COM 对象(这将违反我对该项目的要求),所以我必须避免使用 COM-Stuff。为此,我做了一个解决方法。下面是一个例子来说明这一点(C# 汇编代码):

[EditorBrowsable(EditorBrowsableState.Never)]
    public float[] INTEROP_Position
    
        [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R4)]
        get  return (float[])this.Position; 
        [param: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R4)]
        set  this.Position = (DataTypes.Vec3)value; 
    

    // Inside the Vec3 Definition:
    public static explicit operator Vec3(float[] arr)
    
        if (arr.Length != 3) throw new Exception("The array must have a length of 3.");
        return new Vec3(arr[0], arr[1], arr[2]);
    

    public static explicit operator float[](Vec3 vec3)
    
        return new float[]  vec3.X, vec3.Y, vec3.Z ;
    

在 C++ 方面,我手动编组

_variant_t Convert::svec3tovar(SVec3 vec) 
    LONG index = 0;
    SAFEARRAY* psaStruct = SafeArrayCreateVector(VT_R4, 0, 3);
    SafeArrayPutElement(psaStruct, &index, &vec.X);
    index = 1;
    SafeArrayPutElement(psaStruct, &index, &vec.Y);
    index = 2;
    SafeArrayPutElement(psaStruct, &index, &vec.Z);

    VARIANT vV ;
    vV.vt = VT_ARRAY | VT_R4;
    vV.parray = psaStruct;  
    return _variant_t(vV);

返回

SVec3 Convert::vartosvec3(_variant_t var) 
    SVec3 vec;
    SAFEARRAY* psaStruct = var.parray;
    LONG index = 0;
    SafeArrayGetElement(psaStruct, &index, &vec.X);
    index = 1;
    SafeArrayGetElement(psaStruct, &index, &vec.Y);
    index = 2;
    SafeArrayGetElement(psaStruct, &index, &vec.Z);
    return vec;

而且效果很好。没有那些 COM 的东西。 :)

我知道 James 给出的真正答案,但为了避免 COM,这是我的答案。也许 user786653 的评论是解决我的问题的正确答案。 但是感谢 James(和其他人)投入的时间。

/已解决

【讨论】:

以上是关于如何将自定义结构传递给 C++(非 CLI)中的 _variant_t?的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义验证消息传递给 laravel 中的 cviebrock/image-validator?

将非托管方法作为回调传递给托管 C++/CLI 类

将自定义类型传递给 Oracle 过程

将自定义道具传递给 TypeScript 中的 Redux 表单字段

如何将自定义类型数组传递给 Postgres 函数

如何将自定义道具和历史传递给 Route