Jump to content

Introduction to PM Programming - May 1994: Difference between revisions

From EDM2
mNo edit summary
Ak120 (talk | contribs)
No edit summary
Line 1: Line 1:
Written by [[Larry Salomon Jr.]]
Written by [[Larry Salomon Jr.]]


<h2>Introduction</h2>
==Introduction==
The purpose of this column is to provide the readers out there who are not familiar with PM application development the information necessary to satisfy their curiousity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem
with books in general is that they don't answer the questions you have after you read the book the first time through.


<p>The purpose of this column is to provide the readers out there who are not
I will gladly entertain feedback from the readers about what was "glossed over" or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for. :)
familiar with PM application development the information necessary to
satisfy their curiousity, educate themselves, and give them an advantage
over the documentation supplied by IBM. Of course, much of this stuff
could probably be found in one of the many books out there, but the problem
with books in general is that they don't answer the questions you have
after you read the book the first time through.


<p>I will gladly entertain feedback from the readers about what was "glossed
It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that you would like to see discussed that really do not belong here. Specific questions can be directed to the Scratch Patch, where an attempt to answer them will be made.
over" or what was detailed well, what tangential topics need to be covered
and what superfluous crap should have been removed.  This feedback is
essential in guaranteeing that you get what you pay for. :)


<p>It should be said that you must not depend solely on this column to teach
==Last Month==
you how to develop PM applications; instead, this should be viewed as a
Last month, we introduced the WM_COMMAND and WM_CONTROL messages, which are - as you will see - very important in the development of many PM applications. We also looked at the HELLO dialog procedure in a little detail, and were introduced to three new APIs.
supplement to your other information storehouses (books, the network
conferences, etc.).  Because this column must take a general approach,
there will be some topics that you would like to see discussed that really
do not belong here. Specific questions can be directed to the Scratch
Patch, where an attempt to answer them will be made.


<h2>Last Month</h2>
This month, we will introduce some more APIs, will continue dissecting the dialog procedure, and will begin looking at our first window class in detail.


Last month, we introduced the WM_COMMAND and WM_CONTROL messages, which are
==New APIs==
- as you will see - very important in the development of many PM
PM has a few specific APIs which are more "helper APIs" than anything else, because they replace two APIs, the first of which is always WinWindowFromID().
applications. We also looked at the HELLO dialog procedure in a little
  HWND WinWindowFromID(HWND hwndParent,ULONG ulId);
detail, and were introduced to three new APIs.


<p>This month, we will introduce some more APIs, will continue dissecting the
WinWindowFromID searches the children of hwndParent to find a window whose identifier (remember these from the first column in this series?) matches ulId. It returns the window handle if found, or NULLHANDLE otherwise.
dialog procedure, and will begin looking at our first window class in
detail.


<p>
These new functions are listed below:
<h2>New APIs</h2>
 
PM has a few specific APIs which are more "helper APIs" than anything else,
because they replace two APIs, the first of which is always
WinWindowFromID().
<pre>
HWND WinWindowFromID(HWND hwndParent,ULONG ulId);
</pre>
 
WinWindowFromID searches the children of hwndParent to find a window whose
identifier (remember these from the first column in this series?) matches
ulId.  It returns the window handle if found, or NULLHANDLE otherwise.
 
<p>These new functions are listed below:
<pre>
<pre>
BOOL WinSetDlgItemText(HWND hwndDlg,ULONG ulId,PSZ pszText);
BOOL WinSetDlgItemText(HWND hwndDlg,ULONG ulId,PSZ pszText);
Line 60: Line 31:
LONG WinQueryDlgItemTextLength(HWND hwndDlg,ULONG ulId);
LONG WinQueryDlgItemTextLength(HWND hwndDlg,ULONG ulId);
</pre>
</pre>
These three functions provide the equivalent of the WinSetWindowText(),
These three functions provide the equivalent of the WinSetWindowText(), WinQueryWindowText(), and WinQueryWindowTextLength() functions that we looked at last month. The only difference is that the above three functions call WinWindowFromID() first to determine the handle of the window you are interested in.
WinQueryWindowText(), and WinQueryWindowTextLength() functions that we
looked at last month. The only difference is that the above three functions
call WinWindowFromID() first to determine the handle of the window you are
interested in.
 
<p><h2>Letter carriers and Bulletin Boards</h2>


