Training
Certifications
Books
Special Offers
Community




 
Programming Microsoft® Windows® CE, Second Edition
Author Douglas Boling
Pages 1040
Disk 2 Companion CD(s)
Level All Levels
Published 06/13/2001
ISBN 9780735614437
ISBN-10 0-7356-1443-1
Price(USD) $59.99
To see this book's discounted price, select a reseller below.
 

More Information

About the Book
Table of Contents
Sample Chapter
Index
Related Series
Related Books
About the Author

Support: Book & CD

Rate this book
Barnes Noble Amazon Quantum Books

 


Chapter 15: Extending the Pocket PC continued


Writing an Input Method

The soft input panel, or SIP, provides Pocket PC users with a method of "keyboard"-style input. I put keyboard in quotes because although the application sees keyboard messages from the SIP, the user might be entering those characters using a handwriting recognizer. The Pocket PC comes bundled with two ways of entering character data: a tiny drawing of a keyboard on which the user can tap in characters and a handwriting recognizer that interprets strokes that the user makes with a stylus. You can also design your own method of input rather easily. A component that provides this functionality is called an input method (IM), and it’s merely a COM object that exports an IinputMethod interface, and optionally either an IInputMethodEx or an IInputMethod2 interface. The IInputMethodEx and IInputMethod2 interfaces are both derived from the IInputMethod interface. IInputMethodEx is supported by Windows CE 2.11 and later versions, while IInputMethod2 was added with Windows CE 3.0 and the Pocket PC.

These new interfaces add new methods for dealing with the Input Method Editor (IME). The IME is used to propose a series of candidate characters in response to input in the SIP. The SIP doesn’t provide enough room to allow the user to enter thousands of discrete characters, so the IME is used when working with Asian languages. Unless your SIP needs to interface with the IME, the IInputMethod interface should be sufficient since it is compatible with all versions of Windows CE that support a SIP. No matter which interface is exposed, the purpose of the COM object is to create an input method window in response to requests from the input panel.

The Components of a SIP

A SIP is composed of two main components—the input panel and the input method. The input panel is supplied by the system. It creates the input panel window and provides both the message loop processing for the SIP and the window procedure for the input panel window. The input panel cooperates with the taskbar or another shell program to provide the user with the ability to switch between a number of installed input methods.

The input method is the installable portion of the SIP. It’s responsible for translating pen strokes and taps into keyboard input. The input method is also responsible for the look and feel of the SIP. In almost all cases, the input method creates a window that is a child of the input panel window. Within that child window, the input method draws its interface and interprets mouse messages. The input method then calls back to the input panel when it wants to generate a key event.

Each of these two components implements a COM interface, which then becomes the interface between them. The input method implements one of the IInputMethodxx interfaces, while the input panel implements three very similar interfaces: IIMCallback, IIMCallbackEx, and IIMCallback2. In the following paragraphs, I’ll talk about the IInputMethod, IInputMethod2, IIMCallback, and IIMCallback2 interfaces.

The interaction between the input panel and the input method is driven by the input panel. For the most part, the input method simply responds to calls made to its IInputMethod methods. Calls are made when the input method is loaded, when it’s unloaded, and when it’s shown or hidden. In response, the input method must draw in its child window, interpret the user’s actions, and call methods in the IIMCallback interface to send keys to the system or to control the input panel’s window.

Input methods are implemented as COM in-proc servers. Because of this, they must conform to the standard COM in-proc server specifications. This means that an input method is implemented as a DLL that exports DllGetClassObject and DllCanUnloadNow functions. Input methods must also export DllRegisterServer and DllUnregisterServer functions that perform the necessary registry registration and deregistration for the server DLL.

Threading Issues with Input Methods

Because the input panel and input method components are so tightly interrelated, you must follow a few rules when writing an input method. While you can use multiple threads in an input method, the interaction between the input panel and the input method is strictly limited to the input panel’s primary thread. This means that the input method should create any windows during calls to methods in the IInputMethod interface. This ensures that these windows will use the same message loop as the input panel’s window. This, in turn, allows the input panel to directly call the input method’s window procedures, as necessary. In addition, that same thread should make all calls made back to the IIMCallback interface.

