Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

The Windows CE Emulator

Source Code Accompanies This Article. Download It Now.


Mar99: The Windows CE Emulator

Aspi develops system software for Windows platforms. He can be reached at ahavewala @hotmail.com.


If you're an experienced Windows 95/98/NT developer, you should feel right at home when developing Windows CE applications because the CE API is modeled after the Win32 API. But to say the Windows CE API is a subset of the Win32 API is slightly misleading. Even though the APIs are (for the most part) syntactically the same, the Windows CE API can be substantially different from the Win32 desktop platform.

For instance, one difference between the two environments is that the Windows CE Platform SDK (available at http://www .microsoft.com/windowsce/) includes a functional, albeit incomplete, emulation shell that mimics a Windows CE Handheld PC (HPC) shell. This emulation environment makes it possible for you to jumpstart development by prototyping applications and, in some cases, develop large portions of code that will eventually run on your target hardware.

When designing applications, a question that is bound to pop up is whether you need to use MFC. Using MFC comes with a space penalty -- an issue more important with typical embedded apps than with desktop Windows applications. For instance, if you choose to use MFC, the retail version of the MFC DLL that needs to be included in your build takes up valuable space (the retail version of mfcce20.dll for the x86 platform in Windows CE 2.1 takes up about 287 KB). Whether you use the DLL is a decision you will have to make after determining how much ROM and RAM is available on the target platform. Developing a proposed memory map listing each module you plan to include will simplify such choices.

Still, the object-oriented MFC framework and tools that are part of Visual C++ (like the ClassWizard) can save significant time. If your applications do not require a user interface, MFC's value is questionable. There are some utility classes you can use, but your application will end up lugging a significant amount of code that deals with the GUI in mfcce20.dll and that you won't use at all. MFC does ship with source code, so you can always look to see how Microsoft implemented features and use similar techniques.

When using MFC, you can either link the MFC code statically, or have it accompany your code in the form of a DLL. If you are developing new hardware and creating a custom version of CE, the second option is better. It results in slightly smaller applications, and the complexity of shipping the MFC DLL with your application can be addressed when you specify the files to include in your custom build of Windows CE. Having the MFC DLL in your build will make it available for other applications and services to use.

Creating a Windows CE Application

Assume you've decided to develop an MFC-based application for Windows CE. When you select the File|New menu in Visual Developer Studio, you'll see that the wizard choices available in the Project tab are:

  • WCE MFC ActiveX ControlWizard.
  • WCE MFC AppWizard (dll).
  • WCE MFC AppWizard (exe).

These choices invoke MFC application-generation wizards. After asking you a series of questions, the wizard generates a complete framework for an MFC application you can use. You can create either a DLL or an executable using the appropriate wizards. Apart from the application framework, the wizards also generate a complete build environment for the application. In this article, I'll discuss how to integrate this build into an automated build for the Windows CE Embedded Toolkit (ETK).

On the bottom right side of the dialog box, you'll see a list of checkbox items under the title Platform. Windows CE runs on a variety of hardware processors. To build your application for a particular processor, you use a cross compiler. The Windows CE Toolkit for Visual C++ ships with cross compilers for targets such as x86, SH3, MIPS, and Power PC. The list of checkbox items lets you specify builds that will target each of these platforms. If your application runs on a variety of platforms, you can select each from the list. These compiler-specific builds are called "configurations" in Visual C++ Developer Studio. You can switch platforms by switching configurations and Visual C++ will figure out which cross compiler to invoke. (Actually, when creating your project for the first time, Visual C++ generates a makefile-like project file with a .DSP extension which contains directives on how to build each configuration.) Finally, the linker is called with specific instructions on how to link the executable for a particular platform. This is done by passing the /MACHINE option to the linker with any of the I386, SH3, MIPS, or PPC flags.

The Windows CE Emulator

The Windows CE emulator, on the other hand, runs under Windows NT and emulates an HPC-like desktop complete with a shell and task bar.

You can create a special build of the x86 platform that lets your application run under the Windows CE emulator. Since the CE emulator can run on your x86-based workstation, you use the x86 compiler to compile your code. You then link to Microsoft libraries created specially for emulation. For a Windows CE target, you pass the /subsystem:windowsce parameter to the linker. This tells the linker you are linking code compiled for CE. When linking for emulation, you pass the following flags: /subsystem:windows and /windowsce:emulation. Notice that the subsystem that the linker uses is regular Windows.

For the bulk of its functionality, the emulator translates Windows CE calls into Win32 calls. In fact, for the most part, the applications that run under emulation are Win32 applications with linker modifications that mimic some CE functionality. This is why you tell the linker the subsystem is Windows when building for emulation.

Although it is a clever implementation shortcut, this design limits the extent to which the emulator models an HPC platform. The lesson here is that the emulator is a great prototyping tool, but as soon as you have your hardware available, run your application on it. Set aside some nontrivial time in your schedule for migrating your application to your target hardware. The amount of time you set aside depends on how much functionality remains to be implemented because the emulator cannot support it.

When you implement functionality that is not supported in the emulator, you will break your application under the emulator. To debug your application, you will need to download it to your target hardware. If your target hardware does not support a true flash-file system that lets you refresh a newly built executable in ROM, you will end up bundling your application with the rest of the components to create a full build of CE before downloading it. This can be time consuming, especially when debugging via trial and error. The speed with which you can run your application under emulation, however, is limited only by the speed of your processor and hard disk. In other words, downloading your application to the emulator is near instantaneous.

How can you develop under emulation if you need functionality not supported by emulation? You can't. But, you can debug the user interface and calls that do work under emulation long after you've implemented functionality that breaks under emulation. Preprocessor conditionals are the preferred way to exclude code that you don't want compiled for a certain build. For the emulation build, Visual C++ defines a preprocessor constant called _WIN32_WCE_EMULATION that can be wrapped around code you don't want to compile for emulation:

	#ifndef _WIN32_WCE_EMULATION
	// Do platform specific stuff that 
	// won't work under emulation
	#endif

By sprinkling your source code judiciously with constructs like these, you ensure that your application will be available for ongoing development and debugging on the emulator. (This constant is also used by the MFC code itself. The MFC run-time DLL, mfcce20.dll, is different for emulation than for a regular x86 platform.)

Configuring the Emulator

Microsoft's documentation incorrectly states that, "to run emulation on your desktop PC, you must have Microsoft Visual C++ 5.0 installed." All you really need is the Windows CE Platform SDK. After installing the Platform SDK, you will find the emulator in \Program Files\Windows CE Platform SDK\Wce\Emul.

Although the emulator is targeted for HPC applications by default, there is no reason you cannot prototype your application on it. You should be able to develop the user interface, any operations related to the object store, TCP/IP operations, and registry operations. If you use the socket class for Winsock functionality, you won't be able to prototype that under emulation. The emulator pops up a nonmovable, nonresizable window that looks like the HPC desktop. The default dimensions for the desktop are the standard 480×240 common to early versions of the HPC. You can change these by running the control panel in the emulator (Start|Settings) and starting the Desktop Size applet. This applet lets you tailor the emulator desktop dimensions according to the size of the display you expect your hardware to provide. The desktop dimensions are stored in your workstation registry under the key HKLM\Software\Microsoft\Windows CE Platform SDK\Emulation\HPC\Screen Settings in the values SM_CXSCREEN and SM_CYSCREEN. You can change these settings to make the desktop mimic your hardware form factor. The HPC shell, which provides the task bar and control panel, is not a standard component in the Windows CE ETK. In other words, you will either have to license the shell from Microsoft separately or develop a shell of your own. For many devices, the HPC shell may not be appropriate. If you just have a single application running on your device (which provides the primary user interface), you may want to develop a minimal shell that registers itself as the desktop window and spawns your application.

When you are working with Visual C++, Developer Studio (through the directives in the project file) does the grunt work of copying your application into the emulation object store and running it. Windows CE stores data in RAM in a format called the "object store," which holds all types of application and data files, the system registry, and a small transaction-based database. The emulator maintains an object store in a file called "hpc.obs." Think of this as your emulator's file system. If a file (including your application) must be made available to the emulator, it must be copied into the object store. Normally, since Visual C++ does this for you, you don't need to worry about it. But what if you need to copy over some additional files that are required by your application? The way to do this is to use a program called "empfile.exe" that resides in \Program Files\Windows CE Platform SDK\ Wce\Emul\Hpc\Windows. Unfortunately, you won't know about these options unless you scour the hard disk for interesting applications and invoke them with the "/?" option. Table 1 is a list of options that invoke EMPFILE.

Developing Applications

When developing apps, you'll come across instances of the Win32 API that are not supported in CE. For the most part, the documentation is accurate, but there are exceptions: Named events are an example. The documentation consistently states that they are not supported, even though you can use named events in CE 2.0. If there is a critical piece of the Win32 API that is not supported under Windows CE, it is worth checking out. Look for updated documentation on CE web sites that cater to developers, or ask around in one of the Usenet newsgroups that deal with CE development issues.

On the emulator, Developer Studio supports debugging using its standard debugging interface. The scenario is quite different, however, on your hardware. It's important to understand how debugging will occur on your platform. The usual Windows CE setup uses a parallel and serial port. The parallel port is used to download code via a utility called "Ppsh" (or in CE 2.1, "Cesh"). Ppsh downloads the code to the hardware, then provides a prompt -- a command line into CE running on your hardware. Depending on your hardware platform, you will have to port Ppsh to your target. Microsoft provides the source code for Ppsh in the ETK. When porting Ppsh, read the section titled "Implementing the Parallel Port I/O Code" in the ETK online help.

If the parallel port on your target platform supports bidirectional communications, you can use a modified NULL parallel cable to download code and communicate between your development workstation and target. In this case, you don't have to port Ppsh. Although Redmond Cable (http://www.redcab.com/) sells this modified cable, an alternative is to buy a 25 pin jumper box and, provided you have the equipment, exercise your soldering abilities. Table 2 provides a sample pin out for jumper-box implementations. You can use Ppsh commands to query the status of processes and threads on your system and manipulate zone debugging. If your device has an ethernet interface, you can debug entirely through this interface on CE 2.10.

Zone Debugging

Zone debugging uses macros to categorize the types of traces you want your application to return. For example, you may want to send out certain traces during normal operation. Other traces may be displayed only if you want to see a higher level of detail. Some traces may apply only to validating parameters, others may simply provide information. When debugging, you may want to look at traces pertaining to certain conditions without having to look through all the traces output by your application at run time. You can do just that with debug zones. Listing One presents a typical use of debug zones.

In its first parameter, dpCurSettings holds the name of your application. Then, in an array of strings, it holds the name of each debug zone you are setting up. The zones in Listing One are used to output normal traces, traces with high level of detail, traces for critical conditions, traces related to initialization of your data structures, and any traces related to the setup and use of timers in your code. (The assumption here is that your code uses timers, but it could be just about anything else.)

The final parameter in dpCurSettings holds a bit-masked variable. Each bit that is set in this variable indicates a particular zone that is activated. In other words, traces that belong to this zone will be sent to the debugger. I have activated zones related to normal, critical, and timer-related traces. You may elect to see all traces in your application. Simply set the bit mask accordingly.

Early in the execution of your code, you need to register zones with the debug subsystem of the CE kernel. You do this by calling the macro DEBUGREGISTER (GetModuleHandle(NULL));. Once your zones are registered, you can return traces as in Listing Two.

The first parameter passed to DEBUGMSG is the debug zone that the trace belongs to. In this example, you won't see the first trace from DEBUGMSG, but you will see the second one because I turned off the Initialization zone in the code.

You can also use a macro called RETAILMSG to display traces. This macro works the same way as DEBUGMSG but in the retail build. Recalling the retail build requires WINCEDEBUG to be set to RETAIL. This can be useful when you need to debug those pesky problems that only crop up in the retail build of your software.

MFC also provides macros for traces and assertion conditions. These can be used freely and work the same way as traces generated by DEBUGMSG. However, there is no support for zones. Use of the MFC macro ASSERT is recommended because it lets you instantly debug your point of failure when you select the Retry button on the assertion dialog box.

Once your zones have been registered, you can turn them on/off programmatically. The real value of zones are when you want to turn them on/off interactively while the program is still executing. You do this using the gi and zo commands that are supported by the Ppsh prompt.

The gi command lists all modules, processes, and threads running on your system. The index of each module is what you will need to use when manipulating zones interactively. The zo command lets you turn zones on/off. You need to know to which bit each zone corresponds. In the example discussed here, ZONE_NORMAL corresponds to bit 0, ZONE_DETAILED to bit 1, and so on.

Say you want to turn on a detailed trace, but want to stop looking at trace messages related to timers. First, at the Ppsh prompt, type gi and you will get output similar to Listing Three. The sample application is listed as a process with ID 6. If you wrote a DLL for use with your application (say, Sample.dll), it would show up as a module with ID 1. Armed with this information, you can turn on detailed traces and turn off timer-related traces in your application by using the zo command with the options: zo p 6 on 1, off 4. This command can be translated as such: For the process with index 5 in the system, turn on bit 1, and turn off bit 4. Recall that the active zones are specified using a bit-masked variable. Bits that are turned off correspond to zones that are deactivated. Similarly, bits that are turned on correspond to zones that are activated. In the example, I activated ZONE _DETAILED and deactivated ZONE _TIMERS.

If you wanted to deactivate all traces coming from your sample DLL, you would use zo like this: zo m 1 0. This interactive zone manipulation is a powerful capability and does not require you to recompile modules to select the traces you want to see. Selectively enabling and disabling traces lets you filter the information that comes through to the debugger.

Integrating Your App Into the ETK Build

To integrate your application into an ETK build, you need to first add your application and related files to the binary ROM image for your hardware, then add your application to the build process so that it is built correctly each time with the rest of the ETK components.

To add your application to the list of files that get built into the binary ROM image, you will need to edit the file \WinCE\Public\DDJSampleProject\Files\Project.bib. This Binary Image Builder (BIB) file requires entries similar to Listing Four.

The file Project.bib specifies which modules and files get added to the binary image for your project. There is a corresponding file, called "Platform.bib," which resides under the \WinCE\Platform tree. This file specifies which files are added to the binary image for the platform.

The conditionals are based on environment variables you set prior to invoking the CE build. These conditionals are used to create a modular build, where certain components can be left out, if desired. Although your project may always include the application you just developed, these conditionals are used as an example. Besides, it is sometimes helpful when debugging your hardware to leave certain components out. There may also be a case for cutting down on download times for codevelopers who are not using your component and thus don't need it in the binary image.

The DDJ_COMMCTRL environment variable is used to indicate whether the common controls DLL (CommCtrl.dll) is added to the binary image. If you are using the Windows CE common controls (such as the tree view, list view, tabbed dialog boxes, and so on), you will need to add this DLL. Similarly, DDJ_APP, DDJ_MFC, and DDJ_OLE are used to conditionally add your application. You add the MFC DLL and OLE DLL only if you are using them in your application.

What about nested conditionals within the DDJ_MFC and DDJ_OLE conditionals that appear in the file? The MFC and OLE DLLs have different names for retail and debug versions. For example, the retail version of the MFC DLL is named Mfcce20.dll, while the debug version picks up an extra "d" in its name (for "debug"), mfcce20d.dll. The same applies to the OLE DLL Olece20.dll. The BIB file will need to know which DLL to bundle into the ROM image, depending on whether you are creating a debug or retail build. The Windows CE build process uses a variable called WINCEDEBUG to determine whether a debug or retail build is being created. WINCEDEBUG is set to either DEBUG or RETAIL to signal a debug or retail build, respectively. When checking for the value of an environment variable inside a BIB file, you need to use an underscore in front of the variable name. This is an undocumented quirk of the BIB file preprocessor (Makeimage.exe) that can trip you up. I created a variable called _DDJDEBUG, which I set according to the value of WINCEDEBUG. This variable is then used in the BIB file to switch between the debug and retail versions of the DLL.

When testing your BIB file changes, check the output of the ROM image builder, Romimage.exe. Newly added files should appear in the list of files that Romimage spits out when creating a binary image. This check can save you headaches later. To understand why, you need to know about a feature built into the parallel-port download utility (Cesh.exe). After the download is finished, your binary image will attempt to establish a connection with the debugger (Windbg.exe) running on your workstation over the serial, parallel, or ethernet port (assuming you have created a debug build of the binary image). After a connection has been established, CE will load the modules required to run with the operating system. When doing this, if it cannot find a module in the binary image, the debug build of the Windows CE loader will ask Cesh to load the module for it. If your module's file exists in your \WinCE\Release directory, Cesh will comply with the CE loader's request and download the file dynamically. You will see a degradation in boot speed and the kernel will send a debug trace to WinDbg telling it that it has used Cesh to load a module. However, it is easy to overlook the degradation in boot time, especially if your module is fairly compact, and it is equally easy to lose the corresponding trace among the several hundred traces that get output to WinDbg when a debug build is being executed on the target platform.

The feature is neat, but it creates a problem. When you successfully build a module, it will be copied to the \WinCE\Release directory either by the Build command when the environment variable WINCEREL is set to 1, or by the utility Buildrel.bat. If, for some reason, you forget to add your files to the BIB file (or worse, your additions to Project.bib are not working correctly), you may not notice it. When your module is not found in the binary image, the Windows CE loader will use Cesh as its silent accomplice and load your module. You won't realize what's going on until much later, when you finally flash a retail image into ROM and there is no Cesh around to rescue a failure to load a module.

Adding Your App to the Build Process

Most modules that are added to the ETK build process are compiled with the Build utility. To use Build, create a SOURCES file that lists all your source files along with other information that Build uses to compile and link your module. After performing some housekeeping, Build calls Nmake with a file, called "Makefile.def," as its argument that builds your code. Makefile.def contains directives on how to build ETK modules. However, Makefile.def does not support MFC applications. It doesn't know where to find MFC header files and libraries. Moreover, the ETK Cefilter utility (which prunes header files according to the components you have selected in your CE system) doesn't know how to deal with MFC-related definitions. So, if your application uses MFC, you're better off doing something else, at least until Microsoft adds MFC support to the ETK build process.

The most straightforward way to integrate your MFC-based application into the ETK build process is to bypass Build entirely. Visual C++ Developer Studio has a menu option that lets you generate a makefile from project files. You may recall that project files are files with a .DSP extension and are used by Developer Studio for information on how to build the program under development. This exported makefile can then be plugged into your ETK build process.

There are some gotchas when exporting makefiles using the Project|Export Makefile...menu. When compiling and linking your code, Developer Studio knows where to find your header files and libraries. When you export your makefile and run it from the command line, you're on your own. You will have to set up include paths for header files and libraries in your target before you export your makefile. You can do this by using MSDEVDIR, the environment variable defined by the Visual C++ installation process. A typical include path for an x86-related build is:

$(MSDEVDIR)\..\Wce\Include\Wce200,
$(MSDEVDIR)\..\Wce\Mfc\Include\Wce200.

MSDEVDIR points to the \Program Files\DevStudio\ShareIDE directory. Visual C++ files for Windows CE are installed in \Program Files\DevStudio\Wce, hence you need to step back from the directory defined by MSDEVDIR to find your files. Of course, you could choose to install Developer Studio and the Windows CE Toolkit for Visual C++ in completely different directories. The path specified earlier would then no longer be valid. In such cases, you will have to define your own environment variables that point to your installation of the Windows CE Toolkit for Visual C++ and use these instead of MSDEVDIR.

To specify this path, you need to select the menu option Project|Settings...and then select the C/C++ tab. Select Preprocessor in the Category listbox. In the Additional Include directories, enter the path listed above. This same path needs to be entered in the Additional Resource Include directories Edit box under the Resources tab. Finally, set up your library path so that the linker knows where to find the libraries it needs to link to. Select the Link tab and select Input in the Category list-box. In the Additional Library Path Edit box enter the path:

$(MSDEVDIR)\..\Wce\Lib\Wce200\Wcex86, 
$(MSDEVDIR)\..\Wce\Mfc\Lib\Wce200\Wcex86

Before you enter all this information, select Multiple Configurations in the Settings listbox on the top left corner of the dialog. This lets you enter paths for multiple configurations. You should check off all configurations except those related to emulation -- Win32 (WCE x86em) Debug and Win32 (WCE x86em) Release. You will continue to build for emulation from Developer Studio and thus don't need the paths for that configuration.

When you export the makefile, the paths will be added to your C/C++ compiler, resource compiler, and linker flags. Invoking the makefile from the command line will now work correctly.

Finally, the Always Download option under the Build menu in Developer Studio must be toggled off before exporting makefiles. When this option is toggled on, the exported makefile will contain directives that will attempt to download your executable to a target platform. Of course, you don't want this happening in the middle of the build. Besides, the executable will follow a different route on the way to your hardware platform.

For information on adding the makefile to the ETK build process, see my article, "The Windows CE Build Process," DDJ, August 1998.

DDJ

Listing One

// Set up some debug zones#define ZONE_NORMAL             DEBUGZONE(0)
#define ZONE_DETAILED           DEBUGZONE(1)
#define ZONE_CRITICAL           DEBUGZONE(2)
#define ZONE_INITIALIZATION     DEBUGZONE(3)
#define ZONE_TIMERS             DEBUGZONE(4)


</p>
DBGPARAM dpCurSettings = { L"SampleApp", 
{ 
L"Normal",L"Detailed",L"Critical",L"Initialization",L"Timers",L"Undefined",
   L"Undefined",L"Undefined",L"Undefined",L"Undefined",L"Undefined",
   L"Undefined",L"Undefined",L"Undefined",L"Undefined",L"Undefined" 
}, 
ZONE_NORMAL | ZONE_CRITICAL | ZONE_TIMERS };


</p>

Back to Article

Listing Two

DEBUGMSG (ZONE_INITIALIZATION, (TEXT("About to display initial dialog.")));If (bFailedToInitialize)
{
    DEBUGMSG (ZONE_CRITICAL, (TEXT("Initialization failed. 
                                                   Aborting application")));
    return 1;
}


</p>

Back to Article

Listing Three

PROC: Name            hProcess: CurAKY :dwVMBase:CurZoneTHRD: State :hCurThrd:hCurProc: CurAKY :Cp:Bp
 . . . (stuff deleted)
P06: SampleApp.exe    209ed446 00000040 0e000000 00000015
 T    Sleepg 207db676 209ed446 00000041  3  3
 T    Blockd 209ed466 209f54e2 00000051  3  3
 . . . (stuff deleted)
MOD: Name            pModule :dwInUSE :dwVMBase:CurZone
M01: Sample.dll      808df638 00000015 01ae0000 00000015
 . . . (stuff deleted)


</p>

Back to Article

Listing Four

MODULES;  Name            Path                                           Memory Type
;  --------------  ---------------------------------------------  -----------
IF DDJ_COMMCTRL
   commctrl.dll    $(_FLATRELEASEDIR)\commctrl.dll                NK  SH
ENDIF


</p>
FILES
;  Name            Path                                           Memory Type
;  --------------  ---------------------------------------------  -----------
IF DDJ_SAMPLEAPP
   SampleApp.exe   $(_FLATRELEASEDIR)\SampleApp.exe               NK
   SampleDll.dll   $(_FLATRELEASEDIR)\SampleDll.dll               NK
ENDIF
IF DDJ_MFC
IF _DDJDEBUG=DEBUG
   mfcce20d.dll    $(_FLATRELEASEDIR)\mfcce20d.dll                NK  S
ENDIF
IF _DDJDEBUG=RETAIL
   mfcce20.dll     $(_FLATRELEASEDIR)\mfcce20.dll                 NK  S
ENDIF
ENDIF
IF DDJ_OLE
IF _DDJDEBUG=DEBUG
   olece20d.dll    $(_FLATRELEASEDIR)\olece20d.dll                NK  S
ENDIF
IF _DDJDEBUG=RETAIL
   olece20.dll     $(_FLATRELEASEDIR)\olece20.dll                 NK  S
ENDIF
ENDIF

Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.