Chapter 2: Developing Phase: Process Milestones and Technology Considerations

This chapter introduces the MSF Developing Phase and helps you prepare for it. It also discusses using Microsoft® Visual Studio® .NET 2003 and the Platform software development kit (SDK) for the Developing Phase. The chapter includes a section about how to make your code compliant with both 32-bit and 64-bit architectures. With this knowledge, you can identify the application development environments, configure the environment, and decide the development and testing methodology to use for the Win32/Win64 applications.

On This Page

Goals for the Developing Phase Goals for the Developing Phase
Starting the Development Cycle Starting the Development Cycle
Building a Proof of Concept Building a Proof of Concept
Developing the Solution Components Developing the Solution Components
Developing the Testing Tools and Test Cases Developing the Testing Tools and Test Cases
Building the Solution Building the Solution

Goals for the Developing Phase

The primary goal during the Developing Phase is to build the solution components—the code migration and the documentation. Typically, the migration involves modifying the existing code in a way that enables the application to work in the Windows environment using the Windows API. In this context, both the modification of existing code and the development of new code are considered to be migration activities. Although the development work is the focus of this phase, all team roles are active in building and testing the deliverables. Also, some development work may continue into the Stabilizing Phase in response to test results.

This phase formally ends with the Scope Complete Milestone. Your team achieves this major milestone by getting a formal approval from the sponsors and key stakeholders. The sponsors and key stakeholders must approve that all solution elements are built and that the solution features and functionality are complete in accordance with the functional specifications developed during the Planning Phase.

Major Tasks and Deliverables

The tasks and deliverables for the Developing Phase are listed in Table 2.1, along with the owners for the task.

Table 2.1. Major Tasks and Deliverables

Major Tasks and Deliverables

Owners

Starting the development cycle

The team begins the development cycle by verifying that all tasks identified during the Envisioning and Planning Phases have been completed.

Development team

Building a proof of concept

Before development starts, the team performs a final verification of the concepts from the designs within an environment that mirrors production as closely as possible.

Development team

Developing the solution components

The team develops the solution using the core components and extends them to the specific needs of the solution. The team also develops and conducts unit functional tests to ensure that individual features perform according to the specifications.

Development team and Test team

Developing the testing tools and test cases

The team develops the testing infrastructure and populates it with test cases. This ensures that the entire solution performs according to specifications. This solution test suite typically incorporates, as a subset, the individual feature tests used by developers to build the solution components.

Test team

Building the solution

A series of daily or frequent builds culminate with major internal builds and identification of points at which the development team will deliver key features of the solution. These builds are subjected to all or part of the entire project test suite to verify the percentage of completion and are used as a way of tracking the overall progress of the solution and the solution test suite.

Development team and Test team

Closing the Developing Phase

The team completes all features, delivers the code and documentation, and considers the solution complete, thus initiating the approval process for the Scope Complete Milestone.

Project team

Note   Refer to the UNIX Migration Project Guide (UMPG) for an overview of MSF, general information on the processes that belong to each phase, and additional information about the team roles responsible for the processes. The UMPG is meant to be used in conjunction with the technical and solution-specific information in this guide.

Starting the Development Cycle

During the Developing Phase, every component of the solution is analyzed in terms of how to apply code changes to adapt to the Microsoft Windows® environment. This section mainly focuses on identifying and addressing the risks in the Developing Phase and using a mitigation plan to address these risks.

The Developing Phase of any UNIX migration project can be the most challenging part of the project. Major issues become evident at the beginning of this phase. For example, your team may realize that the Windows API does not have an equivalent of a particular UNIX function, which the code is using. This can be categorized as a risk because a replacement code might need to be written at this stage. Resolving such issues will be the distinguishing factor in determining whether schedules will change, whether the funding is sufficient and, ultimately, whether the project will be successful.

If any item on the task lists of the Envisioning and Planning Phases is not completely satisfied, it could present a risk during the Developing Phase.

The following actions might mitigate these risks:

  • Procure the required software licenses for Visual Studio .NET 2003 before starting the project.

  • Prepare a requirements specification document, which details the scope of the migration project and the design and architecture that must be followed.

  • Perform an impact analysis of the changes and obtain sign-off from the customer on the requested changes. Impact analysis plays a very important function during a migration project. The output from this activity helps the developer in determining the scope of the changes, identifying further activities required to fix the problem, and building and testing the changes due to the migration.

    An impact analysis also helps in identifying the boundaries of migration, identifying the affected elements, and understanding the configuration system for all sources in the application, changes in business process, and types of change with respect to technology changes.

  • Establish the existence of compatible versions of required third-party libraries on Windows and procure licenses for the required third-party libraries.

Implementing these actions is easier if the risks are identified and mitigation plans are formulated and evaluated well ahead of time. Risk mitigation, as part of the risk management process, can be used to keep a project on track through adverse situations.

Note   Additional information about risk mitigation is available at
https://www.microsoft.com/office/solutions/accelerators/sixsigma/default.mspx.

Building a Proof of Concept

Typically, the proof of concept is a continuation of the initial development work (the preliminary proof of concept) that occurred during the Planning Phase. The proof of concept includes developing and testing some key elements of the solution in a nonproduction simulation of the proposed operational environment. The team guides the operations staff and users through the solution to validate their requirements. In addition, during such a review/concept process, the developers may discover design flaws or bugs in the original application being ported that need to be addressed. The proof of concept serves as a "dry run" that tests the worthiness and the ease of the migration process. Pilot migrations help to assess any complications that might occur in the actual migration and also help build confidence in the migration process.

There may be some solution code or documentation that carries through to the eventual solution deliverables. However, the proof of concept is not meant to be production-ready. The proof of concept is considered as throwaway development that gives the team a final chance to verify functional specification content and to address any additional issues before moving into development.

Proof of Concept Complete

The Proof of Concept Complete interim milestone marks the completion of building the proof of concept for the key elements of the solution. The risks associated with the key elements of the solution are identified in this phase, which helps you implement risk management.

Reaching this interim milestone marks the point where the team moves from conceptually validating to building the solution architecture and components.

Developing the Solution Components

The Developing Phase is when the actual solution is built. The individual components are coded and tested to satisfy the project requirements in the Windows environment. Because differences exist between UNIX and Windows, the UNIX code must be modified to work in the Windows environment.

Subsequent chapters of this volume address the potential coding differences related to the following categories:

  • Process management

  • Thread management

  • Memory management

  • File management

  • Infrastructure services

    • Security

    • Handles

    • Exception handling

    • Signals versus events

    • Interprocess communication

    • Networking

  • Migrating Graphical User Interface

  • Shells and scripting

  • Daemons versus services

  • Middleware

  • Component-based development in Windows

For each of these categories, chapters 3, 4, 5, and 6 of this volume:

  • Describe the coding differences.

  • Outline options for converting the code.

  • Illustrate the options with source code examples.

You can then choose the solution that is most appropriate to your application and use these instructions as the basis for constructing your Windows code. This guide gives you sufficient information to choose the best method of converting the code. After you have made your choice, you can refer to the MSDN or Microsoft Windows API documentation to ensure that you understand the details of the Microsoft Windows API functions.

Using the Development Environment

The development environment is the environment in which the user develops and builds the solution. The development environment provides the necessary compiler, linker, libraries, and reference objects. In some cases the integrated development environment (IDE) is also provided.

The setting up of the development environment was discussed in general in Chapter 4, “Planning: Setting Up the Development and Test Environments” of Volume 1: Plan of this guide. In this volume, application development focuses on Microsoft® Win32®/Win64. The application may be one of the following:

  • A 32-bit application to run on a 32-bit architecture.

  • A 64-bit application to run on a 64-bit architecture.

  • A 64-bit–ready application that will be executed on a 32-bit architecture and which can be directly ported to the 64-bit architecture just by recompiling.

These three options suggest three alternate ways of deploying the application, but all three have the same code base. The first two are 32-bit and 64-bit applications running on respective architectures. A 64-bit–ready application can be run on both 32-bit and 64-bit computers. The third alternative suggests keeping the application 64-bit–ready on a 32-bit computer. This can be accomplished by using the 64-bit compiler on the 32-bit computer to compile and remove warnings that may come up when the same application is compiled on a 64-bit computer and thereby produce a clean code.

Tools required for developing the solution using the Windows API are:

  • Latest Platform SDK.

  • Visual Studio .NET 2003.

In Visual Studio .NET 2003, the native 64-bit IDE is still under development, hence no IDE exists for development of 64-bit applications. Therefore, Visual Studio .NET 2003 can be used in conjunction with the compiler for 64-bit applications present in the Platform SDK. The latest Platform SDK includes the compiler, the linker, and other tools for 64-bit development.

The SDK also includes the C-Runtime (CRT) library, the Microsoft Foundation Classes (MFC), and the Active Template Library (ATL) versions for 64-bit production.

Platform SDK