In short, try not to multithread your input method. If you must use multiple threads, create all windows in your input method using the input panel’s thread. Secondary threads can be created, but they can’t call the IIMCallback interface and they shouldn’t create any windows.

The IInputMethod and IInputMethod2 Interfaces

The IInputMethod interface is the core of an IM. Using the interface’s methods, an IM should create any windows, react to any changes in the parent input panel window, and provide any cleanup when it’s released. The IInputMethod interface exports the following methods in addition to the standard IUnknown methods:

  • IInputMethod::Select  The user has selected the IM. The IM should create its window.
  • IInputMethod::Deselect  The user has selected another IM. The IM should destroy its window.
  • IInputMethod::Showing  The IM window is about to be displayed.
  • IInputMethod::Hiding  The IM window is about to be hidden.
  • IInputMethod::GetInfo  The system is querying the IM for information.
  • IInputMethod::ReceiveSipInfo  The system is providing information to the IM.
  • IInputMethod::RegisterCallback  The system is providing a pointer to the IIMCallback interface.
  • IInputMethod::GetImData  The IM is queried for IM-specific data.
  • IInputMethod::SetImData  The IM is provided IM-specific data.
  • IInputMethod::UserOptionsDlg  The IM should display an options dialog box to support the SIP Control Panel applet.

In addition to the preceding methods, the IInputMethod2 interface has the following methods:

  • IInputMethod2::RegisterCallback2  The system is providing a pointer to the IIMCallback2 interface.
  • IInputMethod2::SetIMMActiveContext  The system is informing the IM of the current state of the IME.

Let’s now look at these methods in detail so that we can understand the processing necessary for each. The descriptions of the methods for the IInputMethod interface also apply for the similarly named methods in the IInputMethod2 interface.

IInputMethod::Select

When the user chooses your input method, the DLL that contains your IM is loaded and the Select method is called. This method is prototyped as

HRESULT IInputMethod::Select (HWND hwndSip);

The only parameter is the handle to the SIP window that’s the parent of your input method’s main window. You should return S_OK to indicate success or E_FAIL if you can’t create and initialize your input method successfully.

When the Select method is called, the IM will have just been loaded into memory and you’ll need to perform any necessary initialization. This includes registering any window classes and creating the input method window. The IM should be created as a child of the SIP window because the SIP window is what will be shown, hidden, and moved in response to user action. You can call GetClientRect with the parent window handle to query the necessary size of your input window.

IInputMethod::GetInfo

After the input panel has loaded your IM, it calls the GetInfo method. The input panel calls this method to query the bitmaps that represent the IM. These bitmaps appear in the SIP button on the taskbar. In addition, the IM can provide a set of flags and the size and location on the screen where it would like to be displayed. This method is prototyped as

HRESULT IInputMethod::GetInfo (IMINFO *pimi);

The only parameter is a pointer to an IMINFO structure that the IM must fill out to give information back to the SIP. The IMINFO structure is defined as

typedef struct  {
    DWORD cbSize;
    HANDLE hImageNarrow;
    HANDLE hImageWide;
    int iNarrow;
    int iWide;
    DWORD fdwFlags;
    RECT rcSipRect;
} IMINFO;

The first field, cbSize, must be filled with the size of the IMINFO structure. The next two fields, hImageNarrow and hImageWide, should be filled with handles to image lists that contain the bitmaps that will appear on the taskbar SIP button. The Pocket PC’s menu bar uses the narrow image. However, for embedded systems, the shell has the flexibility to use either the wide 32-by-16-pixel bitmap or the narrow 16-by-16-pixel bitmap, depending on its needs. The input method must create these image lists and pass the handles in this structure. The IM is responsible for destroying the image lists when a user or an application unloads it. You can create these image lists in the GetInfo method as long as you design your application to know not to create the image lists twice if GetInfo is called more than once. Another strategy is to create the image lists in the Select method and store the handles as member variables of the IInputMethod object. Then when GetInfo is called, you can pass the handles of the already created image lists to the input panel.

The next two fields, iNarrow and iWide, should be set to the index in the image lists for the bitmap you want the SIP to use. For example, you might have two different bitmaps for the SIP button, depending on whether your IM is docked or floating. You can then have an image list with two bitmaps, and you can specify the index depending on the state of your IM.

