Step by Step: Introduction to the New Native APIs in Windows Mobile 5.0

 

Microsoft Corporation

June 2006

Applies to:
   Microsoft .NET Compact Framework version 1.0
   Microsoft Visual C++
   Microsoft Visual Studio 2005
   Windows Mobile version 5.0 software for Pocket PCs
   Windows Mobile version 5.0 software for Smartphones

Summary: Learn about the new native application programming interfaces (APIs) in Windows Mobile 5.0 in this self-paced hands-on lab (HOL). This HOL will take 1 hour and 15 minutes to complete. (37 pages)

Download MEDC06_HOL307.msi from the Microsoft Download Center.

Contents

Introduction
Lab 1: Introduction to the New Native APIs in Windows Mobile 5.0
Summary
Appendix A: Terminating an Application That Is Running on a Device or Emulator
Appendix B: Setting Up Your Computer

The following applications are required to run this HOL:

  • Microsoft Windows XP Professional.

  • Visual Studio 2005.

    This lab requires Visual Studio 2005 Standard, Professional, or Team System Editions. It will not work with any of the Express Editions. If you do not have the correct edition of Visual Studio 2005, find out how you can acquire it from the Visual Studio 2005 Developer Center.

  • Microsoft ActiveSync 4.0.

    ActiveSync 4.0 allows for connectivity between a Windows Mobile–based device and your computer.

  • Windows Mobile 5.0 SDKs.

    The Windows Mobile 5.0 SDKs for Pocket PC and Smartphone enable development for Windows Mobile–based devices in Visual Studio 2005.

    Download and install Windows Mobile 5.0 SDK for Pocket PC.

    Download and install Windows Mobile 5.0 SDK for Smartphone.

  • Set up your computer

    Follow the directions in Appendix A and Appendix B to set up your computer for this HOL.

Credentials Used

The following phone numbers are used in this HOL:

  • (603) 555-9999
  • (603) 555-2864
  • (212) 222-5555
  • (310) 555-3662

Introduction

Learn about the new native application programming interfaces (APIs) in Windows Mobile 5.0 in this self-paced hands-on lab (HOL). You will improve on an existing application by making use of these new interfaces. Upon completion of this HOL, you will be ready to use these APIs to simplify your existing code or extend your applications to make use of the many new capabilities in Windows Mobile 5.0.

Lab 1: Introduction to the New Native APIs in Windows Mobile 5.0

This lab introduces many of the newly available Windows Mobile 5.0-based native application programming interfaces (APIs) such as Contact Picker, Picture Picker, Messaging API, Camera Capture API, and the State and Notifications Broker API. To understand these APIs and how they may fit into an application, you will use these new APIs to add features to an existing application written with Visual C++. The existing application provides basic menu handling and screen painting, but it contains no functional features.

The application you will modify is a Smartphone application that field sales representatives use who are from a high-end art and photography dealer. As a high-end dealership, sales representatives spend a larger amount of time on the road working closely with individual clients. These sales representatives must always have easy access to client information and must be able to work effectively when not in the office.

The application will be a contact manager that keeps track of contact names, work phone numbers, mobile phone numbers, and notes about that client. In this lab, you will integrate the application with Microsoft Pocket Outlook providing contact search capabilities. You will use the other APIs to closely integrate the application with the features of the phone, such as locating pictures of the available inventory and automatically sending e-mail. You will also update the application to monitor for incoming or manually placed calls with a client, so client information is automatically displayed. The lab also includes an optional exercise to add support for taking pictures from within the application.

Lab Objective

The objective of this lab is to introduce the native APIs that are available as part of the Windows Mobile 5.0 platform and demonstrate their effectiveness in improving user efficiency.

In this HOL, you will perform the following exercises:

  • Using Contact Picker to Provide Search Capabilities
  • Using Messaging and the Picture Picker to Send E-mail with Attachments
  • Using the State and Notifications Broker API to Retrieve System State Information
  • Using the State and Notifications Broker API to Receive Notifications of Changes in System State
  • Using the Camera Capture API (optional)

Exercise 1: Using Contact Picker to Provide Search Capabilities

In this exercise, you will add a search feature to the application that allows the user to look up a specific contact. To implement this feature, you will use the Contact Picker function, ChooseContact. You will type some of the code yourself, and you will uncomment some existing code.

Before modifying the application, you should briefly become familiar with its implementation.

To become familiar with the existing application

  1. If Visual Studio 2005 is not already open, click Start | All Programs | Microsoft Visual Studio 2005 | Microsoft Visual Studio 2005.

  2. In Visual Studio 2005, click File | Open | Project/Solution.

  3. On the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MED307_Intro_New_WM5_Native_API\Exercises\NewNativeAPIs.

  4. Select NewNativeAPIs.sln, and then click Open.

    The NewNativeAPIs solution opens.

    Note   After the solution opens, the solution, project, and source files should now appear in Solution Explorer in Visual Studio 2005. If Solution Explorer doesn't automatically display, click View | Solution Explorer to make it visible.

  5. In Solution Explorer, expand NewNativeAPIs, expand Source Files, and then double-click NewNativeAPIs.cpp to open the NewNativeAPIs.cpp source file.

    Notice that near the bottom of the file, there are four functions that provide some basic Pocket Outlook housekeeping. Review these functions to be sure you understand their implementations:

    • PocketOutlookLogon

      This function logs on to Microsoft Pocket Outlook and stores the Pocket Outlook application interface pointer in a global variable, g_polApp.

    • GetContact

      This function locates a contact by using the contact's object identifier (OID). This function returns the contact interface pointer and stores it in the global variable, g_pContact.

    • DisplayContact

      The first version of this function accepts a contact interface pointer displaying the contact's relevant information. By using the contact that the interface pointer references, the FileAs, BusinessTelephoneNumber, MobileTelephoneNumber, and Body fields are retrieved from the contact and copied to the global variables that are used to display the values. This function also invalidates the application display area to force a repaint.

    • DisplayContact

      The second version of this function basically combines the functionality of the GetContact function and the other version of the DisplayContact function. It accepts a contact's OID and locates and displays the contact.

  6. Locate the WM_COMMAND case statement in the WndProc function. Notice that the program has two menu options: IDM_EXIT and IDM_SELECT. The code for IDM_EXIT is already provided, and it handles the details of clean up and application shutdown. IDM_SELECT calls the OnCOMMAND_SELECT function.

  7. Locate the OnCOMMAND_SELECT function near the top of the source file. Remember that this function is called in response to the user choosing the Select command. This function calls most of the new code you add.

You are now ready to add the functionality that lets the user choose a specific contact. To add this functionality, you will use the new ChooseContact function.