The latest Platform SDK contains tools to build, debug, test, and deliver applications. It also contains all the definitions (include files) and libraries needed to compile programs. In general, SDK tools are run from the command line, just as applications are run from a shell prompt in UNIX. Output from SDK tools can be files created on the disk, text output to the console (such as stdout in UNIX), or graphical output to one or more dialog boxes. The Platform SDK also contains definitions and documentation for the Microsoft .NET enterprise servers, including Microsoft BizTalk™ Server, Microsoft Commerce Server, and Microsoft SQL Server™.

The Platform SDK is available on CD or as a free download on the Web. It is also available with the Microsoft Visual C++® development system and Visual Studio .NET 2003, or with an MSDN® Professional or Universal subscription.

Note You can order or download the SDK at https://www.microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en.

Using the Platform SDK

The Visual Studio .NET 2003 environment can be used as the development environment for development using the Platform SDK. It contains a series of compiler options that can be used to verify or generate various levels of information, as described in the following table.

Table 2.2. Useful Compiler Options

Debug Information Options

Use

/Zd

Produces an .obj file or executable file containing only global and external symbols and line-number information, but no symbolic debugging information.

/Z7

Produces an .obj file and an .exe file containing line numbers and full symbolic debugging information for use with the debugger.

/Zi

Creates a program debug database (.pdb) file. The debugger uses this file to step through the source during program execution.

/ZI

Similar to /Zi. However, the .pdb file also supports edit and continue.

Additional Debug Information Options

Use

/Yd

Places debug information in the .obj files.

/Yl symbol

Places arbitrary symbol in the object module.

/Yu , /Yu filename

Specifies using a precompiled header (.pch) file during builds.

/YX , /YX filename

Instructs the compiler to use a precompiled header file (.pch) if one exists or to create one if none exists.

Runtime Check Options

Use

/RTCc

Reports run-time truncation error.

/RTCs

Enables run-time stack checking, includes overrun and under-run of local variables, and enables verification of the stack register.

/RTCu

Enables reporting of variables used without initialization.

/RTC1

Combination of RTCs and RTCu.

To set these compiler options in the Visual Studio .NET 2003 development environment

  1. On the Project menu, click Properties to open the project's Property Pages dialog box.

  2. Click the C/C++ folder of Configuration Properties.

  3. Click General Property.

  4. Modify the Debug Information Format property to set the Debug Information options /Zd, /Z7, /Zi, and /ZI options.

  5. Type the compiler option in the Additional Options box of Command Line property to specify /Yd and /Yl.

  6. Click the Precompiled Headers property page.

  7. Create/Use Precompiled Header property to specify /Yu and /YX options.

  8. Click the Code Generation property page.

  9. Modify the properties Basic Runtime Checks or Smaller Type Check to specify the Runtime check options.

Note Additional information on compiler options is available at https://msdn2.microsoft.com/en-us/library/9s7c9wdw(en-US,VS.80).aspx.

Any program written in languages that support the creation of the .pdb files can be used in the Windows debug environment. Thus you can debug an application source file where calls are made using C, C++, and Fortran languages.

You can use the Platform SDK WinDBG tool to open a source file and then run the corresponding debug version of an executable file. WinDBG searches for debug symbol information for the modules that the executable file uses in the symbol image path.

To use WinDBG to debug an executable file

  1. From the graphical user interface (GUI), open the source file corresponding to the executable file that you want to debug.

  2. Set the path for the symbols needed to debug the executable file.

  3. Set desired breakpoints in the source file.

  4. Open the executable file.

Other options for using WinDBG include:

  • Attach to a running process.

  • Open a crash dump file.

  • Use a remote debug session to connect to another system.

The command debugger (CDB) can also be used to debug programs. CDB is especially useful for debugging a running program or for debugging remotely. CDB can also analyze crash dumps by using symbols. For example, the following code uses CDB to analyze a crash dump file:

cdb –y <Symbol Path> -i <Image Path> -z <Dumpfile>

Visual Studio .NET 2003

During the migration of an application from UNIX to Windows, one goal is to take advantage of the Visual Studio development tools. However, it can be a large manual task to manage the creation and administration of Visual Studio projects. This is especially true for large projects with hundreds or thousands of sources. But after the project has been created and the environment is set, the whole application becomes easily manageable.

The Microsoft Visual Studio .NET 2003 IDE can be used to build 64-bit applications while you maintain the same code base for both 32-bit and 64-bit development. You can achieve this by configuring two different 32-bit and 64-bit build environments and compiling the same code base in both environments.

To support developer productivity, Visual Studio .NET 2003 provides debugging and automation facilities, which are particularly useful for repetitive tasks.

Using Visual Studio .NET 2003

The following sections discuss configuring the development environment using the Visual Studio .NET 2003 IDE.