The fdwFlags field should be set to a combination of the flags SIPF_ON, SIPF_DOCKED, SIPF_LOCKED, and SIPF_DISABLECOMPLETION, all of which define the state of the input panel. The first three flags are the same flags that I described in Chapter 14. When the SIPF_DISABLECOMPLETION flag is set, the auto-completion function of the SIP is disabled.

Finally, the rcSipRect field should be filled with the default rectangle for the input method. Unless you have a specific size and location on the screen for your IM, you can simply query the client rectangle of the parent SIP window for this rectangle. Note that just because you request a size and location of the SIP window doesn’t mean that the window will have that rectangle. You should always query the size of the parent SIP window when laying out your IM window.

IInputMethod::ReceiveSipInfo

The ReceiveSipInfo method is called by the input panel when the input panel is shown and then again when an application moves or changes the state of the input panel. The method is prototyped as

HRESULT IInputMethod::ReceiveSipInfo (SIPINFO *psi);

The only parameter is a pointer to a SIPINFO structure that I described in Chapter 14. When this method is called, only two of the fields are valid—the fdwFlags field and the rcSipRect field. The rcSipRect field contains the size and location of the input panel window, while the fdwFlags field contains the SIPF_xxx flags previously described. In response to the ReceiveSipInfo method call, the IM should save the new state flags and rectangle.

IInputMethod::RegisterCallback

The input panel calls the RegisterCallback method once, after the input method has been selected. The method is prototyped as

HRESULT IInputMethod::RegisterCallback (IIMCallback *lpIMCallback);

This method is called to provide a pointer to the IIMCallback interface. The only action the IM must take is to save this pointer so that it can be used to provide feedback to the input panel.

IInputMethod::Showing and IInputMethod::Hiding

The input panel calls the Showing and Hiding methods just before the IM is shown or hidden. Both these methods have no parameters and you should simply return S_OK to indicate success. The Showing method is also called when the panel is moved or resized. This makes the Showing method a handy place for resizing the IM child window to properly fit in the parent input panel window.

IInputMethod::GetImData and IInputMethod::SetImData

The GetImData and SetImData methods give you a back door into the IM for applications that need to have a special communication path between the application and a custom IM. This arrangement allows a specially designed IM to provide additional data to and from applications. The two methods are prototyped as

HRESULT IInputMethod::GetImData (DWORD dwSize, void* pvImData);
 
HRESULT IInputMethod::SetImData (DWORD dwSize, void* pvImData);

For both functions, pvImData points to a block of memory in the application. The dwSize parameter contains the size of the memory block.

When an application is sending data to a custom IM, it calls SHSipInfo with the SPI_SETSIPINFO flag. The pointer to the buffer and the size of the buffer are specified in the pvImData and dwImDataSize fields of the SIPINFO structure. If these two fields are nonzero, the input panel then calls the SetImData method with the pointer and the size of the buffer contained in the two parameters of the method. The input method then accepts the data in the buffer pointed to by pvImData. When an application calls SHSipInfo with the SPI_GETSIPINFO structure and nonzero values in pvImData and dwImDataSize, the input panel then calls the GetImData method to retrieve data from the input method.

IInputMethod::Deselect

When the user or a program switches to a different default IM, the input panel calls Deselect. Your input method should save its state (its location on the screen, for example), destroy any windows it has created, and unregister any window classes it has registered. It should also destroy any image lists it’s still maintaining. The prototype for this method is

HRESULT IInputMethod::Deselect (void);

After the Deselect method is called, the SIP will unload the input method DLL.

IInputMethod::UserOptionsDlg

The UserOptionsDlg method isn’t called by the input panel. Instead, the input panel’s Control Panel applet calls this method when the user taps the Options button. The IM should display a dialog box that allows the user to configure any settable parameters in the input method. The UserOptionsDlg method is prototyped as

HRESULT IInputMethod::UserOptionsDlg (HWND hwndParent);

The only parameter is the handle to the window that should be the parent window of the dialog box. Because the IM might be unloaded after the dialog box is dismissed, any configuration data should be saved in a persistent place such as the registry, where it can be recalled when the input panel is loaded again.

The following two methods are supported only in the IInputMethod2 interface. The IInputMethod2 interface is derived from IInputMethod; all the methods previously described are therefore implemented in IInputMethod2.