To add the select contact functionality

  1. In NewNativeAPIs.cpp, locate the SelectContact function, which has a bool return value. This function has been started for you, and you will complete the implementation.

  2. The SelectContact function starts by declaring a bool variable named bSuccess with an initial value of false and an HRESULT variable named hr with an initial value of E_ABORT. After those lines, declare an array of type CEPROPID that initializes a array with the value PIMPR_ALL_EMAIL, as shown in the following code example. This initialization is explained more in the following steps.

    CEPROPID rgProperties[] = {PIMPR_ALL_EMAIL};
    
  3. Declare an instance of the CHOOSECONTACT structure named cc on the blank line immediately following the comment that instructs you to declare the CHOOSECONTACT structure, as shown in the following code example.

    /* Declare the CHOOSECONTACT structure */
    CHOOSECONTACT cc = {0};
    

    The CHOOSECONTACT structure controls the behavior of the ChooseContact function and contains the user selection when the ChooseContact function returns.

  4. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    cc.cbSize = sizeof(cc);
    cc.dwFlags = CCF_HIDENEW | CCF_CHOOSECONTACTONLY;
    cc.rgpropidRequiredProperties = rgProperties;
    cc.cRequiredProperties = 1;
    

    This code begins by setting the cbSize member to the size of the structure you just added. The Contact Picker provides a number of different options and behaviors—many of which are enabled and disabled by using the dwFlags member of CHOOSECONTACT. Normally, the Contact Picker provides the user with the option to add a new contact. This feature is disabled by using the CCF_HIDENEW flag. The Contact Picker provides the ability to select just a contact or a contact and a specific property of that contact. The CCF_CHOOSECONTACTONLY flag indicates that just the contact should be chosen. The code performs a bitwise-or to combine these flags, so your application selects only a contact and does not allow the user to create a new contact.

    By using the rgpropidRequiredProperties member of CHOOSECONTACT, the Contact Picker can limit the displayed Contacts list to only those contacts that have specific properties populated. Because your application will soon support sending e-mail messages, the code uses PIMPR_ALL_EMAIL to limit the displayed Contacts list to only those contacts that have at least one e-mail property set. The rgpropidRequiredProperties member is an array. That is why you previously declared an array of type CEPROPID initializing that array with the value PIMPR_ALL_EMAIL. Then, the code assigned the rgProperties array to the rgpropidRequiredProperties member of CHOOSECONTACT. The code also set the cRequiredProperties member of CHOOSECONTACT to the number of elements in the array.

    You are now ready to display the Contact Picker.

  5. After the code that you uncommented in Step 4, call the ChooseContact function and pass a pointer to the instance of CHOOSECONTACT you initialized. Assign the value returned by ChooseContact to the hr variable, as shown in the following code example.

    hr = ChooseContact(&cc);
    
  6. After the call to ChooseContact returns, use the SUCCEEDED macro to verify that the user made a selection and assign its value to bSuccess. If the ChooseContact call succeeded, the oidContactID member of CHOOSECONTACT contains the selected contact's OID. Display the selected contact by using the DisplayContact method, as shown in the following code example.

    bSuccess = SUCCEEDED(hr);
    if (bSuccess)
      DisplayContact(cc.oidContactID);
    

    The last line of SelectContact is already typed for you. It returns the value of bSuccess.

  7. You've added all of the necessary code to the SelectContact method. Verify that the code looks like the following.

    bool SelectContact()
    {
      bool bSuccess = false;
      HRESULT hr = E_ABORT;
      CEPROPID rgProperties[] = {PIMPR_ALL_EMAIL};
    
      /* Declare the CHOOSECONTACT structure */
      CHOOSECONTACT cc = {0};
    
      /* Set up Contact Picker */
      cc.cbSize = sizeof (cc);
      cc.dwFlags = CCF_HIDENEW | CCF_CHOOSECONTACTONLY;
      cc.rgpropidRequiredProperties = rgProperties;
      cc.cRequiredProperties = 1;
    
      hr = ChooseContact(&cc);
    
      bSuccess = SUCCEEDED(hr);
      if (bSuccess)
        DisplayContact(cc.oidContactID);
    
      return bSuccess;
    }
    

    The last thing you need to do before testing your new Find Contact feature is to call the SelectContact function from OnCOMMAND_SELECT.

  8. Locate the OnCOMMAND_SELECT method.

  9. Delete the line containing the call to MessageBox, as shown in the following code example.

    MessageBox(g_hWnd, _T("Select Clicked"), _T("Handle_IDM_SELECT"), MB_OK);
    
  10. Replace that line with a call to SelectContact. Assign the value returned by SelectContact to a bool variable named bSuccess. The OnCOMMAND_SELECT method should now look like the following code example.

    void OnCOMMAND_SELECT()
    {
      bool bSuccess = SelectContact();
    }
    

You are now ready to test the new application's functionality.

To test the new functionality

  1. Click Build | Build NewNativeAPIs. Correct any compilation errors before you proceed.

  2. Verify that Windows Mobile 5.0 Smartphone Emulator is selected in the drop-down list box, as shown in Figure 1.

    Figure 1. Emulator selection

  3. Start the application by clicking Debug | Start Debugging.

  4. Do one of the following:

    • If prompted by the Deploy NewNativeAPIs dialog box, verify that Windows Mobile 5.0 Smartphone Emulator is selected, and then click Deploy.
    • If the emulator does not appear, look for a Windows taskbar button, and then click it to bring the emulator to the foreground.
  5. Click the left soft key under the word Select to bring up the Contact Picker. The emulator display should now look similar to Figure 2.

    Figure 2. The Windows Mobile 5.0 Contact Picker

  6. Click one of the contacts. The application displays the appropriate information for that contact, as shown in Figure 3.

    Figure 3. Information for a Windows Mobile 5.0 contact

  7. After you are comfortable that the new feature works correctly, close the application by clicking the right soft key under the word Exit.

Exercise 2: Using Messaging and the Picture Picker to Send E-mail with Attachments

In this exercise, you will use the new Picture Picker and Messaging API to provide the user with the capability to send the selected contact an e-mail message with a picture as an attachment. You will use the Picture Picker to let users select which photographs they want to send to their clients. You will type some of the code yourself, and you will uncomment some existing code.