While thinking about the topics that I should cover this month, I realized
==Letter carriers and Bulletin Boards==
that, although the terms sending and posting messages have been used
While thinking about the topics that I should cover this month, I realized that, although the terms sending and posting messages have been used liberally over the lifespan of this column, the functions to perform these two actions have never been discussed, nor has the difference between the two been discussed.
liberally over the lifespan of this column, the functions to perform these
two actions have never been discussed, nor has the difference between the
two been discussed.
<pre>
<pre>
MRESULT WinPostMsg(HWND hwndWnd,
MRESULT WinPostMsg(HWND hwndWnd,
Line 85: Line 47:
</pre>
</pre>
Both WinSendMsg and WinPostMsg take the same parameter set:
Both WinSendMsg and WinPostMsg take the same parameter set:
;hwndWnd
:the window to receive the message to be posted or sent
;ulMsg
:the message identifier
;mpParm1, mpParm2
:message-specific parameters. See the Programming Reference for detailed information about the parameters required for each
message.


<p><dl><dt><b>hwndWnd</b><dd>the window to receive the message to be posted or sent</dl>
The difference between posting and sending is that the former simply puts the message in the message queue of the recipient, while the latter is (logically speaking) a direct call to the window procedure of the recipient's window procedure. The significance of the last statement is that your window procedure will be blocked until the WinSendMsg() call returns, and that will not happen until the recipient finishes processing the message.


<dl><dt><b>ulMsg</b><dd>the message identifier</dl>
Why is one used over the other? For one thing, since WinSendMsg() is a synchronous call, pointers that are specified are guaranteed to point to valid areas of memory (this is important if they point to local variables on the stack). Note the word valid instead of accessible; if you send a message to another process with a pointer, accessability to the pointer must be established either via shared memory or giveable/gettable memory.
<dl><dt><b>mpParm1, mpParm2</b><dd>message-specific parameters. See the Programming
WinSendMsg() does have one disadvantage, however; it requires that the sender have a message queue. WinPostMsg(), on the other hand, can be sent from anywhere, regardless of whether or not WinCreateMsgQueue() was called previously (this is handy for secondary threads).
Reference for detailed information about the parameters required for each
message.</dl>


<p>The difference between posting and sending is that the former simply puts
Given this information, let us look at one more "dialogish" API - WinSendDlgItemMsg().
the message in the message queue of the recipient, while the latter is
(logically speaking) a direct call to the window procedure of the
recipient's window procedure.  The significance of the last statement is
that your window procedure will be blocked until the WinSendMsg() call
returns, and that will not happen until the recipient finishes processing
the message.
 
<p>Why is one used over the other?  For one thing, since WinSendMsg() is a
synchronous call, pointers that are specified are guaranteed to point to
valid areas of memory (this is important if they point to local variables
on the stack).  Note the word valid instead of accessible; if you send a
message to another process with a pointer, accessability to the pointer
must be established either via shared memory or giveable/gettable memory.
WinSendMsg() does have one disadvantage, however; it requires that the
sender have a message queue. WinPostMsg(), on the other hand, can be sent
from anywhere, regardless of whether or not WinCreateMsgQueue() was called
previously (this is handy for secondary threads).
 
<p>Given this information, let us look at one more "dialogish" API -
WinSendDlgItemMsg().
<pre>
<pre>
MRESULT WinSendDlgItemMsg(HWND hwndDlg,
MRESULT WinSendDlgItemMsg(HWND hwndDlg,
Line 121: Line 68:
                           MPARAM mpParm2);
                           MPARAM mpParm2);
</pre>
</pre>
This function, as you can imagine, behaves the same as WinSendMsg(), except
This function, as you can imagine, behaves the same as WinSendMsg(), except that - as with the first three APIs discussed in this section - it calls WinWindowFromID() to get the handle of the window to whom the message should be sent. It should be noted that there is no function WinPostDlgItemMsg(). I have never been able to get a straight answer for why this is so, unfortunately.
that - as with the first three APIs discussed in this section - it calls
 
WinWindowFromID() to get the handle of the window to whom the message
should be sent. It should be noted that there is no function
WinPostDlgItemMsg(). I have never been able to get a straight answer for
why this is so, unfortunately.
<p>
One last API to look at - WinDefDlgProc().
One last API to look at - WinDefDlgProc().
<pre>
<pre>
MRESULT WinDefDlgProc(HWND hwndWnd,
MRESULT WinDefDlgProc(HWND hwndWnd,
                       ULONG ulMsg,
                       ULONG ulMsg,
Line 136: Line 77:
                       MPARAM mpParm2);
                       MPARAM mpParm2);
</pre>
</pre>
In volume 2, issue 1, we briefly noted how WinDefWindowProc() was used to
In volume 2, issue 1, we briefly noted how WinDefWindowProc() was used to handle all messages that we did not want to process in our window class procedure. WinDefDlgProc() has the same purpose except that it is used within dialog procedures. Please be aware that, using one when you should be using the other will result in very strange behavior, so be careful!
handle all messages that we did not want to process in our window class
procedure. WinDefDlgProc() has the same purpose except that it is used
within dialog procedures. Please be aware that, using one when you should
be using the other will result in very strange behavior, so be careful!
<p>
 
<h2>nameDlgProc() Revisited</h2>


==nameDlgProc() Revisited==
Let's look at nameDlgProc() one more time, this time in its entirety.
Let's look at nameDlgProc() one more time, this time in its entirety.
Following that is the resource file definition for the dialog.
Following that is the resource file definition for the dialog.
Line 163: Line 98:
         HWND hwndLb;
         HWND hwndLb;
         CHAR achText[64];
         CHAR achText[64];
 
         pndiInfo=(PNAMEDLGINFO)PVOIDFROMMP(mpParm2);                // @3
         pndiInfo=(PNAMEDLGINFO)PVOIDFROMMP(mpParm2);                // @3
         WinSetWindowPtr(hwndWnd,0,(PVOID)pndiInfo);                // @4
         WinSetWindowPtr(hwndWnd,0,(PVOID)pndiInfo);                // @4
Line 253: Line 188:
}
}


------------------------------------------