IInputMethod2::RegisterCallback2

The input panel calls the RegisterCallback2 method once, after the input method has been selected. The method is prototyped as

HRESULT IInputMethod2::RegisterCallback2 (IIMCallback2 *lpIMCallback);

This method is called to provide a pointer to the IIMCallback2 interface. The only action the IM must take is to save this pointer so that it can be used to provide feedback to the input panel.

IInputMethod2::SetIMMActiveContext

The input panel calls SetIMMActiveContext to inform the input method of changes in state of the IME. The method is prototyped as

HRESULT SetIMMActiveContext (HWND hwnd, BOOL bOpen, DWORD dwConversion, 
                             DWORD dwSentence, DWORD hkl);

The hwnd parameter is the handle of window control that has changed state. The bOpen parameter indicates whether the IME is on or off. The dwConversion and dwSentence parameters provide status on the current mode of the IME. The hkl parameter contains the handle to the current active keyboard layout.

The IIMCallback and IIMCallback2 Interfaces

The IIMCallback interface allows an IM to call back to the input panel for services such as sending keys to the operating system. Aside from the standard IUnknown methods that can be ignored by the IM, IIMCallback exposes only four methods:

  • IIMCallback::SetImInfo  Sets the bitmaps used by the input panel as well as the location and visibility state of the input method
  • IIMCallback::SendVirtualKey  Sends a virtual key to the system
  • IIMCallback::SendCharEvents  Sends Unicode characters to the window with the current focus
  • IIMCallback::SendString  Sends a string of characters to the window with the current focus

It’s appropriate that the IIMCallback interface devotes three of its four methods to sending keys and characters to the system because that’s the primary purpose of the IM.

The IIMCallback2 interface adds one method:

  • IIMCallback2:: SendAlternatives2  Sends data from the input method to the IME

Let’s take a quick look at each of these methods.

IIMCallback::SetImInfo

The SetImInfo method allows the IM control over its size and location on the screen. This method can also be used to set the bitmaps representing the IM. The method is prototyped as

HRESULT IIMCallback::SetImInfo (IMINFO *pimi);

The only parameter is a pointer to an IMINFO structure. This is the same structure that the IM uses when it calls the GetInfo method of the IInputMethod interface, but I’ll repeat it here for clarity.

typedef struct  {
    DWORD cbSize;
    HANDLE hImageNarrow;
    HANDLE hImageWide;
    int iNarrow;
    int iWide;
    DWORD fdwFlags;
    RECT rcSipRect;
} IMINFO;

This structure enables an IM to provide the input panel with the information that the IM retrieved in GetInfo. The IM must correctly fill in all the fields in the IMINFO structure because it has no other way to tell the input panel to look at only one or two of the fields. You shouldn’t re-create the image lists when you’re calling SetImInfo; instead, use the same handles you passed in GetInfo—unless you want to change the image lists used by the input panel. In that case, you’ll need to destroy the old image lists after you’ve called SetImInfo.

You can use SetImInfo to undock the input panel and move it around the screen by clearing the SIPF_DOCKED flag in fdwFlags and specifying a new size and location for the panel in the rcSipRect field. Because Windows CE doesn’t provide system support for dragging an input panel around the screen, the IM is responsible for providing such a method. The sample IM that I present beginning on page 879 supports dragging the input panel around by creating a gripper area on the side of the panel and interpreting the stylus messages in this area to allow the panel to be moved around the screen.

IIMCallback::SendVirtualKey

The SendVirtualKey method is used to send virtual key codes to the system. The difference between this method and the SendCharEvents and SendString methods is that this method can be used to send noncharacter key codes, such as those from cursor keys and shift keys, that have a global impact on the system. Also, key codes sent by SendVirtualKey are affected by the system key state. For example, if you send an a character and the Shift key is currently down, the resulting WM_CHAR message contains an A character. SendVirtualKey is prototyped as

HRESULT IIMCallback::SendVirtualKey (BYTE bVk, DWORD dwFlags);

The first parameter is the virtual key code of the key you want to send. The second parameter can contain one or more flags that help define the event. The flags can be either 0 or a combination of flags. You would use KEYEVENTF_KEYUP to indicate that the event is a key up event as opposed to a key down event and KEYEVENTF_SILENT, which specifies that the key event won’t cause a key click to be played for the event. If you use SendVirtualKey to send a character key, the character will be modified by the current shift state of the system.

