PInvoke:很难使用缓冲区指针设置结构,该指针可以根据上下文采用各种类型

PInvoke: Struggle to set up struct with a buffer pointer which could take various types depending on context

提问人:Volodymyr Lytvyn 提问时间:7/19/2023 最后编辑:Volodymyr Lytvyn 更新时间:7/24/2023 访问量:45

问:

我正在尝试为由 tsmapi64.dll 表示的 IBM TSM Api(tivoli 存储管理器)构建 C# 客户端 作为参考,我有 api 文档和一个用 C 编写的示例应用程序(源代码 + 标头),它展示了如何使用 api 调用。示例应用程序在所有情况下都可以正常工作,而我的 C# 应用程序在某些函数上成功,但有些函数失败。

在 api 中声明了一个结构体 DataBlk,用于各种函数。它内部有缓冲区指针,可用于发送数据以及接收数据。

typedef struct
{
   dsUint16_t stVersion ;                /* structure version            */
   dsUint32_t bufferLen;                 /* Length of buffer passed below */
   dsUint32_t numBytes;                  /* Actual number of bytes read from */
                                         /* or written to the buffer */
   char       *bufferPtr;                /* Data buffer */
   dsUint32_t numBytesCompressed;        /* on send actual bytes compressed */
   dsUint16_t reserved;                  /* for future use                  */
}DataBlk;

它用于在函数 dsmSendData 中发送数据: dsInt16_t dsmSendData (dsUint32_t dsmHandle, DataBlk *dataBlkPtr);

数据Blk *dataBlkPtr (I/O) 此参数指向一个结构,该结构包括指向要从中发送数据的缓冲区的指针以及缓冲区的大小。返回时,此结构包含实际传输的字节数。 此调用的 C 代码:

    dataBlkArea.bufferPtr = (char *)malloc(20);
    dataBlkArea.stVersion = DataBlkVersion;
    dataBlkArea.bufferLen = 20;
    memcpy(dataBlkArea.bufferPtr,"T1T1T1T1T1T1T1T1T1T1", 20); 

    dsmSendData(handle,&dataBlkArea))

此调用的 C# 代码(成功):

    [DllImport("tsmapi64.dll")]
    private static extern Int16 dsmSendData(UInt32 dsmHandleP, ref DataBlk dataBlkPtr);

    [StructLayout(LayoutKind.Sequential)]
    public struct DataBlk
    {
        public UInt16 stVersion;
        public UInt32 bufferLen;
        public UInt32 numBytes;
        public IntPtr bufferPtr;
        public UInt32 numBytesCompressed;
        public UInt16 reserved;
    }

   DataBlk dataBlkPtr = new DataBlk();
   var buffStr = "12345";
   var buffPtr = Marshal.StringToHGlobalAuto(buffStr);
   dataBlkPtr.bufferPtr = buffPtr;

   DsmSendData(handle, ref dataBlkPtr);

上面的东西成功了,但现在我需要使用函数 dsmGetNextQObj 从服务器查询数据: dsInt16_t dsmGetNextQObj (dsUint32_t dsmHandle, DataBlk *dataBlkPtr);

DataBlk dataBlkPtr (I/O) 指向一个结构,该结构包括指向要接收的数据的缓冲区的指针和缓冲区的大小。此缓冲区是qryResp 数据(qryRespArchiveData、qryRespBackupData 等)响应结构。返回时,此结构包含传输的字节数。下表描述了与每种类型的查询关联的结构 dataBlkPtr 参数必须指向使用 qryResp*Data 结构类型定义的缓冲区。调用 dsmGetNextQObj 的上下文决定了在查询响应中输入的结构类型。

此调用的 C 代码:

typedef struct S_archDetailCG
{
   char         cgName[DSM_MAX_CG_NAME_LENGTH + 1];       /* Copy group name */
   dsUint16_t   frequency;                       /* Copy (archive) frequency */
   dsUint16_t   retainVers;                                /* Retain version */
   dsUint8_t    copySer;       /* for copy serialization values, see defines */
   dsUint8_t    copyMode;         /* for copy mode values, see defines above */
   char         destName[DSM_MAX_CG_DEST_LENGTH + 1];      /* Copy dest name */
   dsmBool_t    bLanFreeDest;            /* Destination has lan free path?   */
   dsmBool_t    reserved;                /* Not currently used               */
   dsUint8_t    retainInit;              /* possible values see above        */
   dsUint16_t   retainMin;               /* if retInit is EVENT num of days  */
   dsmBool_t    bDeduplicate;            /* destination has dedup enabled    */
}archDetailCG;