DLGTEMPLATE DLG_NAME LOADONCALL MOVEABLE DISCARDABLE
DLGTEMPLATE DLG_NAME LOADONCALL MOVEABLE DISCARDABLE
Line 262: Line 196:
   {
   {


       LTEXT "Select a name, or enter it below, and select ""Ok""" ".", -1,
       LTEXT "Select a name, or enter it below, and select ""Ok""" ".", -1, 10, 80, 130, 16, DT_WORDBREAK
10, 80, 130, 16, DT_WORDBREAK


       LISTBOX DNAME_LB_NAMELIST, 10, 45, 130, 35, LS_HORZSCROLL
       LISTBOX DNAME_LB_NAMELIST, 10, 45, 130, 35, LS_HORZSCROLL
Line 273: Line 206:
</pre>
</pre>
From the dialog template, we see that the dialog consists of five controls:
From the dialog template, we see that the dialog consists of five controls:
a text control (LTEXT) whose identifier is -1 (see below), a listbox
a text control (LTEXT) whose identifier is -1 (see below), a listbox control (LISTBOX) whose identifier is DNAME_LB_NAMELIST, an entryfield control (ENTRYFIELD)  whose identifier is DNAME_EF_NAME, and two pushbuttons (the first is DEFPUSHBUTTON meaning it's the default pushbutton and the second which is simply PUSHBUTTON) whose identifiers are DID_OK and DID_CANCEL, respectively.
control (LISTBOX) whose identifier is DNAME_LB_NAMELIST, an entryfield
control (ENTRYFIELD)  whose identifier is DNAME_EF_NAME, and two
pushbuttons (the first is DEFPUSHBUTTON meaning it's the default pushbutton
and the second which is simply PUSHBUTTON) whose identifiers are DID_OK and
DID_CANCEL, respectively.
</pre>
It is important to remember that, while the symbolic constants
DNAME_LB_NAMELIST and DNAME_EF_NAME are defined in HELLORC.H, DID_OK and
DID_CANCEL are defined in &lt;pmwin.h&gt; which is #include-d by &lt;os2.h&gt;.


It is important to remember that, while the symbolic constants DNAME_LB_NAMELIST and DNAME_EF_NAME are defined in HELLORC.H, DID_OK and DID_CANCEL are defined in <pmwin.h> which is #include-d by <os2.h>.


<p>Looking at the easiest message first, at numbers @12 and @13 we see the
Looking at the easiest message first, at numbers @12 and @13 we see the processing of the WM_COMMAND message, which (if you'll remember from last month) is sent by a pushbutton whenever it is pressed ("selected" is the term typically used). You should remember that the identifier of the button is in SHORT1FROMMP(mpParm1), which we "switch" on to determine the appropriate action to take. Here, we simply dismiss the dialog with either TRUE or FALSE returned depending on whether the user completed the dialog
processing of the WM_COMMAND message, which (if you'll remember from last
month) is sent by a pushbutton whenever it is pressed ("selected" is the
term typically used). You should remember that the identifier of the
button is in SHORT1FROMMP(mpParm1), which we "switch" on to determine the
appropriate action to take. Here, we simply dismiss the dialog with either
TRUE or FALSE returned depending on whether the user completed the dialog
or changed their mind and decided to select Cancel.
or changed their mind and decided to select Cancel.


<p>At number @8 we see the WM_CONTROL message. Again, last month we looked at
At number @8 we see the WM_CONTROL message. Again, last month we looked at this message and you should remember that the identifier of the window sending the message is in SHORT1FROMMP(mpParm1), which we "switch" on to determine how to interpret the notification code. Since we're interested in the listbox only, we process "case DNAME_LB_NAMELIST" and let WinDefDlgProc() handle everything else (at number @11). At number @9 is the LN_SELECT notification, which is sent whenever a listbox item is
this message and you should remember that the identifier of the window
selected. Number @10 is the LN_ENTER notification, which is sent whenever ENTER is pressed while the listbox has the input focus or any listbox item is double-clicked. Since we will not be looking at the listbox in any great detail until a future issue, I will not elaborate on these two notifications until that time.
sending the message is in SHORT1FROMMP(mpParm1), which we "switch" on to
determine how to interpret the notification code. Since we're interested
in the listbox only, we process "case DNAME_LB_NAMELIST" and let
WinDefDlgProc() handle everything else (at number @11). At number @9 is
the LN_SELECT notification, which is sent whenever a listbox item is
selected. Number @10 is the LN_ENTER notification, which is sent whenever
ENTER is pressed while the listbox has the input focus or any listbox item
is double-clicked. Since we will not be looking at the listbox in any
great detail until a future issue, I will not elaborate on these two
notifications until that time.
 
<p>At number @2 is the dialog initialization code, which I will not discuss at
this time due to its complexity relative to what has been covered by this
column up to this point in time.
 
<p>
<h2>The WC_ENTRYFIELD Window Class</h2>
 
Entryfields belong to the class WC_ENTRYFIELD and are probably the easiest
of the window classes to use, since there isn't a lot of variation involved
in the control's design.  An entryfield is used to get a single line of
text from the user and has clipboard support built-in (the clipboard will
be discussed in a later issue).  In an entryfield, text can be selected
with the mouse or the keyboard.  A selection consists of an anchor point
and a cursor point.  The anchor point is the point at which the selection
began, and the cursor point is the point where the selection ends, and may
move depending on whether the selection is still in progress.


<p>An entryfield can have the following styles:
At number @2 is the dialog initialization code, which I will not discuss at this time due to its complexity relative to what has been covered by this column up to this point in time.


<dl><dt><b>ES_ANY</b><dd>  relates to DBCS ("double byte character set") support and will not
==The WC_ENTRYFIELD Window Class==
be discussed here.</dl>
Entryfields belong to the class WC_ENTRYFIELD and are probably the easiest of the window classes to use, since there isn't a lot of variation involved in the control's design. An entryfield is used to get a single line of text from the user and has clipboard support built-in (the clipboard will be discussed in a later issue). In an entryfield, text can be selected
with the mouse or the keyboard. A selection consists of an anchor point and a cursor point. The anchor point is the point at which the selection began, and the cursor point is the point where the selection ends, and may move depending on whether the selection is still in progress.


<dl><dt><b>ES_AUTOSCROLL</b><dd>  specifies that, when the user moves the cursor outside of
An entryfield can have the following styles:
the visible area, the entryfield should automatically scroll so that the
;ES_ANY
cursor is in a visible area.</dl>
:relates to DBCS ("double byte character set") support and will not be discussed here.
;ES_AUTOSCROLL
:specifies that, when the user moves the cursor outside of the visible area, the entryfield should automatically scroll so that the cursor is in a visible area.
;ES_AUTOSIZE
:specifies that the entryfield should automatically size itself to insure that its contents are completely visible.
;ES_AUTOTAB
:specifies that, when the user enters the number of characters equal to the current text limit (see EM_SETTEXTLIMIT), the entryfield should automatically give the input focus to the next control on the dialog.
;ES_CENTER
:specifies that the text should be displayed centered.
;ES_COMMAND
:relates to online help and will not be discussed here.
;ES_DBCS
:relates to DBCS ("double byte character set") support and will not be discussed here.
;ES_LEFT
:specifies that the text should be displayed flush-left.
;ES_MARGIN
;ES_MIXED
:relates to DBCS ("double byte character set") support and will not be discussed here.
;ES_READONLY
:specifies that the user should not be able to change the contents.
;ES_RIGHT
:specifies that the text should be displayed flush-right.
;ES_SBCS
:relates to DBCS ("double byte character set") support and will not be discussed here.
;ES_UNREADABLE
:specifies that each character in the entryfield should be displayed as an asterisk ('*').


<dl><dt><b>ES_AUTOSIZE</b><dd>  specifies that the entryfield should automatically size
The entryfield has a number of messages that it understands, and they are listed below. We will discuss some of them here, and will continue in the next issue.
itself to insure that its contents are completely visible.</dl>
*EM_CLEAR
*EM_COPY
*EM_CUT
*EM_PASTE
*EM_QUERYCHANGED
*EM_QUERYFIRSTCHAR
*EM_QUERYREADONLY
*EM_QUERYSEL
*EM_SETFIRSTCHAR
*EM_SETINSERTMODE
*EM_SETREADONLY
*EM_SETSEL
*EM_SETTEXTLIMIT


<dl><dt><b>ES_AUTOTAB</b><dd>  specifies that, when the user enters the number of characters
You've probably noticed that there are no messages for setting and retrieving the contents of an entryfield. This is done using the WinSetWindowText() and WinQueryWindowText() (and through the "dialogish" APIs discussed earlier this issue).
equal to the current text limit (see EM_SETTEXTLIMIT), the entryfield
should automatically give the input focus to the next control on the
dialog.</dl>


<dl><dt><b>ES_CENTER</b><dd>  specifies that the text should be displayed centered. </dl>
Let's look at our first four messages - EM_QUERYCHANGED, EM_QUERYFIRSTCHAR, EM_QUERYREADONLY, and EM_QUERYSEL.
<dl><dt><b>ES_COMMAND</b><dd>  relates to online help and will not be discussed here.</dl>


<dl><dt><b>ES_DBCS</b><dd>  relates to DBCS ("double byte character set") support and will
===EM_QUERYCHANGED===
not be discussed here.</dl>
This message is sent to determine if the text has changed since this message was last sent (or since the control was created).
 
<dl><dt><b>ES_LEFT</b><dd>  specifies that the text should be displayed flush-left.</dl>
<dl><dt><b>ES_MARGIN</b><dd></dl>
 
<dl><dt><b>ES_MIXED</b><dd>  relates to DBCS ("double byte character set") support and will
not be discussed here.</dl>
 
<dl><dt><b>ES_READONLY</b><dd>  specifies that the user should not be able to change the
contents.</dl>
 
<dl><dt><b>ES_RIGHT</b><dd>  specifies that the text should be displayed flush-right.</dl>
 
<dl><dt><b>ES_SBCS</b><dd>  relates to DBCS ("double byte character set") support and will
not be discussed here.</dl>
 
<dl><dt><b>ES_UNREADABLE</b><dd>  specifies that each character in the entryfield should be
displayed as an asterisk ('*').</dl>
 
<p>The entryfield has a number of messages that it understands, and they are
listed below.  We will discuss some of them here, and will continue in the
next issue.
<ul>
<li>EM_CLEAR
<li>EM_COPY
<li>EM_CUT
<li>EM_PASTE
<li>EM_QUERYCHANGED
<li>EM_QUERYFIRSTCHAR
<li>EM_QUERYREADONLY
<li>EM_QUERYSEL
<li>EM_SETFIRSTCHAR
<li>EM_SETINSERTMODE
<li>EM_SETREADONLY
<li>EM_SETSEL
<li>EM_SETTEXTLIMIT
</ul>
 
You've probably noticed that there are no messages for setting and
retrieving the contents of an entryfield.  This is done using the
WinSetWindowText() and WinQueryWindowText() (and through the "dialogish"
APIs discussed earlier this issue).
 
<p>Let's look at our first four messages - EM_QUERYCHANGED, EM_QUERYFIRSTCHAR,
EM_QUERYREADONLY, and EM_QUERYSEL.
 
<p><h3>EM_QUERYCHANGED</h3>
 
This message is sent to determine if the text has changed since this
message was last sent (or since the control was created).


<p><b>Parameters</b>
<p><b>Parameters</b>
<dl>
<dl>
<h4>param1</h4>
;param1
<dt>
<dt>
ulReserved (ULONG)
ulReserved (ULONG)
Line 404: Line 281:
Reserved, 0.
Reserved, 0.
</dl><dl>
</dl><dl>
<h4>param2</h4>
;param2


<dt>
<dt>
Line 411: Line 288:
Reserved, 0.
Reserved, 0.
</dl><dl>
</dl><dl>
<h4>Returns</h4>
;Returns
<dt>
<dt>
bChanged (BOOL)
bChanged (BOOL)
Line 424: Line 301:
</blockquote>
</blockquote>
</dl>
</dl>
<h3>EM_QUERYFIRSTCHAR</h3>


This message is sent to determine the zero-based index of the first
===EM_QUERYFIRSTCHAR===
character visible in the entryfield.
This message is sent to determine the zero-based index of the first character visible in the entryfield.


<p><b>Parameters</b>
<b>Parameters</b>
<dl>
<dl>
<h4>param1</h4>
;param1
<dt>
<dt>
ulReserved (ULONG)
ulReserved (ULONG)
Line 439: Line 315:
<dl>
<dl>


<h4>param2</h4>
;param2
<dt>
<dt>
ulReserved (ULONG)
ulReserved (ULONG)
Line 446: Line 322:
</dl>
</dl>
<dl>
<dl>
<h4>Returns</h4>
;Returns
<dt>
<dt>
sIndex (SHORT)
sIndex (SHORT)
Line 452: Line 328:
the zero-based index of the first visible character.
the zero-based index of the first visible character.
</dl>
</dl>
<h3>EM_QUERYREADONLY</h3>


===EM_QUERYREADONLY===
This message is sent to determine if the entryfield is read-only or not.
This message is sent to determine if the entryfield is read-only or not.


<p><b>Parameters</b>
<b>Parameters</b>
<dl>
;param1
<h4>param1</h4>
:ulReserved (ULONG)
<dt>
ulReserved (ULONG)
<dd>
<dd>
Reserved, 0.
Reserved, 0.
</dl>
</dl>
<dl>
<dl>
<h4>param2</h4>
;param2
<dt>
:ulReserved (ULONG)
ulReserved (ULONG)


<dd>
<dd>
Line 473: Line 346:
</dl>
</dl>
<dl>
<dl>
<h4>Returns</h4>
;Returns
<dt>
:bReadOnly (BOOL)
bReadOnly (BOOL)
<dd>
<dd>
the read-only state of the entryfield.
the read-only state of the entryfield.
Line 482: Line 354:
<br>
<br>
<b>FALSE</b> - the entryfield is not read-only.
<b>FALSE</b> - the entryfield is not read-only.
</blockquote>
</blockquote>
</dl>
</dl>
<h3>EM_QUERYSEL</h3>


===EM_QUERYSEL===
This message is sent to determine the current selection, if any exists.
This message is sent to determine the current selection, if any exists.


<p><b>Parameters</b>
<b>Parameters</b>
<dl>
<dl>
<h4>param1</h4>
;param1
<dt>
<dt>
ulReserved (ULONG)
ulReserved (ULONG)
Line 499: Line 370:
<dl>
<dl>


<h4>param2</h4>
;param2
<dt>
<dt>
ulReserved (ULONG)
ulReserved (ULONG)
Line 506: Line 377:
</dl>
</dl>
<dl>
<dl>
<h4>Returns</h4>
;Returns
<dt>
<dt>
sFirst (SHORT)
sFirst (SHORT)
Line 518: Line 389:
</dl>
</dl>


<p><h2>Summary</h2>
==Summary==
This month we learned a lot of new things:
*We looked at a slew of new APIs, many of which are attuned to dialogs
*We looked at nameDlgProc() is more detail
*We began looking at the WC_ENTRYFIELD class in detail


This month we learned a lot of new things:
<ul>
<li>We looked at a slew of new APIs, many of which are attuned to dialogs
<li>We looked at nameDlgProc() is more detail
<li>We began looking at the WC_ENTRYFIELD class in detail
</ul>
Next month, we will continue with the WC_ENTRYFIELD discussion, but will hold off from discussing nameDlgProc() until we look at the WC_LISTBOX window class.
Next month, we will continue with the WC_ENTRYFIELD discussion, but will hold off from discussing nameDlgProc() until we look at the WC_LISTBOX window class.


[[Category: PM Articles]]
[[Category: PM Articles]]

Revision as of 23:04, 17 May 2016

Written by Larry Salomon Jr.

Introduction

The purpose of this column is to provide the readers out there who are not familiar with PM application development the information necessary to satisfy their curiousity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem with books in general is that they don't answer the questions you have after you read the book the first time through.

I will gladly entertain feedback from the readers about what was "glossed over" or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for. :)

It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that you would like to see discussed that really do not belong here. Specific questions can be directed to the Scratch Patch, where an attempt to answer them will be made.

Last Month

Last month, we introduced the WM_COMMAND and WM_CONTROL messages, which are - as you will see - very important in the development of many PM applications. We also looked at the HELLO dialog procedure in a little detail, and were introduced to three new APIs.

This month, we will introduce some more APIs, will continue dissecting the dialog procedure, and will begin looking at our first window class in detail.

New APIs

PM has a few specific APIs which are more "helper APIs" than anything else, because they replace two APIs, the first of which is always WinWindowFromID().

HWND WinWindowFromID(HWND hwndParent,ULONG ulId);

WinWindowFromID searches the children of hwndParent to find a window whose identifier (remember these from the first column in this series?) matches ulId. It returns the window handle if found, or NULLHANDLE otherwise.

These new functions are listed below:

BOOL WinSetDlgItemText(HWND hwndDlg,ULONG ulId,PSZ pszText);

ULONG WinQueryDlgItemText(HWND hwndDlg,
                          ULONG ulId,
                          LONG ulSzBuffer,
                          PCHAR pchBuffer);

LONG WinQueryDlgItemTextLength(HWND hwndDlg,ULONG ulId);

These three functions provide the equivalent of the WinSetWindowText(), WinQueryWindowText(), and WinQueryWindowTextLength() functions that we looked at last month. The only difference is that the above three functions call WinWindowFromID() first to determine the handle of the window you are interested in.

Letter carriers and Bulletin Boards

While thinking about the topics that I should cover this month, I realized that, although the terms sending and posting messages have been used liberally over the lifespan of this column, the functions to perform these two actions have never been discussed, nor has the difference between the two been discussed.

MRESULT WinPostMsg(HWND hwndWnd,
                   ULONG ulMsg,
                   MPARAM mpParm1,
                   MPARAM mpParm2);

MRESULT WinSendMsg(HWND hwndWnd,
                   ULONG ulMsg,
                   MPARAM mpParm1,
                   MPARAM mpParm2);

Both WinSendMsg and WinPostMsg take the same parameter set:

hwndWnd
the window to receive the message to be posted or sent
ulMsg
the message identifier
mpParm1, mpParm2
message-specific parameters. See the Programming Reference for detailed information about the parameters required for each

message.

The difference between posting and sending is that the former simply puts the message in the message queue of the recipient, while the latter is (logically speaking) a direct call to the window procedure of the recipient's window procedure. The significance of the last statement is that your window procedure will be blocked until the WinSendMsg() call returns, and that will not happen until the recipient finishes processing the message.

Why is one used over the other? For one thing, since WinSendMsg() is a synchronous call, pointers that are specified are guaranteed to point to valid areas of memory (this is important if they point to local variables on the stack). Note the word valid instead of accessible; if you send a message to another process with a pointer, accessability to the pointer must be established either via shared memory or giveable/gettable memory. WinSendMsg() does have one disadvantage, however; it requires that the sender have a message queue. WinPostMsg(), on the other hand, can be sent from anywhere, regardless of whether or not WinCreateMsgQueue() was called previously (this is handy for secondary threads).

Given this information, let us look at one more "dialogish" API - WinSendDlgItemMsg().

MRESULT WinSendDlgItemMsg(HWND hwndDlg,
                          ULONG ulId,
                          ULONG ulMsg,
                          MPARAM mpParm1,
                          MPARAM mpParm2);

This function, as you can imagine, behaves the same as WinSendMsg(), except that - as with the first three APIs discussed in this section - it calls WinWindowFromID() to get the handle of the window to whom the message should be sent. It should be noted that there is no function WinPostDlgItemMsg(). I have never been able to get a straight answer for why this is so, unfortunately.

One last API to look at - WinDefDlgProc().

MRESULT WinDefDlgProc(HWND hwndWnd,
                      ULONG ulMsg,
                      MPARAM mpParm1,
                      MPARAM mpParm2);

In volume 2, issue 1, we briefly noted how WinDefWindowProc() was used to handle all messages that we did not want to process in our window class procedure. WinDefDlgProc() has the same purpose except that it is used within dialog procedures. Please be aware that, using one when you should be using the other will result in very strange behavior, so be careful!

nameDlgProc() Revisited

Let's look at nameDlgProc() one more time, this time in its entirety. Following that is the resource file definition for the dialog.

MRESULT EXPENTRY nameDlgProc(HWND hwndWnd,
                             ULONG ulMsg,
                             MPARAM mpParm1,
                             MPARAM mpParm2)
{
   PNAMEDLGINFO pndiInfo;

   pndiInfo=(PNAMEDLGINFO)WinQueryWindowPtr(hwndWnd,0);              // @1

   switch (ulMsg) {
   case WM_INITDLG:                                                  // @2
      {
         HAB habAnchor;
         HWND hwndLb;
         CHAR achText[64];
 
         pndiInfo=(PNAMEDLGINFO)PVOIDFROMMP(mpParm2);                // @3
         WinSetWindowPtr(hwndWnd,0,(PVOID)pndiInfo);                 // @4

         WinSendDlgItemMsg(hwndWnd,                                  // @5
                           DNAME_EF_NAME,
                           EM_SETTEXTLIMIT,
                           MPFROMSHORT(sizeof(pndiInfo->achName)),
                           0);

         habAnchor=WinQueryAnchorBlock(hwndWnd);
         hwndLb=WinWindowFromID(hwndWnd,DNAME_LB_NAMELIST);

         WinLoadString(habAnchor,                                    // @6
                       NULLHANDLE,
                       STR_TOM,
                       sizeof(achText),
                       achText);

         WinInsertLboxItem(hwndLb,LIT_END,achText);                  // @7

         WinLoadString(habAnchor,
                       NULLHANDLE,
                       STR_DICK,
                       sizeof(achText),
                       achText);

         WinInsertLboxItem(hwndLb,LIT_END,achText);

         WinLoadString(habAnchor,
                       NULLHANDLE,
                       STR_HARRY,
                       sizeof(achText),
                       achText);

         WinInsertLboxItem(hwndLb,LIT_END,achText);
      }
      break;
   case WM_CONTROL:                                                  // @8
      switch (SHORT1FROMMP(mpParm1)) {
      case DNAME_LB_NAMELIST:
         switch (SHORT2FROMMP(mpParm1)) {
         case LN_SELECT:                                             // @9
            {
               HWND hwndLb;
               SHORT sIndex;

               hwndLb=WinWindowFromID(hwndWnd,DNAME_LB_NAMELIST);

               sIndex=WinQueryLboxSelectedItem(hwndLb);
               WinQueryLboxItemText(hwndLb,
                                    sIndex,
                                    pndiInfo->achName,
                                    sizeof(pndiInfo->achName));

               WinSetDlgItemText(hwndWnd,
                                 DNAME_EF_NAME,
                                 pndiInfo->achName);
            }
            break;
         case LN_ENTER:                                              // @10
            WinPostMsg(hwndWnd,WM_COMMAND,MPFROMSHORT(DID_OK),0);
            break;
         default:
            return WinDefDlgProc(hwndWnd,ulMsg,mpParm1,mpParm2);     // @11
         } /* endswitch */
         break;
      default:
         return WinDefDlgProc(hwndWnd,ulMsg,mpParm1,mpParm2);
      } /* endswitch */
      break;
   case WM_COMMAND:
      switch (SHORT1FROMMP(mpParm1)) {
      case DID_OK:                                                   // @12
         WinDismissDlg(hwndWnd,TRUE);
         break;
      case DID_CANCEL:                                               // @13
         WinDismissDlg(hwndWnd,FALSE);
         break;
      default:
         return WinDefDlgProc(hwndWnd,ulMsg,mpParm1,mpParm2);
      } /* endswitch */
      break;
   default:
      return WinDefDlgProc(hwndWnd,ulMsg,mpParm1,mpParm2);
   } /* endswitch */

   return MRFROMSHORT(FALSE);
}


DLGTEMPLATE DLG_NAME LOADONCALL MOVEABLE DISCARDABLE
{
   DIALOG  "Input Required", DLG_NAME, 112, 59, 150, 100,
      WS_VISIBLE,
      FCF_SYSMENU | FCF_TITLEBAR
   {

      LTEXT "Select a name, or enter it below, and select ""Ok""" ".", -1, 10, 80, 130, 16, DT_WORDBREAK

      LISTBOX DNAME_LB_NAMELIST, 10, 45, 130, 35, LS_HORZSCROLL
      ENTRYFIELD "", DNAME_EF_NAME, 12, 30, 127, 8, ES_MARGIN
      DEFPUSHBUTTON "Ok", DID_OK, 10, 10, 40, 13
      PUSHBUTTON "Cancel", DID_CANCEL, 55, 10, 40, 13
   }
}

From the dialog template, we see that the dialog consists of five controls: a text control (LTEXT) whose identifier is -1 (see below), a listbox control (LISTBOX) whose identifier is DNAME_LB_NAMELIST, an entryfield control (ENTRYFIELD) whose identifier is DNAME_EF_NAME, and two pushbuttons (the first is DEFPUSHBUTTON meaning it's the default pushbutton and the second which is simply PUSHBUTTON) whose identifiers are DID_OK and DID_CANCEL, respectively.

It is important to remember that, while the symbolic constants DNAME_LB_NAMELIST and DNAME_EF_NAME are defined in HELLORC.H, DID_OK and DID_CANCEL are defined in <pmwin.h> which is #include-d by <os2.h>.

Looking at the easiest message first, at numbers @12 and @13 we see the processing of the WM_COMMAND message, which (if you'll remember from last month) is sent by a pushbutton whenever it is pressed ("selected" is the term typically used). You should remember that the identifier of the button is in SHORT1FROMMP(mpParm1), which we "switch" on to determine the appropriate action to take. Here, we simply dismiss the dialog with either TRUE or FALSE returned depending on whether the user completed the dialog or changed their mind and decided to select Cancel.

At number @8 we see the WM_CONTROL message. Again, last month we looked at this message and you should remember that the identifier of the window sending the message is in SHORT1FROMMP(mpParm1), which we "switch" on to determine how to interpret the notification code. Since we're interested in the listbox only, we process "case DNAME_LB_NAMELIST" and let WinDefDlgProc() handle everything else (at number @11). At number @9 is the LN_SELECT notification, which is sent whenever a listbox item is selected. Number @10 is the LN_ENTER notification, which is sent whenever ENTER is pressed while the listbox has the input focus or any listbox item is double-clicked. Since we will not be looking at the listbox in any great detail until a future issue, I will not elaborate on these two notifications until that time.

At number @2 is the dialog initialization code, which I will not discuss at this time due to its complexity relative to what has been covered by this column up to this point in time.

The WC_ENTRYFIELD Window Class

Entryfields belong to the class WC_ENTRYFIELD and are probably the easiest of the window classes to use, since there isn't a lot of variation involved in the control's design. An entryfield is used to get a single line of text from the user and has clipboard support built-in (the clipboard will be discussed in a later issue). In an entryfield, text can be selected with the mouse or the keyboard. A selection consists of an anchor point and a cursor point. The anchor point is the point at which the selection began, and the cursor point is the point where the selection ends, and may move depending on whether the selection is still in progress.

An entryfield can have the following styles:

ES_ANY
relates to DBCS ("double byte character set") support and will not be discussed here.
ES_AUTOSCROLL
specifies that, when the user moves the cursor outside of the visible area, the entryfield should automatically scroll so that the cursor is in a visible area.
ES_AUTOSIZE
specifies that the entryfield should automatically size itself to insure that its contents are completely visible.
ES_AUTOTAB
specifies that, when the user enters the number of characters equal to the current text limit (see EM_SETTEXTLIMIT), the entryfield should automatically give the input focus to the next control on the dialog.
ES_CENTER
specifies that the text should be displayed centered.
ES_COMMAND
relates to online help and will not be discussed here.
ES_DBCS
relates to DBCS ("double byte character set") support and will not be discussed here.
ES_LEFT
specifies that the text should be displayed flush-left.
ES_MARGIN
ES_MIXED
relates to DBCS ("double byte character set") support and will not be discussed here.
ES_READONLY
specifies that the user should not be able to change the contents.
ES_RIGHT
specifies that the text should be displayed flush-right.
ES_SBCS
relates to DBCS ("double byte character set") support and will not be discussed here.
ES_UNREADABLE
specifies that each character in the entryfield should be displayed as an asterisk ('*').

The entryfield has a number of messages that it understands, and they are listed below. We will discuss some of them here, and will continue in the next issue.

  • EM_CLEAR
  • EM_COPY
  • EM_CUT
  • EM_PASTE
  • EM_QUERYCHANGED
  • EM_QUERYFIRSTCHAR
  • EM_QUERYREADONLY
  • EM_QUERYSEL
  • EM_SETFIRSTCHAR
  • EM_SETINSERTMODE
  • EM_SETREADONLY
  • EM_SETSEL
  • EM_SETTEXTLIMIT

You've probably noticed that there are no messages for setting and retrieving the contents of an entryfield. This is done using the WinSetWindowText() and WinQueryWindowText() (and through the "dialogish" APIs discussed earlier this issue).

Let's look at our first four messages - EM_QUERYCHANGED, EM_QUERYFIRSTCHAR, EM_QUERYREADONLY, and EM_QUERYSEL.

EM_QUERYCHANGED

This message is sent to determine if the text has changed since this message was last sent (or since the control was created).

Parameters

param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
bChanged (BOOL)
changed indicator

TRUE - the contents have changed since the last time this message was sent.
FALSE - the contents have not changed since the last time this message was sent.

EM_QUERYFIRSTCHAR

This message is sent to determine the zero-based index of the first character visible in the entryfield.

Parameters

param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
sIndex (SHORT)
the zero-based index of the first visible character.

EM_QUERYREADONLY

This message is sent to determine if the entryfield is read-only or not.

Parameters

param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
bReadOnly (BOOL)
the read-only state of the entryfield.

TRUE - the entryfield is read-only.
FALSE - the entryfield is not read-only.

EM_QUERYSEL

This message is sent to determine the current selection, if any exists.

Parameters

param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
sFirst (SHORT)
the zero-based index of the anchor point.
sLast (SHORT)
the zero-based index of the cursor point.

Summary

This month we learned a lot of new things:

  • We looked at a slew of new APIs, many of which are attuned to dialogs
  • We looked at nameDlgProc() is more detail
  • We began looking at the WC_ENTRYFIELD class in detail

Next month, we will continue with the WC_ENTRYFIELD discussion, but will hold off from discussing nameDlgProc() until we look at the WC_LISTBOX window class.