IIMCallback::SendCharEvents

The SendCharEvents method can be used to send specific characters to the window with the current focus. The difference between this method and the SendVirtualKey method is that SendCharEvents gives you much more control over the exact information provided in the WM_KEYxxx and WM_CHAR messages generated. Instead of simply sending a virtual key code and letting the system determine the proper character, this method allows you to specify the virtual key and associate a completely different character or series of characters generated by this event. For example, in a simple case, calling this method once causes the messages WM_KEYDOWN, WM_CHAR, and WM_KEYUP all to be sent to the focus window. In a more complex case, this method can send a WM_KEYDOWN and multiple WM_CHAR messages, followed by a WM_KEYUP message.

This method is prototyped as

HRESULT IIMCallback::SendCharEvents (UINT uVK, UINT uKeyFlags, 
                   UINT uChars, UINT *puShift, UINT *puChars);

The first parameter is the virtual key code that will be sent with the WM_KEYDOWN and WM_KEYUP messages. The second parameter is an unsigned integer containing the key flags that will be sent with the WM_KEYDOWN and WM_KEYUP messages. The third parameter is the number of WM_CHAR messages that will be generated by this one event. The next parameter, puShift, should point to an array of key state flags, while the final parameter, puChar, should point to an array of Unicode characters. Each entry in the shift array will be joined with the corresponding Unicode character in the character array when the WM_CHAR messages are generated. This allows you to give one key on the IM keyboard a unique virtual key code and to generate any number of WM_CHAR messages, each with its own shift state.

IIMCallback::SendString

You use the SendString method to send a series of characters to the focus window. The advantage of this function is that an IM can easily send an entire word or sentence, and the input panel will take care of the details such as key down and key up events. The method is prototyped as

HRESULT IIMCallback::SendString (LPTSTR ptszStr, DWORD dwSize);

The two parameters are the string of characters to be sent and the number of characters in the string.

IIMCallback2:: SendAlternatives2

The SendAlternatives2 method provides a mechanism for the input method to send alternative characters to the IME. For languages with hundreds or thousands of characters, the input method might have to guess at the intended character entered by the user. These guesses or alternative characters are sent using SendAlternatives2 to the IME so that it can present the alternatives to the active control. If the control doesn’t handle the alternative suggestions, the first character in the list is used as the correct character. The prototype of SendAlternatives2 is

HRESULT SendAlternatives2(LMDATA * plmd);

The one parameter is a pointer to an LMDATA structure defined as

typedef struct  _tagLMDATA {
    DWORD dwVersion;
    DWORD flags;
    DWORD cnt;
    DWORD dwOffsetSymbols;
    DWORD dwOffsetSkip;
    DWORD dwOffsetScore;
}LMDATA;

The version field should be set to 0x10000. The flags field describes the format of the data in the table provided. The cnt field contains the number of entries in the table. The dwOffsetSymbols, dwOffsetSkip, and dwOffsetScore fields contain the offset of the start of the respective tables containing the alternative data. The data in the tables vary depending on how the IME and the input method agree to share data.

The NumPanel Example Input Method

The NumPanel example code demonstrates a simple IM. NumPanel gives a user a simple numeric keyboard including keys 0 through 9 as well as the four arithmetic operators: +, –, *, and / and the equal sign key (=). Although it’s not particularly useful to the user, NumPanel does demonstrate all the requirements of an input method. The NumPanel example is different from the standard IMs that come with the Pocket PC in that it can be undocked. The NumPanel IM has a gripper bar on the left side of the window that can be used to drag the SIP around the screen. When a user double-taps the gripper bar, the SIP snaps back to its docked position. Figure 15-4 shows the NumPanel IM in its docked position, while Figure 15-5 shows the same panel undocked.

Click to view graphic
Click to view graphic

Figure 15-4 The NumPanel IM window in its docked position

Click to view graphic
Click to view graphic

Figure 15-5 The NumPanel IM window undocked

