【问题标题】:Marshalling an array of strings from managed to native code将字符串数组从托管代码编组为本机代码
【发布时间】:2012-11-19 05:24:29
【问题描述】:

我有一个带有以下声明(接口和实现)的托管函数:

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
String[] ManagedFunction()
{
    String[] foo = new String[1];
    foo[0] = "bar";
    return foo;
}

还有一个本地 C++ 接口,其方法与托管接口相同,在该接口内部,该方法具有以下声明:

void ManagedFunction(SAFEARRAY* foo); 

本机代码调用该函数的方式如下:

void NativeFunction(ManagedBinding binding)
{
    CComSafeArray<BSTR> cComSafeArray;
    cComSafeArray.Create(); 
    LPSAFEARRAY safeArray = cComSafeArray.Detach();
    binding.comObject->ManagedFunction(safeArray); 
}

我不确定我做错了什么,但在调用我的托管函数后,safeArray 似乎有垃圾值,在将返回值编组回本机代码时出现问题。在.Net interop 方面比我有更多经验的人可以对此有所了解吗?此外,可能值得一提的是,我从托管函数返回ValueTypes 没有问题(如果你好奇的话,boolean),关于返回String 数组的一些事情搞砸了。谢谢!

【问题讨论】:

    标签: c# c++ .net interop marshalling


    【解决方案1】:

    1) 你的函数返回一个SAFEARRAY,那么为什么要在调用函数之前分配它呢?
    2)ManagedFunction 应该返回一个SAFEARRAY,所以它应该得到一个SAFEARRAY* 才能返回它!所以你应该说:

    LPSAFEARRAY lpsa;
    binding.comObject->ManagedFunction(&lpsa);
    CComSafeArray<BSTR> cComSafeArray;
    cComSafeArray.Attach(lpsa);
    

    【讨论】:

    • 那部分不是我特别喜欢接触的代码。我想我应该提到这一点。我不知道以调用方式调用binding.comObject 上的方法的原因,但我确信这是有充分理由的。 ManagedFunction 获取数组没有任何问题。它的编组回本机代码是问题所在。
    • 问题是ManagedFunction 永远不应该得到SAFEARRAY,因为您通过COM 对象调用托管代码并且在COM 返回值标记有[out, retval] 导致编组者从不将它们从调用者编组,而是从被调用者编组
    • 好吧,但这就是我想要的,数组一开始是空的。只要调用者内部所做的更改得到传播,我就会很高兴。我尝试了许多其他技术(例如,将 SAFEARRAY 作为 out 参数传递给托管函数,而不是将其保留为返回值),但它们都不起作用。我认为通常对字符串数组或引用类型进行编组具有某种复杂性,而这并没有得到足够好的记录。
    【解决方案2】:

    好吧,我终于让它工作了。我创建了一个名为ManagedSafeArraySAFEARRAY 的托管表示(从这里窃取:http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/):

    [StructLayout(LayoutKind.Sequential)]
    struct ManagedSafeArray
    {
        public ushort   dimensions;     // Count of dimensions in the SAFEARRAY
        public ushort   features;       // Flags to describe SAFEARRAY usage
        public uint     elementSize;    // Size of an array element
        public uint     locks;          // Number of times locked without unlocking
        public IntPtr   dataPtr;        // Pointer to the array data
        public uint     elementCount;   // Element count for first (only) dimension
        public int      lowerBound;     // Lower bound for first (only) dimension
    }
    

    我将方法的签名更改为:

    void ManagedMethod(ref ManagedSafeArray foo);
    

    在我的方法中,我通过调用Marshal.AllocCoTaskMem(...) 手动更新了dataPtr 字段,然后复制了我希望SAFEARRAY 包含的字符串。

    我不知道为什么 CLR 不能自动将参数编组到本机代码和从本机代码中提取,如果有人能尝试解释一下,我仍然很感激。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      • 2013-03-06
      • 1970-01-01
      • 1970-01-01
      • 2017-06-30
      相关资源
      最近更新 更多