Setting the 64-Bit Build Environment

The process for configuring Visual Studio .NET 2003 to work with the 64-bit Windows is conceptually similar to that for reconfiguring Visual Studio 6, although some of the screens are different.

To set the 64-bit build environment variables

  1. In Programs of the Microsoft Platform SDK, select Open Build Environment Window. Then select Set Windows XP 64 Build Environment, and then click Set Windows XP 64 Build Environment (Debug). A console window with the build environment set for a 64-bit build is displayed.

  2. At the command prompt:

    1. Change the folder to C:\Program Files\Microsoft SDK.

    2. To load the 64-bit settings in Visual Studio .NET 2003, execute the commands:

      SetEnv.Bat /AMD64 /RETAIL

      devenv /useenv.

      Do not open a new command window to open devenv.exe. The Visual Studio .NET 2003 IDE is displayed, but the include files, the library, and the executable directories are set for a 64-bit build environment.

Note If devenv.exe is not in the path, change the folder to the \Microsoft Visual Studio .NET 2003\Common7\IDE folder before you run devenv.exe.

  1. To resume working with the 32-bit settings, quit Visual Studio .NET 2003 and execute the following statements at the command prompt to relaunch the IDE:

    1. Call "C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\Bin\VCVARS32.BAT

    2. devenv /useenv

  2. After you have launched Visual Studio .NET 2003, in the Tools menu, click Options. Click Projects branch, and then VC++ Directories to view a sample of the environment, as shown in Figure 2.1.

    Figure 2.1. Visual Studio .NET 2003 environment

    Figure 2.1. Visual Studio .NET 2003 environment

After you have launched Visual Studio .NET 2003 with the proper environment, you must build the right type of release and debug project configurations for the AMD64 platform.

Note   Visual Studio .NET 2003 picks up its default tool chain through environment variables defined at startup if you use the /USEENV option.

To add a 64-bit debug configuration

  1. On the Build menu, click Configurations, and then click Add.

  2. Type a name in the Configuration text box, for example, Release AMD64 or Debug AMD64, and from the Copy settings list, choose the corresponding configuration for the Win32 platform, such as Win32 Release or Win32 Debug. Clear the Also create new project configuration(s) check box.

  3. Repeat step 2 for the debug configuration and any other configuration in the project so that for each Win32 configuration, there is a corresponding configuration for the AMD64 platform. Figure 2.2 shows how the configurations will look.

    Figure 2.2 Build configurations

    Figure 2.2 Build configurations

Several Visual Studio compiler and linker options do not apply to the 64-bit compiler and linker.

To modify compiler or linker options

  1. On the Project menu, click Properties.

  2. In the Project Properties dialog box, click the General tab. Under Output directory and the Intermediate Directory edit boxes, type Debug64.

  3. On the C/C++ tab, under General, select Program Database (compiler option, /Zi) in the Debug information format list. Make sure that Program Database for Edit and Continue is not enabled.

  4. Under Detect 64-bit portability issues, select Yes (/Wp64).

  5. On the C/C++ tab, under Command Line, remove /FD compiler flag. (The /FD flag is generated when exporting makefiles from earlier versions of Visual Studio and should be deleted.)

  6. Remove the /Gm compiler flag.

  7. Add the /Wp64 and /W4 compiler flags to enable the most sensitive IA-64–related compiler warnings.

  8. If you want to use 32-bit pointer variables, add the /Ap32 compiler option (the default is /Ap64).

  9. If you want to use a maximum of 4 GB (2^32) of virtual address space (Small Address Space), add the /As32 compiler option (default is /As64).

  10. In case of an IA64 computer, on the Linker tab in Command Line, append /machine:IA64 or /machine:AMD64 to the options list. Visual Studio .NET 2003 will not let you remove the /machine:I386 option, but if /machine:AMD64 comes after it, it can be removed.

  11. On the View menu, click Workspace. To delete the MyApplication.hpj file from the project, click the MyApplication.hpj file in the Solution Explorer window, and then press DEL key.    

Note This file may have already been removed.

  1. In all 64-bit configurations, under the General branch, change the output directories in order to avoid mixing AMD64 and Win32 object files.

  2. If your application is an MFC application, you must add an MFC path to avoid receiving Linkers Tool Error LNK1104 on the Mfc42d.lib file. To add an MFC path, perform the following steps:

    1. On the Tools menu, click Options.

    2. On the Projects tab, select VC++ Directories.

    3. On the Show Directories for drop-down box, select Library Files.

    4. Add the \Microsoft SDK\lib\IA64\mfc path if it is not listed.

