.KEYWORD ceprogram1299
.FLYINGHEAD PROGRAMMING POWER
.TITLE Freeing up DLLs, avoiding suspend mode, and listbox tricks
.FEATURE
.AUTHOR Andrew Tucker
.SUMMARY Welcome to the first installment of the Windows CE Power Magazine Programming Power column! Each month we’ll address tough issues about writing software for Windows CE and provide solutions that usually include a code snippet or two. This month, Andrew Tucker explains how to free up DLLs, shows off a hidden feature that lets your application stay out of suspend mode, and shows you how to make a listbox do a horizontal scrollbar.
Welcome to the first installment of the Windows CE Power Magazine Programming Power column! Each month we’ll address tough issues about writing software for Windows CE and provide solutions that usually include a code snippet or two.
This month, we’ve selected issues that are common stumbling blocks. But what will really make this column work is if we answer questions from those of you fighting daily battles in the trenches. Want to know how to workaround an API that’s not available on Windows CE? Need help determining if your code will work on both a Palm-sized PC and a H/PC Pro? Send us email at poweranswers@bsquare.com, including code where appropriate, and we’ll try our hardest to find a resolution for you.
Most of the topics we’ll cover are for those of you using the Visual C++ Toolkit for Windows CE, but we’ll address occasional questions about Visual Basic for Windows CE and other development tools as well. Without further ado, let’s jump into this month’s questions.
.H1 How come calling CoFreeUnusedLibraries doesn’t seem to release any of my DLLs, even though there are no outstanding COM objects?
Using memory efficiently is always a good idea for any software system, but it’s paramount for most Windows CE applications. Since embedded devices don’t have the seemingly endless RAM and hard drive space of desktop systems, a memory hogging application can quickly have a dramatic affect on system performance.
The designers of COM had this issue in mind when they came up with the CoFreeUnusedLibraries API. Calling this function occasionally, usually in a message loop or when the system is idle, will release any DLLs that don’t have any COM objects currently dependent on them. A DLL indicates a willingness to be unloaded by returning S_OK from its exported DllCanUnloadNow function. On Windows CE however, a return value of S_OK doesn’t result in the DLL being freed up immediately.
Once a DLL has indicated that it’s able to be unloaded, CoFreeUnusedLibraries just captures the current system time. On subsequent calls to CoFreeUnusedLibraries if DllCanUnloadNow still returns S_OK and a specified time period has elapsed, then the DLL is actually freed. If during the time period DllCanUnloadNow returns S_FALSE, the clock is reset and the whole process starts over.
The default time period that CoFreeUnusedLibraries waits is ten minutes — much longer than the patience of most developers that are trying to test their DllCanUnloadNow implementation.
.CALLOUT Fortunately, there’s a back door you can use to modify the timeout value.
Fortunately, there’s a back door you can use to modify the timeout value. When the operating system starts, it initializes the timeout period from the DWORD registry value FreeTimeout under the HKEY_LOCAL_MACHINE\Software\Microsoft\OLE key. The time is specified in milliseconds, so a value of 30,000 would indicate a 30 second time period. If the registry key does not exist, the default time of ten seconds (or 600,000 milliseconds) is used.
.H1 What can I do to avoid having my device go into suspended mode?
Power conservation is a big issue for a Windows CE device that’s running on batteries, so the operating system is designed to suspend itself into a low-power mode after a period of inactivity. Unfortunately, this is not ideal for all situations. For example, if your program has an open serial port connection or a TCP/IP connection over a PC Card network adapter, these connections will be broken when the device goes into suspend mode.
A hacked up solution to this is to occasionally fake a keystroke with the keybd_event or SendInput APIs. While this will usually work for most devices, it can be problematic if the keystroke is interpreted as input to the active application. Additionally, the keybd_event API doesn’t work on most Palm-sized PCs, which makes it an even less desirable approach.
Luckily, there is a right way to do this that works on all devices. Since version 1.0, Windows CE has had the well-hidden SystemIdleTimerReset API, which takes no parameters and has no return value. When this API is called, the system countdown to device suspension starts over from the beginning. Since the countdown time is about 30 seconds, calling SystemIdleTimerReset at least once every 30 seconds will stop the device from suspending itself. The easiest way to do this is to launch a thread like this:
.BEGIN_CODE
DWORD WINAPI AvoidSuspendThread(LPVOID pv)
{
\// Application code should set g_bAvoidSuspend to false when
\// it’s OK to enter suspend mode again
while ( g_bAvoidSuspend )
{
SystemIdleTimerReset();
Sleep(30 * 1000);
}
return 0;
}
.END_CODE
Although this API has been available since version 1.0, it only started showing up in the SDK header files in CD version 2.11. For older devices, you may have to add the prototype yourself. You won’t need to manually load the function with GetProcAddress, though — it’s already available in the standard SDK import library COREDLL.LIB.
.H1 How can I make the horizontal scrollbar in a listbox work properly?
Let’s say you’re putting the finishing touches on a dialog box for your application and you notice that some of the entries in a listbox extend past the right margin. "No problem", you say to yourself, "I’ll just enable the horizontal scrollbar".
You open up the dialog in the resource editor, enable the "Horizontal Scroll" radio button and rebuild. When you test your changes you’re surprised to see that the horizontal scrollbar doesn’t show up. Fiddling around with the other styles has no effect — the little bugger just won’t appear. What gives?
As the saying goes, it’s a feature not a bug. In fact, this "feature" is present in all releases of Windows CE, Windows NT, Windows 95 and Windows 98. The problem is that, by default, a listbox does not update its horizontal extent as strings are added and removed. The horizontal extent is simply the logical unit width of a string when it’s drawn with a particular font. Fortunately, if you manually update a listbox with the maximum horizontal extent of the strings it currently contains, the horizontal scrollbar will magically appear. Here’s come code that shows how to do this:
.BEGIN_CODE
/*
This function updates the horizontal extent for hwndList so that a horizontal scroll bar will show up and work properly.
*/
void UpdateHorzExtent(HWND hwndList)
{
HDC hdc = GetDC(hwndList);
HFONT hFont = (HFONT)SendMessage(hwndList, WM_GETFONT, 0, 0);
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
int iLongestExtent = 0;
int iCount = ListBox_GetCount(hwndList);
TCHAR chBuf[MAX_STRING_LENGTH];
for ( int i = 0; i < iCount; i++ )
{
ListBox_GetText(hwndList, i, chBuf);
SIZE size;
GetTextExtentPoint32(hdc, chBuf, _tcslen(chBuf), &size);
size.cx += 6; \// fudge factor
if ( size.cx > iLongestExtent )
iLongestExtent = size.cx;
}
ListBox_SetHorizontalExtent(hwndList, iLongestExtent);
SelectObject(hdc, hOldFont);
ReleaseDC(hwndList, hdc);
}
.END_CODE
The first thing we do is get an HDC for the window and select the current font into it. We then walk through the strings in the listbox, calculate the size of each with the GetTextExtentPoint32 API, and record the maximum. A "fudge factor" of six units is added to the width to account for a small right hand margin between the end of the text and the edge of the listbox. After we drop out of the loop, the listbox is updated with a call to ListBox_SetHorizontalExtent and we release the HDC after resetting the original font handle.
There are a couple of drawbacks to UpdateHorzExtent. It’s up to you, the developer, to make sure it gets called after each string is inserted or deleted. This could be automated by subclassing the listbox or using a C++ class wrapper. The other problem is that the code assumes that no string in the listbox is bigger than the MAX_STRING_LENGTH constant. An industrial strength application would probably want to dynamically allocate the memory to make sure that chBuf never overflows.
.H1 I’m trying to copy some text to the clipboard, but it doesn’t seem to work. How come?
Edit controls have built-in capability to perform cut, copy, and paste actions by responding to the WM_CUT, WM_COPY, and WM_PASTE messages, respectively, but it’s also possible to manually put data on the clipboard and retrieve it. Unfortunately, the Windows CE implementation of the clipboard APIs has some quirks.
Although the documentation states that the OpenClipboard API allows a NULL parameter, this doesn’t work for Windows CE. The call, and all subsequent clipboard calls, will still return TRUE, but the data will not end up on the clipboard. Instead, Windows CE requires a valid window handle for the OpenClipboard call and, additionally, the window must have been created by the process calling OpenClipboard. If the window was created by another process, you can put data on the clipboard, but any paste operation to remove it will cause a series of access violation exceptions and require a warm boot of the device.
Also, the desktop requires that the data being put on the clipboard be allocated with GlobalAlloc. Windows CE doesn’t support GlobalAlloc, so LocalAlloc with the LMEM_MOVEABLE flag should be used.
The following code snippet shows how to work around Windows CE’s shortcomings and put the clipboard APIs to work:
.BEGIN_CODE
/*
Put a text buffer on the clipboard
*/
BOOL PutTextOnClipboard(HWND hwnd, LPTSTR pszText)
{
BOOL bSuccess = FALSE;
if ( IsWindow(hwnd) && OpenClipboard(hwnd) )
{
EmptyClipboard();
LPTSTR pszCopy = (LPTSTR)LocalAlloc(LMEM_MOVEABLE, (_tcslen(pszText)+1) * sizeof(TCHAR));
if ( pszCopy )
{
_tcscpy(pszCopy, pszText);
UINT uiFormat;
#ifdef UNICODE
uiFormat = CF_UNICODETEXT;
#else
uiFormat = CF_TEXT;
#endif
bSuccess = (SetClipboardData(uiFormat, pszCopy) != NULL);
\// if the clipboard doesn’t own the data, we need to free it
if ( bSuccess == FALSE )
LocalFree(pszCopy);
}
CloseClipboard();
}
return bSuccess;
}
.END_CODE
That’s all we have room for this month. I look forward to hearing about what ails you (regarding development for CE, that is), at poweranswers@bsquare.com.
.BIO Andrew Tucker works on developer tools for Windows CE at bSQUARE Corporation. He has been writing code for Windows since 1991 and has published articles in C/C++ Users Journal, Dr Dobbs Journal, and Windows Developer Journal. He recently co-authored SAMS Teach Yourself Windows CE Programming in 24 Hours. Andrew has a BSCS from Seattle Pacific University and is working on a MSCS at the University of Washington. He can be reached at poweranswers@bsquare.com.


