2009-10-18 3 views
2

Я скомпилировал DLL в Visual Studio (исходный код на C++, который я почти не понимаю). Вот кусок Scraper.h:Невозможно использовать DLL (написанную на C++) в Delphi: точка входа в процедуру не может быть найдена

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

SCRAPER_API bool ScraperGetWinList(SWin winList[100]); 

Теперь я пытаюсь использовать вышеуказанную функцию в моем Delphi приложения:

type 
    tWin = record 
    title: String; 
    hwnd: HWND; 
    end; 

function ScraperGetWinList(var WinList: Array of tWin): Boolean; external 'Scraper.dll'; 

var 
    myWinList: Array [1..100] of tWin; 

procedure TMainForm.GetWinListButtonClick(Sender: TObject); 
begin 
    ScraperGetWinList(myWinList); 
    ... 

Проект не компилируется, и я получаю следующее сообщение: Точка входа процедуры ScraperGetWinList не может быть расположена в динамической библиотеке ссылок: Scraper.dll.

Что я делаю неправильно?

+1

Сообщение об ошибке, которое вы цитируете, не принадлежит компилятору. Он исходит из ОС, когда он пытается запустить программу. Таким образом, ваш проект * компилируется *, иначе не было бы ничего, что могло бы заставить ОС выполнить. –

+0

название не должно быть «string» в Delphi - использовать упакованный массив [1..512] из char –

+0

Спасибо, Джерри. Я забыл об этом, имея дело с остальной частью беспорядка :-) – Mikhail

ответ

6

Из моего опыта работы с Linux я бы сказал, что вы столкнулись с так называемой «name-mangling». Точка входа в вашу процедуру не называется «ScraperGetWinList», но что-то вроде «_ZN18ScraperGetWinListEpN4SWin».

Дело в том, что в отличие от C на языке C++ имя точки входа не совпадает с именем функции. Неудивительно: предположим, у вас есть набор перегруженных функций; они должны иметь разные точки входа в вашу DLL. Именно здесь вступает в игру название mangling.

Наиболее распространенным решением этой проблемы является определение интерфейса вашей библиотеки таким образом, что она будет использовать соглашение о вызове C. Тогда с функциями интерфейса не будет никакого имени.

Обратите внимание, что вам не нужно писать всю библиотеку на C, вы должны только объявить функции для их испускания C-подобных точек входа.

Обычно это написано так:

extern "C" { 

SCRAPER_API bool ScraperGetWinList(SWin winList[100]); 
// More functions 

} 

Перекомпилируйте вашу библиотеку и использовать его в Delphi без проблем.

Примечание, что вы также должны настроить соглашения о вызовах (stdcall или cdecl) для их соответствия в заголовке C++ и коде Delphi. Однако это лучше всего объяснить в другом вопросе.

2

Также убедитесь, что такое же соглашение о вызове используется в C++ и delphi. Взгляните на jn ответ here и here

+0

Спасибо за подсказку. Я изменил объявление на: функция ScraperGetWinList (var WinList: tWinList): Boolean; STDCALL; внешний «Scraper.dll»; Когда я запускаю приложение, через мгновение или два я получаю сообщение о нарушении доступа. Быстрые идеи, почему это может произойти, или я должен задать отдельный вопрос? – Mikhail

+0

Михаил, задайте отдельный вопрос для этого, где вы точно уточняете, какие объявления вы используете, и можете точно описать, что вы делаете, чтобы воспроизвести ошибку. «Подождите несколько минут» недостаточно. –

4

Название mangling - скорее всего проблема. Название mangling обычно делается с кодом C++, и при написании DLL на C++, который должен использоваться кодом в другом langauge, вы должны использовать конструкцию Extern «C», как уже предложил Павел Швед.

При использовании библиотек DLL, особенно когда writtin на других языках, вы также должны поддерживать глазу называть соглашения. Я предлагаю вам указать в delphi и C++, чтобы использовать stdcall, вызывающий convstion. Это соглашение о вызове, также используемое окнами api, поэтому оно гарантирует лучшую совместимость между различными компиляторами.

Это означало бы

EXTERN "C" {

SCRAPER_API __stdcall Его ScraperGetWinList (Swin WinList [100]);

}

и

функция ScraperGetWinList (вар WinList: Массив TWIN): Boolean; внешний «Scraper.dll»;

Но это еще не все, то STDCALL соглашения о вызове оказывает влияние на имя коверкая, и получился бы быть что-то вроде _ScraperGetWinList @ 4 (где 4 является размером параметра, где массив будет иметь указатель на первый элемент, так что 4 байта)

Чтобы подтвердить правильные символы для использования, я предлагаю Dependency Walker (http://www.dependencywalker.com/), эта программа показывает, что именно имена функций экспортируются DLL. Подтвердив имя, чтобы быть '_ScraperGetWinList @ 4', то вы добавить это в delpgi так:

функция ScraperGetWinList (вар WinList: Массив TWIN): Boolean; external 'Scraper.dll' name '_ScraperGetWinList @ 4';

1

Действительно ли вы экспортировали функцию точки входа в код C++? Это на самом деле помешало мне в первый раз, когда я скомпилировал dll C++ в Visual Studio для использования в приложении dotnet.

Например, мне нужно было открыть драйвер печати в неуправляемом коде, чтобы некоторые другие разработчики могли получить к нему доступ в VB.net. Это то, что я сделал.

В foo.cpp:

extern "c" { 
    ___declspec(dllexport) bool FooBar() 
    { 
    // Call some code on my cpp objects to implement foobar 
    } 
} 

Затем в файле под названием foo.def:

LIBRARY "mylib" 

EXPORTS 
     FooBar 
     AnyOtherFunctionsItExports 

Это, как я получил его на работу. Я, возможно, не делаю все возможное. Я немного освещен опытом C++, а также в основном не работаю над окнами.

Смежные вопросы