Note If MyApplication is an MFC application and the project uses MFC .dll files, make sure that the .dll files are copied from the \Microsoft SDK\NoRedist\win64 folder to the \System32 folder on the IA64 computer. Following are the DLLs to be copied: MFC71.dll.
This folder also contains the symbols for the MFC, ATL, and MSVCRT debug and release versions.

To build or rebuild the project or solution

  1. To build the solution, in Solution Explorer, select or open the desired solution.

  2. On the Build menu, click Build Solution if you want to compile the project files and components that have changed since the last build.

  3. On the Build menu, click Rebuild Solution to clean the solution first, and then build all project files and components.

  4. To build any specific project, in Solution Explorer, select or open the specific project.

  5. On the Build menu, click Build <project name> to build only the project files that have changed since the last build.

  6. On the Build menu, click Rebuild <project_name> to clean the project first, and then rebuild all the project files.

After a successful build, you will have a 64-bit application that is ready to be deployed to an IA64 computer.

To debug the .exe file from the Visual Studio .NET 2003 IDE on a 64-bit computer

Note You cannot debug the .exe file from the Visual Studio .NET 2003 IDE.

  1. Create a folder named C:\VS2003MSVSMON on the IA64 computer.

  2. Copy the following files from the x86 computer to this new folder:

    • Msvcmon.exe

    • Dm.dll

    • Msdis110.dll

    • Tln0t.dll

    These files are located in the \Microsoft Visual Studio .NET 2003\Common7\Packages\Debugger folder.

  3. After you copy the files, run Msvcmon.exe on the IA64 computer, and then click Connect.

  4. In the Visual Studio .NET 2003 IDE on the x86 computer, on the Build menu, click Debugger Remote Connection. In the Remote Connection dialog box, click Network TCP/IP, and then click Settings. In the Target computer name or address box, type the name of the IA64 computer. To close the dialog box, click OK.

  5. In the Visual Studio C++ IDE, on the Project menu, click Settings. In the left pane, expand MyApplication, and then click the Debug tab. You will notice that the Executable for debug session textbox contains the path of MyApplication.exe. This will be C:\<X86Path>\MyApplication.exe.

  6. In the Remote executable path and file name textbox, type MyApplication.exe with a full path. This full path looks like \\<X86ComputerName>\C$\<x86Path>\MyApplication.exe. Click OK to close the window.

  7. To run the .exe file, press CTRL+F5 or click Execute MyApplication.exe on the Build menu. The .exe file runs on the IA64 computer.

  8. If MyApplication is an MFC application and if the project uses MFC .dll files, make sure that the .dll files are copied from the \Microsoft SDK\NoRedist\Win64 folder to the \System32 folder on the IA64 computer. Following are the .dll files:

    • Mfc71.dll

    This folder also contains the symbols for the MFC, the ATL, and the MSVCRT debug and release versions.

Note The 64-bit versions of Microsoft Windows XP and Windows Server™ 2003 include NTSD, a symbolic debugger that works with both 32-bit and 64-bit applications. You can also use the regular WinDbg debugger to let you work on 32-bit applications under 64-bit Windows. New versions of WinDbg are available for Itanium and native x64 (beta) at
https://www.microsoft.com/whdc/devtools/debugging/64bit-home.mspx.

Debugging with Visual Studio .NET 2003

To debug a program using Visual Studio .NET 2003, the program must be compiled with the appropriate options set. For more information about the options, see Table 2.2 in “Using the Platform SDK” earlier in this chapter. To use the Visual Studio .NET 2003 debugger, in the configuration manager, set the active configuration to debug. When the project is compiled, the debug session can begin.

You can run a debug session within a project opened in Visual Studio .NET 2003 or by opening a remote connection from the IDE. You can choose either option from the Build menu in the Visual Studio .NET 2003 IDE. More information on debugging using Visual Studio .NET 2003 is available at https://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/_asug_How_Do_I_Topics3a_Debugging.asp.

Note Do not install the 64-bit version of the WinDbg debugging tool on the same computer where Visual Studio .NET 2003 is installed. More information about the 64-bit version of WinDbg is available at the Platform SDK 64-bit Readme. Additional information is available in the Production Debugging for .NET Framework Applications section at https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/DBGrm.asp.

64-Bit Programming in UNIX and Windows

The difference between UNIX/64 and Windows 64-bit programming is because of the different data models. This section describes the new data types in UNIX/64 and Win64 and how they affect your code.

New Explicitly Sized Data Types