typedef struct S_backupDetailCG
{
   char         cgName[DSM_MAX_CG_NAME_LENGTH + 1];       /* Copy group name */
   dsUint16_t   frequency;                               /* Backup frequency */
   dsUint16_t   verDataExst;                         /* Versions data exists */
   dsUint16_t   verDataDltd;                        /* Versions data deleted */
   dsUint16_t   retXtraVers;                        /* Retain extra versions */
   dsUint16_t   retOnlyVers;                         /* Retain only versions */
   dsUint8_t    copySer;       /* for copy serialization values, see defines */
   dsUint8_t    copyMode;         /* for copy mode values, see defines above */
   char         destName[DSM_MAX_CG_DEST_LENGTH + 1];      /* Copy dest name */
   dsmBool_t    bLanFreeDest;            /* Destination has lan free path?   */
   dsmBool_t    reserved;                /* Not currently used               */
   dsmBool_t    bDeduplicate;            /* destination has dedup enabled    */
}backupDetailCG;

typedef struct S_qryRespMCDetailData
{
   dsUint16_t      stVersion;                       /* structure version */
   char            mcName[DSM_MAX_MC_NAME_LENGTH + 1];        /* mc name */
   char            mcDesc[DSM_MAX_MC_DESCR_LENGTH + 1]; /*mc description */
   archDetailCG    archDet;                 /* Archive copy group detail */
   backupDetailCG  backupDet;                /* Backup copy group detail */
}qryRespMCDetailData;

    DataBlk                 qData;
    qryRespMCDetailData     qRespMCData, *mcResp;

    qData.stVersion = DataBlkVersion;
    qData.bufferLen = sizeof(qryRespMCDetailData);
    qData.bufferPtr = (char *)&qRespMCData;

    dsmGetNextQObj(handle,&qData)

我正在尝试的 C# 代码,但它失败了:

    [StructLayout(LayoutKind.Sequential)]
    public struct DataBlk
    {
        public UInt16 stVersion;
        public UInt32 bufferLen;
        public UInt32 numBytes;
        public IntPtr bufferPtr;
        public UInt32 numBytesCompressed;
        public UInt16 reserved;
    }    

[StructLayout(LayoutKind.Sequential)]
    public struct qryRespMCDetailData
    {
        public UInt16 stVersion { get; set; }
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string mcName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string mcDesc;
        public archDetailCG archDet;
        public backupDetailCG backupDet;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct archDetailCG
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string cgName;
        public UInt16 frequency;
        public UInt16 retainVers;
        public byte copySer;
        public byte copyMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string destName;
        public byte bLanFreeDest;
        public byte reserved;
        public byte retainInit;
        public UInt16 retainMin;
        public byte bDeduplicate;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct backupDetailCG
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string cgName;
        public UInt16 frequency;
        public UInt16 verDataExst;
        public UInt16 verDataDltd;
        public UInt16 retXtraVers;
        public UInt16 retOnlyVers;
        public byte copySer;
        public byte copyMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string destName;
        public byte bLanFreeDest;
        public byte reserved;
        public byte bDeduplicate;
    }     

            qryRespMCDetailData qryRespMCDetailData = new qryRespMCDetailData();
            backupDetailCG backupDetailCG = new backupDetailCG();
            archDetailCG archDetailCG = new archDetailCG();
            qryRespMCDetailData.backupDet = backupDetailCG;
            qryRespMCDetailData.archDet = archDetailCG;
            qryRespMCDetailData.stVersion = 4;

            IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf<qryRespMCDetailData>());
            Marshal.StructureToPtr(qryRespMCDetailData, buffer1, false);

            DataBlk dataBlkPtr = new DataBlk();
            dataBlkPtr.stVersion = 2;
            dataBlkPtr.bufferPtr = buffer1;
            dataBlkPtr.bufferLen = (uint)Marshal.SizeOf<qryRespMCDetailData>();

var rc = dsmGetNextQObj(handle, ref dataBlkPtr);
            if (rc != 0)
            {
                string message = "";
                var messagePtr = Marshal.StringToHGlobalAnsi(message);
                dsmRCMsg(handle, rc, messagePtr);
                Console.WriteLine("dsmGetNextQObj rc info = " + Marshal.PtrToStringAnsi(messagePtr));
            }
            else
            {
                var struc = Marshal.PtrToStructure<qryRespMCDetailData>(dataBlkPtr.bufferPtr);
                Console.WriteLine("mcName = " + struc.mcName);
            }

我收到错误“2065 E DSM_RC_WRONG_VERSION_PARM调用方的结构版本与 TSM 库版本不同。

我认为问题出在字段 DataBlk.bufferPtr 和/或 DataBlk.numBytes 的类型/值上,因为在找到其缓冲区指针和大小的正确组合之前,我对 dsmSendData 调用有相同的错误。

