Вызов определенного Win32 API из Delphi - почему исключения летают без asm pop?

Я использую Delphi для создания надстройки XLL для Excel, которая включает в себя множество обращений к Excel4v функции xlcall32.dll. Однако, как я предполагаю, очень немногие эксперты Delphi здесь работали с этим конкретным API, я надеюсь, что проблема могла наблюдаться и в других API.

В C, особенно в файле xlcall.h, который поставляется с Microsoft Excel 2007 XLL SDK Excel4v определяется как:

int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);

В Delphi я использую:

function Excel4v(xlfn: Integer; operRes: LPXLOPER; count: Integer;
    opers: array of LPXLOPER): Integer; stdcall; external 'xlcall32.dll';

LPXLOPER - это указатель на структуру (в C) или запись (в Delphi).

Я делал домашнюю работу по объявлению функций C в Delphi (эта отличная статья была отличная помощь), и я думаю, что правильно объявляю Excel4v. Однако вызовы из кода Delphi в эту функцию вызывают исключения («нарушение прав доступа ...» - это то, что я продолжаю видеть) , если за ними не следует следующая строка:

asm pop sink; end;

Где «сток» определяется где-то как целое число.

Я понятия не имею о сборке ... Так что я бы ни за что не подумал попробовать исправить исключения с помощью "asm pop раковина; end;". Но "asm pop раковина; конец;" действительно исправляет исключения. Впервые я увидел его в этой полезной статье о создании файлов XLL используя Delphi. Вот самая актуальная цитата:

«В Delphi большой камень преткновения с надстройками - это дополнительный параметр после адреса возврата в стеке. Он предоставляется бесплатно при каждом вызове Excel. Я так и не узнал, что он содержит, но если вы его выбросите , ваша надстройка будет работать нормально. Добавьте строку asm pop variable, end; после каждого вызова, где переменная может быть любой глобальной, локальной или объектной переменной длиной не менее 4 байтов, можно использовать длинное целое число. Чтобы повторить - ЭТО ДОЛЖНО БЫТЬ ВКЛЮЧАЕТСЯ после каждого вызова Excel4v. В противном случае вы создаете бомбу замедленного действия ».

В основном я хочу понять, что на самом деле происходит и почему. Что могло заставить функцию Win32 возвращать «дополнительный параметр после адреса возврата в стеке» и что это на самом деле означает?

Может быть другой способ исправить это, например с другим параметром компилятора или другим способом объявления функции?

И есть ли что-нибудь рискованное в том, чтобы называть "asm pop раковина; конец;" после каждого вызова Excel4v ...? Вроде работает нормально, но, поскольку я не понимаю, что происходит, кажется немного опасным ...


person MB.    schedule 07.07.2009    source источник
comment
Я пишу XLL на Delphi и хочу с вами связаться. Напишите мне на [email protected]   -  person garethm    schedule 27.01.2011


Ответы (3)


Я не верю, что это pascal vs stdcall - они очень похожи на соглашения о вызовах и не должны приводить к несоответствию стека при выходе из функции.

Из упомянутой статьи,

Это действительно был бы очень хороший синтаксис, но он не совпадает с приведенным выше определением массива. Параметры массива - это параметры открытого массива. Они могут выглядеть как любой массив, и они принимают любой массив, но они получают дополнительный (скрытый) параметр, который содержит наивысший индекс в массиве (значение High). Поскольку это так только в Delphi, а не в C или C ++, у вас возникнет настоящая проблема. (См. Также мою статью об открытых массивах), поскольку реальное количество параметров не совпадает.

Вы получаете дополнительный параметр «наивысший индекс массива», передаваемый функции. Это тип int, и его нужно очищать при выходе из функции, чтобы не получить поврежденный стек и сбой. В статье показано, как передавать массивы в функции C.

Что-то типа:

type
 PLPXLOPER  = ^LPXLOPER;

И передайте PLPXLOPER в качестве последнего параметра.

person Michael    schedule 07.07.2009
comment
Спасибо, Майкл, для меня это звучит как объяснение :) Я немного поиграю с этим, чтобы быть уверенным, и еще раз прокомментирую, когда буду. - person MB.; 07.07.2009
comment
Фантастика! Насколько я могу судить, он работает отлично. Определите функцию как функцию Excel4v (xlfn: Integer; operRes: LPXLOPER; count: Integer; opers: PLPXLOPER): Integer; stdcall; внешний 'xlcall32.dll'; затем для его вызова создайте массив Delphi из LPXLOPER и вызовите Excel4v с @myArray [0] для PLPXLOPER. Отлично работает со stdcall, и нет необходимости в подозрительных вызовах asm pop :) - person MB.; 07.07.2009

Ваше соглашение о вызовах неверно, в частности "stdcall". Объявление C указано как «паскаль».

Stdcall передает параметры в порядке справа налево, ожидает, что процедура очистится, и не использует регистры. Паскаль, OTOH передает параметры в порядке слева направо. Следовательно, в любом случае все происходит не так, как ожидает другая половина кода.

Измените свое объявление Delphi на «паскаль» вместо «stdcall».

person Ken White    schedule 07.07.2009
comment
Я попытался указать его как паскаль, но тогда мне не удалось заставить его работать. Но функция определенно работает правильно с stdcall, если за ней следует строка asm pop ... В статье на странице rvelthuis.de/articles/articles-convert.html упоминается паскаль, имеющий противоречащий интуиции смысл, хотя это не так уж и конкретно. - person MB.; 07.07.2009
comment
Раньше у них были разные соглашения о вызовах. В настоящее время pascal - это макрос, который означает stdcall в заголовках C. - person Rob Kennedy; 07.07.2009
comment
Я также видел PASCAL # define'd для __stdcall. . . но я не уверен, что это значит по отношению к delphi. Моя первоначальная мысль также заключалась в несовпадении соглашения о вызовах, но для такой функции стек не будет отклоняться только на 4 байта между соглашениями о сохранении вызывающего и вызываемого. - person Michael; 07.07.2009
comment
MSDN, похоже, мало что может сказать о паскале, кроме того, что он устарел согласно ссылке, на которую любезно указал ChrisW: msdn.microsoft.com/en-us/library/wda6h6df (VS.80) .aspx Я поискал код в XLL SDK и не смог Я не нашел ничего, что определяло бы паскаль как stdcall или что-то еще, так что это немного загадка. Но теперь все работает нормально, проблема с массивом исправлена, поэтому я доволен, хотя все еще немного запутался (как обычно с Win32 ...) :) - person MB.; 08.07.2009

Большинство функций Windows используют __stdcall для своего соглашения о вызовах.

person ChrisW    schedule 07.07.2009