2


0

アンマネージdllのDelphiコードをC#に変換

私が現在経験している問題について誰かが私を助けてくれることを願っています。 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 criteriaList = new List();
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関数がまったく同じ入力でデータを返すためではありません。

私は間違いなくここで何か間違ったことをしていることを知っており、いくつかのことを試しましたが、何も機能していないようです。

正しい方向への支援やナッジは大歓迎です。

ありがとう、リアーン

1 Answer


2


ここにいくつかあります。

まず、C ++の「ULONG」は32ビット整数であり、C#では「uint」になります。「ulong」は64ビットです。

構造体については、バイト配列をいじる必要はありません。 文字列と「ByValTStr」を使用します。 また、「読み取り専用」や相互運用構造体のプロパティに悩む価値はありません。 はい、変更可能な値型は純粋な.NET APIでは一般に悪いですが、この場合は既存のAPIであり、それをマスクする意味はありません。 So:

[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];
}
  • [編集] *さらに2つ。

CHAR cObjectType`は、現在使用している char cObjectType`ではなく、 `byte cObjectType`になります。

また、あなたの例では配列のマーシャリングに問題があります。 P / Invoke宣言は ref SearchCriteria pSearchCriteria`であるため - i.e. 参照によって渡される_single_値-それはまさに P / Invoke mashalerが行います。 構造体にアンマネージ型のフィールドのみが含まれている場合(上記の `fixed`配列のフィールドはそうで、 string`の配列はそうではない)を除き、マーシャラーは構造体をコピーする必要があることに注意してください。 配列の最初の要素にアドレスを直接渡すだけではありません。 そしてこの場合、そこに配列だとは言わなかったので、参照する単一の要素のみをコピーします。

したがって、 string`フィールドで struct`のバージョンを使用する場合、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」も必要です。