Important: |
This is retired content. This content is outdated and is no
longer being maintained. It is provided as a courtesy for
individuals who are still using these technologies. This content
may contain URLs that were valid when originally published, but now
link to sites or pages that no longer exist. |
Microsoft Corporation
June 2000
Summary:Rich Ink technology allows a user to write and draw
on a touch-sensitive screen. This technology is supported by the
Microsoft Windows CE RichInk edit control. This paper describes how
to add a RichInk control to an application and provides an example.
The paper also provides the API reference for the RichInk control,
including functions, messages, structures, and data types. An
Appendix describes the process of converting RichInk PWI (Pocket
Word Ink) files to RTF or text formats. (10 printed pages)
Contents
Introduction
Adding the RichInk Edit Control to an
Application
Example
API
Appendix: Converting RichInk PWI
Data
Introduction
Rich Inkis the underlying technology that lets a user write
and draw on a touch-sensitive screen using a stylus. It provides a
convenient means for applications to accept input from a user
without use of a keyboard.
Rich Ink technology is exposed in Windows CE through the
RichInk edit control.Taking notes or drawing sketches with
the RichInk control is very much like writing or drawing on paper.
The control includes powerful editing and formatting capabilities.
Typical examples of Rich Ink applications include an electronic
form that accepts a user's handwritten signature, or a calendar
that lets a user jot down a "To Do" list for a selected date.
RichInk libraries reside in the ROM of those Windows CE
platforms that support Rich Ink technology. The RichInk header file
(richink.h) is supplied with those platform SDKs that support
it.
The functions, messages, structures, and data types included in
the RichInk API are listed later in this paper.
Adding the RichInk Edit Control to an
Application
Providing a RichInk edit control for a Windows CE application
requires the following steps:
To embed the RichInk control in your application:
- Call
InitCommonControlsto load the common control DLL.
- Call
InitInkXto load and initialize RichInk control.
- Call
CreateWindowand specify the class name as WC_INKX.
(Alternatively, call
CreateDialogto instantiate a dialog box with a custom ink
control.)
After initialization, the RichInk control communicates with the
calling application using the standard Windows CE messaging system.
Specifically, the control performs the following tasks:
- Sends the IM_SHOWCMDBAR message to the ink control to show or
hide the command bar.
- Sends the IM_GETDATALEN, IM_GETDATA, and IM_SETDATA messages
between the ink control and the application to transmit inking
data, such as a note or sketch.
- Sends the IM_REINIT message to the ink control to erase all the
content from the control.
- Sends the standard EM_GETMODIFY and EM_SETMODIFY messages to
the ink control to determine if its content has been modified and
to set the modification flag in the control, respectively.
Example
As an example of using the RichInk control, consider a calendar
application that has a RichInk control (named InkX) embedded in a
dialog box. Invoking SendDlgItemMessage to send an IM_SHOWCMDBAR
message can toggle the control's command bar. The state of the
command bar is specified in the accompanying wParam:
SendDlgItemMessage(hInk, IM_SHOWCMDBAR,
(WPARAM)m_bCmdBar, 0L);
Here
hInkis a handle to the
InkXcontrol and
m_bCmdBaris set to either TRUE or FALSE to specify whether
or not the command bar is visible.
To save an edited or a newly created note, obtain the data
length using the following:
InkDataLen=SendDlgItemMessage(hInkX,
IM_GETDATALEN, 0, 0L);
For each date entry, the application keeps an ink note,
pInkData, of the
BYTEpointer type. The application must first allocate
sufficient memory to store the ink note, and then pass the
pInkDatapointer to the control through the message's lParam
parameter:
InkDatalen = SendDlgItemMessage(hInkX,
IM_GETDATA, InkDataLen, (LPARAM)pInkData);
When the user taps a calendar date, the application retrieves
any previously saved ink data and brings up the ink control. It
then sends the following message to refresh the document view with
the retrieved ink data:
SendDlgItemMessage(hInkX, IM_SETDATA,
dwInkDataLen, (LPARAM)pInkData);
The
dwInkDataLenparameter gives the length of the ink data, and
pInkDatais a pointer to the data itself. You should release
the ink data,
pInkData, once it has been passed to the ink control.
API
The following tables list the elements of the RichInk API. These
items are documented in detail in the online Help topics for
Microsoft eMbedded Visual Tools 3.0.
Function |
Description |
EditStreamCallback |
An application-defined callback used with the
EM_STREAMIN and EM_STREAMOUT messages. Transfers a data stream to
or from a RichInk control. |
InitRichInkDLL |
Loads and initializes the RichInk control. |
Message |
Description |
EM_CANREDO |
Determines if actions can be redone. |
EM_CANUNDO |
Determines if actions cannot be redone. |
EM_CLEARALL |
Clears all of the current document's data. |
EM_GETPAGESTYLE |
Gets the page style of the RichInk Control. |
EM_GETPENMODE |
Gets the new pen mode. |
EM_GETVIEW |
Gets the view style. |
EM_GETWRAPMODE |
Gets the wrap mode—window or page. |
EM_GETZOOMPERCENT |
Gets the zoom percentage. |
EM_INSERTLINKS |
Inserts the passed string as a link string. |
EM_REDOEVENT |
Redoes the allowable action. |
EM_SETINKLAYER |
Sets the page style—including ruled lines. |
EM_SETPAGESTYLE |
Sets individual aspects of the view mode. |
EM_SETPENMODE |
Sets the new pen mode. |
EM_SETVIEW |
Sets the view style—typing, writing, or
drawing. |
EM_SETVIEWATTRIBUTES |
Sets the view attributes. |
EM_SETWRAPMODE |
Sets the wrap mode. |
EM_SETZOOMPERCENT |
Sets the zoom percentage. |
EM_STREAMIN |
Replaces the contents of a RichInk Control with a
stream of data provided by an application-defined
EditStreamCallbackfunction. |
EM_STREAMOUT |
Causes a RichInk Control to pass its contents to an
application-defined
EditStreamCallbackcallback function. |
EM_UNDOEVENT |
Reverts to the state of the last action. |
Structure |
Description |
EDITSTREAM |
Contains information that an application passes to
a rich edit control in an EM_STREAMIN or EM_STREAMOUT message. |
VIEWATTRIBUTES |
Sets mask values for EM_SETVIEWATTRIBUTES. |
Data Type |
Description |
DWORD |
Data type required for the RichInk stream
messages. |
The RichInk control also provides support for several Rich Edit
and Windows messages, including the following:
EM_CANPASTE |
EM_GETMODIFY |
EM_GETSEL* |
EM_REPLACESEL* |
EM_SETMODIFY |
EM_SETSEL* |
WM_CLEAR |
WM_COPY |
WM_CUT |
WM_GETTEXT |
WM_GETTEXTLENGTH |
WM_PASTE |
WM_SETTEXT |
|
|
*Messages with an asterisk are intended for
use with text in the VT_TYPINGVIEW and VT_WRITINGVIEW view modes.
Use with VT_DRAWINGVIEW will produce inconsistent results, and is
not recommended.
Appendix: Converting RichInk PWI
Data
The following pages describe a process for converting the
RichInk data found in .pwi (Pocket Word Ink) files to RTF or Text
formats. These instructions are intended for knowledgeable Windows
programmers who are familiar with the messaging architecture in
Windows.
Preliminary notes
- The RichInk DLL is installed on the desktop with the Windows CE
Services and exists in ROM on Windows CE devices. The RichInk.dll
can be used for converting from the RichInk internal format (.pwi
files) to either .rtf or text file formats.
- On the desktop, ink data containing ink strokes is converted to
RTF metafile data. This technique is not currently supported on the
device, so data converted on the device will lose all non-text ink
data.
- If you want to use RichInk technology to convert files
on the desktop, use InkEng.dll (found in the ActiveSync
folder) rather than RichInk.dll. InkEng.dll maintains the same
interfaces for EM_STREAMIN and EM_STREAMOUT as the device uses.
Conversion process
// Defines and typedefs needed for the
conversion: #define EM_STREAMIN (WM_USER + 73) #define EM_STREAMOUT
(WM_USER + 74) typedef DWORD (CALLBACK *EDITSTREAMCALLBACK) (DWORD
dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb); typedef struct
_editstream { DWORD dwCookie; /* user value passed to callback as
first parameter */ DWORD dwError; /* last error */
EDITSTREAMCALLBACK pfnCallback; } EDITSTREAM; typedef struct
tagCOOKIE { HANDLE hFile; LPBYTE pbStart; LPBYTE pbCur; LONG
bCount; DWORD dwError; } COOKIE, * PCOOKIE; // Stream formats:
#define SF_TEXT 0x0001 #define SF_RTF 0x0002 #define SF_UNICODE
0x0010 // Unicode data of some kind #define SF_UTEXT SF_TEXT |
SF_UNICODE // Unicode text file #define SFF_PWI 0x0800 #define
SF_PWI ( SF_RTF | SFF_PWI | 0x010000 ) // PWI format
The first step is to load the RichInk DLL:
HINSTANCE hLib; hLib = LoadLibrary(
TEXT("richink.dll" ) ); if (!hLib) return ERROR;
The next step is to create a RichInk window:
HWND hwndInk = NULL; hwndInk =
CreateWindow("richink", ES_READONLY, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0, NULL, NULL); if (!hwndInk)
return ERROR;
With the RichInk window we can send EM_STREAMIN and EM_STREAMOUT
messages in a manner very similar to the RichEdit desktop
control:
EDITSTREAM es; COOKIE cookie; // Stream in the
Ink Data from a memory block. memset(&cookie, 0,
sizeof(cookie)); cookie.dwError = 0; cookie.pbStart = (LPBYTE)pb;
// Pointer to memory block cookie.pbCur = cookie.pbStart;
cookie.bCount = cb; // Size of ink data es.dwCookie =
(DWORD)&cookie; es.dwError = 0; es.pfnCallback =
BufferReadCallback; lResult = SendMessage (hwndInk, EM_STREAMIN,
(WPARAM)SF_PWI, (LPARAM)&es); // Next call stream out to get
the size of the Unicode text. cookie.dwError = 0; cookie.pbStart =
0; cookie.pbCur = 0; cookie.bCount = 0; es.dwCookie =
(DWORD)&cookie; es.dwError = 0; es.pfnCallback =
BufferWriteCallback; lResult = SendMessage (hwndInk, EM_STREAMOUT,
(WPARAM)SF_UTEXT, (LPARAM)&es); // Allocate memory and stream
out the Unicode text to it. sz = (LPWSTR)LocalAlloc(LPTR, (cbSz =
Cookie.pbCur - Cookie.pbStart) + 2); // Check for alloc err.
cookie.dwError = 0; cookie.pbStart = (LPBYTE)sz; cookie.pbCur =
cookie.pbStart; cookie.bCount = cbSz; es.dwCookie =
(DWORD)&cookie; es.dwError = 0; es.pfnCallback =
BufferWriteCallback; lResult = SendMessage (hwndInk, EM_STREAMOUT,
(WPARAM)SF_UTEXT, (LPARAM)&es);
Now finally some sample Callback functions:
static DWORD CALLBACK BufferReadCallback(DWORD
dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { PCOOKIE pCookie =
(PCOOKIE)dwCookie; LONG bytesLeft; LONG bytesRead; // Calc the
bytes read. bytesRead = pCookie->pbCur - pCookie->pbStart; //
Calc bytes left to read. if (bytesRead < pCookie->bCount) {
// Calc the bytes left to read. bytesLeft = pCookie->bCount -
bytesRead; } else { bytesLeft = 0; } // Don't read past the end of
buffer. if (cb > bytesLeft) cb = bytesLeft; // Set bytes read.
*pcb = cb; // Copy any bytes. if (cb) { memcpy(pbBuff,
pCookie->pbCur, cb); pCookie->pbCur += cb; } // Return no
error. return 0; } // BufferReadCallback() static DWORD CALLBACK
BufferWriteCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG
*pcb) { PCOOKIE pCookie = (PCOOKIE)dwCookie; PCOOKIE pCookie =
(PCOOKIE)dwCookie; // Do not overwrite the end of the buffer. // If
there is no output buffer, then we're // only here to determine the
space required // for the stream out. If (pCookie->pbStart
&& (pCookie->pbCur + cb > pCookie->pbStart +
pCookie->bCount)) { // Writing all this data would overflow the
buffer. // So only write what will fit. cb = pCookie->pbStart +
pCookie->bCount - pCookie->pbCur; } *pcb = cb; if
(pCookie->pbStart) memcpy(pCookie->pbCur, pbBuff, cb);
pCookie->pbCur += cb; return 0; } // BufferWriteCallback()
The foregoing example streams out Unicode text, but could be
used to stream out RTF or ANSI text by changing the Stream Format
to SF_RTF or SF_TEXT. By changing the callback functions, data can
be read from and/or written to a file.
Sample callback functions for reading/writing
Here are some sample callback functions for file reading and
writing:
static DWORD CALLBACK ReadCallback(DWORD
dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { PCOOKIE pCookie =
(PCOOKIE)dwCookie; // Zero the bytes read. *pcb = 0; // If there is
an error return it. if (ReadFile(pCookie->hFile, pbBuff, cb,
pcb, 0) == 0) return (pCookie->dwError = GetLastError()); //
Return no error. return 0; } // ReadCallback() static DWORD
CALLBACK WriteCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG
*pcb) { PCOOKIE pCookie = (PCOOKIE)dwCookie; if
(WriteFile(pCookie->hFile, pbBuff, cb, pcb, 0) == 0) return
(pCookie->dwError = GetLastError()); return 0; } //
WriteCallback()
Compatibility issues
The previous code describes the interface for desktop Windows CE
Services 2.2. This interface is also the same for Handheld PC 2.11
and Palm-sized PC 1.2 devices. For previous versions of Windows CE
Services or for HPC 2.0 or Palm-size PC 1.0 devices, the following
change must be made: