Chapter 1, the following Windows CE console application runs under Windows CE. Aside from the difference of the entry point, a Windows CE console application looks like any other standard C command-line application.
//
// HelloCon - A simple console application
//
#include <windows.h> // For all that Windows stuff
// Program entry point
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
// You don't use Unicode for the stdio functions...
printf ("Hello World\n");
//...but you can with the 'w' versions.
wprintf (TEXT ("Hello World\n"));
return 0;
}
Windows CE console applications have access to the Win32 API. In fact, a console application can create windows, enter a message loop, and operate as if it were a standard Windows application. The difference is that the first time you call one of the stdio C library functions, such as printf, a console window is created and the result of that function will be seen in that window.
Consoles are implemented under Windows CE using a console driver with the appropriate device name of CON. Up to 10 console windows can be opened at any one time. The limit comes from the CON0 through CON9 naming convention used by drivers under Windows CE. Console applications don't directly open a CON driver to read and write to the window. At the current time, support for console applications is limited to a subset of the standard C library character mode functions, although this subset seems to grow with every release of Windows CE.Because the initialization of the console driver occurs only after the first call to an I/O library function, it's possible for a console application to run to completion and terminate without ever creating a console window for output. If you want a console window to always be created, you'll need to include a printf or other console input or output call to force the console to be created. You can always insert a line like
printf (" \b");
which prints a space and then backspaces over the space to force the console to be created.
The CEFind Example Program
The following program is a short console application that searches the Windows CE file system for matching file names. The program can be launched from a console window using Cmd.exe, or it can be launched from the Explorer. Because no concept of a current directory is built into Windows CE, the search always starts from the root of the file system unless a path is specified with the filename specification. Figure 16-3 shows the results of CEFind when looking for all the TrueType fonts on a system.

Figure 16-3: The results of a CEFind search for TrueType font files
The CEFind source is contained in one file,