To use Picture Picker

  1. Locate the global variable declarations near the top of NewNativeAPIs.cpp. Below the other global variables, add a declaration for a TCHAR array named g_tszSelectedPicture with a size of 260, as shown in the following code example. This variable will be used to store the file path to the picture the user selects.

    TCHAR g_tszSelectedPicture[260];
    
  2. Locate the function named SelectPicture that has a bool return value. This function has been started for you, and you will complete the implementation. The function starts by declaring a bool variable named bSuccess with an initial value of false and a TCHAR array named tszFile of size 260 to provide a buffer to receive the selected picture's file name.

  3. Declare an instance of the OPENFILENAMEEX structure named ofnex on the blank line immediately following the comment that instructs you to declare the OPENFILENAMEEX structure, as shown in the following code example.

    /* Declare the OPENFILENAMEEX structure */
    OPENFILENAMEEX ofnex = {0};
    

    Native code developers access the Picture Picker by using the GetOpenFileNameEx function. The OPENFILENAMEEX structure controls the behavior of GetOpenFileNameEx and contains the path to the picture file the user selected when GetOpenFileNameEx returns.

  4. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    ofnex.lStructSize = sizeof(ofnex);
    ofnex.hInstance = g_hInst;
    ofnex.hwndOwner = g_hWnd;
    

    This code sets the lStructSize member to the size of the structure and the hInstance and hwndOwner members to the global variables g_hInst and g_hWnd respectively.

  5. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    ofnex.ExFlags = 
       OFN_EXFLAG_THUMBNAILVIEW | OFN_EXFLAG_HIDEDRMFORWARDLOCKED;
    ofnex.lpstrInitialDir = _T("\\Images");
    ofnex.lpstrFile = tszFile;
    ofnex.nMaxFile = 260;
    

    When you display the Picture Picker, it sets the ExFlags member to OFN_EXFLAG_THUMBNAILVIEW. In many cases, the pictures stored on a device may be Digital Rights Management (DRM)–protected, so you must also consider how to handle DRM issues. Any images that have been specifically protected against being forwarded should not be displayed because the attempt to send them will fail, so you must also set the OFN_EXFLAG_HIDEDRMFORWARDLOCKED flag.

    Note   If you wanted to hide all DRM–protected images, set the OFN_EXFLAG_HIDEDRMPROTECTED flag.

    By default, the Picture Picker initially displays the files in \My Documents\My Pictures. To start with a different initial directory, the code assigns the desired directory name, \Images, to the lpstrInitialDir member.

    Note   Normally the Picture Picker allows the user to browse to directories other than the initial directory. If you want to prevent the user from browsing to other directories, set the OFN_EXFLAG_LOCKDIRECTORY extended flag (ExFlag member).

    Next, the code assigns the tszFile buffer that was already declared to the lpstrFile member. The nMaxFileSize member is set to size of the lpstrFile member.

    You are now ready to display the Picture Picker.

  6. Call GetOpenFileNameEx passing the address of the OPENFILENAMEEX structure. Assign the GetOpenFileNameEx return value to bSuccess, as shown in the following code example.

    bSuccess = GetOpenFileNameEx(&ofnex);
    
  7. If GetOpenFileNameEx returns true, copy the selected file name to the global variable that you declared in Step 1, as shown in the following code example.

    if (bSuccess)
        _tcscpy(g_tszSelectedPicture, ofnex.lpstrFile);
    

    The last line of SelectPicture is already typed for you. It returns the value of bSuccess.

  8. You've added all of the necessary code to the SelectPicture method. Verify that the code looks like the following code example.

    bool SelectPicture()
    {
        bool bSuccess = false;
        TCHAR tszFile[260] ;
    
        /* Declare the OPENFILENAMEEX structure */
        OPENFILENAMEEX ofnex = {0};
    
        /* Set up File Open dialog */
        ofnex.lStructSize = sizeof(ofnex);
        ofnex.hInstance = g_hInst;
        ofnex.hwndOwner = g_hWnd;
    
        /* Set up dialog for picture selection */
        ofnex.ExFlags = 
            OFN_EXFLAG_THUMBNAILVIEW | OFN_EXFLAG_HIDEDRMFORWARDLOCKED ;
        ofnex.lpstrInitialDir = _T("\\Images");
        ofnex.lpstrFile = tszFile;
        ofnex.nMaxFile = 260;
    
        bSuccess = GetOpenFileNameEx(&ofnex);
        if (bSuccess)
            _tcscpy(g_tszSelectedPicture, ofnex.lpstrFile);
    
        return bSuccess;
    }
    

    Now you need to add a call to the SelectPicture function from OnCOMMAND_SELECT. The SelectPicture function should only be called if the SelectContact function returned indicating success.

  9. Locate the OnCOMMAND_SELECT method.

  10. Immediately after the call to SelectContact, add an if statement to verify the called return successfully. In the if statement, call SelectPicture assigning the return value to bSuccess, as shown in the following code example.

    if (bSuccess)
    {
        bSuccess = SelectPicture();
    }
    
  11. Verify that the OnCOMMAND_SELECT method looks like the following code example.

    void OnCOMMAND_SELECT()
    {
        bool bSuccess = SelectContact();
        if (bSuccess)
        {
            bSuccess = SelectPicture();
        }
    }
    

In the next procedure, you will add the header file include statement and library reference to use the Messaging API. You will use the Messaging API to create an e-mail message, attach the selected picture, and then display the message to the user before sending the message. To make the task of communicating with clients as easy for the user as possible, the e-mail message automatically addresses the message, provides a subject and a message body, and includes the selected image as an attachment.

