throbber
300
`
`Chapter 10 • RAP I: How the Outside World Talks to CE
`
`The Pascal unit you'll use to create our Delphi RAPI application is a freeware
`unit, generously provided by Scott Crossen. Crossen created a Pascal-/Delphi(cid:173)
`based import of all of the functions, types, and constants listed in Rapi . h. This in
`itself is no small feat-many of the structures in Rapi . h are quite complex and
`were probably very difficult to port. But the best thing about the Rapi . pas unit
`that Crossen created is that all of the functions are loaded dynamically.
`
`In C/C++, when you use the default Rapi . h, the compiler statically links
`Rapi. lib into your program. When the program starts on the user's machine
`and they're not running CE Services, it will crash before it even gets off the
`ground.
`
`What Crossen did differently was to attempt to find and load Rapi . dll at run(cid:173)
`time. If you can't find Rapi . dll, you exit gracefully and return an error code. In
`contrast to linking in rapi . lib, Crossen's implementation first checks to see if
`RAPI is available. If RAPI isn't available on the user's machine, the program
`won't crash; it can still perform every non-RAPI feature just fine.
`
`If you look at the code to do this, you'll see it starts with a call to Loadl i brary(),
`passing in 1 RAPI. DLL 1 as the library to load:
`RapiModule := LoadLibrary('RAPI.DLL 1);
`Check to make sure that the library was successfully loaded:
`if RapiModule > HINSTANCE_ERROR then
`begin
`Result := True;
`
`Next, proceed to retrieve the addresses of all of the RAPI functions, one at
`a time:
`
`@mCeRapiinit := GetProcAddress(RapiModule, 1CeRapiinit 1);
`// ... and repeat for each RAPI function
`@mCeGetSystemPowerStatusEx:= GetProcAddress(RapiModule 1 'CeGet-
`SystemPowerStatusEx1);
`
`end
`else
`Result := False;
`end;
`
`You've managed to successfully dynamically load all of the RAPI functions at
`once. The amazing thing about the "any Desktop development tool" aspect of
`
`Page 00326
`
`

