2

C++ で記述され、DLL としてコンパイルされた関数があり、これを Delphi アプリケーションで使用したいと考えています。

スクレイパー.cpp:

SCRAPER_API bool ScraperGetWinList(SWin winList[100])
{
    iCurrWin=0;
    memset(winList,0,100 * sizeof(SWin));
    return EnumWindows(EnumProcTopLevelWindowList, (LPARAM) winList);
}

Scraper.h:

#ifdef SCRAPER_EXPORTS
#define SCRAPER_API __declspec(dllexport)
#else
#define SCRAPER_API __declspec(dllimport)
#endif

struct SWin
{
    char title[512];
    HWND hwnd;
};

extern "C" {
    SCRAPER_API bool ScraperGetWinList(SWin winList[100]);
}

これは、Delphi アプリケーションで関数を宣言する方法です。

type
  tWin = record
    Title: Array [0..511] of Char;
    hWnd: HWND;
  end;

  tWinList = Array [0..99] of tWin;

function ScraperGetWinList(var WinList: tWinList): Boolean; stdcall; external 'Scraper.dll';

関数は機能しますが、終了すると、Debugger Fault Notification: Project ... faulted with message: ''access violation at 0x0012f773: write of address 0xffffffc0' を受け取ります。プロセスが停止しました。Step または Run を使用して続行します。

__stdcallScraper.cpp と Scraper.h に(の後に)を追加するSCRAPER_API boolと、Delphi アプリケーションがまったく起動しません。プロシージャ エントリ ポイント ScraperGetWinList がダイナミック リンク ライブラリ Scraper.dll に見つかりませんでした。

4

4 に答える 4

3

__stdcallの後に置く必要がありますbool。すべてのマクロが展開された後の完全な宣言は、次のようになります。

extern "C"
{
    __declspec(dllexport)
    bool __stdcall ScraperGetWinList(SWin winList[100]);
}

編集: そこには .def ファイルも必要なようです。これは、DLL でエクスポートされたすべての関数をリストするファイルであり、この場合、C++ コンパイラがエクスポートされた名前を壊さないようにするためだけに必要です。内容は次のようになります。

EXPORTS
ScraperGetWinList

どの C++ コンパイラを使用しているかはわかりませんが、通常は .cpp と共に .def ファイルを指定するだけです。たとえば、VC++ では次のように動作します。

cl.exe foo.cpp foo.def

また、 Delphi 関数宣言のstdcall直前にキーワードを挿入して、Delphi に stdcall も使用するように指示する必要があります。external

于 2009-10-19T07:47:59.550 に答える
0

アクセス違反が発生した場所を正確に把握しておくとよいでしょう。ランタイムがアクセスしようとしている変数/メモリの場所はどこですか?

次に、この場所に実際にアクセスできる必要があるかどうか、アクセスできる場合はアクセスできない理由を確認します。

私の疑い:正しく初期化されていない配列要素にアクセスします。

  Index := 0;
  S := ConvertToString(myWinList[Index].Title); 
  while S <> '' do
  begin
    WinListMemo.Lines.Add(S);
    Inc(Index);
    //////// Is Index pointing to a valid entry here?  No check!
    S := ConvertToString(myWinList[Index].Title);
  end;

また

  • dllはそれを正しく初期化しません、
  • または、最後の要素を見つける別の方法があります。
  • または、単に配列を完全に実行しました。101番目の要素も逆参照されます。そして102番目、そのメモリ位置にたまたま0文字が含まれている場合。
于 2009-10-20T03:57:57.390 に答える
0

charのパック配列[1..512]を使用する場合、ConvertToString()関数は必要ありません。

「packedarrayofchar」は、Delphi文字列と互換性のある割り当てです(これは、Pascalの非常に初期の形式に戻ります。packedarrayof charは文字列型でした)。C文字列の終わりを見つけるには、null($ 0)文字の結果をかさぶたにする必要があります。

また、どのDelphiバージョンを使用していますか?Delphi 2009 +の場合、 AnsiCharのパック配列[1..512]を使用する必要があります。

于 2009-10-19T22:01:45.453 に答える
-1

Delphi 関数の定義が、C++ 関数の宣言内容と一致していることも確認してください。特に、最後に stdcall があること、およびブール値が一貫していることを確認してください。C++ と Delphi では、C++ コンパイラに応じて bool の値とサイズが異なるため、適切なサイズの整数を使用する方がよい場合があります。bool のサイズが C++ のサイズと一致しない場合があるため、スタックに影響を与え、アクセス違反が発生する可能性があります。

[混合言語のダフ応答を削除するために編集]

于 2009-10-19T07:59:01.583 に答える