I am trying to build C# client for IBM TSM Api (tivoli storage manager) represented by tsmapi64.dll For reference I have api documentation and also a sample application (source code + headers), written in C, which shows how to use api calls. Sample application works ok in all cases, while my C# application succeeds for some of functions, but some fail.
There is a struct declared in api, DataBlk, which is used in various functions. It has buffer pointer inside and can be used for sending data as well as for receiving.
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;
It is used for sending data in function dsmSendData: dsInt16_t dsmSendData (dsUint32_t dsmHandle, DataBlk *dataBlkPtr);
DataBlk *dataBlkPtr (I/O) This parameter points to a structure that includes both a pointer to the buffer from which the data are to be sent, as well as the size of the buffer. On return, this structure contains the number of bytes that is actually transferred. C code for this call:
dataBlkArea.bufferPtr = (char *)malloc(20);
dataBlkArea.stVersion = DataBlkVersion;
dataBlkArea.bufferLen = 20;
memcpy(dataBlkArea.bufferPtr,"T1T1T1T1T1T1T1T1T1T1", 20);
dsmSendData(handle,&dataBlkArea))
C# code for this call (Succeeds):
[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);
Upper stuff succeeds, but now I need to query data from the server with function dsmGetNextQObj: dsInt16_t dsmGetNextQObj (dsUint32_t dsmHandle, DataBlk *dataBlkPtr);
DataBlk dataBlkPtr (I/O) Points to a structure that includes both a pointer to the buffer for the data to be received and the size of the buffer. This buffer is the qryRespData (qryRespArchiveData, qryRespBackupData, etc.) response structure. On return, this structure contains the number of bytes that is transferred. The structure that is associated with each type of query is described in the following table The dataBlkPtr parameter must point to a buffer that is defined with the qryResp*Data structure type. The context in which dsmGetNextQObj is called determines the type of structure that is entered on the query response.
C code for this call:
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# code which I am trying, but it fails:
[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);
}
I receive error "2065 E DSM_RC_WRONG_VERSION_PARM The caller's structure version is different than the TSM library version."
I assume problem is with types/values of fields DataBlk.bufferPtr and/or DataBlk.numBytes, because I had same error for dsmSendData calls until I found proper combination of its buffer pointer and size.
So, could you please suggest how should I set pointer and size for structure qryRespMCDetailData? This one is used for management class query but for other types of queries other types of return structures expected, for example 'qryRespArchiveData', but I assume approach should be similar.
I have seen somewhere that in C (char *) could mean kind of universal pointer, but how do I apply it for returning structure?
Is there sense to try unsafe pointers for this task?
Thank you!
Eventually this worked
[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);
}