To use the Messaging API to send a picture

  1. Add a #include statement for cemapi.h to NewNativeAPIs.cpp, as shown in the following code example, near the other #include statements at the top of the file.

    #include <cemapi.h>
    
  2. To add the cemapi.lib library reference, you need to modify the project properties. First, on the Visual Studio menu, click File | Save. Next, click Project | NewNativeAPIs Properties.

  3. On the left side of the Property Pages dialog box, expand Configuration Properties, as shown in Figure 4.

    Figure 4. The NewNativeAPIs.cpp Property Pages dialog box

  4. Expand Linker.

  5. Under Linker, select Input.

  6. On the right side of the dialog box, locate the Additional Dependencies field.

  7. In the Additional Dependencies field, type a space, and then type cemapi.lib after pimstore.lib, as shown in Figure 5.

    Figure 5. Project properties after adding the cemapi.lib library reference

  8. Click OK.

  9. Locate the function named SendEmailMessage with a void return value. This function has been started for you, and you will complete the implementation.

    Before you can send a message, you need to have the e-mail address to send it to. The SendEmailMessage function starts by declaring a BSTR variable named bstrEmailAddress to hold the e-mail address. The currently selected contact is stored in the g_pContact global variable.

  10. After the line that declares bstrEmailAddress, use the get_Email1Address function to retrieve the contact's e-mail address, as shown in the following code example.

    g_pContact->get_Email1Address(&bstrEmailAddress);
    

    Note   Because in Exercise 1, the Contacts list was filtered by PIMPR_ALL_EMAIL, there is no guarantee that there is an e-mail address that is stored in the contact's Email1Address—only that one of the contact's e-mail addresses are populated. In this lab, this assumption is safe because all of the contacts have had Email1Address populated. In a real-life scenario, you have two options: write the code to check each of the contact's e-mail address fields until a populated e-mail address is found, or filter the contacts with the PIMPR_EMAIL1_ADDRESS flag rather than using the PIMPR_ALL_EMAIL flag. In general, it's preferable to search through the available e-mail addresses rather than require that a specific one be populated.

  11. The MailComposeMessage function displays an e-mail message by using the standard message compose form. The MAILCOMPOSEFIELDS structure contains the fields required to initialize MailComposeMessage. Declare an instance of the MAILCOMPOSEFIELDS structure named mailFields on the blank line immediately following the comment that instructs you to declare the MAILCOMPOSEFIELDS structure, as shown in the following code example.

    /* Declare the MAILCOMPOSEFIELDS structure */
    MAILCOMPOSEFIELDS mailFields = {0};
    
  12. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    mailFields.cbSize = sizeof(mailFields);
    mailFields.pszSubject = _T("The picture we discussed");
    mailFields.pszBody = tszBaseMessage;
    mailFields.pszTo = bstrEmailAddress;
    

    This code sets the cbSize member to the size of the structure. Next, the code sets the values for the message subject and body. The pszSubject member is assigned the string "The picture we discussed". For convenience, a constant named tszBaseMessage has been declared and contains the message body text, which is assigned to the pszBody member.

    The e-mail message is addressed by assigning the e-mail address that you retrieved from the contact to the pszTo member.

    Note   To address the e-mail message to multiple recipients, list all of the e-mail addresses in a single string with each e-mail address separated by a semicolon.

  13. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    mailFields.pszAttachments = g_tszSelectedPicture;
    mailFields.cAttachments = 1;
    mailFields.pszAccount = _T("ActiveSync");
    mailFields.dwFlags = MCF_ACCOUNT_IS_NAME;
    

    This code attaches the user-selected picture to the message by assigning the file name that is stored in the global variable g_tszSelectedPicture to the pszAttachments member. The code also assigns the number of attachments to the cAttachments member.

    Note   To include multiple attachments, list all of the attachment file names in a single string with each attachment file name separated by \0. Be sure to specify the appropriate number of attachments in cAttachments.

    Devices often include several messaging accounts; therefore, you must specify which account to use when sending the message. When identifying the account, you can either specify an account name or the message transport. In either case, the value is assigned to the pszAccount member. Whether the pszAccount value is interpreted as an account name or transport, the meaning of the pszAccount value is controlled by the MCF_ACCOUNT_IS_NAME or MCF_ACCOUNT_IS_TRANSPORT flags. In this exercise, an account name of "ActiveSync" is used, and the MCF_ACCOUNT_IS_NAME flag is assigned to the dwFlags member.

  14. Display the message to the user by calling MailComposeMessage and passing the address of the MAILCOMPOSEFIELDS structure, as shown in the following code example. After the message displays, the user can review, modify, send, or cancel the message.

    MailComposeMessage(&mailFields);
    
  15. When MailComposeMessage returns, free the memory associated bstrEmailAddress with SysFreeString, as shown in the following code example.

    SysFreeString(bstrEmailAddress);
    
  16. You've added all of the necessary code to the SendEmailMessage method. Verify that the code looks like the following code example.

    void SendEmailMessage()
    {
        BSTR bstrEmailAddress;
        g_pContact->get_Email1Address(&bstrEmailAddress);
    
        /* Declare the MAILCOMPOSEFIELDS structure */
        MAILCOMPOSEFIELDS mailFields = {0};
    
        /* Set up the e-mail message */
        mailFields.cbSize = sizeof(mailFields);
        mailFields.pszSubject = _T("The picture we discussed");
        mailFields.pszBody = tszBaseMessage;
    
        /* Attach the selected picture to the message 
           and specify the account to use */
        mailFields.pszTo = bstrEmailAddress;
        mailFields.pszAttachments = g_tszSelectedPicture;
        mailFields.cAttachments = 1;
        mailFields.pszAccount = _T("ActiveSync");
        mailFields.dwFlags = MCF_ACCOUNT_IS_NAME;
    
        MailComposeMessage(&mailFields);
    
        SysFreeString(bstrEmailAddress);
    }
    

    The last thing you need to do before testing the application is to call the SendEmailMessage function from OnCOMMAND_SELECT. The SendEmailMessage function should only be called if the user selected an image.

  17. Locate the OnCOMMAND_SELECT method.

  18. Immediately after the call to SelectPicture, add an if statement to verify that the called return successfully. In the if statement, call SendEmailMessage, as shown in the following code example.

    if (bSuccess)
    {
        SendEmailMessage();
    }
    
    
  19. Verify that the OnCOMMAND_SELECT method looks like the following code example.

    void OnCOMMAND_SELECT()
    {
        bool bSuccess = SelectContact();
        if (bSuccess)
        {
            bSuccess = SelectPicture();
            if (bSuccess)
            {
                SendEmailMessage();
            }
        }
    }
    

You are ready to test the new application functionality.

To test the new functionality

  1. Click Build | Build NewNativeAPIs. Correct any compilation errors before you proceed.

  2. Verify that Windows Mobile 5.0 Smartphone Emulator is still selected in the drop-down list.

  3. Start the application by clicking Debug | Start Without Debugging. Be aware that it may take a few seconds for the emulator to start.

    Note   You cannot use the debugger when the application displays the Picture Picker. If you launch the application with the debugger, the emulator will freeze when you attempt to select a picture by using the Picture Picker. If you need to launch the application with the debugger to debug other aspects of your program, comment out the line in OnCOMMAND_SELECT that calls SelectPicture and replace it with the following line of code, which copies a file path into the same global variable that SelectPicture populates.

    // bSuccess = SelectPicture();
    _tcscpy(g_tszSelectedPicture, _T("\\Images\\eek.jpg"));
    
  4. If necessary, bring the emulator to the foreground, and then click the left soft key under the word Select.

  5. Click Adams, Jay from the Contact Picker.

    The Picture Picker appears and should look similar to Figure 6.

    Figure 6. The Picture Picker

  6. Using the navigation pad, browse to a picture, and then click the left soft key under the word Select to select the picture.

  7. The e-mail form displays and is fully populated with the e-mail address, subject, body text, and attachment. Verify that the e-mail form looks similar to Figure 7.

    Figure 7. Composing an e-mail message with a picture attachment. Click the thumbnail for a larger image.

  8. You can modify any portion of the message. Send the message by clicking the left soft key under the word Send.

  9. Close the application by clicking the right soft key under the word Exit.

    To verify that the message was actually submitted to the e-mail system, check the Pocket Outlook Outbox folder.

  10. Display the Smartphone Home screen by clicking the Home button (the button below the left soft key and it has a picture of a house).

  11. Click the left soft key under the word Start. Using the navigation pad, navigate to the Messaging icon, select it, and then click Enter.

  12. Click Enter on the navigational pad to select Outlook E-mail.

  13. Click the right soft key under the word Menu.

  14. Select Folders, as shown in Figure 8, and then click Enter.

    Figure 8. Pocket Outlook E-mail folders

  15. Select Outbox, and then click Enter on the emulator's navigational pad.

    You should see the following message: The picture we discussed. If you selected Adams, Jay, the message will be addressed to jadams@wingtiptoys.com. This is the message that the application sent.

Exercise 3: Using the State and Notifications Broker API to Retrieve System State Information

The State and Notifications Broker API is a very comprehensive API that provides access to over one hundred device state values and state value change notifications. All state values are retrieved from the registry. In this exercise, you will use the State and Notifications Broker API to retrieve the device owner's name and phone number. You will then use this information to update the e-mail message you sent in the last exercise to provide a more professional closing. You will type some of the code yourself, and you will uncomment some existing code.

The State and Notifications Broker API provides two functions for retrieving device state information: the RegistryGetString function for string state values and the RegistryGetDWORD function for DWORD values. You can find the registry location and data type of the available state values in the Windows Mobile 5.0 SDK documentation. This documentation is a great help, but because the registry key names and value names are strings, there is no way to know about any error that might exist in the registry key or value name strings at compile time. Instead, the program will fail at run time. To avoid this failure, the State and Notifications Broker API provides a header file, snapi.h, that provides a complete list of #define values for the standard State and Notifications Broker API state values. The #define values include the root key, the key name, and value name for each state value.