Both UNIX/64 and Windows provide new fixed-length or explicitly sized data types. These data types are defined in header files that must be included for either UNIX/64 or Windows Server 2003 (64-bit) programming. In UNIX, they are defined in inttypes.h and in Windows in basetsd.h.

Using explicitly sized types can be helpful in clarifying code purposes and making maintenance easier. However, as these types do not automatically scale with the target architecture, they should not be used for pointers, as pointers in the 64-bit architecture are commonly 64 bits.

UNIX/64 and the Windows UDM have introduced different names for fixed-length types. Fortunately, ANSI standard names, such as short and int, can still be used for some of these types, which make code migration from UNIX to Windows easier. Whenever possible, it is best to use ANSI types. However, ANSI has no standardized, explicitly sized 64-bit types. Table 2.3 lists new explicitly sized data types in 64-bit UNIX.

Table 2.3. New UNIX*/64 Explicitly Sized Data Types

Architecture

New Data Types

Old Data Types

64-bit

int64_t, uint64_t

long long, quad_t, u_quad_t

32-bit

int32_t, uint32_t

int, unsigned int

16-bit

int16_t, uint16_t

short, unsigned short

8-bit

int8_t, uint8_t

char

Table 2.4 lists new explicitly sized data types in Win64.

Table 2.4. New Win64 Explicitly Sized Data Types

Architecture

New Data Types

Old Data Types

64-bit

INT64, UINT64, LONG64, ULONG64, DWORD64

__int64, unsigned --__int64

32-bit

INT32, UINT32, LONG32, ULONG32, DWORD32

int, unsigned int

16-bit

 

short, unsigned short

8-bit

 

char, unsigned char

Notice that with Windows you will no longer be using int, long, and dword. You will need to specify the length if you want to use an explicitly sized type.

New Scalable Data Types

Scalable, or pointer-precision, data types are polymorphic and adjust to the size of the architecture (and thus the pointer size) during compilation. If you use one of these types, it will compile with a 32-bit size under the 32-bit architecture and with a 64-bit size under the 64-bit architecture. You must conditionally compile for these types.

UNIX/64 and Windows Server 2003 (64-bit) have their own respective scalable types. However, there are also some ANSI types available in both environments as listed in Tables 2.5 and 2.6.     

Note Do not mix the scalable and fixed data types.

The examples that accompany the tables help you identify how to use the new types. Table 2.5 lists new scalable data types in 64-bit UNIX.

Table 2.5. New UNIX*/64 Scalable Data Types

Description

Data Types

Integral data types that may contain a type-cast pointer.

intptr_t, uintptr_t

Integral data types intended to always contain counting numbers.

long, size_t, ssize_t

Table 2.6 lists new scalable data types in Win64.

Table 2.6. New Win64 Scalable Data Types

Description

Standard

Data Types

Integral data types that may contain a type-cast pointer.

ANSI

intptr_t, uintptr_t

Integral data types that may contain a type-cast pointer.

Win64

LPARAM, WPARAM, LRESULT, INT_PTR, UINT_PTR, WORD_PTR, LONG_PTR, ULONG_PTR

Integral data types intended to always contain counting numbers.

ANSI

size_t, ssize_t

Integral data types intended to always contain counting numbers.

Win64

__int3264, SIZE_T, SSIZE_T

Many Win32 APIs now use these new types, and your code must adapt to them.

There are also new functions in Win64; these are discussed in detail in the following sections.

Rules for Making Win32 Code Compatible with Win64

In general, to ensure that porting occurs comfortably between 32-bit and 64-bit, a few rules must be followed. The following rules can be followed to write code that is compatible with a 64-bit environment, hence having the same code base for 32-bit and 64-bit architectures.

  • When using pointers in 64-bit code, if you declare the pointer using a 32-bit type, the operating system creates the pointer by truncating a 64-bit pointer. Pointers are 64 bits on 64-bit Windows. To cast pointers to int, long, UINT, ULONG, or DWORD, use UINT_PTR, INT_PTR, ULONG_PTR, or DWORD_PTR. Make no assumptions about the length of a pointer or xxxx_PTR or xSIZE_T; assume that these are compatible precision.

    Note   Do not cast your pointers to the types ULONG, LONG, INT, UINT, or DWORD.

    HANDLE is defined as a void*, so typecasting a HANDLE value to a ULONG value to test, set, or clear the low-order 2 bits will cause an error on 64-bit Windows.

  • If you must truncate a pointer to a 32-bit value, use the PtrToLong or PtrToUlong function.