那么,您能否建议我应该如何为结构 qryRespMCDetailData 设置指针和大小? 这个用于管理类查询,但对于其他类型的查询,其他类型的返回结构是预期的,例如“qryRespArchiveData”,但我认为方法应该是类似的。

我在某处看到过 C (char *) 可能意味着一种通用指针,但是我如何将其应用于返回结构?

为这项任务尝试不安全的指针有意义吗?

谢谢!

pinvoke 编组

评论

0赞 Simon Mourier 7/20/2023
据我所知,如果没有完整的程序,这似乎是正确的。您要做的是检查您使用的所有 SizeOf 是否与本机匹配。例如,只需编写一个转储 sizeof(qryRespMCDetailData) 的小型 C 程序,并与定义的 Marshal.Sizeof 进行比较。填充和/或字符集可以改变事情。
0赞 Volodymyr Lytvyn 7/21/2023
事实上,C 在 C# 中显示 444 而不是 488,所以我既应该分配这个数量的 mem 又应该将其作为缓冲区 len,对吧?良好的渔获@SimonMourier
0赞 Simon Mourier 7/21/2023
它们不匹配的事实意味着您的 C# 定义不正确,因此您必须将其修复为每个完美的 C# sizeofs == C sizeofs。它可以是字符集(ansi vs unicode learn.microsoft.com/en-us/dotnet/api/...)或打包(我之前说过“填充”,但我指的是打包)问题(learn.microsoft.com/en-us/dotnet/api/...)。
0赞 Volodymyr Lytvyn 7/24/2023
我在 C# 中将 bool 更改为 byte,并且结构的大小现在匹配。但是问题没有消失(((还有别的吗@SimonMourier?
1赞 Volodymyr Lytvyn 7/24/2023
成功了!!!我想在使尺寸匹配后,我只是错过了qryRespMCDetailData.stVersion字段 谢谢你,祝福你@SimonMourier祝你有美好的一天!

答:

0赞 Volodymyr Lytvyn 7/24/2023 #1

最终,这奏效了

   [StructLayout(LayoutKind.Sequential)]
    public struct DataBlk
    {
        public UInt16 stVersion;
        public UInt32 bufferLen;
        public UInt32 numBytes;
        public IntPtr bufferPtr;
        public UInt32 numBytesCompressed;
        public UInt16 reserved;
    }    

[StructLayout(LayoutKind.Sequential)]
    public struct qryRespMCDetailData
    {
        public UInt16 stVersion { get; set; }
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string mcName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string mcDesc;
        public archDetailCG archDet;
        public backupDetailCG backupDet;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct archDetailCG
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string cgName;
        public UInt16 frequency;
        public UInt16 retainVers;
        public byte copySer;
        public byte copyMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string destName;
        public byte bLanFreeDest;
        public byte reserved;
        public byte retainInit;
        public UInt16 retainMin;
        public byte bDeduplicate;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct backupDetailCG
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 31)]
        public string cgName;
        public UInt16 frequency;
        public UInt16 verDataExst;
        public UInt16 verDataDltd;
        public UInt16 retXtraVers;
        public UInt16 retOnlyVers;
        public byte copySer;
        public byte copyMode;
        public string destName;
        public byte bLanFreeDest;
        public byte reserved;
        public byte bDeduplicate;
    }     

            qryRespMCDetailData qryRespMCDetailData = new qryRespMCDetailData();
            backupDetailCG backupDetailCG = new backupDetailCG();
            archDetailCG archDetailCG = new archDetailCG();
            qryRespMCDetailData.backupDet = backupDetailCG;
            qryRespMCDetailData.archDet = archDetailCG;
            qryRespMCDetailData.stVersion = 4;

            IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf<qryRespMCDetailData>());
            Marshal.StructureToPtr(qryRespMCDetailData, buffer1, false);

            DataBlk dataBlkPtr = new DataBlk();
            dataBlkPtr.stVersion = 2;
            dataBlkPtr.bufferPtr = buffer1;
            dataBlkPtr.bufferLen = (uint)Marshal.SizeOf<qryRespMCDetailData>();

var rc = dsmGetNextQObj(handle, ref dataBlkPtr);
            if (rc != 0)
            {
                string message = "";
                var messagePtr = Marshal.StringToHGlobalAnsi(message);
                dsmRCMsg(handle, rc, messagePtr);
                Console.WriteLine("dsmGetNextQObj rc info = " + Marshal.PtrToStringAnsi(messagePtr));
            }
            else
            {
                var struc = Marshal.PtrToStructure<qryRespMCDetailData>(dataBlkPtr.bufferPtr);
                Console.WriteLine("mcName = " + dataBlkPtr.bufferPtr);
            }