The source code that implements NumPanel is divided into two main files, IMCommon.cpp and NumPanel.c. IMCommon.cpp provides the COM interfaces necessary for the IM, including the IInputMethod interface and the IClassFactory interface. IMCommon.cpp also contains DllMain and the other functions necessary to implement a COM in-proc server. NumPanel.c contains the code that implements the NumPanel window. This code comprises the NumPanel window procedure and the supporting message-handling procedures. The source code for NumPanel is shown in Figure 15-6.

Figure 15-6 The NumPanel source code

NumPanel.def

;
;Standard COM library DEF file
;
 
LIBRARY   NUMPANEL.DLL
 
EXPORTS 
   DllCanUnloadNow     @1 PRIVATE
   DllGetClassObject   @2 PRIVATE
   DllRegisterServer   @3 PRIVATE
   DllUnregisterServer @4 PRIVATE

IMCommon.rc

//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================
#include "windows.h"                 // For all that Windows stuff
#include "NumPanel.h"                // Program-specific stuff
 
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON        ICON   "NumPanel.ico" // Module icon
 
NarrowBmp      BITMAP "nkbd.bmp"     // Bmp used in image list
NarrowMask     BITMAP "nmask.bmp"    // Mask used in image list
WideBmp        BITMAP "widekbd.bmp"  // Bmp used in image list
WideMask       BITMAP "widemask.bmp" // Mask used in image list

IMCommon.h

//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================

#define ID_ICON        1
 
// **** Start of Generic COM declarations **** 
//======================================================================
// MyClassFactory - Object declaration
//
class MyClassFactory : public IClassFactory {
 
private:
    long m_lRef;
 
public:
    MyClassFactory();
    ~MyClassFactory();
 
    //IUnknown methods
    STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppv);
    STDMETHODIMP_(ULONG) AddRef (THIS);
    STDMETHODIMP_(ULONG) Release (THIS);
 
    //IClassFactory methods
    STDMETHODIMP CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, 
                                 LPVOID *ppv);
    STDMETHODIMP LockServer (BOOL fLock);
};
// **** End of Generic OLE declarations **** 
 
//======================================================================
// MyIInputMethod - Object declaration
//
class MyIInputMethod : public IInputMethod2 {
 
private:
    long m_lRef;
    HWND m_hwndParent;
    HWND m_hwndMyWnd;
    HIMAGELIST m_himlWide;
    HIMAGELIST m_himlNarrow;
    IIMCallback *m_pIMCallback;
 
public:
    MyIInputMethod();
    ~MyIInputMethod();
 
    //IUnknown methods
    STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppvObj);

    STDMETHODIMP_(ULONG) AddRef (THIS);
    STDMETHODIMP_(ULONG) Release (THIS);
 
    //IInputMethod
    HRESULT STDMETHODCALLTYPE Select (HWND hwndSip);
    HRESULT STDMETHODCALLTYPE Deselect (void);
    HRESULT STDMETHODCALLTYPE Showing (void);
    HRESULT STDMETHODCALLTYPE Hiding (void);
    HRESULT STDMETHODCALLTYPE GetInfo (IMINFO __RPC_FAR *pimi);
    HRESULT STDMETHODCALLTYPE ReceiveSipInfo (SIPINFO __RPC_FAR *psi);
    HRESULT STDMETHODCALLTYPE RegisterCallback (
                                   IIMCallback __RPC_FAR *lpIMCallback);
    HRESULT STDMETHODCALLTYPE GetImData (DWORD dwSize, LPVOID pvImData);
    HRESULT STDMETHODCALLTYPE SetImData (DWORD dwSize, LPVOID pvImData);
    HRESULT STDMETHODCALLTYPE UserOptionsDlg (HWND hwndParent);
 
    //IInputMethod2
    HRESULT STDMETHODCALLTYPE SetIMMActiveContext(HWND hwnd, BOOL bOpen,
                        DWORD dwConversion, DWORD dwSentence, DWORD hkl);
       HRESULT STDMETHODCALLTYPE RegisterCallback2 (
                                    IIMCallback2 RPC_FAR *lpIMCallback);
};

NumPanel.h

//======================================================================
// NPWnd.h - An include file
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================
 
#ifdef __cplusplus
extern "C"{
#endif 
 
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))   
 
struct decodeUINT {                            // Structure associates
    UINT Code;                                 // messages 
                                               // with a function. 
    LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
}; 
 