Note Once truncated, the pointers should never be used as a pointer again or unexpected results, including General Protection Faults, may occur.

  • The count types reflect the maximum size to which a pointer can refer. This can be used for a count that must span the full range of a pointer.

  • Be cautious when you use unions with pointers.

  • Use OUT parameters with caution. Instead, use ULONG_PTR as the data type for OUT parameters.

    Typecasting &ul to PULONG* prevents a compiler error, but the function will write a 64-bit pointer value into the memory at &ul. This code works on 32-bit Windows but will cause data corruption on 64-bit Windows.

  • Data structures stored on a disk or exchanged with 32-bit processes need to be handled. Structures that contain the types that change size, for example, LPARAM, WPARAM, and LRESULT, need to be handled.

  • Be cautious with polymorphic interfaces. Do not create functions that accept DWORD parameters for polymorphic data. If the data can be a pointer or an integral value, use the UINT_PTR or PVOID type.

    For example, do not create a function that accepts an array of exception parameters typed as DWORD values. The array should be an array of DWORD_PTR values. Therefore, the array elements can hold addresses or 32-bit integral values. The general rule is that if the original type is DWORD and it needs to be pointer width, convert it to a DWORD_PTR value. That is why there are corresponding pointer-precision types. If you have code that uses DWORD, ULONG, or other 32-bit types in a polymorphic way (that is, you want the parameter or structure member to hold an address), use UINT_PTR in place of the current type.

  • Use the new window-class functions on both 32-bit and 64-bit Windows. Also available is a set of new functions to access pointers or handles in class private data.

    If you have window-class private data that contains pointers, your code must use the following new functions:

    • GetClassLongPtr

    • GetWindowLongPtr

    • SetClassLongPtr

    • SetWindowLongPtr

    Additionally, you must access pointers or handles in class private data using the new functions on 64-bit Windows. To aid you in finding these cases, the following indexes are not defined in Winuser.h during a 64-bit compile:

    • GWL_WNDPROC

    • GWL_HINSTANCE

    • GWL_HWDPARENT

    • GWL_USERDATA

    Instead, Winuser.h defines the following new indexes:

    • GWLP_WNDPROC

    • GWLP_HINSTANCE

    • GWLP_HWNDPARENT

    • GWLP_USERDATA

    • GWLP_ID

    For example, the following code does not compile:

    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

    It must be changed to the following:

    Note: The line has been split into multiple lines for readability.However, while trying it out on a system you must enter it as one line without breaks.

    SetWindowLongPtr(hWnd, GWLP_WNDPROC, 
    

(LONG_PTR)MyWndProc);

  • When setting the cbWndExtra member of the WNDCLASS structure, be sure to reserve enough space for pointers. For example, if you are currently reserving sizeof (DWORD) bytes for a pointer value, reserve sizeof (DWORD_PTR) bytes.

  • The LPARAM, WPARAM, and LRESULT types change size with the platform. When compiling 64-bit code, these types expand to 64 bits because they typically hold pointers or integral types. Do not mix these values with DWORD, ULONG, UINT, INT, int, or long values. Examine how you use these types and ensure that you do not inadvertently truncate values.

  • Access all window and class data using FIELD_OFFSET. Accessing window data using hard-coded offsets is not portable to 64-bit Windows. To make your code portable, access your window and class data using the FIELD_OFFSET macro. Do not assume that the second pointer has an offset of 4.

    Note: Some of the lines in the following code have been displayed on multiple lines for better readability.

    struct foo {
    

DWORD NumberOfPointers; PVOID Pointers[1]; } xx; Wrong: malloc(sizeof(DWORD)+100sizeof(PVOID)); Correct: malloc(offsetof(struct foo, Pointers)+100sizeof (PVOID));

  • The LRESULT type could be changed to represent a 64-bit value to avoid portability problems.

  • Use %p as a format specifier to print pointers.

  • Using %x format specifier does not work as expected in a 64-bit environment. Use %I32x or %I64x format specifier instead of %x.

  • HMODULE is a pointer to the beginning of an EXE or DLL module. In a 64-bit environment, an HMODULE must be 64 bits.

  • If you pass too few parameters to a function, even if the function is careful not to access that parameter until some other conditions are met, the compiler may find that it needs to discard the parameter, thereby raising the STATUS_REG_NAT_CONSUMPTION exception.

  • Assembly code is not x86 (IA32) assembler, so rewrite any assembly code to high-level languages. Also rewrite the 16-bit applications and 16-bit API-based code.

  • Be cautious when porting the code that accesses bit fields and bit-wise operations.

  • Avoid using hard-coded memory locations.

  • Be cautious when porting drivers to 64-bit Microsoft Windows.

  • Ensure plug-in interfaces are RPC compliant.

  • Enable COM objects to run out-of-process.