To use State and Notifications Broker API to retrieve device information

  1. Verify that the owner information has been properly set on your emulator. Click the Home button. You should see the owner name of Judy Lew, as shown in Figure 9. The device owner information provides a standard way to access personalized information about the device owner and is easily customized for each individual user's device.

    Figure 9. The Home screen on a Smartphone emulator that displays the owner's name. Click the thumbnail for a larger image.

  2. To use the State and Notifications Broker API, you must include the RegExt.h header file, and to use the State and Notifications Broker #define values, you must include the snapi.h header file. Add these #include lines of code, as shown in the following code example, near the other #include statements at the top of NewNativeAPIs.cpp.

    #include <RegExt.h>
    #include <snapi.h>
    
  3. Locate the GetMessageClosing function that returns a TCHAR *. This function has been started for you, and you will complete the implementation.

  4. In the function, an HRESULT variable named hr with an initial value of S_OK is already declared for you. Declare two TCHAR arrays: tszOwnerName and tszOwnerPhone. They should be sized to 128 and 32 respectively, as shown in the following code example. These arrays will be used to store the values retrieved from the State and Notifications Broker API.

    TCHAR tszOwnerName[128];
    TCHAR tszOwnerPhone[32];
    
  5. Use RegistryGetString, the SN_OWNERNAME_VALUE constant, the SN_OWNERNAME_PATH constant, and the SN_OWNERNAME_VALUE constant from snapi.h to retrieve the device's owner name. Store the value in tszOwnerName. Place this code on the blank line immediately following the comment that instructs you to retrieve device owner settings, as shown in the following code example.

    /* Retrieve device owner settings */
    hr = RegistryGetString(SN_OWNERNAME_ROOT, 
        SN_OWNERNAME_PATH, SN_OWNERNAME_VALUE,
        tszOwnerName, sizeof(tszOwnerName));
    
  6. Retrieve the device owner's phone number by using the SN_OWNERPHONENUMBER_ constants in snapi.h. Store the value in tszOwnerPhone, as shown in the following code example.

    hr = RegistryGetString(SN_OWNERPHONENUMBER_ROOT, 
        SN_OWNERPHONENUMBER_PATH, SN_OWNERPHONENUMBER_VALUE,
        tszOwnerPhone, sizeof(tszOwnerPhone));
    
  7. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    _tcscpy(tszMessageClosing, _T("\n\nSincerely,\n"));
    _tcscat(tszMessageClosing, tszOwnerName);
    _tcscat(tszMessageClosing, _T("\n"));
    _tcscat(tszMessageClosing, tszOwnerPhone);
    

    This code generates a closing phrase for the e-mail message by using the owner's name and phone number that you retrieved in the previous steps, and by storing the phrase in tszMessageClosing. The message is formatted as "\n\nSincerely, \n name\n phone".

    The last line of the function is already typed for you; it returns tszMessageClosing.

  8. Verify that the complete GetMessageClosing function looks like the following code example.

    TCHAR *GetMessageClosing(TCHAR *tszMessageClosing)
    {
        HRESULT hr = S_OK;
        TCHAR tszOwnerName[128];
        TCHAR tszOwnerPhone[32];
    
        /* Retrieve device owner settings */
        hr = RegistryGetString(SN_OWNERNAME_ROOT, 
            SN_OWNERNAME_PATH,SN_OWNERNAME_VALUE, 
            tszOwnerName, sizeof(tszOwnerName));
        hr = RegistryGetString(SN_OWNERPHONENUMBER_ROOT, 
            SN_OWNERPHONENUMBER_PATH,  SN_OWNERPHONENUMBER_VALUE,
            tszOwnerPhone, sizeof(tszOwnerPhone));
    
        /* Generate the closing message with the retrieved values */
        _tcscpy(tszMessageClosing, _T("\n\nSincerely,\n"));
        _tcscat(tszMessageClosing, tszOwnerName);
        _tcscat(tszMessageClosing, _T("\n"));
        _tcscat(tszMessageClosing, tszOwnerPhone);
    
        return tszMessageClosing;
    }
    

    The last part of this procedure is to concatenate the closing to the end of the e-mail message.

  9. In the SendEmailMessage function, immediately after the call to get_Email1Address, declare two TCHAR array variables tszMessageBody and tszMessageClosing. Size them to 1024 and 512 respectively, as shown in the following code example.

    TCHAR tszMessageBody[1024];
    TCHAR tszMessageClosing[512];
    
  10. Call GetMessageClosing passing tszMessageClosing. Copy the tszMessageBase constant into tszMessageBody, and then concatenate tszMessageClosing to the end of tszMessageBody, as shown in the following code example.

    GetMessageClosing(tszMessageClosing);
    _tcscpy(tszMessageBody, tszBaseMessage);
    _tcscat(tszMessageBody, tszMessageClosing);
    
  11. Modify the assignment to the pszBody member of the MAILCOMPOSEFIELDS structure to use tszMessageBody instead of tszBaseMessage. Change the line that currently sets mailFields.pszBody = tszBaseMessage to the following code.

    mailFields.pszBody = tszMessageBody;
    
  12. Verify that the updated SendEmailMessage function looks like the following code example.

    void SendEmailMessage()
    {
        BSTR bstrEmailAddress;
        g_pContact->get_Email1Address(&bstrEmailAddress);
    
        TCHAR tszMessageBody[1024];
        TCHAR tszMessageClosing[512];
        GetMessageClosing(tszMessageClosing);
        _tcscpy(tszMessageBody, tszBaseMessage);
        _tcscat(tszMessageBody, tszMessageClosing);
    
        /* Declare the MAILCOMPOSEFIELDS structure */
        MAILCOMPOSEFIELDS mailFields = {0};
    
        /* Set up the e-mail message */
        mailFields.cbSize = sizeof(mailFields);
        mailFields.pszSubject = _T("The picture we discussed");
        mailFields.pszBody = tszMessageBody;
    
    
        /* Attach the selected picture to the message 
            and specify the account to use */
        mailFields.pszTo = bstrEmailAddress;
        mailFields.pszAttachments = g_tszSelectedPicture;
        mailFields.cAttachments = 1;
        mailFields.pszAccount = _T("ActiveSync");
        mailFields.dwFlags = MCF_ACCOUNT_IS_NAME;
    
        HRESULT hr = MailComposeMessage(&mailFields);
    
      SysFreeString(bstrEmailAddress);
    }
    

    To verify that the message now contains the closing, follow the steps in the procedure titled, "To test the new functionality," in Exercise 2. You should see that the e-mail message now contains a closing with Sincerely Judy Lew on the second line from the bottom of the message, which is followed by 603.555.9999 on the next line, as shown in Figure 10.

    Figure 10. The owner's name and phone number have been added to the message. Click the thumbnail for a larger image.

Exercise 4: Using the State and Notifications Broker API to Receive Notifications of Changes in System State

In addition to providing access to the many state values on the device, the State and Notifications Broker API also supports notifying an application about changes to a value. In this exercise, you will update the application to automatically show a contact's information when a contact calls the user and when the user calls a contact. You will do this by monitoring the connected caller OID state value.

This value contains the OID of a Pocket Outlook contact when the Smartphone is on a call to a phone number that is contained in the Contacts list. In this exercise, you will add the code necessary to register with the State and Notifications Broker API to be notified any time the connected caller's contact OID changes. By using this notification, the application can easily locate the contact that corresponds to the connected caller.

The State and Notifications Broker API provides a number of different ways to receive notifications including callback functions, message queues, and window messages. In this exercise, you will use the RegistryNotifyWindow function that registers the application to receive a user-defined message sent to an application window, which indicates that a value has changed.

To register for notification

  1. You need to define the message identifier to be sent to the application. Near the top of the NewNativeAPIs.cpp source file, locate the tszBaseMessage constant declaration. Immediately following the tszBaseMessage declaration, declare a constant DWORD value named WM_CALLEROIDCHANGED. Set the value to WM_APP + 1, as shown in the following code example.

    const DWORD WM_CALLEROIDCHANGED = WM_APP + 1;
    
  2. The global variable declarations are located above the constant declarations. Add a global declaration for an HREGNOTIFY handle named g_hNotify that is initialized to NULL, as shown in the following code example.

    HREGNOTIFY          g_hNotify = NULL;
    
  3. Add the actual notification registration to the InitInstance function before the function's return statement. Use RegistryNotifyWindow. Similar to when you retrieve values, you must specify the registry key and value name that corresponds to the value of interest. You can use the constants that are declared in snapi.h to reference the registry key and value. Pass g_hWnd as the window to notify, WM_CALLEROIDCHANGED as the message to send, and the address of g_hNotify to receive the registration handle, as shown in the following code example.

    HRESULT hr = RegistryNotifyWindow(SN_PHONETALKINGCALLERCONTACT_ROOT,
        SN_PHONETALKINGCALLERCONTACT_PATH, SN_PHONETALKINGCALLERCONTACT_VALUE, 
        g_hWnd, WM_CALLEROIDCHANGED, 0, NULL, &g_hNotify);
    

    In addition to registering for notification, the application is responsible for closing the notification that is handled when the application no longer needs to receive the notification.

  4. To close the notification handle, use RegistryCloseNotification. Locate the IDM_EXIT case statement in WndProc. Add the function call to close g_hNotify immediately before the call to DestroyWindow, as shown in the following code example. Only close the notification handle if it is non-NULL.

    if (g_hNotify)
        RegistryCloseNotification(g_hNotify);
    

    You have completed the notification registration. The application main window receives a WM_CALLEROIDCHANGED message any time a call is received from or made to someone in the user's Contacts list.

In the next procedure, you will add the code that is necessary for the application to handle the WM_CALLEROIDCHANGED message. You will modify the WndProc function to call the OnCALLEROIDCHANGED function when the message is received. Then, in the following procedure, you will add code to implement the OnCALLEROIDCHANGED function.

