简体   繁体   English

从托管C#代码中使用SAFEARRAY ** ppsa参数调用非托管C方法

[英]Calling a unmanaged C method with a SAFEARRAY **ppsa argument from managed C# code

I've been struggling on how to migrate this VB6 code into C#. 我一直在努力将VB6代码迁移到C#中。 It involves calling a function inside a DLL passing an array of structure, among other things. 它涉及在DLL内部调用传递结构数组的函数。

So in VB6, the "struct" declaration is like this: 因此在VB6中,“结构”声明如下所示:

'Define structure for RGETDAT_STR procedure call
Type rgetdat_str_data_str
    type As Integer                                     'data type (set internally)
    file As Integer                                     'file in database
    rec As Integer                                      'record in file
    word As Integer                                     'word offset in record
    start_bit As Integer                                'UNUSED
    length As Integer                                   'length of string
    flags As Integer                                    'flags
    padding1 As Integer                                 'UNUSED
    value As String                                     'database value
    status As Integer                                   'return status
    padding2 As Integer                                 'UNUSED
End Type 

and one function that uses this "struct" has a method declared as this: 使用此“结构”的一个函数具有声明为以下内容的方法:

Public Declare Function rgetdat_str Lib "hscnetapi.dll" _
    Alias "rgetdat_str_vb" _
    (ByVal Server As String, ByVal num_points As Integer, _
    getdat_str_data() As rgetdat_str_data_str) As Integer

So, I attempted to convert these 2 pieces of code into C#. 因此,我试图将这两段代码转换为C#。 I had tried so many variations, but I will post here the latest one that I have. 我尝试了很多变体,但是我将在这里发布最新的变体。 The idea is to call the function via P/Invoke . 这个想法是通过P / Invoke调用函数。

The C# struct (so far): C#结构(到目前为止):

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct rgetdat_str_data_str
{
    public short type;
    public short file;
    public short rec;
    public short word;
    public short start_bit;
    public short length;
    public short flags;
    public short padding1;
    [MarshalAs(UnmanagedType.LPStr)]
    public string value;
    public short status;
    public short padding2;
}

and the function import (so far): 和功能导入(到目前为止):

    [DllImport("hscnetapi.dll", EntryPoint = "rgetdat_str_vb")]
    public static extern short rgetdat_str(
        [MarshalAs(UnmanagedType.LPTStr)]
        string Server,
        short num_points,
         [In,Out, MarshalAs(UnmanagedType.LPArray)]
         ref  rgetdat_str_data_str[] getdat_str_data);

Nothing worked so far in my various experiments on marshalling attributes with the parameters. 到目前为止,在我对带有参数的属性进行编组的各种实验中,没有任何工作。

I managed to find the C header file for this DLL, and the declaration looks like this: 我设法找到了这个DLL的C头文件,并且声明看起来像这样:

EXTERN_C short __loadds CALLBACK rgetdat_str_vb_ansi
                _DECLARE((char *szHostname, short cRequests, SAFEARRAY **ppsa)); 

and the "struct" in the C world is declared like this: C世界中的“结构”的声明如下:

/* define union used in rgetdat_value in RGETDAT procedure call */
typedef union rgetdat_value_str
{
    n_short         int2;
    n_long          int4;
    n_float         real4;
    n_double        real8;
    n_char          *str;
    n_ushort        bits;
} rgetdat_value;

/* define structure for RGETDAT procedure call */
typedef struct rgetdat_data_str
{
    n_ushort            type;
    n_ushort            file;
    n_ushort            rec;
    n_ushort            word;
    n_ushort            start_bit;
    n_ushort            length;
    n_short             flags;
    rgetdat_value       value;
    n_short             status;
} rgetdat_data; 

In my frustration, I tried to open this DLL with the ITypeLib Viewer tool. 令我沮丧的是,我尝试使用ITypeLib Viewer工具打开此DLL。 I was surprised that the DLL file can be opened, even though I cannot add this DLL as a Reference in my project. 即使无法在项目中添加此DLL作为引用,我也惊讶地打开了DLL文件。 Anyway, a couple of things that I observed within the viewer. 无论如何,我在查看器中观察到了几件事。

The function has this signature: 该函数具有以下签名:

[entry("rgetdat_str_vb"), helpstring("...")]
short _stdcall rGetdat_Str(
                [in] LPSTR Server, 
                [in] short num_points, 
                [in, out] SAFEARRAY(rGetdat_Str_Data_Str)* getdat_str_data); 

and the "struct" looked like this: 而“结构”如下所示:

typedef struct tagrGetdat_Str_Data_Str {
    short type;
    short file;
    short rec;
    short word;
    short start_bit;
    short length;
    short flags;
    short padding1;
    BSTR value;
    short status;
    short padding2;
} rGetdat_Str_Data_Str;

Based on these observations, I played around with the marshalling attributes of the C# struct, for example, 基于这些观察,我玩弄了C#结构的编组属性,例如,

1.) Changing the struct's value attribute to [MarshalAs(UnmanagedType.BStr)] 1.)将结构的value属性更改为[MarshalAs(UnmanagedType.BStr)]

2.) Changing the function's getdat_str_data parameter attribute to MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD) 2.)将函数的getdat_str_data参数属性更改为MarshalAs(UnmanagedType.SafeArray,SafeArraySubType = VarEnum.VT_RECORD)

and still nothing works. 仍然没有任何效果。

There's an blog/article talking about a similar topic here: http://limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/ but I can't just wrap my head around it. 这里有一个博客/文章在谈论类似的话题: http : //limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/但我不能只是把我的头缠住它。

It seems that VB6 can do it very simply compared to C# (.Net) with this DLL function call. 与使用此DLL函数调用的C#(.Net)相比,VB6似乎可以非常简单地完成此操作。 Any hints or ideas out there on how to DLLImport declare this function in C# (.Net)? 关于如何DLLImport在C#(.Net)中声明此功能的任何提示或想法?

You need to use MarshalAs with UnmanagedType.SafeArray to tell the marshaller that you want the array marshalled as a SAFEARRAY . 您需要将MarshalAsUnmanagedType.SafeArray一起使用,以告诉编组器您希望将数组编组为SAFEARRAY

[DllImport("hscnetapi.dll", EntryPoint = "rgetdat_str_vb")]
public static extern short rgetdat_str(
    [MarshalAs(UnmanagedType.LPStr)]
    string Server,
    short num_points,
    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_USERDEFINED)]
    ref rgetdat_str_data_str[] getdat_str_data
);

In your C# struct, you handle the BSTR member incorrectly. 在您的C#结构中,您错误地处理了BSTR成员。 It should be 它应该是

[MarshalAs(UnmanagedType.BStr)]
public string value;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM