2009-11-27 83 views
2

我希望有人能帮助我解决我目前遇到的问题。我们有很多Delphi遗留代码,需要将我们的一些Delphi应用程序转换为C#。将非托管dll的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; 

和添加data to 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

+0

'指针pReserverd' - 你的意思是'IntPtr'? – 2009-11-27 07:30:40

+0

我不确定。 C++ API将其作为VOID pReserved,并且可以工作的Delphi代码将其定义为指针pReserved。 – Riaan 2009-11-29 09:20:23

回答

2

这里有几件事。

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

对于结构体,你不需要搞乱字节数组。使用字符串,并ByValTStr。此外,它不值得打扰readonly和interop结构的属性。是的,可变值类型在纯.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会执行的操作。请记住,除非你的结构只有非托管类型的字段(上面的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为好,虽然。

+0

嗨帕维尔, 谢谢你修改后的结构。我也这样做过,但将其改回到您的建议。 非常感谢您发现ULONG错误。我也相应地更新了我的代码,但不幸的是我仍然收到“FALSE”,这意味着该函数没有执行correclty。 只是另一个问题,也许是非托管DLL无法访问托管数组的第一个元素的内存? – Riaan 2009-11-27 08:18:05

+0

是的,我没有发现阵列存在问题。更新了答案。 – 2009-11-27 16:44:47

+0

Hi Pavel, 我添加了searchCriteria正在添加的currenlty的方式。随着结构根据你的推荐而改变,我的代码目前还没有编译。更新AddSearchCriteria函数以接受新的结构数据成员的正确方法是什么?我试过Convert.ToByte,但它也给编译错误。感谢所有的帮助。 – Riaan 2009-11-29 09:54:37