I'm using Delphi to make an XLL add-in for Excel, which involves making a lot of calls to the Excel4v function of xlcall32.dll. However, as I'm guessing very few Delphi experts here have worked with that specific API, I'm hoping that the problem might have been observed in other APIs too.
In C, specifically in the xlcall.h file that comes with the Microsoft Excel 2007 XLL SDK, Excel4v is defined as:
int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);
In Delphi I'm using:
function Excel4v(xlfn: Integer; operRes: LPXLOPER; count: Integer;
opers: array of LPXLOPER): Integer; stdcall; external 'xlcall32.dll';
LPXLOPER is a pointer to a struct (in C) or record (in Delphi).
I've been doing my homework on declaring C functions in Delphi (this excellent article was a great help), and I think I'm declaring Excel4v properly. However, calls from Delphi code into that function cause exceptions ("access violation..." is what I keep seeing) unless they are followed by the following line:
asm pop sink; end;
Where "sink" is defined somewhere as an integer.
I have no clue about assembly... So there's no way would I have thought to try fixing the exceptions with "asm pop sink; end;". But "asm pop sink; end;" does indeed fix the exceptions. I first saw it used in this useful article on making XLLs using Delphi. Here's the most relevant quote:
"From Delphi the big stumbling block with add-ins is the extra parameter after the return address on the stack. This comes free with every call to Excel. I’ve never found out what it holds, but so long as you throw it away, your add-in will work fine. Add the line asm pop variable, end; after every call where variable can be any global, local or object variable that is at least 4 bytes long- integer is fine. To repeat- THIS MUST BE INCLUDED after every Excel4v call. Otherwise you are constructing a time-bomb."
Basically I want to understand what's actually happening, and why. What could be causing a Win32 function to return an "extra parameter after the return address on the stack", and what does that actually mean?
Might there be another way to fix this, e.g. with a different compiler option or a different way of declaring the function?
And is there anything risky about calling "asm pop sink; end;" after every call to Excel4v...? It seems to work fine, but, as I don't understand what's going on, it feels a little dangerous...
I don't believe it's pascal vs stdcall - they are very similar calling conventions and should not result in a mismatched stack on function exit.
From the referenced article,
This would indeed be a very nice syntax, but it is not the same as the above array definition. Array-of parameters are open array parameters. They may look like any array, and they do accept any array, but they get an extra (hidden) parameter, which holds the highest index in the array (the High value). Since this is only so in Delphi, and not in C or C++, you'd have a real problem. (See also my article on open arrays), since the real number of parameters wouldn't match.
You're getting the extra "highest array index" parameter being passed to the function. This is an int and has to be cleaned up when the function exits so that you don't wind up with a corrupted stack and crash. The article indicates how to pass arrays to C functions.
Something like:
type
PLPXLOPER = ^LPXLOPER;
And pass PLPXLOPER as the last parameter.