To handle state change notifications

  1. In the WndProc function, modify the switch (message) statement to include a case to handle WM_CALLEROIDCHANGED, as shown in the following code example.

    case WM_CALLEROIDCHANGED:
    
  2. In the newly added case, add a call to the OnCALLEROIDCHANGED function that is followed by a BREAK statement, as shown in the following code example.

    OnCALLEROIDCHANGED();
    break;
    
  3. Verify that the WndProc function looks similar to the following code example.

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, 
                WPARAM wParam, LPARAM lParam)
    {
        int wmId, wmEvent;
    
        switch (message) 
        {
            case WM_CALLEROIDCHANGED:
                OnCALLEROIDCHANGED();
                break;
            case WM_COMMAND:
                // Code to handle WM_COMMAND ...
            case WM_CREATE:
                // Code to handle WM_CREATE ...
            case WM_PAINT:
                // Code to handle WM_PAINT ...
            case WM_DESTROY:
                // Code to handle WM_DESTROY ...
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    

    At this point, the OnCALLEROIDCHANGED function will be called any time the user has a phone conversation with someone in the Contacts list. You need to modify the OnCALLEROIDCHANGED function so the correct contact information is displayed.

To display the caller's contact information

  1. Locate the OnCALLEROIDCHANGED function with a void return type. This function has been started for you, and you will complete the implementation.

  2. In the function, a DWORD variable named dwOID is declared with an initial value of -1, and an HRESULT variable named hr is declared with an initial value of S_OK. Retrieve the value of the caller OID into dwOID with the RegistryGetDWORD function. Use the same registry key and value name that you specified in the procedure "To register for notification.". Assign the function return value to hr, as shown in the following code example.

    hr = RegistryGetDWORD(SN_PHONETALKINGCALLERCONTACT_ROOT, 
        SN_PHONETALKINGCALLERCONTACT_PATH, SN_PHONETALKINGCALLERCONTACT_VALUE, 
        &dwOID);
    

    You're now ready to display the contact by using DisplayContact.

  3. Remember that every call may not be to someone who is stored in the Contacts list. So, you need to add code that will only display the contact if the call to RegistryGetDWORD successfully retrieves a value, as shown in the following code example.

    if (SUCCEEDED(hr))
    {
        DisplayContact(dwOID);
    }
    
  4. There's a strong chance that your application may be running in the background at the time a phone call occurs. To assure your application is in the foreground, call the SetForegroundWindow function and pass the main window handle. Add the call to SetForegroundWindow immediately after the call to DisplayContact, as shown in the following code example.

    SetForegroundWindow(g_hWnd);
    
  5. Verify that the complete OnCALLEROIDCHANGED function looks like the following code example.

    void OnCALLEROIDCHANGED()
    {
        DWORD dwOID = -1;
        HRESULT hr = S_OK;
    
        hr = RegistryGetDWORD(SN_PHONETALKINGCALLERCONTACT_ROOT, 
            SN_PHONETALKINGCALLERCONTACT_PATH, SN_PHONETALKINGCALLERCONTACT_VALUE, 
            &dwOID);
        if (SUCCEEDED(hr))
        {
            DisplayContact(dwOID);
            SetForegroundWindow(g_hWnd);
        }
    }
    

You are now ready to test the new application functionality. There is no support for receiving calls to the emulator, so you'll test the feature by manually placing a phone call to one of the contacts.

To test the new functionality

  1. Click Build | Build Solution. Correct any compilation errors before you proceed.

  2. Verify that Windows Mobile 5.0 Smartphone Emulator is selected in the drop-down list.

  3. Start the application by clicking Debug | Start Without Debugging.

  4. View the device's Home screen by clicking the Home button. The application is still running in the background.

  5. Using the emulator's keypad, dial the phone number 6035552864.

  6. Click the Talk button (the button below the Home button, and it has a picture of a green phone receiver on it). The phone dials, and then connects the call. Just after the phone call connects, the contact information for Jim Wilson automatically appears, as shown in Figure 11.

    Figure 11. The contact that you dialed is displayed in the application. Click the thumbnail for a larger image.

  7. End the call by clicking the End button (the button with a picture of a red phone receiver on it.) Try repeating Steps 4 through 7 by using a phone number that does not correspond to a contact, such as 2122225555. The application remains in the background.

  8. End the call, and then try another phone number that corresponds to a contact such as 3105553662. Just after the phone call connects, the contact information for Jane Clayton automatically appears. End the call.

  9. When you are satisfied that the application is working as you expect, close the application by clicking the right soft key under the word Exit. You may need to first press the Back key to return to the application.

Exercise 5: Using the Camera Capture API (optional)

This optional exercise demonstrates the new Camera Capture API. As you might expect, this API requires that a device with a camera is attached. The Windows Mobile 5.0 emulators do not provide camera support; therefore, they are not compatible with the Camera Capture API unless you install a virtual camera driver. If you are sitting at a lab station with a physical Smartphone attached to the desktop computer, the attached Smartphones have cameras; therefore, you can use the attached device to do this optional exercise.

The Camera Capture API is exposed to native code developers through the SHCameraCapture function. It provides a window for viewing and capturing both photos and video in a device-independent way. In this exercise, you will use the Camera Capture API to modify the application to allow the user to take a photo and send it to a contact—rather than sending one of the stored images from the device. You will type some of the code yourself, and you will uncomment some existing code.

To add Camera Capture API support

  1. Locate the function named TakePicture that has a bool return value. This function has been started for you, and you will complete the implementation.

  2. In the function a bool variable named bSuccess is declared with an initial value of false, and an HRESULT variable named hr is declared with an initial value of S_OK. Native code developers access the Camera Capture API by using the SHCameraCapture function. The SHCAMERACAPTURE structure controls the behavior of SHCameraCapture and contains the path to the created picture file when SHCameraCapture returns. Declare an instance of the SHCAMERACAPTURE structure named shcc on the blank line immediately following the comment that instructs you to declare the SHCAMERACAPTURE structure, as shown in the following code example.

    /* Declare the SHCAMERACAPTURE structure */
    SHCAMERACAPTURE shcc = {0};
    
  3. Uncomment the following lines of code by highlighting them in the code editor, and then by clicking Edit | Advanced | Uncomment Selection.

    shcc.cbSize = sizeof(shcc);
    shcc.hwndOwner = g_hWnd;
    shcc.Mode = CAMERACAPTURE_MODE_STILL;
    shcc.StillQuality = CAMERACAPTURE_STILLQUALITY_NORMAL;
    shcc.pszInitialDir = _T("\\My Documents\\My Photos");
    

    This code sets the cbSize member to the size of the structure and sets the hwndOwner member to the global variable g_hWnd.

    The Camera Capture API supports capturing still photos, video, and video with sound. The Mode member of SHCAMERACAPTURE controls which of these modes is used. In your application, you will be capturing a still photo, so the code assigns CAMERACAPTURE_MODE_STILL to the Flags member.

    The StillQuality member indicates the compression level of the captured image, which affects the image quality. A value of CAMERACAPTURE_QUALITY_LOW indicates that the maximum supported compressions is to be used, thereby creating the smallest image file size of the available options which in turn have the lowest image quality. CAMERACAPTURE_QUALITY_HIGH indicates that minimal compression should be used. This option produces the largest files with the highest quality image. In your application, the code sets the quality to CAMERACAPTURE_QUALITY_NORMAL, which produces images of a size and quality between CAMERACAPTURE_QUALITY_LOW and CAMERACAPTURE_QUALITY_HIGH.

    Note   The exact level of the compression used in each case depends on the capabilities of the actual camera and drivers.

    The pszInitialDir member indicates where the photo files are to be stored. The code sets this location to \My Documents\My Photos.

    You are now ready to make the call to SHCameraCapture.

  4. Call SHCameraCapture, and pass the address of the SHCAMERACAPTURE structure. Assign the SHCameraCapture return value to hr, as shown in the following code example.

    hr = SHCameraCapture(&shcc);
    
  5. After the call to SHCameraCapture, use the SUCCEEDED macro to verify that the call was successful; assign its value to bSuccess. If the call succeeded, copy the generated file name (szFile) to the same global variable used in your implementation of the SelectPicture method, g_tszSelectedPicture, as shown in the following code example.

    bSuccess = SUCCEEDED(hr);
    if (bSuccess)
        _tcscpy(g_tszSelectedPicture, shcc.szFile);
    

    The last line of TakePicture is already typed for you. It returns the value of bSuccess.

  6. You've added all of the necessary code to the TakePicture method. Verify that the code looks like the following code example.

    bool TakePicture()
    {
        bool bSuccess = false;
        HRESULT hr = S_OK;
    
        /* Declare the SHCAMERACAPTURE structure */
        SHCAMERACAPTURE shcc = {0};
    
        /* Set up the camera capture structure */
        shcc.cbSize = sizeof(shcc);
        shcc.hwndOwner = g_hWnd;
        shcc.Mode = CAMERACAPTURE_MODE_STILL;
        shcc.StillQuality = CAMERACAPTURE_STILLQUALITY_NORMAL;
        shcc.pszInitialDir = _T("\\My Documents\\My Photos");
    
        hr = SHCameraCapture(&shcc);
        bSuccess = SUCCEEDED(hr);
        if (bSuccess)
            _tcscpy(g_tszSelectedPicture, shcc.szFile);
    
        return bSuccess;
    }
    
  7. Comment out the call to the SelectPicture function in OnCOMMAND_SELECT, as shown in the following code example.

    //bSuccess = SelectPicture();
    
  8. Replace the call to the SelectPicture function with a call to TakePicture, as shown in the following code example.

    bSuccess = TakePicture();
    

    Similar to the call to SelectPicture, TakePicture should only be called if the SelectContact function returned a value indicating success, so the rest of the code will work as it is currently typed.

  9. Verify that the OnCOMMAND_SELECT function looks like the following code example.

    void OnCOMMAND_SELECT()
    {
        bool bSuccess = SelectContact();
        if (bSuccess)
        {
            // bSuccess = SelectPicture();
            bSuccess = TakePicture();
            if (bSuccess)
            {
                SendEmailMessage();
            }
        }
    }
    

You are now ready to test the new application functionality.

To test the new functionality

  1. Click Build | Build Solution. Correct any compilation errors before you proceed.

  2. Verify that Windows Mobile 5.0 Smartphone Device is selected in the drop-down list.

  3. Start the application by clicking Debug | Start Without Debugging.

  4. Click the left soft key under the word Select.

  5. Select Adams, Jay from the Contact Picker. The Camera Capture window appears.

  6. Point the Smartphone at a person or object you would like to photograph, and then press the left soft key to take a picture.

  7. The e-mail form appears and will be fully populated with the e-mail address, subject, body text, and your photo as the attachment. Send the e-mail message.

  8. Close the application.

    You can now verify that the photo was actually captured.

  9. On the actual Smartphone, open the Home screen by pressing the Home button.

  10. Press the left soft key to open the Start menu.

  11. Press the left soft key again for more programs.

  12. Choose Pictures & Videos, and then press the Action key (in the middle of the navigation pad).

  13. You should now see the thumbnails of several pictures—including the one you just took. Browse to and select your picture to see it enlarged. If you do not see your picture, press the right soft key to bring up the menu, and then select Option 7 to go to My Documents\My Pictures.

Summary

In this lab, you performed the following exercises:

  • Using Contact Picker to Provide Search Capabilities
  • Using Messaging and the Picture Picker to Send E-mail with Attachments
  • Using the State and Notifications Broker API to Retrieve System State Information
  • Using the State and Notifications Broker API to Receive Notifications of Changes in System State
  • Using the Camera Capture API (optional)

In this lab, you used the some of the new Windows Mobile 5.0 APIs to use the Contact Picker to provide search capabilities for Pocket Outlook contacts. By using the Picture Picker, you provided the user the ability to locate pictures by using thumbnail views. By using the Messaging API, the application generated an e-mail message with the picture as an attachment and presented the e-mail message to the user for review. The message was displayed using the standard e-mail message compose form.

By using the new State and Notifications Broker API, you retrieved the owner information from the Smartphone and used that information to provide a customized closing to the e-mail message that included the Smartphone owner's name and telephone number. By using the State and Notifications Broker API, you also added support to the application for being notified about when the Smartphone user is talking to someone whose phone number is stored in the Pocket Outlook Contacts list. When such a call occurs, the application automatically locates the contact's information and displays it to the user. If you performed the optional exercise, you also provided integrated support for taking a picture and attaching it to the e-mail message.

Appendix A: Terminating an Application That Is Running on a Device or Emulator

This appendix describes how to terminate an application that is running on a device or emulator. This is useful for cases where an application has been started without having the debugger attached and the application needs to be terminated so a new copy of the application can be deployed. You will terminate the application by using the Remote Process Viewer remote tool in Visual Studio.

Before you can terminate a process, you need to know the name of the executable file. In most cases, this is the same name as the Visual Studio project. If you are uncertain about the name of the executable file, you can find it in the project's properties.

To terminate an application that is running on a device or emulator by using the Remote Process Viewer remote tool

  1. In Visual Studio, click Project | xxx Properties, where xxx represents the name of the current project.

  2. In the Project Properties dialog box, note the value in the Assembly Name field. This is the name that the executable file will be running on the device or emulator.

  3. Close the Properties dialog box.

    Now, you can terminate the process.

  4. On the desktop computer, click Start | Microsoft Visual Studio 2005 | Visual Studio Remote Tools | Remote Process Viewer.

  5. When prompted by the Select a Windows CE Device dialog box, select the emulator or device where the application is running, as shown in Figure 12, and then click OK.

    Figure 12. Select a Windows CE Device dialog box

  6. After the connection to the emulator or device completes, select the application that you want to terminate in the top pane of the Remote Process Viewer, as shown in Figure 13.

    Figure 13. Selecting the application to terminate. Click the thumbnail for a larger image.

    You may need to widen the Process column (the leftmost column) to fully view the process names.

  7. In Windows CE Remote Process Viewer, click File | Terminate Process to terminate the process.

    Note   Be certain that the correct process is selected before you click Terminate Process. Terminating the incorrect process may render the device or emulator unusable, requiring it to be reset before you can use it again.

  8. Verify that the process is terminated by selecting Target | Refresh. Scroll to the top pane of Windows CE Remote Process Viewer again. If the application name still appears, the process was not terminated, and you need to repeat these steps.

Note   Most processes terminate in one try; however, depending on the state of the application, terminating a process occasionally takes two attempts.

Appendix B: Setting Up Your Computer

Before starting the HOL, you need to run an application to add the necessary Pocket Outlook contacts and image files to the emulator.

Note   Don't be concerned that the initialization program is written by using the .NET Compact Framework. This application was chosen for its simplicity in producing this type of program. All coding done in the HOL is performed in C++.

To populate the emulator with test data

  1. Download and install MEDC06_HOL307.msi.

    Note   If you used an emulator in a previous HOL, you should do a hard reset on the emulator before starting this HOL. (On the emulator, click File | Reset | Hard.)

    Note   If you receive an error during deployment that indicates that the process or file is in use, this means that the program is still running on the emulator and must be closed before a new copy can be deployed and run. This error may appear anytime in the HOL that you deploy the emulator. See Appendix A in this HOL for instructions about exiting a running application.

  2. If Visual Studio 2005 is not already open, open it by clicking Start | All Programs | Microsoft Visual Studio 2005 | Microsoft Visual Studio 2005.

  3. In Visual Studio 2005, click File | Open | Project/Solution.

  4. In the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MED307_Intro_New_WM5_Native_API\Setup Files\.

  5. Select InitializeLab.sln.

  6. Click Open. The InitializeLab solution opens.

  7. In the Configuration Manager, verify that Windows Mobile 5.0 Smartphone Emulator is selected as the targeted device, as shown in Figure 14.

    Figure 14. Emulator selection

  8. Click Debug | Start Debugging to start the application.

  9. If prompted by the Deploy InitializeLab dialog box, verify that Windows Mobile 5.0 Smartphone Emulator is selected, and then click Deploy.

    Note   If the emulator does not appear, look for a Windows taskbar button, and click it to bring the emulator to the foreground. Be aware that the first time the emulator starts, it may take several minutes.

    Note   If you receive an error during deployment that indicates that the process or file is in use, this means that the program is still running on the emulator and must be exited before a new copy can be deployed and run. This error may appear any time in the HOL that you deploy the emulator. See Appendix A in this HOL for instructions about exiting a running application.

  10. When the application appears on the emulator, click the left soft key, which is the grey button located under the word Start, as shown in Figure 15. The application displays a wait cursor and indicates that contacts and then images are being added.

    Figure 15. Initializing the device for the HOL

  11. When the application displays Initialization Complete, click the right soft key, which is the grey button located under the word Exit to close the application.

  12. In Visual Studio, click File | Close Solution.

  13. If Visual Studio prompts you to save, click Yes.

    Note   If you close the emulator, be sure to save the emulator state.

Before starting this HOL, you need to populate the device owner information.

To populate the device owner information

  1. On the emulator, display the Home screen by pressing the Home button.

  2. Press the left soft key under the word Start.

  3. Browse to, and then select the Settings icon by using the emulator's navigational pad and ENTER key.

  4. Choose option 9 More.

  5. Choose option 4 Owner Information.

  6. Type Judy Lew for the value of Name.

  7. Type 603.555.9999 for the value of Telephone Number.

  8. Press the left soft key under the word Done.

  9. Press the left soft key again under the word Done.

  10. Choose the Settings icon by using the emulator's ENTER key.

  11. Choose option 4 Home Screen.

  12. Press the ENTER key to view the Home screen layout options.

  13. Use the direction keys to scroll to Windows Simple, and press the ENTER key. This setting causes the owner's name to appear on the Home screen, which is not necessary to complete this HOL, but it allows you to see that the State and Notifications Broker API uses the owner information.

  14. Press the left soft key under the word Done.

  15. Again, press the left soft key under the word Done.

  16. Press the Home button, and verify that the owner name is displayed, as shown in Figure 16.

    Figure 16. The Home screen displays the owner's name. Click the thumbnail for a larger image.