Developing the Testing Tools and Test Cases

After developing the solution components, you need to perform testing for the code changes made as part of the development. The testing process helps identify and address potential issues prior to deployment. Testing spans the Developing and the Stabilizing Phases. It starts when you begin developing the solution and ends in the Stabilizing Phase, when the test team certifies that the solution components address the schedule and high-quality goals in the project plan. This also involves using the automated test tools and test scripts.

Figure 2.3 illustrates where the testing activities occur within the phases of the MSF Process Model.

GR506.gif

Figure 2.3 MSF Process Model – Testing activities across the Developing and Stabilizing Phases

Testing is performed, parallel to development, throughout the Developing Phase. This section discusses the unit testing activity that needs to be performed during the Developing Phase. The other necessary testing activities are discussed in Chapter 8, “Deployment Considerations and Testing Activities” and Chapter 9, “Stabilizing Phase” of this volume.

During the Developing Phase, testing is not done as a stand-alone activity, but in conjunction with the building of the solution. When building software, the development team designs, documents, and writes the code. Testing is done at this stage through unit testing (discussed in the following section) and daily builds. The testing team designs and documents test specifications and test cases, writes automated scripts, and runs acceptance tests on components submitted for a formal round of testing. The testing team assesses the solution, makes a report on its overall quality and feature completeness, and certifies that the solution features, functions, and components meet the project goals.

Testing in migration projects involving infrastructure services is focused on finding discrepancies between the behavior of the original application, as seen by its clients, and that of the newly migrated application. All discrepancies must be investigated and fixed. It is best to add any new functionality to a migrated application or new capabilities to a migrated service in a separate project initiated after migration is complete.

Unit Testing

Unit testing is the process of verifying whether a specific unit (which can be a class, a program, or a specific functionality) of the code is working according to its functional specifications. It also helps in determining whether the specific unit will be capable of interacting with the other units as defined in the functional specifications.

Unit testing in a UNIX to Windows migration project is the process of finding the discrepancies between the functionality and output of the individual units in the Windows application and the original UNIX application. This might not always be the case; in some cases the design in Windows may differ from the UNIX design, thereby identifying units that are different from the UNIX units. Basic smoke testing, boundary conditions, and error tests are done based on the functional specification of the unit.

The test cases for unit testing include constraints on the inputs and outputs (pre-conditions and post-conditions), the state of the object (in case of a class), the interactions between methods, attributes of the object, and other units.

The unit test cases for migrating UNIX to Win32/64 should mainly focus on the following:

  • Data type size validation to identify overflow and truncation errors.

  • Parameter data type validation to the APIs.

  • Memory allocation routines and usage.

  • File size and offset length validations.

  • Data type casting.

  • Extrapolate test cases on boundary conditions.

  • Usage of bit fields and bitwise operations.

Building the Solution

  • By this stage, the individual components of the solution are developed and tested in the Windows environment using Win32/Win64 APIs to satisfy the project requirements. This stage helps you build the solution with the developed and tested components, and then make the migrated application ready for internal release.

  • As a good practice, MSF recommends that teams working on development projects perform daily builds of their solution. In migration projects, on the other hand, you typically have to examine large bodies of existing code to understand what they are intended for and to make changes to the code. However, code changes can happen only after addressing porting issues, hence daily builds may not be required. The process of creating interim builds allows a team to find issues early in the development process, which shortens the development cycle and lowers the cost of the project. Note that these interim builds are not deployed in the live production environment. Only when the builds are thoroughly tested and stable are they ready for a limited pilot release to a subset of the production environment. Rigorous configuration management is essential to keeping builds in synch.

Interim Milestone: Internal Release

The project needs interim milestones to help the team measure their progress in the actual building of the solution during the Developing Phase. Each internal release signifies a major step toward the completion of the solution feature sets and achievement of the associated quality level. Depending on the complexity of the solution, any number of internal releases may be required. Each internal release represents a fully functional addition to the solution’s core feature set that is potentially ready to move on to the Stabilizing Phase. As each new release of the application is built, fewer bugs must be reported and triaged. Each release marks a significant progress in the approach of the team toward deployment. With each new candidate, the team must focus on maintaining tight control over quality.

The subsequent chapters of this volume describe the necessary code changes required for the migration of the UNIX code to the Windows environment using Win32/Win64 APIs. You can use these instructions to develop the solution components in the Developing Phase.

Download

Get the UNIX Custom Application Migration Guide

Update Notifications

Sign up to learn about updates and new releases

Feedback

Send us your comments or suggestions