Listing 16-2: The CEFind program
CEFind.cpp
//======================================================================
// CEFind - A Windows CE console file search application
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
int SrchDirectory (LPTSTR pszDir);
//----------------------------------------------------------------------
// Global data
//
int nTotal = 0;
int nFiles = 0;
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
TCHAR pInput[256];
if (wcslen (lpCmdLine) == 0) {
printf ("USAGE: CEFIND filespec\n");
return 0;
}
printf ("\n"); // Initialize the console.
// We always start at the root.
memset (pInput, 0, sizeof (pInput));
if (*lpCmdLine != TEXT ('\\')) {
pInput[0] = TEXT ('\\');
}
wcscat (pInput, lpCmdLine);
// Perform recursive search.
SrchDirectory (pInput);
wprintf (L"\n %9d file(s) found. %d bytes.\n", nFiles, nTotal);
return 0;
}
//----------------------------------------------------------------------
// SrchDirectory - Recursive routine that searches a dir and all
// child dirs for matching files
//
int SrchDirectory (LPTSTR pszDir) {
WIN32_FIND_DATA fd;
TCHAR szNew[MAX_PATH];
INT i, rc, nErr = 0;
HANDLE hFind;
TCHAR *pPtr, *pSrcSpec;
// Separate subdirectory from search specification.
for (pSrcSpec = pszDir + lstrlen (pszDir); pSrcSpec >= pszDir;
pSrcSpec--)
if (*pSrcSpec == TEXT ('\\'))
break;
// Copy the search specification up to the last directory
// separation character.
if (pSrcSpec <= pszDir)
lstrcpy (szNew, TEXT ("\\"));
else {
for (i = 0; (i < dim(szNew)-10) &&
((pszDir+i) <= pSrcSpec); i++)
szNew[i] = *(pszDir+i);
szNew[i] = TEXT ('\0');
}
pPtr = szNew + lstrlen (szNew);
// Find matching files.
hFind = FindFirstFile (pszDir, &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
// Report all matching files.
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
wprintf (L" %9d\t %s%s\n", fd.nFileSizeLow, szNew,
fd.cFileName);
nTotal += fd.nFileSizeLow;
nFiles++;
}
rc = FindNextFile (hFind, &fd);
} while (rc);
FindClose (hFind);
} else {
rc = GetLastError();
if ((rc != ERROR_FILE_NOT_FOUND) &&
(rc != ERROR_NO_MORE_files)) {
wprintf (L"1Find Error. Str:%s rc:%d", pszDir, rc);
return -1;
}
}
// Create generic search string for all directories.
lstrcat (szNew, TEXT ("*.*"));
hFind = FindFirstFile (szNew, &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// Recurse to the lower directory.
lstrcpy (pPtr, fd.cFileName);
lstrcat (pPtr, pSrcSpec);
nErr = SrchDirectory (szNew);
if (nErr) break;
*pPtr = TEXT ('\0');
}
rc = FindNextFile (hFind, &fd);
} while (rc);
FindClose (hFind);
} else {
rc = GetLastError();
if ((rc != ERROR_FILE_NOT_FOUND) &&
(rc != ERROR_NO_MORE_files)) {
wprintf (L"2Find Error:%d", rc);
return -1;
}
}
return nErr;
}
The console can be quite useful in debugging. Because all Windows CE applications can call console functions, such as printf, you can use the console as a quick-and-dirty debug console. This is especially helpful in debugging applications on unusual embedded systems that don't have a method to connect to the development tools on a PC.For systems that don't support the console, the standard console functions are still exported. It's just that the console functions simply look for a console driver that, on that system, isn't there. Even for systems without a console driver, there's no reason you can't put a console driver on the system. In fact, one of the tricks of the Windows CE gurus is to create a console driver and place it in the Windows directory of a Pocket PC for quick-and-dirty debugging.
In addition, DLLs can also use the console as easily as executables can. This feature is handy for debugging DLLs that are loaded by processes other than the process you have developed. One caveat, however: the system automatically creates a console for a process only once. If for some reason the console is closed, subsequent output to the console for that application is lost.
Console Redirection
Windows CE supports two functions, GetStdioPathW and SetStdioPathW, that allow the console input and output functions to be redirected to files or drivers other than the default console driver. The console functions support the three traditional console paths: input, output, and error, identified by the traditional labels stdin, stdout, and stderr, respectively.To query the current console settings, call the function GetStdioPathW prototyped as
BOOL GetStdioPathW (DWORD id, PWSTR pwszBuf, LPDWORD lpdwLen);
The id parameter specifies which path to be queried: stdin for input, stdout for output, and stderr for error. By default, all three paths point to a single console driver instance such as CON1:. The second parameter, pwszBuf, receives the name of the driver or file that that the specified path is directed toward. The final parameter, lpdwLen, should contain the length, in characters, of pwszBuf and should be set to the length of the string returned.The individual console paths can be redirected with the function SetStdioPathW. Its prototype is
BOOL SetStdioPathW (DWORD id, LPCWSTR pwszPath);
As with GetStdioPathW, the first parameter identifies the path to set. The second parameter specifies the name of the device driver instance or the file that will be the source (in the case of the input path) or the destination (in the case of the output or error paths for the console).In the following code fragment, the output of Cmd.exe is redirected to a file named tempout.txt.
// Query the current output path.
dwLen = MAX_PATH;
fRet = GetStdioPathW(stdout, szStdOut, &dwLen);
// Set output path to file.
SetStdioPathW(1, TEXT("\\tempout.txt"));
// Launch cmd.exe with a DIR command.
CreateProcess (TEXT("Cmd.exe"), TEXT("/c dir"), NULL, NULL, FALSE, 0,
NULL, NULL, NULL, &pi);
WaitForSingleObject (pi.hProcess, 2000);
// Clean up and restore default output path.
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
SetStdioPathW(stdout, szStdOut);
The preceding code first queries the current output path setting so that it can be restored at the end of the code fragment. Next cmd.exe is launched with a command-line string telling cmd.exe to list the contents of the current directory and then terminate. After the process is launched, the code waits for two seconds for the command to succeed and then closes the handles returned by CreateProcess and restores the output path.