【问题标题】:using Delphi DLL with string[255] types and structure in C# code在 C# 代码中使用带有字符串 [255] 类型和结构的 Delphi DLL
【发布时间】:2016-06-01 08:40:20
【问题描述】:

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

我尝试使用这两个 API 函数但没有成功。通常 string[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);
    

================= API描述============================ ===============

这些函数声明是 Delphi 代码,所有字符串引用都是 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;

有什么帮助吗?

【问题讨论】:

  • @ZENsan 这里还有更多。 string[255] 需要一些编组。
  • 大卫,你能澄清一下“string[255] 需要一些编组”是什么意思吗?
  • 嗯,该类型的大小为 256。第一个字节是长度,从 0 到 255。其余字节是组成字符串的 ANSI 字符。我不相信字符串一定是空终止的。所以你所有的编组都是错误的。我会将其作为byte[] 编组为长度为 256 的ByValArray。我会编写辅助方法在 C# 字符串和 Delphi 短字符串 string[255] 之间移动。
  • 另一个问题是德尔福Boolean。那是一个1字节的类型。您需要将其编组为UnmanagedType.U1 IIRC。我真的无法详细写出所有这些的答案,因为这将是一个非常长的答案,而且我没有时间。感觉就像我正在为您编写整个代码。无论如何,我希望这两个指针有所帮助!

标签: c# delphi


【解决方案1】:

不需要翻译记录,由于使用了 Delphi 短字符串,这非常棘手。相反,您可以简单地致电Open_Study。 Delphi 声明是:

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 位字符数组。而且这个数组不需要以null结尾。

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

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

【讨论】:

  • UnmanagedType.ByValArray 仅适用于结构字段。顺便说一句,我遵循了 string[255] 结构字段的手动转换建议并使其工作。非常感谢。
  • UnmanagedType.ByValArray 仅适用于结构字段。 正确。这正是我提倡使用它的地方。
【解决方案2】:

最终解决方案如下(结构体声明、函数声明和将字符串转换为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;
    }

【讨论】:

  • 您不一定需要这样做。调用高级函数。编码应该是 ANSI 而不是 ASCII。您可以直接复制字节数组内容而不是循环。
  • 我用复制功能替换循环,谢谢。但我不明白如何使用 ANSI 而不是 ASCII。 (“ANSI:没有一种固定的 ANSI 编码 - 有很多。通常当人们说“ANSI”时,他们的意思是“我的系统的默认语言环境/代码页”,它是通过 Encoding.Default 获得的,通常是 Windows-1252但可以是其他语言环境。”见 - link)
  • 嗯,就是这样。 Delphi 类型是 ANSI 编码的。使用区域设置的默认代码页。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
  • 2012-07-28
  • 2016-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多