#define MYSIPCLS       TEXT ("MyNumPanelWndCls")
#define MYMSG_METHCALL (WM_USER+100)           // Used to pass info
#define MSGCODE_REGCALLBACK   0                // Notification codes for
#define MSGCODE_GETINFO       1                // MYMSG_METHCALL

#define MSGCODE_SETINFO       2
#define MSGCODE_REGCALLBACK2  3
 
#define GRIPWIDTH      9                       // Width of the gripper
#define FLOATWIDTH     200                     // Width of floating wnd
#define FLOATHEIGHT    100                     // Height of floating wnd
 
#define CXBTNS         5                       // Num columns of buttons
#define CYBTNS         3                       // Num rows of buttons
 
//
// Local data structure for keypad IM window
//
typedef struct {
    DWORD dwBtnDnFlags;
    IIMCallback *pIMCallback;
    IIMCallback2 *pIMCallback2;
    RECT rectDocked;
    BOOL fMoving;
    POINT ptMovBasis;
    POINT ptMovStart;
    IMINFO imi;
    RECT rectLast;
} SIPWNDSTRUCT, *LPSIPWNDSTRUCT;
INT DrawButton (HDC hdc, RECT *prect, LPTSTR pChar, BOOL fPressed);
 
//
// Prototypes for functions implemented by custom IM code
//
HWND CreateIMWindow (HWND hwndParent);
int DestroyIMWindow (HWND hwnd);
 
LRESULT CALLBACK NPWndProc (HWND, UINT, WPARAM, LPARAM);
 
LRESULT CALLBACK DoCreateSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoSetSipInfo (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoPaintSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoMouseSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoDestroySip (HWND, UINT, WPARAM, LPARAM);
 
#ifdef __cplusplus
}
#endif

IMCommon.cpp

//======================================================================
// IMCommon - Common code for a Windows CE input method
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================
#include <windows.h>                 // For all that Windows stuff
#include <commctrl.h>                // Command bar includes
#define INITGUID
#include <initguid.h>
#include <coguid.h>
#include <aygshell.h>                // Pocket PC shell includes
#include <sip.h>                     // SIP includes
 
#include "IMCommon.h"                // My IM common includes
#include "NumPanel.h"                // IM window specific includes
long g_DllCnt = 0;                   // Global DLL reference count
 
extern "C" {
HINSTANCE hInst;                     // DLL instance handle
}
//
// GUID defines for my input method.  Create a new one with GUIDGEN.
//
const GUID CLSID_NumPanel2 = 
{ 0xc8311f61, 0x12df,0x4107,{0xb5,0xea,0xb0,0xb0,0xd5,0x5c,0xec,0x50}};
 
const TCHAR szCLSIDNumPanel2[] = 
TEXT ("{C8311F61-12DF-4107-B5EA-B0B0D55CEC50}");
const TCHAR szFriendlyName[] = TEXT ("Numeric Keypad");
 
//======================================================================
// DllMain - DLL initialization entry point
//
BOOL WINAPI DllMain (HANDLE hinstDLL, DWORD dwReason, 
                     LPVOID lpvReserved) {
    hInst = (HINSTANCE)hinstDLL;
    return TRUE;
}
//======================================================================
// DllGetClassObject - Exported function called to get pointer to 
// Class factory object.
//

STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
    MyClassFactory *pcf;
    HRESULT hr;
 
    // See if caller wants us.
    if (IsEqualCLSID (rclsid, CLSID_NumPanel2)) {
 
        // Create IClassFactory object.
        pcf = new MyClassFactory();
        if (pcf == NULL)
            return E_OUTOFMEMORY;
 
        // Call class factory’s query interface method.
        hr = pcf->QueryInterface (riid, ppv);
        // This will cause an obj delete unless interface found.
        pcf->Release();
        return hr;
    }
    return CLASS_E_CLASSNOTAVAILABLE;
}
//======================================================================
// DllCanUnloadNow - Exported function called when DLL can unload
//
STDAPI DllCanUnloadNow () {
 
    if (g_DllCnt)
        return S_FALSE;
    return S_OK;
}
//======================================================================
// DllRegisterServer - Exported function called to register the server
//
STDAPI DllRegisterServer () {
    TCHAR szName[MAX_PATH+2];
    TCHAR szTmp[128];
    DWORD dwDisp;
    HKEY hKey, hSubKey;
    INT rc;
 
    GetModuleFileName (hInst, szName, sizeof (szName));
    // Open the key.
    wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNumPanel2);
    rc = RegCreateKeyEx (HKEY_CLASSES_ROOT, szTmp, 0, TEXT(""), 
                         0, 0, NULL, &hKey, &dwDisp);
    if (rc != ERROR_SUCCESS)
        return E_FAIL;

    // Set the friendly name of the SIP.
    RegSetValueEx (hKey, TEXT (""), 0, REG_SZ, (PBYTE)szFriendlyName,
                        (lstrlen (szFriendlyName)+1) * sizeof (TCHAR));
 
    // Create subkeys.
    // Set the module name of the SIP.
    rc = RegCreateKeyEx (hKey, TEXT ("InProcServer32"), 0, TEXT(""), 
                         0, 0, NULL, &hSubKey, &dwDisp);
    rc = RegSetValueEx (hSubKey, TEXT (""), 0, REG_SZ, (PBYTE)szName,
                        (lstrlen (szName)+1) * sizeof (TCHAR));
    RegCloseKey (hSubKey);
 
    // Set the default icon of the server.
    RegCreateKeyEx (hKey, TEXT ("DefaultIcon"), 0, TEXT(""), 
                    0, 0, NULL, &hSubKey, &dwDisp);
    lstrcat (szName, TEXT (",0"));
    RegSetValueEx (hSubKey, TEXT (""), 0, REG_SZ, (PBYTE)szName,
                   (lstrlen (szName)+1) * sizeof (TCHAR));
    RegCloseKey (hSubKey);
 
    // Set the flag indicating this is a SIP.
    RegCreateKeyEx (hKey, TEXT ("IsSIPInputMethod"), 0, TEXT(""), 
                    0, 0, NULL, &hSubKey, &dwDisp);
    lstrcpy (szTmp, TEXT ("1"));
    RegSetValueEx (hSubKey, TEXT (""), 0, REG_SZ, (PBYTE)szTmp, 4);
    RegCloseKey (hSubKey);
 
    RegCloseKey (hKey);
    return S_OK;
}
//======================================================================
// DllUnregisterServer - Exported function called to remove the server
// information from the registry
//
STDAPI DllUnregisterServer() {
    INT rc;
    TCHAR szTmp[128];
 
    wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNumPanel2);
    rc = RegDeleteKey (HKEY_CLASSES_ROOT, szTmp);
    if (rc == ERROR_SUCCESS)
        return S_OK;
    return E_FAIL;
}
//**********************************************************************
// MyClassFactory Object implementation
//----------------------------------------------------------------------

// Object constructor
MyClassFactory::MyClassFactory () {
    m_lRef = 1;     // Set ref count to 1 on create.
    return;
}
//----------------------------------------------------------------------
// Object destructor
MyClassFactory::~MyClassFactory () {
    return;
}
//----------------------------------------------------------------------
// QueryInterface - Called to see what interfaces this object supports
STDMETHODIMP MyClassFactory::QueryInterface (THIS_ REFIID riid, 
                                             LPVOID *ppv) {
 
    // If caller wants our IUnknown or IClassFactory object, 
    // return a pointer to the object.
    if (IsEqualIID (riid, IID_IUnknown) || 
        IsEqualIID (riid, IID_IClassFactory)) {
 
        *ppv = (LPVOID)this;     // Return pointer to object.
        AddRef();                // Increment ref to prevent delete on return.
        return NOERROR;
    }
    *ppv = NULL;
    return (E_NOINTERFACE);
}
//----------------------------------------------------------------------
// AddRef - Increment object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::AddRef (THIS) {
    ULONG cnt;
 
    cnt = (ULONG)InterlockedIncrement (&m_lRef);
    return cnt;
}
//----------------------------------------------------------------------
// Release - Decrement object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::Release (THIS) {
    ULONG cnt;
 
    cnt = (ULONG)InterlockedDecrement (&m_lRef);
    if (cnt == 0)
        delete this;
    return cnt;
}


Previous   |  Table of Contents   |   Next




Top of Page


Last Updated: Saturday, July 7, 2001