Преобразование кода Delphi для неуправляемой DLL в C # - PullRequest
2 голосов
/ 27 ноября 2009

Я надеюсь, что кто-то может помочь мне с проблемой, с которой я сейчас сталкиваюсь. У нас много унаследованного кода 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);

, где attributeList определяется как

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 и attributeCount определяется как

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

и добавление данных в критерии поиска:

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 действительно возвращает данные с точно таким же вводом.

Я знаю, что я определенно что-то делаю не так, и я попробовал пару вещей, но, похоже, ничего не работает.

Любая помощь или толчок в правильном направлении будет принята с благодарностью.

Спасибо, Риан

1 Ответ

2 голосов
/ 27 ноября 2009

Несколько вещей здесь.

Прежде всего C ++ ULONG является 32-разрядным целым числом и становится uint в C # - ulong является 64-разрядным.

Для структуры вам не нужно связываться с байтовыми массивами. Используйте строки и 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, то есть значение single , передаваемое по ссылке, - это именно то, что будет делать 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...