`
`r
`
`Using Other languages
`
`301
`
`RAPI is that the code to retrieve all of the databases looks virtually the same in
`Pascal as it does in C/C++.
`
`For instance, when you retrieve the list of databases in your Delphi application,
`the code is virtually identical to that of the C/C++ version:
`
`hEnumContext := CeFindFirstDatabase(DBType);
`if hEnumContext = INVALID_HANDLE_VALUE then
`begin
`ShowMessage('Error retrieving DB Info');
`Exit;
`end;
`for i := 1 to TVCEDB.Items.Count-1 do
`if Assigned(TVCEDB.Items[i] .Data) then
`Dispose(TVCEDB.Items[i].Data);
`TVCEDB.Items.Clear;
`Node := TVCEDB.Items.Add(nil,
`Node.Imageindex := 0;
`Node.Selectedlndex := 0;
`ObjiD := CeFindNextDatabase(hEnumContext);
`while ((ObjiD <> 0) and (ObjiD <> ERROR_NO_MORE_ITEMS) and (ObjiD <>
`ERROR_INVALID_PARAMETER)) do
`begin
`CeOidGetinfo(ObjiD, CeOIDinfo);
`if CeOIDinfo.wObjType <> OBJTYPE_DATABASE then
`begin
`ObjiD := CeFindNextDatabase(hEnumContext);
`Continue;
`end;
`Application.ProcessMessages;
`
`'Device Databases');
`
`The only difference comes when you actually add the items to your Tree View,
`in that you're adding some data along with the actual text:
`
`with Node, CeOIDinfo.infDatabase do
`begin
`Node := TVCEDB.Items.AddChild(TVCEDB.Items[O], String(szDbase-
`Name));
`Data := new(PCeOIContainerStruct);
`TCeOIContainerStruct(DataA).OID := ObjiD;
`TCeOIContainerStruct(DataA).OIDinfo := CeOIDinfo;
`Imagelndex := 1;
`Selectedindex := 3;
`
`Page 00327
`
`

`
`302
`
`Chapter 10 • RAPI: How the Outside World Talks to CE
`
`end;
`ObjiD := CeFindNextDatabase(hEnumContext);
`end;
`if ObjiD = ERROR_INVALID_PARAMETER then
`ShowMessage('An Error occured while retrieving information from the
`CE device.');
`TVCEDB.Items[O] . Expand(True);
`
`With that one simple exception, the two sets of source code are virtually the same.
`
`By using some free source code, you can give your Delphi applications access to
`the entire RAPI library. And, since Inprise (formerly Borland) does not appear to
`have plans for a Delphi that compiles a true CE-based executable, this is definitely
`the next best thing.
`
`Summary
`
`RAPI helps extend the CE application into the Desktop by giving the other machines
`in your system access to the data and files on the CE device. This is especially
`important given that the data on these devices wouldn't be worth very much if
`you couldn't get that data circulated to other machines and other pieces of soft(cid:173)
`ware. In this chapter, you saw how you could get a Desktop program to perform the
`very same types of data access possible on the CE devices themselves. In addi(cid:173)
`tion, you saw how it was possible to extend even non-Microsoft development
`products so that they, too, could access and talk to a CE device.
`
`Page 00328
`
`

`
`Page 00329
`
`Page 00329
`
`

`
`Page 00330
`
`Page 00330
`
`

`
`CHAPT{R
`
`How CE Talks to the
`Outside World
`
`• Serial Communications Issues
`
`• Modem-Based Communications Issues
`
`• PC Card/PCMCIA Communications Issues
`
`• Winsock Commmunications Issues
`
`Page 00331
`
`

`
`306
`
`Chapter 11 • How CE Talks to the Outside World
`
`In the last chapter, you saw how Desktop programs can talk to a CE device. In
`this chapter, we'll be looking at how to get your CE device talking to the outside
`world, including all of your other devices, Desktop machines, or any other piece
`of equipment you might have in mind. We'll explore all areas of CE-based com(cid:173)
`munication from serial I/O to CE's Winsock support, starting with a brief overview
`of your options when it comes to CE communications. Then we'll look at each of
`the options in detail.
`
`What's in the Box?
`
`There are two aspects to Windows CE-based communication:
`
`• The hardware aspect
`
`• The software aspect
`
`Although this may seem like an obvious distinction, it's not. That's because
`tmder Windows 98/NT, an application could open a serial port or begin a Winsock
`operation without paying much attention to the underlying hardware. Windows
`CE is closer to the hardware level, however, so you generally have no such lux(cid:173)
`ury. And, as you've seen in previous chapters, each manufacturer may expose dif(cid:173)
`ferent features.
`
`The Hardware Aspect
`When it comes to hardware, there are only two ports you can count on:
`
`• A serial port
`
`• An Infrared (IR) port
`
`In addition to these ports, there is an entire set of uncertain hardware, including:
`
`• Modems
`
`•
`
`PCMCIA cards
`
`Page 00332
`
`

`
`What's in the Box?
`
`307
`
`The Serial Port
`
`The default serial port is usually the same one used to connect to the Desktop
`computer, and it is usually COMl. For the most part, it behaves like a serial port
`on a Desktop machine. Just as in Windows 98/NT, you can open the port in CE
`with a call to CreateFi 1 e(), read from it with ReadFi l e(), and so on. Most of
`your existing Windows-based serial communications code should port to CE
`rather easily.
`
`TheIR Port
`
`The IR port is more of a gray area, however. Although all commercial CE devices
`offer an IR port, the port number (i.e., COMl, COM2, etc.) changes from one device
`to the next.
`
`Also, some manufacturers configure their devices so that both the serial port
`and theIR port appear as COMl. In this case, the only way to specify which one
`you want is to open the port and then try to set it into IR mode.
`
`And, as if it isn't confusing enough already, some devices allow you to open a
`serial port and an IR port at the same time. This goes against everything we've
`ever been told about CE allowing only one serial connection at a time.
`
`Modems and PCMCIA Cards
`
`As for devices that may or may not be available to you, some devices, such as the
`HP Jornada, have a built-in modem; other devices offer it as an upgrade; and
`some don't offer a modem at all. Similarly, the PCCard (PCMCIA) slot opens the
`device up to such hardware as networking cards, cell phone modems, additional
`serial ports, or higher-speed modems.
`
`TIP
`
`If your application calls for additional serial ports, your best bet is either the serial
`1/0 card or the dual serial I/O card offered by Socket Communications of Newark,
`California (http: I /www. socketcom. com). Both are PCMCIA cards that instantly
`add one or two RS-232 ports to your CE device.
`
`All of this hardware variety only makes it more of a challenge to design reliable
`communications-related software.
`
`Page 00333
`
`

`
`308
`
`Chapter 11 • How CE Talks to the Outside World
`
`The Software Aspect
`Obviously, with so much possible hardware available on aCE device, there's
`some complexity to the software as well. When it comes to communications, Win(cid:173)
`dows CE supports a mix of everything from Win32 serial communications func(cid:173)
`tions to Winsock to special blends of Winsock and IR.
`
`Serial Communications
`
`Serial communications haven't changed very much from Windows 98/NT to
`Windows CE. CE supports 16 of the 23 communications-related API calls, and the
`ones that aren't there probably won't affect your applications much, if at all. The
`seven unsupported functions are
`
`• Buil dCommDCB()
`• BuildCommDCBAndTimeouts()
`• CommConfigDialog()
`• GetCommConfig()
`• GetDefaultCommConfig()
`• SetCommConfig()
`• SetDefaultCommConfig()
`
`It is possible to do almost everything these functions do with the functions
`that are supported by Windows CE. For instance, most of the functionality of
`GetCommConfi g() can easily be replaced with the function GetCommState(),
`which is supported by CE.
`
`The following is a list of the 16 functions that are supported under Windows CE:
`• ClearCommBreak()
`• ClearCommError()
`• EscapeCommFunction()
`• GetCommMask()
`• GetCommModemStatus()
`• GetCommProperties()
`
`Page 00334
`
`

`
`What's in the Box?
`
`309
`
`• GetCommState()
`• GetCommTimeouts()
`• PurgeComm()
`• SetCommBreak()
`• SetCommMas k ()
`• SetCommState()
`• SetCommTimeouts()
`• SetupComm()
`• TransmitCommChar()
`• WaitCommEvent()
`
`With this many supported functions, it's clear that the few functions that are
`missing shouldn't affect you too much.
`
`NOTE
`
`For more information on some of the missing API functions, see Chapter 3.
`
`IR Communications
`
`IR communications on a CE device are available in three classes:
`
`• RawiR
`
`•
`
`•
`
`IrCOMM
`
`Infrared Sockets (IrSock)
`
`Raw IR Raw IR means that you'll be using theIR port as though it was any other
`serial port. There is no special handshaking and no error handling.
`
`The most difficult part about using the IR port as a raw serial port comes from
`the fact that, as noted above, every manufacturer seems to assign a different port
`number to the IR port. With some of the earlier CE devices, it was possible to
`query a certain key in the registry to determine the IR port number, but it seems
`that all manufacturers do not store that information in the same place.
`
`Page 00335
`
`

`
`310
`
`Chapter 11 • How CE Talks to the Outside World
`
`The only truly reliable way to determine the logical designation of theIR port is
`to loop through all of the ports and ask each one if it's theIR port.
`
`The way to ask a port whether it is an IR port or a standard serial port is to call
`EscapeCommFun cti on () and pass in the handle to the open port and a flag
`(SETIR) indicating that you want to put the port into IR mode:
`
`EscapeCommFunction(hPort, SETIR);
`
`Since this function only returns TRUE if the port is an IR port, it's safe to
`assume that if the call fails, the port is not an IR port, and you need to keep look(cid:173)
`ing. The most efficient and effective way to use this trick is probably to create a
`routine to detect the presence of theIR port. To do this, first initialize the result of
`the function to 0 to indicate that an IR port could not be found and then allocate
`memory to store the "name" (COMl, COM2, etc.) of the comm port:
`result = 0; //zero indicates error, could not find port
`szPort = (TCHAR ''')LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`
`Then, loop from 1-the lowest comm port number-to MAX_PORTS, a prede(cid:173)
`fined constant indicating the highest port number to test:
`for (i=l; i <= MAX_PORTS;
`
`i++)
`
`WARNING
`
`The MAX_PORTS value differs greatly from one device to another. On some devices,
`such as the Casio E-1 0, the highest comm port number is COM3; on the Casio 2400,
`the highest comm port number is COM4; and on the HP Jornada, the highest comm
`port number is COM6!
`
`Next, construct the name of the comm port using the value of i as the number
`of the comm port:
`
`_tcscpy(szPort, TEXT("COM"));
`_itow(i, szNum, 10); //convert
`_tcscat(szPort, szNum);
`_tcscat(szPort, TEXT(":"));
`
`to string
`
`NOTE
`
`In the above code, there is a call to the RTL function _ i tow(), which only works with
`Unicode-based strings. Ideally, the function to use is_ itot(), which works
`with the generic-string types. However,_ i tot() is not supported on all of the CE
`platforms. See Appendix A for more information.
`
`Page 00336
`
`

`
`--
`
`What's in the Box?
`
`311
`
`WARNING
`
`You'll notice that the last step in the above code is to append a colon to the port
`name. Although Windows 98 and Windows NT do not require that the port name
`be followed with a colon, Windows CE does.
`
`The next step is to open the port whose name you've just created. Just as you
`would on Windows 98/NT, do this using the CreateFi 1 e() function:
`hPort = CreateFile(szPort, GENERIC_READ I GENERIC_WRITE,
`0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
`if (hPort != INVALID_HANDLE_VALUE)
`{
`
`Now, if you've been able to successfully open the port, you can apply the trick
`of attempting to set the port to IR mode. If it returns TRUE, that means you found
`theIR port:
`
`if (EscapeCommFunction(hPort, SETIR))
`{
`
`//Ir Port Found!!!
`result = i ;
`CloseHandle(hPort);
`break;
`
`The rest of the function is just clean up and remembering to return the numeric
`value representing theIR comm port:
`
`CloseHandle(hPort);
`}
`LocalFree(szPort);
`return result;
`
`When fully assembled (and with some function calls to display the status of
`your search in a list box), the full function looks like this:
`
`int DetectiRPort(void)
`{
`
`int i, result;
`TCHAR * szPort;
`TCHAR szNum[4];
`HANDLE hPort;
`
`Page 00337
`
`

`
`312
`
`Chapter 11 • How CE Talks to the Outside World
`
`result = 0; //zero indicates error, could not find port
`szPort = (TCHAR ''')Local A 11 oc(LMEM_ZEROINIT, MAX_PATH);
`for (i=l; i <= MAX_PORTS; i++)
`{
`
`_tcscpy(szPort, TEXT("COM"));
`_itow(i, szNum, 10); //convert i to string
`_tcscat(szPort, szNum);
`_tcscat(szPort, TEXT(":"));
`SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("Now testing
`port:"));
`SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)szPort);
`SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)TEXT(" ... "));
`hPort = CreateFile(szPort, GENERIC_READ I GENERIC_WRITE,
`0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
`if (hPort != INVALID_HANDLE_VALUE)
`{
`
`if (EscapeCommFunction(hPort, SETIR))
`{
`
`SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)TEXT( "IR Port
`Found! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! "));
`SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)szPort);
`result = i;
`CloseHandle(hPort);
`break;
`
`CloseHandle(hPort);
`}
`LocalFree(szPort);
`return result;
`
`When integrated into a sample test application, the result looks like the applica(cid:173)
`tion shown in Figure 11.1.
`
`lrCOMM In addition to the raw IR, which we've just examined, there is another
`kind of IR communication available under Windows CE, IrCOMM. This is a more
`reliable form of IR serial communication that provides handshaking, error handling,
`and many other features that make it an attractive alternative to raw IR.
`
`Page 00338
`
`

`
`What's in the Box?
`
`313
`
`FIGURE 11.1:
`
`The lrDetect application
`
`IRDetect- queues serial ports t ... l3
`
`Now tBsting port:
`COMl:
`...
`lR Port Found!!!!!!!!!!ll!l!!!
`COMl:
`
`The best part about all of these additional features is that they're completely
`transparent-as far as your code is concerned, the IrCOMM port is just like any
`other serial port. In other words, the IrCOMM port can be used as though it were
`a standard serial port. That means you can call CreateFi l e(), ReadFi l e(), etc.,
`while still enjoying the benefits of a more reliable connection.
`
`In fact, the only difference between raw IR and IrCOMM that you'll actually
`notice is that the IrCOMM port is on a different logical comm port number than
`the raw IR port! To ensure that you open the correct IrCOMM port, you'll need to
`consult the registry. The key containing the information about the IrCOMM port
`is HKEY _ LOCAL_MACHINE\Dri vers\Bui l tin\IrCOMM. The actual port number is
`contained in the value Index under this key. Or, as it would appear in code:
`
`HKEY hKeyiR;
`if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE,
`TEXT("Drivers\\Builtin\\IrCOMM"), 0, KEY_READ, &hKeyiR))
`{
`
`DWORD dwiRport;
`DWORD dwSizeData = sizeof (DWORD);
`if (ERROR_SUCCESS == RegQueryValueEx (hKeyiR, TEXT("Index "),
`NULL, NULL, (LPBYTE)&dwiRport, &dwSizeData))
`
`WCHAR wszMsg[64];
`
`Page 00339
`
`

`
`~14
`
`Chapter 11 • How CE Talks to the Outside World
`
`wsprintf (wszMsg, TEXT"IrCOMM port number is: %u"),
`dwiRport);
`MessageBox (NULL, wszMsg, TEXT("IR Port Info "), MB_OK);
`
`Infrared Sockets (lrSock) Infrared Sockets is a Winsock-like wrapper for
`communicating over the Infrared port. Refer to the "Winsock-Based Communica(cid:173)
`tions" section later in this chapter to see what it takes to convert a simple
`Winsock-based application to an Infrared Sockets application.
`
`Modem-Based Communications
`There are really three classes of modems when it comes to CE:
`
`• Standard external modems connected via the serial port
`
`• Built-in modems
`
`•
`
`PC Card/PCMCIA modems
`
`Standard External Modems Standard external modems are just that: stan(cid:173)
`dard. There is nothing special about opening a serial port and dialing a modem
`connected to that serial port when working with Windows 98/NT, and that's true
`under Windows CE as well.
`
`Built-in Modems It's tempting to conclude that built-in modems work just like
`external modems-and they do. The trick, however, is finding the internal modem,
`or more correctly, finding the comm port of the internal modem. The way to do this
`is to iterate through the HKEY _LOCAL_MACHINE\ExtModems key of the registry.
`Under this key, there will be a key for each of the modems the device knows
`about. For example, all CE devices have a default Hayes Compatible modem set(cid:173)
`ting that refers to any external modem on COMl. Therefore, under the HKEY _LOCAL_
`MACHIN E\ExtModems key, there is a Hayes Compati b 1 e key. To get the actual
`comm port name, which you can then use to open the port, simply retrieve the
`string stored in the Port value of the Hayes Compati b 1 e key. Or, in code, open
`the ExtModems key under HKEY _LOCAL_MACHINE as the first step:
`
`RegOpenKeyEx (HKEY_LOCAL_MACHINE, TEXT("ExtModems"), 0, KEY_READ,
`&hKey);
`if (hKey)
`
`Page 00340
`
`

`
`What's in the Box?
`
`315
`
`Next, create awhile loop that retrieves the names of the subkeys under the
`ExtModems key (i.e., the subkeys for each of the different modems).
`
`retCode = ERROR_SUCCESS;
`i = 0;
`while (retCode == ERROR_SUCCESS)
`{
`
`cbName = MAX_PATH;
`memset(szSubKeyName, 0, MAX_PATH);
`retCode = RegEnumKeyEx(hKey, i, szKeyName,&cbName, NULL, NULL,
`NULL, NULL);
`i++;
`
`If you are able to successfully enumerate the key, then try to open the key so
`you can retrieve the values you're after:
`
`if (retCode == (DWORD)ERROR_SUCCESS)
`{
`
`memset(szModeminfo, 0, MAX_PATH);
`memset(szPortinfo, 0, MAX_PATH);
`memset(szDevName, 0, MAX_PATH);
`RegOpenKeyEx (hKey, szKeyName, 0, KEY_READ, &hSubKey);
`
`If you are able to open the key, you can retrieve the Port value and the
`Fri endl yName of the modem:
`
`if (hSubKey)
`{
`
`dwBytes = MAX_PATH;
`RegQueryValueEx (hSubKey, TEXT("Port"), NULL, &dwType,
`(LPBYTE)szPortlnfo, &dwBytes);
`dwBytes = MAX_PATH;
`RegQueryValueExW(hSubKey, TEXT("FriendlyName"), NULL, &dwType,
`(LPBYTE)szDevName, &dwBytes);
`
`Optionally, you could then format the two strings retrieved and add the for(cid:173)
`matted string to a ComboBox:
`
`wsprintf (szModeminfo, TEXT("%s %s"), szPortinfo, szDevName);
`SendDlgitemMessage(hwnd, IDC_CBOMODEMS, CB_ADDSTRING, 0,
`(LPARAM)szModeminfo);
`
`Finally, clear out your strings for the run through the next subkey:
`
`memset(szModeminfo, 0, MAX_PATH);
`memset(szPortlnfo, 0, MAX_PATH);
`
`Page 00341
`
`

`
`316
`
`Chapter 11 • How CE Talks to the Outside World
`
`memset(szDevName, 0, MAX_PATH);
`RegCloseKey(hSubKey);
`
`}
`}
`}
`}
`
`If you then create a simple dialog with a ComboBox and put this code into, say,
`a WM_INITDIALOG message handler, the final assembled message handler looks
`like this:
`
`case WM_INITDIALOG:
`{
`
`HKEY hKey, hSubKey;
`DWORD dwDisposition;
`DWORD dwType;
`DWORD dwBytes = 0;
`TCHAR *szSubKeyName;
`TCHAR tcszKeyName;
`TCHAR ClassName[MAX_PATH] =TEXT( "" ); I I Buffer for class name .
`DWORD cbName ;
`TCHAR
`''szPortinfo;
`TCHAR >r s zDevName;
`TCHAR *szModeminfo;
`DWORD retCode;
`i nt i;
`
`szSubKeyName = (TCHAR *)LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`szKeyName = (TCHAR ''' )LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`szPortlnfo = (TCHAR ''')LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`szDevName = (TCHAR *)LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`szModeminfo = (TCHAR *)LocalAlloc(LMEM_ZEROINIT, MAX_PATH);
`
`RegOpenKeyEx (HKEY_LOCAL_MACHINE, TEXT("ExtModems "), 0, KEY_READ,
`&hKey);
`if (hKey)
`{
`
`retCode = ERROR_SUCCESS;
`i = 0;
`while (retCode == ERROR_SUCCESS)
`{
`
`cbName = MAX_PATH;
`memset(szSubKeyName, 0, MAX_PATH);
`
`Page 00342
`
`

`
`What's in the Box?
`
`.. 317 J
`
`retCode = RegEnumKeyEx(hKey, i, szKeyName,&cbName, NULL,
`NULL, NULL, NULL);
`
`i++;
`if (retCode == (DWORD)ERROR_SUCCESS)
`{
`
`memset(szModeminfo, 0, MAX_PATH);
`memset(szPortlnfo, 0, MAX_PATH);
`memset(szDevName, 0, MAX_PATH);
`RegOpenKeyEx (hKey, szKeyName, 0, KEY_READ, &hSubKey);
`if (hSubKey)
`{
`
`dwBytes = MAX_PATH;
`RegQueryValueEx (hSubKey, TEXT("Port"), NULL, &dwType,
`(LPBYTE)szPortinfo, &dwBytes);
`dwBytes = MAX_PATH;
`RegQueryValueExW(hSubKey, TEXT("FriendlyName"), NULL,
`&dwType, (LPBYTE)szDevName, &dwBytes);
`wsprintf (szModeminfo, TEXT("%s %s"), szPortinfo,
`szDevName);
`SendDlgltemMessage(hwnd, IDC_CBOMODEMS, CB_ADDSTRING, 0,
`(LPARAM)szModeminfo);
`memset(szModeminfo, 0, MAX_PATH);
`memset(szPortinfo, 0, MAX_PATH);
`memset(szDevName, 0, MAX_PATH);
`RegCloseKey(hSubKey);
`
`LocalFree(szSubKeyName);
`LocalFree(szKeyName);
`LocalFree(szPortinfo);
`Loca1Free(szDevName);
`LocalFree(szModeminfo);
`return TRUE;
`
`The resulting Modem Selector dialog looks something like the one pictured in
`Figure 11.2.
`
`Page 00343
`
`

`
`318
`
`Chapter 11 • How CE Talks to the Outside World
`
`FIGURE 11.2:
`
`The Modem Selector dialog
`
`Modem Selector
`
`r:::J 13
`
`PC Cards/PCMCIA Modems PCMCIA modems are really just another kind of
`PCMCIA device. In the next section, we'll be looking at all PCMCIA devices as a
`group, regardless of whether the actual card is a modem, a serial I/ 0 card, or a
`network interface card.
`
`PC Cards/PCMCIA Cards and Communications
`
`PC cards/PCMCIAcards are fairly transparent when it comes to communica(cid:173)
`tions. Your application could open a comm port and never really know that the
`port it just opened was really, say, a PCMCIA modem.
`
`However, there are two occasions when your application will care about the
`PCMCIA slot:
`
`• When the application starts and needs to find out if the desired card/port is
`inserted into the slot and available for use
`
`• When the application is running and the card in the slot changes (i.e., a card
`is added or removed)
`
`When the Application Starts Officially, the Microsoft documentation says
`that when your application starts, you can use a function called EnumPnpids() to
`retrieve a double-NULL terminated list of strings representing the device(s) cur(cid:173)
`rently inserted in the PCMCIA slot(s). However, there is one problem with this
`function: it doesn't exist in any of the . h files for any versions of Windows CE!
`
`TIP
`
`The Pnp in EnumPnpids() stands for Plug and Play
`
`This doesn't mean you can't get a list of available PCMCIA cards when your
`program starts, however; you just can't get that list using the EnumPnpids()
`
`Page 00344
`
`

`
`What's in the Box?
`
`319
`
`function. Instead, you can create a function that does the exact same thing by
`querying the registry.
`
`In this case, the key you're interested in is HKEY_LOCAL_MACHINE\Drivers\
`Active. In this key you will find a set of double-digit subkeys numbered 00 to nn,
`where nn is a double-digit integer.
`
`Each of these double-digit subkeys specifies a different driver or hardware compo(cid:173)
`nent that is currently being used by the device. The values of nn are assigned
`somewhat sequentially, with the OEM system components taking the lower num(cid:173)
`bers first. For instance, on many systems, the 00 entry contains information about
`the sound component, WAVl: . The value of nn assigned to a PCMCIA card
`depends on the number of times any cards have been inserted since the device
`was last reset. If the OEM has used, say 00 through 09 for system devices, the first
`PCMCIA card inserted will get an nn value of 10, the second card a value of 11,
`and so on. Also, if a card is inserted, removed, and then inserted at a later time
`(but before a reset) that card may or may not be assigned a different value than
`the one it had the last time it was used.
`
`The way this set of double-digit subkeys helps make it possible to get informa(cid:173)
`tion about the PCMCIA cards currently available on the device is that all PCMCIA(cid:173)
`related subkeys will have a value called Pnpid. If an entry has this value and a
`Name value containing the string COM, then it is a serial communications card of
`some kind.
`
`Note that this method also detects compact flash cards, so if your application
`expects a comm port in the form of a compact flash card, this technique will work
`to detect its presence as well.
`
`Using the same logic employed above in the modem detection routine, you can
`start by opening the HKEY _LOCAL_MACHINE\Dri ve rs\Acti ve key:
`
`RegOpenKeyEx (HKEY_LOCAL_MACHINE, TEXT("Drivers\\Active"), 0, KEY_READ,
`&hKey);
`
`We present only a portion of the full code here so as to avoid duplicating the
`modem detection code above. The full code for the Pnpid enumeration is on the
`CD for this book, in the directory for this chapter.
`
`NOTE
`
`NOTE
`
`NOTE
`
`Page 00345
`
`

`
`320
`
`Chapter 11 • How CE Talks to the Outside World
`
`Just as before, you can enumerate the nn subkeys:
`
`RegEnumKeyEx(hKey, i, szKeyName,&cbName, NULL, NULL, NULL, NULL);
`
`And for each subkey enumerated, you can attempt to retrieve the Pnpid and
`the device name (i.e., COMl, COM2, etc.):
`
`I I ...
`RegQueryValueEx (hSubKey, TEXT("Pnpid"), NULL, &dwType,
`(LPBYTE)szPnpld, &dwBytes);
`// ...
`RegQueryValueExW(hSubKey, TEXT("Name "), NULL, &dwType,
`(LPBYTE)szDevName, &dwBytes);
`
`If the string retrieved from the Pnpid value is non-NULL, add this item to your
`list box of PCMCIA devices:
`
`if (_tcscmp(szPnpld, TEXT( "" )) != 0) / / there was a Pnpid
`
`wsprintf (szDevName, TEXT(" %s %s"), szDevName, szPnpld);
`SendDlgitemMessage(hwnd, IDC_LSTPNPIDS, LB_ADDSTRING, 0,
`(LPARAM)szDevName);
`
`When this code is hooked up to a dialog box with a ListBox on it, the result is
`something like Figure 11.3.
`
`FIGURE 11.3:
`
`PCMCIA-Pnpids
`application
`
`CMCIA·Pnplds
`
`m:J 13
`
`PCMCIA-Pnplds (c) 1999 Terence Gogg1n from
`"The Windows CE Developer's Handboo~."
`
`TIP
`
`It turns out that the EnumPnplds( )actually does exist, it's just completely undocu(cid:173)
`mented. John Psuik, technical editor of this book, has included a sample on the
`CD documenting how to call this function, if you prefer to go the undocu mented
`route.
`
`Page 00346
`
`

`
`What's in the Box?
`
`321
`
`When the Card in the Slot Changes If you are doing serial communications
`in your application, you're going to have to know when the card in the PCMCIA
`slot changes.
`
`Why? Well, first you have the issue of error prevention or detection. If your
`application is in the middle of a file transfer over a PCMCIA-based serial port, and
`the user pulls the card out, your application will immediately know an error has
`occurred. However, your application will be much more robust if it is able to deter(cid:173)
`mine that the source of the error was the user removing the card and not, say, a
`problem with the other computer.
`
`Second, even if your application is not actually using a cornrn port at the moment
`a card is removed or added, you'll probably want to update the cornrn port options
`that you offer the user. If the user inserted a modern or a serial 1/0 card, it's a safe
`guess they want to use that port with your program, and they don't want to have to
`restart your application in order to refresh the list of available cornrn ports.
`
`Now that we know the advantages of monitoring changes in the PCMCIA slot,
`let's investigate how to do it. Like the techniques outlined in the section above,
`detecting a change in the current PCMCIA card requires some undocumented-or
`at least under-docurnented--CE h·icks. Here again, the documentation says that an
`application need only respond to the WM_DEVICECHANGE message in order to be
`notified when a PCMCIA card is inserted or removed. However, there appears to
`be a small problem with this in that other portions of the documentation indicate
`that WM_DEVICECHANGE doesn't even exist onCE!
`
`So what's the truth?
`
`The WM_DEVICECHANGE message does exist, but it's not defined in the wi nuser. h
`header file where all of the other WM_ messages are defined. Instead, the
`WM_DEVICECHANGE message and a number of related constants and structures are
`all defined in a separate header file called dbt. h.
`
`The first step, then, in handling the WM_DEVICECHANGE message is including this
`header file.
`
`The second step is correctly interpreting the wParam value that the
`WM_DEVICECHANGE message passes to your application. From testing, it appears
`that there are only two values that matter under Windows CE:
`
`•
`
`DBT_ DEVICEARRIVAL A card has just been inserted into the slot.
`
`• DBT _DEVICEREMOVECOMPLETE A card has just been removed from the slot.
`
`Page 00347
`
`

`
`322
`
`Chapter 11 • How CE Talks to the Outside World
`
`Code to handle the wParam of the message, then, might look like this:
`
`switch(wParam)
`{
`case DBT_DEVICEARRIVAL:
`//Card was inserted
`break;
`case DBT_DEVICEREMOVECOMPLETE:
`//Card was removed
`break;
`
`The l Par am of WM_DEVICECHANGE is a pointer to a structure that should tell you
`a little bit of information about the device. The trick to using the l Par am is that it
`points to one of several possible structures, depending on what type of card has
`been inserted. In order to determine what type of card and, therefore, which
`structure l Param is pointing to, you must first cast the l Param to a generic struc(cid:173)
`ture and read one of that structure's members.
`
`The generic structure is called DEV_BROADCAST _HEADER and is defined as follows:
`
`struct _DEV_BROADCAST_HEADER
`{
`
`DWORD
`DWORD
`DWORD
`} ;
`
`dbcd_size;
`dbcd_devicetype;
`dbcd_reserved;
`
`In order to determine the type of card that was just inserted or removed, you
`must examine the value of the structure's dbcd_devi cetype member. The possible
`values for dbcd_devi cetype are
`
`•
`•
`•
`
`DBT_DEVTYP _OEM Unspecified OEM type card
`
`DBT _DEVTYP _PORT Serial or parallel port
`
`DBT_DEVTYP _NET Network resource
`
`NOTE
`
`There are other possible values for the dbcd_devi cetype member, but they do
`not appear to have meaning under Windows CE.
`
`Page 00348
`
`

`
`What's in the Box?
`
`323
`
`Once you've tested the dbcd_devi cetype value, you can cast the l Param to a
`more detailed structure specific to the card's type. For some reason, however, it
`appears that CE reports all PCMCIA cards as being DBT _DEVTYP _ PORT cards-in
`other words, it considers all cards to be serial- or parallel-port cards.
`
`The positive side of this is that you only have to worry about casting the l Par am
`to one type of structure; the negative side is that you'll have to work even harder to
`differentiate the cmmn port cards from the other types of cards.
`
`The comm port-specific structure is called DEV _BROADCAST _PORT and is defined
`as follows:
`
`typedef struct DEV_BROADCAST_PORT __ W
`{
`
`DWORD
`DWORD
`DWORD
`wchar_t
`} ;
`
`dbcp_size;
`dbcp_devicetype;
`dbcp_reserved;
`dbcp_name[l];
`
`As you may have guessed, the member of this structure that actually makes it
`possible to determine whether or not the card in question is a comm port is
`dbcp_ name [1]. If the card is a comm port, the name will contain the text "COM "
`followed by a number and a colon. Th

This document is available on Docket Alarm but you must sign up to view it.


Or .

Accessing this document will incur an additional charge of $.

After purchase, you can access this document again without charge.

Accept $ Charge
throbber

Still Working On It

This document is taking longer than usual to download. This can happen if we need to contact the court directly to obtain the document and their servers are running slowly.

Give it another minute or two to complete, and then try the refresh button.

throbber

A few More Minutes ... Still Working

It can take up to 5 minutes for us to download a document if the court servers are running slowly.

Thank you for your continued patience.

This document could not be displayed.

We could not find this document within its docket. Please go back to the docket page and check the link. If that does not work, go back to the docket and refresh it to pull the newest information.

Your account does not support viewing this document.

You need a Paid Account to view this document. Click here to change your account type.

Your account does not support viewing this document.

Set your membership status to view this document.

With a Docket Alarm membership, you'll get a whole lot more, including:

  • Up-to-date information for this case.
  • Email alerts whenever there is an update.
  • Full text search for other cases.
  • Get email alerts whenever a new case matches your search.

Become a Member

One Moment Please

The filing “” is large (MB) and is being downloaded.

Please refresh this page in a few minutes to see if the filing has been downloaded. The filing will also be emailed to you when the download completes.

Your document is on its way!

If you do not receive the document in five minutes, contact support at support@docketalarm.com.

Sealed Document

We are unable to display this document, it may be under a court ordered seal.

If you have proper credentials to access the file, you may proceed directly to the court's system using your government issued username and password.


Access Government Site

We are redirecting you
to a mobile optimized page.





Document Unreadable or Corrupt

Refresh this Document
Go to the Docket

We are unable to display this document.

Refresh this Document
Go to the Docket