Reputation: 37
I have only DLL and text file of API description from customer (see below). I don't have any more details regarding the DLL. I don't know the Delphi version etc.
I tried to use both of API functions but was not successful. Generally first two parameters of string[255] (PatientID and AccessionNo) are important. Any my attempts to pass strings to that DLL don't provide proper result. I see random garbage values or part of strings in application GUI. I looked at all related question on this site and searched in the internet but didn't find anything which helped me.
OpenStudy function - I tried different settings for CharSet = CharSet.Ansi and Auto, for MarshalAs all suitable values (see below). I see the garbage random value in the managed application GUI text fields.
[DllImport("Lib\\RISInterface.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "Open_Study", ExactSpelling = false)]
static extern internal int OpenStudy(
// try to use here and for all string fields [MarshalAs(UnmanagedType.AnsiBStr)] , LPStr, LPTStr, LPWStr, BStr, TBStr, HString
string PatientID,
string AccessionNo,
bool CloseCurrentStudy,
bool AddToWindow,
int SeriesRows,
int SeriesCols,
int PresentationMode,
bool AutoTile,
bool AutoLoad,
bool RemoteExam);
OpenStudy1 function - I fill the structure and call function. I see PatientID and AccessionNo as normal string but PatientID misses the first letter and AccessionNo misses the first two letters. ( send: "qwerty", "12345" and see: "werty", "345")
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TIQStudyAutomation
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string PatientID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string AccessionNo;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string StudyUID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string SeriesUID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string InstanceUID;
public bool CloseCurrentStudy;
public bool AddToWindow;
public int SeriesRows;
public int SeriesCols;
public int PresentationMode;
public bool AutoTile;
public bool AutoLoad;
public bool RemoteExam;
public bool LoadFromAllSources;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string ArchiveName;
}
[DllImport("Lib\\RISInterface.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "Open_Study1", ExactSpelling = false)]
static extern internal int OpenStudy1(TIQStudyAutomation automationInfo);
================= API description ==========================================
These function declarations are Delphi Code and all string references are Ansi strings.
function Open_Study(PatientID, AccessionNo: PAnsiChar; CloseCurrentStudy, AddToWindow: Boolean;
SeriesRows, SeriesCols, PresentationMode: Integer;
AutoTile, AutoLoad, RemoteExam: Boolean): Integer; stdcall;
Return
Error Code.
Remarks
The parameters will be packed into a TIQStudyAutomation record and passed to Open_Study1.
//--------------------------------------------------------------------------------------------------
function Open_Study1(AutomationInfo: TIQStudyAutomation): Integer; stdcall;
Return
Error Code.
Parameters
Takes a TIQStudyAutomation record.
//--------------------------------------------------------------------------------------------------
TIQStudyAutomation = record
PatientID, AccessionNo, StudyUID, SeriesUID, InstanceUID: STRING[255];
CloseCurrentStudy, AddToWindow: BOOLEAN;
SeriesRows, SeriesCols, PresentationMode: Integer;
AutoTile, AutoLoad, RemoteExam, LoadFromAllSources : BOOLEAN; ArchiveName: STRING[255];
end;
Any help?
Upvotes: 2
Views: 762
Reputation: 37
The final solution is following (structure declaration, function declaration and function to convert string to Delphi String[255] as a byte array):
public struct TIQStudyAutomation
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] PatientID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] AccessionNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] StudyUID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] SeriesUID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] InstanceUID;
[MarshalAs(UnmanagedType.U1)]
public bool CloseCurrentStudy;
[MarshalAs(UnmanagedType.U1)]
public bool AddToWindow;
public int SeriesRows;
public int SeriesCols;
public int PresentationMode;
[MarshalAs(UnmanagedType.U1)]
public bool AutoTile;
[MarshalAs(UnmanagedType.U1)]
public bool AutoLoad;
[MarshalAs(UnmanagedType.U1)]
public bool RemoteExam;
[MarshalAs(UnmanagedType.U1)]
public bool LoadFromAllSources;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] ArchiveName;
}
[DllImport("Lib\\RISInterface.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "Open_Study1", ExactSpelling = false)]
static extern internal int OpenStudy1(TIQStudyAutomation automationInfo);
private byte[] ConvertToDelphiString255(string input)
{
var output = new byte[256];
if(String.IsNullOrEmpty(input))
{
return output;
}
var ar = Encoding.ASCII.GetBytes(input);
var length = ar.Length > 255 ? 255 : ar.Length;
output[0] = (byte)length;
for(int i=0; i<length; i++)
{
output[i+1] = ar[i];
}
return output;
}
Upvotes: 0
Reputation: 612954
There's no need to translate the record, which is quite tricky due to the use of Delphi shortstrings. Instead you can simply call Open_Study
. The Delphi declaration is:
function Open_Study(
PatientID: PAnsiChar;
AccessionNo: PAnsiChar;
CloseCurrentStudy: Boolean;
AddToWindow: Boolean;
SeriesRows: Integer;
SeriesCols: Integer;
PresentationMode: Integer;
AutoTile: Boolean;
AutoLoad: Boolean;
RemoteExam: Boolean
): Integer; stdcall;
The only real wrinkle in translating this is that the Delphi Boolean
type is a 1 byte type. But C# bool
defaults to marshalling as a 4 byte type, to match the Win32 type BOOL
.
So I would translate the function like so:
[DllImport("Lib\\RISInterface.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi, EntryPoint = "Open_Study", ExactSpelling = true)]
static extern internal int OpenStudy(
string PatientID,
string AccessionNo,
[MarshalAs(UnmanagedType.U1)] bool CloseCurrentStudy,
[MarshalAs(UnmanagedType.U1)] bool AddToWindow,
int SeriesRows,
int SeriesCols,
int PresentationMode,
[MarshalAs(UnmanagedType.U1)] bool AutoTile,
[MarshalAs(UnmanagedType.U1)] bool AutoLoad,
[MarshalAs(UnmanagedType.U1)] bool RemoteExam
);
As for string[255]
that is somewhat tricky to deal with. That type has size 256. The first byte is the string length, and the remaining bytes are the payload. This payload is an array of ANSI encoded 8 bit characters. And this array need not be null-terminated.
You cannot marshal that automatically because the p/invoke framework does not support such a type. Personally I would declare translate this type to byte[]
. I would marshal it with [MarshalAs(UnmanagedType.ByValArray, SizeConst=256)]
. Then I would write helper functions to marshal to and from a C# string. These would involve calls to Encoding.GetString
and Encoding.GetBytes
, taking care to handle the string length appropriately.
It's certainly possible, but a little messy. Hence I suggest you avoid doing so if possible and call the helper function Open_Study
.
Upvotes: 1