Welcome to MSDN Blogs Sign in | Join | Help

DECLARE DLL performance questions

I was writing a sample about DECLARE DLL to show some of its features which I implemented about 12 years ago, when I rediscovered an interesting performance issue.

 

The purpose of DECLARE DLL is to allow the user to call functions in a DLL directly. For example, most of the Win32 API  lives in DLLs and thus being able to call the API directly is very powerful.

 

In order to use the function in a DLL, you needed to know its name and its parameter signature. For example, the GetWindowText function of the Windows API will return the text associated with a Window handle. If the Window handle is a normal user Window, it’s the title of the window. If it’s the Window handle of a button, then it’s the text on the button. GetWindowText takes 3 parameters: the Window Handle, a string buffer to place the answer, and the size of that buffer. It returns the length of the result put in the string buffer.

 

In order to use this API from VB or VFP, you would need to know that it lives in User32.dll in your Windows System directory (typically c:\windows\system32\user32.dll). Also, there are actually 2 versions of this API, as the windows SDK header file for this API (win32sdk\include\winuser.h) shows:

 

#ifdef UNICODE

#define GetWindowText  GetWindowTextW

#else

#define GetWindowText  GetWindowTextA

#endif // !UNICODE

 

In fact, there are 2 versions of most Win32 APIs that deal with strings: one for UNICODE (2 bytes per character) and one for non-UNICODE (double byte (1 or 2 bytes per character) and ANSI (1 byte per character). These header files are how the programmer (almost all were C/C++ developers in the old days) can use the Win32API.

 

To hide this Unicode complexity from the user, the header file has a conditional #define, so the user just has to write GetWindowText, and the #define macro expands it in the C compiler preprocessor

The UNICODE version appends a “W” and the non-UNICODE version appends a “A”.

 

That means the actual DLL does not export “GetWindowText”, but has 2 exports: “GetWindowTextA” and “GetWindowTextW”

 

You can see the two by typing this in a Visual Studio command prompt:

D:\>link /dump /exports c:\windows\system32\user32.dll | find /i "getwindowtext"

        376  177 0000F002 GetWindowTextA

        377  178 0001F1BE GetWindowTextLengthA

        378  179 0000DC5F GetWindowTextLengthW

        379  17A 0000BA08 GetWindowTextW

        402  191 0000C057 InternalGetWindowText

 

So the user would have to declare GetWindowTextA rather than GetWindowText.

To hide this complexity from the every day user, VFP will try to see if the specified function exists in the DLL. If it doesn’t, a “A” is appended to the function name and the DLL is queried again.

Because of this trial and error approach, one would think that appending the “A” before calling the API would make a difference in performance.

 

Another complexity arises for Win32 API calls (but not for other DLLs). The user is required to know in which particular DLL of Windows the function resides. Back in the old days, it wasn’t quite as clear. With MSDN online now, it’s pretty easy to see at the bottom of the MSDN topic for the API. The DECLARE DLL command allows the user to just type “Win32API” instead of the DLL name, which means User32.dll, Gdi32.dll, Kernel32.dll , Advapi32.dll and Mpr.dll are searched (in that order).

 

So the question is which would be faster: specifying the particular DLL name directly, or using “Win32API”?  How about specifying with or without the “A” ?

 

See also:

DECLARE DLL allows OBJECT type

What happens if external code throws an exception?

Is there a way in VFP to pass a DWORD to an API function from VFP?

Will GetLastError ever work properly in VFP8.0?

Undocumented APIs and 16 bit DLLs

What external code does your EXE depend on?

 

Here’s some sample code to get you started. The answer may surprise you!

 

 

          DECLARE integer GetWindowText IN win32api integer hWnd, string @ lpString, integer nMaxCout

          cStr=SPACE(100)

          ?GetWindowText(_vfp.hWnd,@cStr,LEN(cStr))

          ?cStr

nStart=SECONDS()

CLEAR DLLS

FOR i = 1 TO 10000

          cStr=SPACE(100)

          IF .f.

                   DECLARE integer GetWindowText IN win32api integer hWnd, string @ lpString, integer nMaxCout

                   GetWindowText(_vfp.hWnd,@cStr,LEN(cStr))

          ELSE

                   DECLARE integer GetWindowTextA IN c:\windows\system32\user32.dll integer hWnd, string @ lpString, integer nMaxCout

                   GetWindowTextA(_vfp.hWnd,@cStr,LEN(cStr))

          ENDIF

         

ENDFOR

?SECONDS()-nStart

RETURN

Published Monday, April 24, 2006 3:14 PM by Calvin_Hsia

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: DECLARE DLL performance questions

Tuesday, April 25, 2006 3:09 AM by Martin Jindra
The command DECLARE API... is slow if  specifying the particular DLL name directly, because VFP must load DLL into memory and locate function's pointer by function's name (...and user32.dll has many functions).

If specify keyword WIN32API the VFP search loaded API functions into memory and because  GetWindowTextA/W() is system function is always loaded into memory.

# re: DECLARE DLL performance questions

Tuesday, April 25, 2006 3:14 AM by Timo
Hi Calvin, nice time difference. Using the non-'A' version with win32api is 10 times faster.
I noticed that appending the 'A' to the function is indeed faster (5%) than using the function as-is, but using a fully qualified path to the DLL slows it down.
Might it be that VFP already loaded the win32api DLL's, so that when we use 'IN win32api' VFP looks in it's own set of loaded functions?

Regards,

# re: DECLARE DLL performance questions

Tuesday, April 25, 2006 3:57 AM by Fabio Lunardon
Calvin, you compare two independent things,
- dll location
- dll function

clear

#IF .T.
#DEFINE LOOP  300000
* this is optimized : uses VFP dlls
#DEFINE DLLFILE win32api
#ELSE
#DEFINE LOOP  300000/10
* this open the file : 10x slower. Calvin, you can optimize it too !!!!!
#DEFINE DLLFILE c:\windows\system32\user32.dll
#ENDIF


nStart=SECONDS()

FOR i = 1 TO LOOP

   #IF .T.
    * TRY AND ADD ANSI SUFFIX : NOT A GOOD FEATURE FO ME

DECLARE integer GetWindowText IN DLLFILE AS GWT integer hWnd, string @ lpString, integer nMaxCout
   #ELSE

* this is 10% faster
DECLARE integer GetWindowTextA IN DLLFILE AS GWT integer hWnd, string @ lpString, integer nMaxCout

   #ENDIF
   
ENDFOR

? SECONDS()-nStart

cStr=SPACE(100)

? GWT(_vfp.hWnd,@cStr,LEN(cStr)),cStr

CLEAR DLLS  GWT

implement the C++ #define with dynamic code
is a bug for me, because it can produce
not deterministic calls.

Example:
-the dll XDLL version NN have a function "namefunc" and "namefuncA"
-the dll XDLL version NN+1 remove the function "namefunc"
When VFP open XDLL version NN+1 every call
to "namefunc" is mapped to "namefuncA" !

Suggest:
- on VFP9 SP2 restrict this "A" feature to
win32api.
- because dll declarations are autocorrelated a lot, optimize the IN clause for every dllname

# re: DECLARE DLL performance questions

Thursday, April 27, 2006 1:28 PM by SarekOfVulcan
Calvin, have you looked at your blog in Firefox lately? The formatting is kind of wonky. I have the <a href="http://chrispederick.com/work/webdeveloper/">Web Developer</a> extension installed, which lets you edit a page's CSS on the fly. When I removed the body {line-height: 1.2em} directive, the page looked much better.

# More DECLARE DLL performance discussion

Thursday, April 27, 2006 1:58 PM by Calvin Hsia's WebLog
In this post DECLARE DLL performance questions I asked whether using the WIN32API keyword or the particular...

# What API calls reset GetLastError between Declare DLL calls in VFP8?

Wednesday, May 03, 2006 2:32 PM by Calvin Hsia's WebLog
I received a comment on this post: Will GetLastError ever work properly in VFP8.0?. &amp;nbsp;I was consistently...

# How to log application API calls using import module addresses

Monday, February 26, 2007 1:44 PM by Calvin Hsia's WebLog

Let’s log all the calls that Excel makes to open or create a file. Start Visual Studio (any version),

# Unknown

Monday, March 12, 2007 8:58 AM by Unknown

<a href='http://films.eoe1o.info/download-film-independent.html'>download film independent</a>

# Unknown

Tuesday, March 20, 2007 8:16 PM by Unknown

<a href='http://tvinternet.jedo.info/classic-internet-tv.html'>classic internet tv</a>

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker