Modules
The format of Windows CE modules is identical to the PE format used by Windows XP. Unlike Windows XP, Windows CE doesn't support the SYS file format used for device drivers. Instead, Windows CE device drivers are implemented as DLLs.The difference between an EXE and a DLL is actually quite subtle. The format of the files is identical, save a few bytes in the header of the module. In practice, however, the difference is quite pronounced. When Windows launches an EXE, it creates a separate process space for that module, resolves any imported functions, initializes the proper static data areas, creates a local heap, creates a thread, and then jumps to the entry point of the module.DLLs, on the other hand, can't be launched independently. The only way a DLL is loaded is by a request from an EXE or another DLL. The request to load a DLL can occur in two ways. The first way is implicit loading. In this case, a DLL is loaded automatically when Windows loads an EXE that lists the DLL in its import table. The linker generates the import table when the EXE is linked, and the table contains the list of DLLs and the functions within those DLLs that the EXE might call during the life of the application. When the EXE is loaded, Windows looks at the list of DLLs in the EXE's import table and loads those DLLs into the process space of the application. DLLs also contain import tables. When a DLL is loaded Windows also looks at the import table of the DLL and loads any DLLs needed by that DLL.When a DLL is built, it contains zero or more functions it exports. These are the functions that are callable from EXEs or other DLLs. A DLL that has no functions is still useful because it might contain resource data needed by the application.The other way a DLL can be loaded is through explicit loading. In this case, Windows doesn't automatically load the DLL; it's loaded programmatically by the application using one of two calls, LoadLibrary or LoadLibraryEx.LoadLibrary is prototyped as
HINSTANCE LoadLibrary (LPCTSTR lpLibFileName);
The only parameter is the filename of the DLL. If the filename does not have path information, the system searches for DLLs in the following order:
The image of the DLL that has already been loaded in memory
The directory of the executable loading the library
If a relative path was specified, the relative path based on the root (\)
The Windows directory (\Windows)
The root directory in the object store (\)
The image of the DLL in ROM if no relative path is specified
The path specified in the SystemPath value in [HKEY_LOCAL_ MACHINE]\Loader
If the DLL name is a completely specified path name, the search is as follows:
The image of the DLL that has already been loaded in memory
The completely specified name in the lpLibFileName parameter
If the DLL is specified with a relative pathname, that is, one without a leading backslash (\) character, the relative path is appended to the directories listed in the SystemPath registry variable. So if the DLL name is temp\bob.dll and a directory in the path is pathdir, the resulting search is pathdir\temp\bob.dll. This characteristic of merging relative paths with the SystemPath directories can easily lead to unexpected results. Because of this, applications should avoid the use of relative paths when specifying DLLs.Notice in all the earlier search sequences that if the DLL has already been loaded into memory, the system uses that copy of the DLL. This behavior is true even if your pathname specifies a different file from the DLL originally loaded. Another peculiarity of LoadLibrary is that it ignores the extension of the DLL when comparing the library name with what's already in memory. For example, if Simple.dll is already loaded in memory and you attempt to load the control panel applet Simple.cpl, which under the covers is simply a DLL with a different extension, the system won't load Simple.cpl. Instead, the system returns the handle to the previously loaded Simple.dll.LoadLibrary returns either an instance handle to the DLL that's now loaded or 0 if for some reason the function couldn't load the library. Calling GetLastError will return an error code specifying the reason for the failure.Once you have the DLL loaded, you get a pointer to a function exported by that DLL by using GetProcAddress, which is prototyped as
FARPROC GetProcAddress (HMODULE hModule, LPCWSTR lpProcName);
The two parameters are the handle of the module and the name of the function you want to get a pointer to. The function returns a pointer to the function, or 0 if the function isn't found. Once you have a pointer to a function, you can simply call the function as if the loader had implicitly linked it.When you are finished with the functions from a particular library, you need to call FreeLibrary, prototyped as
BOOL FreeLibrary (HMODULE hLibModule);
FreeLibrary decrements the use count on the DLL. If the use count drops to 0, the library is removed from memory.The following routine solves the problem of an application not knowing whether the menu bar API is present on a system.
fMenuBarCreated = FALSE;
hLib = LoadLibrary (TEXT ("aygshell.dll"));
if (hLib) {
FARPROC lpfn = GetProcAddress (hLib, TEXT ("SHCreateMenuBar"));
if (lpfn) {
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // Init structure
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hWnd;
mbi.hInstRes = hInst;
mbi.nToolBarId = ID_MENU;
mbi.dwFlags = SHCMBF_HMENU; // Use std menu resource
(*lpfn) (&mbi);
fMenuBarCreated = TRUE;
}
}
if (!fMenuBarCreated) {
// Create a command bar instead
}
In this code, the menu bar is created only if the system supports it. If the library AygShell.dll or the SHCreateMenuBar function can't be found, a standard command bar is created.Windows CE also supports the LoadLibraryEx function, prototyped as
HMODULE LoadLibraryEx (LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
The first parameter is the name of the DLL to load. The second parameter, hFile, isn't supported by Windows CE and must be set to 0. The last parameter, dwFlags, defines how the DLL is loaded. If dwFlags contains the flag DONT_RESOLVE_DLL_REFERENCES, the DLL is loaded, but any modules the DLL requires are not loaded. In addition, the entry point of the DLL, typically DllMain, isn't called. If dwFlags contains LOAD_LIBRARY_AS_DATAFILE, the DLL is loaded into memory as a data file. The DLL is not relocated or prepared in any way to be called from executable code. However, the handle returned can be used to load resources from the DLL using the standard resource functions such as LoadString.When a DLL is loaded, its entry point, traditionally named DllMain, is called. DllMain is prototyped as
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved);
In addition to being called when the DLL is first loaded, DllMain is also called when it's unloaded or when a new thread is created or destroyed in the process that loads it. The second parameter, ul_reason_for_call, indicates the reason for the call to DllMain.DLLs should avoid doing anything more than simple initialization from within DllMain. An action such as loading other DLLs or any other action that might load other DLLs can cause problems with the Windows CE loader. This restriction can cause problems for DLLs that have been ported from the desktop versions of Windows because those operating systems are much more tolerant of actions within DllMain.One last DLL function is handy to know about. The function DisableThreadLibraryCalls tells the operating system not to send DLL_THREAD_ ATTACH and DLL_THREAD_DETACH notifications to the DLL when threads are created and terminated in the application. Preventing these notifications can improve performance and reduce the working set of an application because the DLL's LibMain isn't called when threads are created and destroyed. The function is prototyped as
BOOL DisableThreadLibraryCalls (HMODULE hLibModule);
The only parameter is the handle to the DLL identifying the DLL that doesn't want to be notified of the thread events.