在C#代码中使用带有字符串[255]类型和结构的Delphi DLL

时间:2016-06-01 08:40:21

标签: c# delphi

我只有来自客户的API描述的DLL和文本文件(见下文)。我没有关于DLL的更多细节。我不知道Delphi版本等。

我尝试使用两种API函数,但没有成功。通常,字符串[255](PatientID和AccessionNo)的前两个参数很重要。我尝试将字符串传递给该DLL的任何尝试都无法提供正确的结果。我在应用程序GUI中看到随机垃圾值或部分字符串。我查看了这个网站上的所有相关问题,并在互联网上搜索,但没有找到任何帮助我的东西。

  1. OpenStudy函数 - 我尝试了CharSet = CharSet.Ansi和Auto的不同设置,对于MarshalAs所有合适的值(见下文)。 我在托管应用程序GUI文本字段中看到了垃圾随机值。

    [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);
    
  2. OpenStudy1函数 - 我填充结构和调用函数。我将PatientID和AccessionNo视为普通字符串,但PatientID错过了第一个字母而AccessionNo错过了前两个字母。 (发送:“qwerty”,“12345”并参见:“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);
    
  3. ================= API描述============================ ==============

    这些函数声明是Delphi Code,所有字符串引用都是Ansi字符串。

    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;
    

    任何帮助?

2 个答案:

答案 0 :(得分:1)

没有必要翻译记录,由于使用Delphi短串,这非常棘手。相反,您只需拨打Open_Study即可。德尔福声明是:

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;

翻译它的唯一真正的缺点是Delphi Boolean类型是1字节类型。但是C#bool默认为4字节类型的编组,以匹配Win32类型BOOL

所以我会像这样翻译函数:

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

至于string[255]处理起来有些棘手。该类型的大小为256.第一个字节是字符串长度,其余字节是有效负载。此有效负载是ANSI编码的8位字符的数组。此数组不需要以空值终止。

您无法自动编组,因为p / invoke框架不支持这种类型。我个人会声明将此类型翻译为byte[]。我会用[MarshalAs(UnmanagedType.ByValArray, SizeConst=256)]编组。然后我会编写辅助函数来编组来自C#字符串和来自C#字符串。这些将涉及对Encoding.GetStringEncoding.GetBytes的调用,注意适当地处理字符串长度。

这当然是可能的,但有点凌乱。因此,我建议你尽可能避免这样做,并调用辅助函数Open_Study

答案 1 :(得分:0)

最终解决方案如下(结构声明,函数声明和函数将字符串转换为Delphi String [255]作为字节数组):

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