Porting Graphical Apps to Windows CE, Part 1
Finding Unsupported Graphics APIs
Up to this point, we've been working with porting user interface elements to Windows CE. We started there because we have a big advantage in that arena: Most user interface elements are defined in the application's resource file, which provides a very handy tool for quickly locating unportable features. As we approach less superficial aspects of an application, the challenges of porting begin to emerge a bit more aggressively.
In the user interface, for the most part, either something is portable or its not. We'll be examining some APIs from here on out that may raise portable issues that are subtler. You have two key jobs. First, you have to find unsupported APIs, and either trim the features they represent, or create a workaround. Next, you'll have to find and modify APIs that support only subsets of the flags and parameters that are defined under Win32. In this installment, we're going to learn a technique for handling the first problem. We'll use the linker to find unsupported APIs embedded in the implementation of your application's behaviors.
The Linker, Your New Best Friend
Graphics is a good place to start porting application behaviors, because you can see how your effort is going. Here's our initial strategy for getting graphics behaviors up on the Windows CE side:
- Choose the subset of graphics behaviors that are essential to the Win CE version of your application.
- Copy the code for the behaviors into a new Win CE project.
- Run a build to identify unsupported APIs
It's possible that the steps above will yield fully functional Windows CE graphics code. If you get a clean compile and link, try running your code on a CE device to test it. Don't be too disappointed if this "clean build" doesn't work properly. Some of the graphics APIs behave differently under CE and many support only subsets of the Win 32 flags used as function parameters.
The more likely result of the steps above is that the linker will report some unresolved externals. Below is fragment of code from a project called Unresolved. This application is an automatically generated "Hello World " program, with the sole addition of the two lines shown in bold and their associated parameter.
Listing 1 - Adding Two Unsupported Graphics APIs to "Hello World"
case WM_PAINT: RECT rt; static POINT ptCurrent; hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt); LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); MoveToEx( hdc, 0, 0, &ptCurrent ); LineTo( hdc, rt.right. rt.bottom ); DrawText(hdc, szHello, _tcslen(szHello), &rt, DT_SINGLELINE | DT_VCENTER | DT_CENTER); EndPaint(hWnd, &ps); break;
When the project is built, this is the text shown in the output window:
Figure 1 - Linker Reporting Unresolved External References
---------Configuration: Unresolved - Win32 (WCE MIPS) Debug----------|
C \Unresolved\Unresolved.cpp(164) : error C2065: 'MoveToEx' : undeclared identifier
C:\Unresolved\Unresolved.cpp(165) : error C2065: 'LineTo' : undeclared identifier
Error executing clmips.exe.
Unresolved.exe - 2 error(s), 0 warning(s)
This is incredibly helpful information to the porting process, but there are a couple of caveats to observe in putting the linker in the harness as your porting workhorse.
First, to get started, generate a Windows CE project and carefully add your own code to it. In particular, you have to pay attention to the system settings for the path to header files. If you use a generated "Hello World" application targeting all of the Windows CE platforms, this is automatic. If you create an empty project and add your files, then getting the proper header and library paths set is much more time consuming and difficult. Remember, all of the different CE targets have processor specific libraries and linkage.
Porting Tip: When porting graphics and other behaviors, start with an automatically generated "Hello World" project and copy your code into the appropriate places in the generated files.
The second thing to be careful of is copying code files from your own projects that include hard coded paths to Win 32 header files. Here is how a hard coded include path looks:
#include "c:\ Unresolved\Unresolved.h"
The quotes around the file name tell the compiler to look in very specific places for the header file Unresolved.h. In this case, the search begins in the "current directory" ( the one where the project workspace lives ) and then proceeds through the directories specified in the INCLUDE environment variable. The compiler will use the first occurrence of Unresolved.h it comes upon in its search. While it is not likely that a file named Unresolved.h will turn up unexpectedly, the header file names for various Windows programming components are exactly the same and occur in a variety of directories. Hard coded Windows API header file names which resolve incorrectly will send the build process wildly awry.
Porting Tip: Search for the string #include in your own code and double check hard coded include file paths. Change or eliminate any that point to Win32 header files. Remember that header files often contain #include statements.
Most all your Windows include files will be specified in statements like the one shown below:
The angle brackets around the include file name mean "look in standard places". The standard places for your Windows CE project are always generated correctly if you start with a "Hello World" application.
Third, the linker report may include unresolved externals that don't actually occur in the source code you wrote. Here's what to do if you get an unresolved external that looks some thing like this:
C:\Unresolved\Unresolved.cpp(165) : error C2065: '_gcvt' : undeclared identifier
I've seen linker errors like this one in areas of my code that had to do with pointer arithmetic. Before you do anything, search your code files and make sure the reference is not just a typo. If the string isn't found in your code files, choose the Project.Settings menu item, and go to the C++ tab. Select the category Listing Files, and under Listing File Type, choose Assembly with Source.
Figure 2 - Getting a Source and Assembly Listing
Press Ok, which will dismiss the dialog, and then rebuild. This build generates the file you need to examine in order to find the unresolved external "_gcvt". The file will have the suffix ".asm". For example, if we did this for the Unresolved project, we'd want to check the file Unresolved.asm.
When you search this file for the unresolved external "_gcvt" you'll find it somewhere in between two lines of your own source code. The one that precedes it is the source line that generated the unresolved external. Experiment with rearranging code or eliminating the statement. I've never had any really extreme difficulty getting past these, but I have found that sometimes it helps not to do too many operations in one statement. For example, if you de-reference a pointer and increment it in one statement, you are doing multiple operations, and the success may depend on rules of precedence. It might be a better approach to break the operations up and have more explicit control of the order in which they happen.
The fourth and final caveat about letting the linker help you with the "rough cut" is that many of the Windows API functions implemented on CE are limited in the parameters and flag values they support. For example, the CE API CreateDIBSection() requires that the last two of its six parameters be set to 0. These parameters have legitimate uses under Win 32, and ported code may well include non-zero values for them. A clean build tells you that no unsupported functions remain in your code, but it doesn't assure proper content in the function parameter lists.
Next time we'll begin a hands-on exploration of graphic functionality on Windows CE, starting with line drawing primitives. We'll learn more about finding APIs that have limited parameter and flags support, and see how to create basic graphical output on CE devices.
About the Author
Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.
In addition to writing for Developer.com, she has written several books including Making Win 32 Applications Mobile. She has also written numerous articles on programming technology for national publications including Dr. Dobbs, BYTE Magazine, Microsoft Systems Journal, PC Magazine; Computer Shopper, Windows Sources and Databased Advisor.
# # #