April 1999 |
by Jose Mojica
Reprinted with permission from Visual Basic Programmer's Journal, Apr 1999, Volume 9, Issue 4, Copyright 1999, Fawcette Technical Publications, Palo Alto, CA, USA. To subscribe, call 1-800-848-5523, 650-833-7100, visit www.vbpj.com, or visit The Development Exchange at www.devx.com.
Windows CE is Microsoft's operating system for handheld devices (H/PCs), palm-sized devices (P/PCs), automobile PCs (AutoPCs), and Internet terminals (WebTV). Windows CE comes with Pocket Office, which comprises smaller versions of the office programs, and Windows CE Services, which detects when the H/PC is connected to a desktop computer and automatically notifies components with the appropriate interfaces so they can synchronize correctly.
In this article, you'll get an overview of Windows CE development using VBCE and ADOCE. Then you'll create a small inventory-tracking companion program and write a desktop program that imports the data into an Access database. To try this example, you need VBCE 1.0 or later. You don't need a handheld device; the toolkit provides an emulation program where you can try the example.
The VBCE Toolkit The VBCE toolkit does not produce executables; instead, it creates Pocket Visual Basic (PVB) files, a compacted Unicode representation of the source. The toolkit includes an executable called PVB.exe that, along with supporting DLLs, interprets the PVB file at run time. The executable and supporting DLLs are available for each of the different processors in H/PCs such as the Hitachi Sb processor and the MIPS 3900 and 4100 series (at press time). There is also a PVB.exe file that runs on the emulator. You can let the user think the PVB file is an executable by associating the PVB extension of the pseudo- compiled source files with the PVB.exe program. Although the PVB files are not fully compiled, this architecture has advantages. The first: PVB files are extremely smallabout 15K for a large VBCE program. This is crucial in Windows CE programming because of the H/PC's limited memory. The newer H/PCs only have about 16 MB of memory, and no storage devices such as hard drives. Instead, the device uses half of its RAM for storage (remember RAM drives?) and half of its memory for running applications. The PVB architecture's second advantage is that PVB files are processor independent. Windows CE programs need to be compiled for each of the available processors, but only the PVB.exe file needs to be processor dependent because of the VBCE architecture. The same PVB file will run in each processor once PVB.exe is installed on the different machines. The third advantage: A number of the vendors are putting the PVB.exe file and accompanying DLLs into their ROM chips. This means VBCE developers can run their applications simply by copying the PVB file to the device and executing it. The VBCE toolkit also includes tools that ease Windows CE development, such as Remote Heap Walker, Remote Process Viewer, Remote Registry Editor, Remote Spy, and Remote Zoom. These tools are called Remote because they run on the desktop machine but perform their task on the handheld device through a serial connection. For example, Remote Spy reports the Windows messages taking place on a window running on the handheld device. In addition to the remote development tools, you can also take advantage of the Application Install Wizard (a Setup Wizard that produces setup programs for Windows CE), Control Manager (which helps the developer transfer and register ActiveX controls in the devices), and a new set of help files.
Differences Between VBCE and VB Another difference is that you can't use the standard VB debugger when creating VBCE apps; you use the toolkit's external debugger instead. When you run a VBCE program from the VB IDE (with debug information), VBCE runs the external debugger and loads your source code. It also launches the program on the device or emulator, depending on a project setting (more on that later). You can't place breakpoints while in the standard IDE, but you can while in the external debugger. The program runs on the remote device until it comes to the line with a breakpoint. Then execution halts, and the external debugger shows the line of code with the breakpoint. You can't modify code while in the external debugger and you can't enter commands in the Immediate window, but you can examine variable values and single-step through commands. With VBCE development, you'll also face limitations that arise from one of three sources: VB components, ActiveX controls, and language syntax (see Table 1). You cannot use classes, UserControls, or UserDocuments, and you can't have more than one module per application. Instead of making API calls through Declares, you must wrap them into ActiveX controls. Current third-party ActiveX controls don't work with Windows CE. Vendors need to provide a special version of each control compiled for Windows CE, a version for each processor that supports Windows CE, a version that runs on the emulator, and a desktop version. The desktop version does not need to do any real work; it is available so you can drop it onto a form at design time. Windows CE supports ATL and MFC controls. Just as some devices include the VBCE runtimes in ROM, some also include the MFC runtimes. I have not seen a machine that includes atlce.dll, however. Because VBCE's language is VBScript, as opposed to VB or VBA, all variables must be Variants. Also, only late binding is supportedthis means ActiveX components for Windows CE need to support the IDispatch interface. Aside from the standard VBCE components, your program needs database support. Because VB's data control doesn't work in VBCE (see Table 1), Microsoft has provided a version of ADO that runs on Windows CE. Just like VBCE, ADOCE is a scaled-down version of its counterpart. Let's examine some of the differences between ADOCE and ADO.
Differences Between ADOCE and ADO Each table is part of the system's object store and is assigned a unique ID number. You can reference the tables either by ID or by name. The tables support up to four indexes. You can't run queries or join tables. One peculiar difference between VBCE tables and other database tables: Each record in a VBCE table can have a different number of fields. Fields can be integer, unsigned integer, long, unsigned long, string, filetime, and BLOB types. Fields, like tables, have a unique ID number based on a programmer-assigned number and the field type, but they do not have names. You can search fields from the beginning or the end, and you can search for an exact value or a value less than or greater than the value. A great advantage of having built-in support for database files is that most applications use the same format for storing their data. This means you can read the contacts database generated by Pocket Outlook, for example. Even though Windows CE offers this database support natively, you can't use it through VBCE directly. I mentioned earlier that VBCE doesn't let you make API calls. (This feature will be available in the next release of the toolkit.) Making API calls involves the use of structures, and structures are not supported in VBCE. The only way to reach these APIs is through an ActiveX control. ADOCE is a wrapper for these database APIs. You can create recordset objects, run queries, and reference fields by name with ADOCE, which adds this functionality by creating system tables that store field names for each field ID. The only limitation with this approach: You can't create recordsets from databases that were not created with ADOCE. ADOCE has a lot of limitations over standard ADO (see Table 2). (With the code for this article, I have included an ATL control for Windows CE that enables you to access any database in the system. Download the control, called ByteSized DB, here.)
ByteInventory Tracker
Start Visual Basic 5.0. You have a new project type if you installed the VBCE toolkit: Windows CE Application. Double-clicking on this icon brings up the Project Properties dialog (see Figure 2).
The Local Path field asks for the location where VBCE will store the output file when it compiles the program for debugging purposes. The Remote Path is the path in the emulator or H/PC where the program will be copied after it is compiled. For example, you can specify the Storage Card directory created when you inserted the Flash Memory card, a device used by most H/PCs for storing files. The only other field of interest is the Run On Target field. This option frame lets you select whether you want to run the program on the emulator or on the H/PC. Enter ByteInventory for the project name, leave the size fields at their default values, change the local and remote path fields to match the new project name, and make sure the emulator is selected. Then click on OK.
Take care of aesthetics before you get into database programming. Because there are two possible screen sizes for H/PCs, make sure the form is sized to fit the screen and the controls inside it are resized proportionally. To do that, create a generic function called ResizeForm and put it in a module. Add a module to your project, name it modGeneral, and add code (see Listing 1). The code calculates what the form's rate of increase will be after resizing, then resizes the form and changes the size of each control in the form's control collection by the same ratio. It might seem strange that the code loops through the control array until it gets an error, but this is necessary because VBCE does not support the Count property in the control's collection. So, you can't loop for a predefined number of times. Also note that the code's Dim statements don't have a type declaration. This isn't poor programming practiceVBCE doesn't let you specify a data type for variables. All variables must be Variants. Keep in mind that Variants are Variants until they are assigned a value, after which they take on the data type of the value. If a function in an ActiveX control expects a number, your Variant needs to contain a number. The best way to keep track of this in a VBCE program is to name the variables according to the value they are going to hold. You can modify the form's Form_Load event to call the ResizeForm routine once the code is in place:
Notice that the function call sends frmInv as the parameter instead of Me. As you might guess, Me is also not supported. The inventory form, frmInv, opens the program's database table, truckinv, and populates a grid with the table's records. The trackinv table has three fields: ItemID (String - 15), Description (String - 50), and Quantity (Numeric - Integer). Under our example, the truckinv table can only be created by the desktop application when the driver uses the program for the first time. Add this declaration in the general section of the form:
rs will hold the ADOCE recordset (again, this variable is of type Variant). Add code to frmInv to create a recordset with all inventory records (see Listing 2). The LoadRecords routine first creates the recordset object by calling the CreateObject method, then checks for errors after each call. VBCE doesn't support On Error GoTo, but instead supports On Error Resume Next and On Error GoTo 0. Next, open the recordset. The second parameter is left blank because there are no connections in ADOCE. The third and fourth parameters indicate you are requesting a read-only Keyset cursor. Populate the grid after the recordset is built (see Listing 3). Place this code in the module modGeneral. The CE grid control operates like the MSFlexGrid control. You can set the number of rows and columns, then go through each cell and set the text. The grid control functions like an array in that it retains its values even while the grid scrolls. Navigating through the recordset in ADOCE is similar to navigating through the recordset in the desktop version. The code loops through the records until EOF is true, and puts the value of each field in the grid's cells. With this code in place, you can now complete your Form_Load event:
All the program needs to do now is subtract or add inventory items. I have created an UpdateQuantity function whose job is to update a single record in the truckinv table. It creates a recordset object like the code in LoadRecords, except that it opens it in optimistic mode (in ADOCE, this means the object is updatable). This code shows a segment of this routine:
The complete routine is available here. The routine updates the recordset after it is created:
You can call this routine accordingly in the Use and Add buttons:
Inventory Transfer Program Both calls have the same parameters. The first parameter is the location of the desktop database file, which can be an Access database or an ODBC Connection string. The second parameter is a list of tables that will be updated. The list is period separatedone period to separate table names and two periods to indicate the end of the list. The third parameter, not yet supported, could be used in a later release to specify whether you wish to do synchronization. In the current version, the APIs transfer the entire tables. The fourth parameter indicates whether you wish to overwrite the table if it is already present, or append records to the table. The last parameter is also for a future release; leave it blank. Create a new Standard EXE project. Rename the project's form (Form1) frmTransfer. Add two buttons to the form: one to transfer data to the device, "Start" (cmdStart); and one to transfer data back to the desktop, "Finish" (cmdFinish). Then, declare the API functions in the form and enter the code from Listing 5. If the table is not present in the device the API creates it first. The cmdFinish event transfers the data back to the desktop.
You now have a small inventory tracking program for Windows CE and a desktop program to transfer data to and from it. To turn it into a full-fledged program, you might want to transfer data to a temporary table, then use your own program to synchronize from this temporary table to your own table.
|