"They picked up various other people who wanted to get it over, as they went along, until they had absorbed all the persons in the maze. People who had given up all hopes of ever getting either in or out, or of ever seeing their home and friends again, plucked up courage at the sight of Harris and his party, and joined the procession, blessing him. Harris said he should judge there must have been twenty people, following him, in all; and one woman with a baby, who had been there all the morning, insisted on taking his arm, for fear of losing him. Harris kept on turning to the right, but it seemed a long way, and his cousin said he supposed it was a very big maze. "Oh, one of the largest in Europe," said Harris. "Yes, it must be," replied the cousin, "because we've walked a good two miles already.""
Three Men in a Boat. Jerome K. Jerome
What I really hate
What I really hate is to see "Unresolved external symbol referenced from module" or "E2268 Call to undefined function 'function' " on screen after fifteen minutes of compilation. It means that I had forgotten something relevant - to define a function or to add an obj or DLL in the project. Sometimes it means something unexpected, for example, incorrect changing in the project. When I encountered this, I tried to search, for such a function and almost always had problems with it. For example, it is possible to invoke Find File dialog and use "*.DLL" as a mask for the name of file and the function name as a text for searching. The example of this you can see on Figure 1.
Figure 1. Find files
The search results are 603 in number. In this list are: DLLs that are imported as a function, exported it and even those DLLs that contain a function name as the text inside. But we need only one file from where function is exported. If even only one DLL is found, then the next step - is to look through it. Utility Impdef is a good application for this, but as for me not very comfortable because it is a console application. And at last, we use utility Implib for creating Lib-file that should be added in the project (let us remember that Microsoft lib is not the same thing as Borland lib).
In my opinion, those three tasks (searching DLL, look through it and creating Lib-file) should be solved by one utility. This was my thought when I saw E2268 compiler message. And it is an idea of FindFunction. On the next figure, you can see the main form of FindFunction.
Figure 2. FindFunction
FindFunction does search at 50% quickly then Find Files and in the result, we have only one file (sometimes it may be two or three). Using FindFunction, we can find functions in EXE-files as well as in DLL-files. Check "File extension:" (EXE or/and DLL) for this. It seems to be funny, but we never find any exported function in the EXE-file created by Visual C++, but EXEs created by C++ Builder can have exported function. After end of search, we can select one of the names by clicking and go to another page (Explore DLL/EXE). Figure 3 illustrates the second page of application.
Figure 3. FindFunction (second page)
And last step - creating lib-file. For this, let us push "Create Lib" button and select a target path by Save Dialog. FindFunction invokes Borland's Implib.exe and lib will be created. If you need only exe of FindFunction, it is time to stop reading and go here.
How it works
FindFunction performs three subtasks:
- searching DLL
- looking through DLL
- creating lib file
For searching, it is very important to examine all directories including subdirectories on the target path. It can be achieved by recursive search through directories. Let us imagine function that is looking for file in the directory and when it finds another directory, it calls self to continue searching inside that directory which was found. Win32 API has the following functions:
FindClose, that can help to do this work.
FindFirstFile opens handle for searching.
FindNextFile does search, finding one file for one call. And when
FindNextFile can't find next file,
FindClose closes handle for search. For example:
void RecursiveSearch(AnsiString Funct,AnsiString PathFind)
/* search for any file */
/*open handle */
temp.c_str(), // pointer to name of file to search for
&FindFileData // pointer to returned information
if(next=(FileSearching != INVALID_HANDLE_VALUE))
/*call self if find directory*/
if((FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) &&
FileSearching, // handle to search
&FindFileData // pointer to structure for data on found file
); /* do search */
/*do not freeze form till search continuing*/
/*Search in the subdir - end*/
FindFunction puts all exported and imported functions of DLL in the window on the right side of the form. This window is a Rich Edit control and it simplifies printing, saving and finding information. How to browse inside DLL or EXE? Any Win32 DLL or EXE should have special PE-format. Some structures of this format are declared in the winnt.h. Declarations in winnt.h are a very compressed story about PE-format. In the end of the article, I've put some links where you can find detailed info about these structures. It is very suitable to use File Mapping for working with files that have special format. The common definition: memory-mapped files offer a special memory management feature to access files on disk in the same way they access virtual memory of process - through pointers. So we can browse inside file using pointers, i.e., we can use pointers to structure from winnt.h setting aside that it is a file. Really it is a file but file that is mapped in the process memory. Now when a DLL is found and successfully browsed, we only need to add references to the DLL in the project. So lib-file should be created for this. No need to reinvent the wheel - let us use Implib.
ShellExecute Win32 API function or
CreateProcess can run Implib and pass strings that specify parameters. For
"C:\\", /* pointer to string that specifies default directory*/
SW_HIDE /* whether file is shown when opened*/
This way implib will be executed from c:\MyFolder directory. So it is almost the same if next string will be put in the Start->Run dialog:
c:\MyFolder\implib.exe "c:\mylib\kern" "c:\WinNT\system32\kernel32.dll"
The quotes are extremely useful if directory path contains spaces. In another case, Windows reports you about error. The source code of FindFunction can be download from here.
The roads to Minataur
Using FindFirstFile, FindNextFile, FindClose in Win98
The main problem of using these Win32 API functions in Windows 98 is that it searches files by short name. So if you use "*.dll" as mask for search, the result can't be that you want. For example, you have file with name: c:\MyDirectory\NoPEFormat.dllabc. The extension of short name of this file will be .dll but it is not really a DLL. Next code does not work properly in Windows98:
FileSearching=FindFirstFile( "c:\\MyDirectory\\*.dll", &FindFileData );
For example, next file c:\MyDirectory\NoPEFormat.dllabc will be found as DLL. The solution is to use"*.dll" as mask and compare last four characters of long file name with ".dll"
Incorrect file format
During debugging, I have some crashes of the application. Every time when I saw fpl.dll in the Status Bar, the program had exception. fpl.dll is my library and I never had problems with it before. I noted that fpl.dll was found in the Windows temporary directory and its size was lesser than real. The file had incorrect format and it was surprising for me. When my program walked inside of fpl.dll maze, it met incorrect reference to non-existent parts of library. To avoid this problem, it is possible to use
IsBadStringPtr Win32 API functions. Another idea is a
catch block, so exception can be controlled.
Early versions of Implib
I noted that early (for example, from BCB3) versions of Implib was not always correctly working with my application. The problem is the path to Implib. Early versions work with short path. Solution is simple - using
GetShortPathName Win32 API function and call
ShellExecute using this name.