将非托管dll的Delphi代码转换为C#

时间:2009-11-27 07:23:32

标签: c# c++ delphi

我希望有人可以帮我解决我目前遇到的问题。我们有很多Delphi遗留代码,需要将我们的一些Delphi应用程序转换为C#。

我目前正在努力解决的遗留代码是从第三方应用程序的非COM DLL调用函数。

这是用于特定功能的C风格的头和结构:

/*** C Function AwdApiLookup ***/  
extern BOOL APIENTRY AwdApiLookup( HWND hwndNotify, ULONG ulMsg,  
                                   BOOL fContainer, CHAR cObjectType,  
                                   SEARCH_CRITERIA* searchCriteria,  
                                   USHORT usCount, USHORT usSearchType,  
                                   VOID pReserved );  

/*** C Struct SEARCH_CRITERIA ***/  
typedef struct _search_criteria  
{  
    UCHAR dataname[4];  
    UCHAR wildcard;  
    UCHAR comparator[2];  
    UCHAR datavalue[75];  
} SEARCH_CRITERIA;  

在我们的Delphi代码中,我们将上述函数和结构转换为:

(*** Delphi implementation of C Function AwdApiLookup ***)
function AwdApiLookup(hwndNotify: HWND; ulMsg: ULONG; fContainer: Boolean;
    cObjectType: Char; pSearchCriteria: Pointer; usCount: USHORT;
    usSearchType: USHORT; pReserved: Pointer): Boolean; stdcall;
    external 'AWDAPI.dll';

(*** Delphi implementation of C Struct SEARCH_CRITERIA ***)
TSearch_Criteria = record
    dataname:   array [0..3] of char;
    wildcard:   char;
    comparator: array [0..1] of char;
    datavalue:  array [0..74] of char;
end;
PSearch_Criteria = ^TSearch_Criteria;

我们在Delphi中调用上述代码的方式是:

AwdApiLookup(0, 0, true, searchType, @criteriaList_[0], 
             criteriaCount, AWD_USE_SQL, nil);

其中criteriaList定义为

criteriaList_: array of TSearch_Criteria;

在完成所有这些之后,我们现在可以查看C#代码,这是我无法工作的。我确定我在这里做错了,或者我的C标头没有正确翻译。我的项目确实正确编译,但是当调用该函数时,我得到一个“FALSE”值,表示该函数在DLL中没有正确执行。

到目前为止我的C#代码:

/*** C# implementation of C Function AwdApiLookup ***/
DllImport("awdapi.dll", CharSet = CharSet.Auto)]
public static extern bool AwdApiLookup(IntPtr handle, ulong ulMsg, 
                                       bool fContainer, char cObjectType, 
                                       ref SearchCriteria pSearchCriteria, 
                                       ushort usCount, ushort usSearchType, 
                                       Pointer pReserverd);

/*** C# implementation of C Struct SEARCH_CRITERIA ***/
[StructLayout(LayoutKind.Sequential)]
public struct SearchCriteria
{
    private readonly byte[] m_DataName;
    private readonly byte[] m_Wildcard;
    private readonly byte[] m_Comparator;
    private readonly byte[] m_DataValue;

    public SearchCriteria(string dataName, string comparator, string dataValue)
    {
        m_DataName = Encoding.Unicode.GetBytes(
                                dataName.PadRight(4, ' ').Substring(0, 4));
        m_Wildcard = Encoding.Unicode.GetBytes("0");
        m_Comparator = Encoding.Unicode.GetBytes(
                                    comparator.PadRight(2, ' ').Substring(0, 2));
        m_DataValue = Encoding.Unicode.GetBytes(
                                    dataValue.PadRight(75, ' ').Substring(0, 75));
    }

    public byte[] dataname { get { return m_DataName; } }
    public byte[] wildcard { get { return m_Wildcard; } }
    public byte[] comparator { get { return m_Comparator; } }
    public byte[] datavalue { get { return m_DataValue; } }
}

我对C#函数的C#调用如下所示

var callResult = UnsafeAwdApi.CallAwdApiLookup(IntPtr.Zero, 0, true, 'W', 
                                               ref searchCriteria[0], criteriaCount,
                                               66, null);

其中searchCriteria和criteriaCount定义为

List<SearchCriteria> criteriaList = new List<SearchCriteria>();
var searchCriteria = criteriaList.ToArray();
var criteriaCount = (ushort)searchCriteria.Length;

并将数据添加到searchCriteria:

public void AddSearchCriteria(string dataName, string comparator, string dataValue)
{
    var criteria = new SearchCriteria();
    criteria.DataName = dataName;
    criteria.Wildcard = "0";
    criteria.Comparator = comparator;
    criteria.DataValue = dataValue;
    criteriaList.Add(criteria);
}

就像我说的,我的代码编译正确,但是当函数执行时,它返回“FALSE”,这不应该是这种情况,因为Delphi函数确实返回具有完全相同输入的数据。

我知道我肯定在这里做错了,我尝试了几件事,但似乎没有任何工作。

非常感谢任何正确方向的帮助或推动。

谢谢,Riaan

1 个答案:

答案 0 :(得分:2)

这里有几件事。

首先,C ++ ULONG是一个32位整数,在C#中成为uint - ulong是64位。

对于struct,您不需要弄乱字节数组。使用字符串和ByValTStr。此外,使用readonly和互操作结构的属性并不值得打扰。是的,可变值的类型在纯.NET API中通常很糟糕,但在这种情况下它是现有的API,屏蔽它没有意义。所以:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SearchCriteria
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4]
    public string m_DataName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1]
    public string m_Wildcard;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2]
    public string m_Comparator;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 75]
    public string m_DataValue;
}

如果你真的想自己做所有的字符串转换,那么使用unsafe和固定大小的数组可能会更容易:

public unsafe struct SearchCriteria
{
    public fixed byte m_DataName[4];
    public byte m_Wildcard;
    public fixed byte m_Comparator[2];
    public fixed byte m_DataValue[75];
}

[编辑] 还有两件事。

CHAR cObjectType应该变为byte cObjectType,而不是您当前使用的char cObjectType

此外,是的,您的示例中的数组封送存在问题。由于你的P / Invoke声明是ref SearchCriteria pSearchCriteria - 即通过引用传递的单个值 - 这正是P / Invoke mashaler将要做的事情。请记住,除非你的struct只有非托管类型的字段(上面有fixed数组的那个是那个,string不是),编组人员必须复制结构。它不会直接将地址传递给数组的第一个元素。在这种情况下,由于你没有告诉它它是一个数组,它只会复制你引用的单个元素。

因此,如果您使用带有struct字段的string版本,则需要更改P / Invoke声明。如果您只需要将SEARCH_CRITERIA个对象传递给函数,但在返回后不需要从它们中读取数据,只需使用数组:

public static extern bool AwdApiLookup(IntPtr handle, uint ulMsg, 
                                       bool fContainer, byte cObjectType, 
                                       SearchCriteria[] pSearchCriteria, 
                                       ushort usCount, ushort usSearchType, 
                                       Pointer pReserverd);

并称之为:

var callResult = UnsafeAwdApi.CallAwdApiLookup(
    IntPtr.Zero, 0, true, (byte)'W', 
    searchCriteria, criteriaCount,
    66, null);

如果函数将数据写入该数组,并且您需要读取它,请使用[In, Out]

[In, Out] SearchCriteria[] pSearchCriteria, 

如果您使用带有fixed byte[]数组的版本,您还可以将P / Invoke声明更改为SearchCriteria* pSearchCriteria,然后使用:

fixed (SearchCriteria* p = &searchCriteria[0])
{
    AwdApiLookup(..., p, ...);
}

但这也需要unsafe