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

本文关键字:参数 ppsa 调用 方法 代码 SAFEARRAY | 更新日期: 2023-09-27 18:29:16

我一直在为如何将此VB6代码迁移到C#而苦苦挣扎。它包括调用传递结构数组的DLL内部的函数等。

因此,在VB6中,"struct"声明如下:

'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 

一个使用这个"结构"的函数有一个声明为这样的方法:

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

因此,我尝试将这两段代码转换为C#。我已经尝试了很多变体,但我会在这里发布我的最新变体。其思想是通过P/Invoke调用该函数。

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;
}

和功能导入(到目前为止):

    [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);

到目前为止,在我关于用参数编组属性的各种实验中,没有任何结果。

我设法找到了这个DLL的C头文件,声明如下:

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

C世界中的"struct"是这样声明的:

/* 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; 

在我的沮丧中,我试图用ITypeLib Viewer工具打开这个DLL。我很惊讶DLL文件可以打开,即使我不能在我的项目中添加这个DLL作为引用。总之,我在观众中观察到了一些事情。

该函数具有以下签名:

[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); 

"结构"看起来是这样的:

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;

基于这些观察结果,我对C#结构的编组属性进行了研究,例如

1.)将结构的属性更改为[MarshalAs(UnmanagedType.BStr)]

2.)将函数的getdat_str_data参数属性更改为MarshalAs(UnmanagedType.SafeArray,SafeArraySubType=VarEnum.VT_RECORD)

但仍然没有任何效果。

这里有一篇关于类似主题的博客/文章:http://limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/但我不能只想着它。

与使用此DLL函数调用的C#(.Net)相比,VB6似乎可以做得非常简单。关于如何在C#(.Net)中声明DLLImport函数,有什么提示或想法吗?

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

您需要将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
);

在C#结构中,您错误地处理了BSTR成员。应该是

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