{█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█}
{█ █}
{█ Virtual Pascal Examples. Version 1.10 █}
{█ PM Print Example █}
{█ The Art Of OS/2, Chapter 28, p542-556 █}
{█ ─────────────────────────────────────────────────█}
{█ Copyright (c) 1992-1996 by Larry Salomon █}
{█ VP/2 Version Copyright (C) 1996 fPrint UK Ltd █}
{█ █}
{▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀}
Program Print;
{$PMTYPE PM}
{$R *.RES}
{$D Printing example - Copyright (c) 1992 by Larry Salomon}
Uses
Use32, Dos, VPUtils, Os2Def, Os2Base, Os2PmApi, Strings;
{$IFDEF VPDEMO}
{&Dynamic VP11Demo.Lib}
{$ENDIF}
Const
RES_CLIENT = 256;
WND_LISTBOX = 257;
M_SAMPLE = 256;
MI_PRINT = 257;
MI_SETUP = 258;
MI_REFRESH = 259;
M_EXIT = 260;
MI_EXIT = 261;
MI_RESUME = 262;
CLS_CLIENT = 'SampleClass';
CPL_ERROR = 0;
CPL_NOPRINTERS = 1;
CPL_SUCCESS = 2;
Type
ptPrnListInfo = ^tPrnListInfo;
tPrnListInfo = Record
DosPrinter : DEVOPENSTRUC;
achDevice : Array[0..255] of Char;
end;
ptPrnThreadInfo = ^tPrnThreadInfo;
tPrnThreadInfo = Record
ulSizeStruct : ULong;
hwndOwner : HWnd;
dosPrinter : DEVOPENSTRUC;
end;
ptClientInfo = ^tClientInfo;
tClientInfo = Record
hwndListbox : HWnd;
end;
procedure extractPrnInfo( ppiQueue : PPRQINFO3;
pDosPrinter : pDEVOPENSTRUC );
{---------------------------------------------------------------}
{ This function extracts the needed information from the }
{ specified PRQINFO3 structure and places it into the specifies }
{ DEVOPENSTRUC structure. }
{ }
{ Input: ppiQueue - points to the PRQINFO3 structure }
{ Output: pdosPrinter - points to the initialized DEVOPENSTRUC }
{ structure }
{---------------------------------------------------------------}
Var
pchPos : pChar;
begin
pdosPrinter^.pszLogAddress := ppiQueue^.pszName;
pdosPrinter^.pszDriverName := ppiQueue^.pszDriverName;
pchPos := strPos( pdosPrinter^.pszDriverName, '.');
if pchPos <> nil then
pchPos^ := #0;
{------------------------------------------------------------}
{ The following if statement helps fix a bug from the 1st }
{ edition. The sample originally copied the pointer into }
{ the DEVOPENSTRUC structure, but the memory into which }
{ the memory was pointing was freed by createPrnList(). }
{ }
{ The fix, obviously, is to allocate our own area of memory }
{ and to copy the printer data to the new buffer. Note that }
{ destroyPrnList() must free the memory. destroyPrnList() }
{ doesn't get called until the application exits. }
{------------------------------------------------------------}
if ppiQueue^.pDriverData <> nil then
begin
GetMem( pdosPrinter^.pdriv, ppiQueue^.pDriverData^.cb );
if pdosPrinter^.pdriv = nil then
Exit;
Move( ppiQueue^.pDriverData^,
pdosPrinter^.pdriv^,
ppiQueue^.pDriverData^.cb );
end
else
pdosPrinter^.pdriv := nil;
pdosPrinter^.pszDataType := 'PM_Q_STD';
pdosPrinter^.pszComment := ppiQueue^.pszComment;
if strlen(ppiQueue^.pszPrProc) > 0 then
pdosPrinter^.pszQueueProcName := ppiQueue^.pszPrProc
else
pdosPrinter^.pszQueueProcName := nil;
if strlen(ppiQueue^.pszParms) > 0 then
pdosPrinter^.pszQueueProcParams := ppiQueue^.pszParms
else
pdosPrinter^.pszQueueProcParams := nil;
pdosPrinter^.pszSpoolerParams := nil;
pdosPrinter^.pszNetworkParams := nil;
end; { ExtractPrnInfo }
procedure destroyPrnList( hwndListbox: HWnd );
{---------------------------------------------------------------}
{ This function destroys the printer list and returns the memory}
{ to the system. }
{ }
{ Input: hwndListbox - handle of the listbox containing the }
{ printer list }
{---------------------------------------------------------------}
Var
usNumItems : UShort;
usIndex : UShort;
ppliInfo : ptPrnListInfo;
begin
usNumItems := WinQueryLboxCount(hwndListbox);
for usIndex := 0 to usNumItems-1 do
begin
ppliInfo :=
ptPrnListInfo(
pVoidFromMR( WinSendMsg( hwndListbox,
LM_QUERYITEMHANDLE,
MPFROMSHORT(usIndex),
0) ) );
if ppliInfo <> nil then
begin
{------------------------------------------------------}
{ The following 3 lines help fix a bug from the 1st }
{ edition }
{------------------------------------------------------}
if ppliInfo^.dosPrinter.pdriv <> nil then
FreeMem(ppliInfo^.dosPrinter.pdriv,ppliInfo^.dosPrinter.pdriv^.cb);
Dispose(ppliInfo);
end;
end;
WinSendMsg( hwndListbox,
LM_DELETEALL,
0,
0 );
end; { DestroyPrnList }
Function createPrnList( hwndListbox : HWnd ) : UShort;
{---------------------------------------------------------------}
{ This function enumerates the printers available and inserts }
{ them into the specified listbox. }
{ }
{ Input: hwndListbox - handle to the listbox to contain the }
{ list }
{ Returns: an CPL_* constant }
{---------------------------------------------------------------}
Var
seError : SPLERR;
ulSzBuf : ULONG;
ulNumQueues : ULONG;
ulNumReturned : ULONG;
ulSzNeeded : ULONG;
ulIndex : ULONG;
ppiQueue : PPRQINFO3;
pp : pPrqInfo3;
pchPos : PCHAR;
ppliInfo : ptPrnListInfo;
sInsert : SmallInt;
begin
{------------------------------------------------------------}
{ Get the size of the buffer needed }
{------------------------------------------------------------}
seError := SplEnumQueue( nil,
3,
nil,
0,
ulNumReturned,
ulNumQueues,
ulSzNeeded,
nil );
if seError <> ERROR_MORE_DATA then
begin
createPrnList := CPL_ERROR;
Exit;
end
else
if ulNumQueues = 0 then
begin
createPrnList := CPL_NOPRINTERS;
Exit;
end;
GetMem( ppiQueue, ulszNeeded );
if ppiQueue = nil then
begin
createPrnList := CPL_ERROR;
Exit;
end;
ulSzBuf := ulSzNeeded;
{------------------------------------------------------------}
{ Get the information }
{------------------------------------------------------------}
SplEnumQueue(nil,
3,
ppiQueue,
ulSzBuf,
ulNumReturned,
ulNumQueues,
ulSzNeeded,
nil);
{------------------------------------------------------------}
{ ulNumReturned has the count of the number of PRQINFO3 }
{ structures. }
{------------------------------------------------------------}
pp := ppiQueue;
for ulIndex := 0 to ulNumReturned-1 do
begin
{---------------------------------------------------------}
{ Since the 'comment' can have newlines in it, replace }
{ them with spaces }
{---------------------------------------------------------}
pchPos := strpos(pp^.pszComment,#10);
while pchPos <> nil do
begin
pchPos^ := ' ';
pchPos := strpos(pp^.pszComment,#10);
end;
New( ppliInfo );
if ppliInfo = nil then
continue;
{---------------------------------------------------------}
{ Extract the device name before initializing the }
{ DEVOPENSTRUC structure }
{---------------------------------------------------------}
pchPos := strpos(pp^.pszDriverName, '.');
if pchPos <> nil then
begin
pchPos^ := #0;
strcopy(ppliInfo^.achDevice, pchPos+1);
end;
extractPrnInfo( pp, @ppliInfo^.dosPrinter);
sInsert := WinInsertLboxItem( hwndListbox,
0,
pp^.pszComment);
WinSendMsg( hwndListbox,
LM_SETITEMHANDLE,
MPFROMSHORT(sInsert),
MPFROMP(ppliInfo) );
if pp^.fsType AND PRQ3_TYPE_APPDEFAULT <> 0 then
WinSendMsg( hwndListbox,
LM_SELECTITEM,
MPFROMSHORT(sInsert),
ord(TRUE) );
Inc( pp );
end; { For }
FreeMem( ppiQueue, ulSzNeeded );
createPrnList := CPL_SUCCESS;
end;
Function drawPage( hpsDraw : Hps; usPage: UShort ) : Bool;
{---------------------------------------------------------------}
{ This function draws a box for page 1. Any other page number }
{ returns an error. }
{ }
{ Input: usPage - specifies the page number to draw. }
{ Returns: TRUE if successful, FALSE otherwise }
{---------------------------------------------------------------}
Var
ptlMove : PointL;
s : String;
begin
DrawPage := True;
Case usPage of
1 :
begin
ptlMove.x := 10;
ptlMove.y := 10;
GpiMove( hpsDraw, ptlMove );
ptlMove.x := 100;
ptlMove.y := 100;
{ Additional drawings added in Virtual Pascal version }
GpiBox( hpsDraw,
DRO_OUTLINE,
ptlMove,
0,
0 );
GpiMove( hpsDraw, ptlMove );
ptlMove.x := 300;
ptlMove.y := 300;
GpiBox( hpsDraw,
DRO_FILL,
ptlMove,
0,
0 );
ptlMove.x := 10;
GpiMove( hpsDraw, ptlMove );
s := 'Presentation Manager Print Sample'#0;
GpiCharString( hpsDraw, Length(s)-1, @s[1] );
{ End of block added in Virtual Pascal version }
end;
else
DrawPage := False;
end; { Case usPage }
end;
function printThread( pArg : Pointer ) : Longint;
{---------------------------------------------------------------}
{ This function is the secondary thread which prints the output.}
{ }
{ Input: pptiInfo - points to the ptRNTHREADINFO structure }
{ containing needed information }
{---------------------------------------------------------------}
Var
habAnchor : Hab;
hmqQueue : HMq;
hdcPrinter : HDC;
szlHps : SizeL;
hpsPrinter : Hps;
OutCount : Long;
pptiInfo : ptPrnThreadInfo absolute pArg;
begin
habAnchor := WinInitialize(0);
hmqQueue := WinCreateMsgQueue(habAnchor,
0);
WinCancelShutdown(hmqQueue,
TRUE);
{------------------------------------------------------------}
{ Open a device context (HDC) }
{------------------------------------------------------------}
hdcPrinter := DevOpenDC( habAnchor,
OD_QUEUED,
'*',
9,
@pptiInfo^.dosPrinter,
NULLHANDLE);
if hdcPrinter = NULLHANDLE then
begin
{---------------------------------------------------------}
{ An error occurred }
{---------------------------------------------------------}
WinAlarm( HWND_DESKTOP,
WA_ERROR);
WinMessageBox( HWND_DESKTOP,
pptiInfo^.hwndOwner,
'Error creating the device context.',
'Error',
0,
MB_INFORMATION OR MB_OK );
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose(pptiInfo);
Endthread(0);
end;
{------------------------------------------------------------}
{ Query the width and height of the printer page }
{------------------------------------------------------------}
DevQueryCaps( hdcPrinter,
CAPS_WIDTH,
1,
szlHps.cx );
DevQueryCaps( hdcPrinter,
CAPS_HEIGHT,
1,
szlHps.cy );
{------------------------------------------------------------}
{ Create a presentation space (HPS) associated with the }
{ printer HDC }
{------------------------------------------------------------}
hpsPrinter := GpiCreatePS( habAnchor,
hdcPrinter,
szlHps,
PU_LOENGLISH OR GPIT_MICRO OR GPIA_ASSOC );
if hpsPrinter = NULLHANDLE then
begin
{---------------------------------------------------------}
{ An error occurred }
{---------------------------------------------------------}
DevCloseDC(hdcPrinter);
WinAlarm( HWND_DESKTOP,
WA_ERROR );
WinMessageBox( HWND_DESKTOP,
pptiInfo^.hwndOwner,
'Error creating the presentation space.',
'Error',
0,
MB_INFORMATION OR MB_OK );
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose( pptiInfo );
Endthread(0);
end;
{------------------------------------------------------------}
{ Tell the printer that we are starting a print job }
{------------------------------------------------------------}
if (DevEscape(hdcPrinter,
DEVESC_STARTDOC,
strlen(pptiInfo^.dosPrinter.pszComment),
pptiInfo^.dosPrinter.pszComment,
OutCount,
nil) <> DEV_OK) then
begin
{---------------------------------------------------------}
{ An error occurred }
{---------------------------------------------------------}
GpiDestroyPS(hpsPrinter);
DevCloseDC(hdcPrinter);
WinAlarm( HWND_DESKTOP,
WA_ERROR );
WinMessageBox(HWND_DESKTOP,
pptiInfo^.hwndOwner,
'Error starting the print job.',
'Error',
0,
MB_INFORMATION OR MB_OK);
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose( pptiInfo );
endthread(0);
end;
{------------------------------------------------------------}
{ Draw sample output }
{------------------------------------------------------------}
if not drawPage( hpsPrinter, 1) then
begin
{---------------------------------------------------------}
{ An error occurred so abort the print job }
{---------------------------------------------------------}
DevEscape( hdcPrinter,
DEVESC_ABORTDOC,
0,
nil,
OutCount,
nil);
GpiDestroyPS(hpsPrinter);
DevCloseDC(hdcPrinter);
WinAlarm( HWND_DESKTOP,
WA_ERROR );
WinMessageBox(HWND_DESKTOP,
pptiInfo^.hwndOwner,
'Error drawing the printer page.',
'Error',
0,
MB_INFORMATION OR MB_OK);
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose( pptiInfo );
EndThread(0);
end;
{------------------------------------------------------------}
{ Tell the printer that we are finished with the print job }
{------------------------------------------------------------}
if (DevEscape( hdcPrinter,
DEVESC_ENDDOC,
0,
nil,
OutCount,
nil) <> DEV_OK) then
begin
{---------------------------------------------------------}
{ An error occurred so abort the print job }
{---------------------------------------------------------}
DevEscape( hdcPrinter,
DEVESC_ABORTDOC,
0,
nil,
OutCount,
nil );
GpiDestroyPS(hpsPrinter);
DevCloseDC(hdcPrinter);
WinAlarm( HWND_DESKTOP,
WA_ERROR);
WinMessageBox(HWND_DESKTOP,
pptiInfo^.hwndOwner,
'Error ending the print job.',
'Error',
0,
MB_INFORMATION OR MB_OK);
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose(pptiInfo);
EndThread(0);
end;
{------------------------------------------------------------}
{ Destroy the HPS and close the printer HDC }
{------------------------------------------------------------}
GpiDestroyPS(hpsPrinter);
DevCloseDC(hdcPrinter);
WinAlarm(HWND_DESKTOP,
WA_NOTE);
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
Dispose( pptiInfo );
EndThread(0);
end; { PrintThread }
Function ClientWndProc ( hwndClient : HWND ;
ulMsg : ULONG;
mpParm1 : MPARAM;
mpParm2 : MPARAM ) : mResult; cdecl;
Var
pciInfo : ptClientInfo;
sSelect : Integer;
ppliInfo : ptPrnListInfo;
sIndex : Integer;
pptiInfo : ptPrnThreadInfo;
usSelect : Integer;
usResult : SmallInt;
hpsPaint : Hps;
rclPaint : RectL;
begin
pciInfo := WinQueryWindowPtr( hwndClient, 0 );
ClientWndProc := ord( False );
Case ulMsg of
WM_CREATE :
begin
{------------------------------------------------------}
{ Allocate and initialize the CLIENTINFO structure }
{------------------------------------------------------}
New( pciInfo );
if pciInfo = nil then
begin
WinMessageBox(HWND_DESKTOP,
HWND_DESKTOP,
'An error occurred in initialization.',
'Error',
0,
MB_OK OR MB_INFORMATION);
ClientWndProc := ord( True );
end;
WinSetWindowPtr( hwndClient,
0,
pciInfo );
pciInfo^.hwndListbox := WinCreateWindow(hwndClient,
WC_LISTBOX,
'',
LS_HORZSCROLL OR
LS_NOADJUSTPOS,
0,
0,
0,
0,
hwndClient,
HWND_TOP,
WND_LISTBOX,
nil,
nil);
if pciInfo^.hwndListbox = NULLHANDLE then
begin
WinMessageBox(HWND_DESKTOP,
HWND_DESKTOP,
'An error occurred in initialization.',
'Error',
0,
MB_OK OR MB_INFORMATION);
Dispose( pciInfo );
ClientWndProc := ord( True );
end;
WinSendMsg( hwndClient,
WM_COMMAND,
MPFROMSHORT(MI_REFRESH),
0 );
end; { WM_CREATE }
WM_DESTROY :
begin
{---------------------------------------------------------}
{ Return the resources to the system }
{---------------------------------------------------------}
destroyPrnList(pciInfo^.hwndListbox);
WinDestroyWindow(pciInfo^.hwndListbox);
end; { WM_DESTROY }
WM_SIZE :
WinSetWindowPos( pciInfo^.hwndListbox,
NULLHANDLE,
0,
0,
SHORT1FROMMP(mpParm2),
SHORT2FROMMP(mpParm2),
SWP_MOVE OR SWP_SIZE OR SWP_SHOW );
WM_INITMENU :
Case Short1FromMP(mpParm1) of
M_SAMPLE :
begin
sSelect := WinQueryLboxSelectedItem(pciInfo^.hwndListbox);
ppliInfo :=
ptPrnListInfo(
pVoidFromMR(WinSendMsg(pciInfo^.hwndListbox,
LM_QUERYITEMHANDLE,
MPFROMSHORT(sSelect),
0)) );
{---------------------------------------------------}
{ If no printer is selected, disable the print }
{ menuitem }
{---------------------------------------------------}
if sSelect <> LIT_NONE then
WinEnableMenuItem( HWNDFROMMP(mpParm2),
MI_PRINT,
TRUE )
else
WinEnableMenuItem( HWNDFROMMP(mpParm2),
MI_PRINT,
FALSE );
{---------------------------------------------------}
{ If no printer is selected or there is no driver }
{ data, disable the setup menuitem }
{---------------------------------------------------}
if ((sSelect <> LIT_NONE) AND (ppliInfo <> nil) AND
(ppliInfo^.dosPrinter.pdriv <> nil)) then
WinEnableMenuItem( HWNDFROMMP(mpParm2),
MI_SETUP,
TRUE)
else
WinEnableMenuItem( HWNDFROMMP(mpParm2),
MI_SETUP,
FALSE );
end; { M_SAMPLE }
else
ClientWndProc := WinDefWindowProc( hwndClient,
ulMsg,
mpParm1,
mpParm2);
end; { WM_INITMENU }
WM_COMMAND :
Case Short1FromMP(mpParm1) of
MI_PRINT :
begin
{---------------------------------------------------}
{ Query the selected printer }
{---------------------------------------------------}
sIndex := WinQueryLboxSelectedItem(pciInfo^.hwndListbox);
ppliInfo :=
ptPrnListInfo(
pVoidFromMR(WinSendMsg(pciInfo^.hwndListbox,
LM_QUERYITEMHANDLE,
MPFROMSHORT(sIndex),
0)) );
{---------------------------------------------------}
{ Allocate and initialize the PRNTHREADINFO }
{ structure to pass to the thread }
{---------------------------------------------------}
New( pptiInfo );
if pptiInfo = nil then
begin
WinMessageBox( HWND_DESKTOP,
HWND_DESKTOP,
'An error occurred while trying to print',
'Error',
0,
MB_OK OR MB_INFORMATION );
ClientWndProc := ord( True );
Exit;
end;
pptiInfo^.ulSizeStruct := sizeof(tPrnThreadInfo);
pptiInfo^.hwndOwner := hwndClient;
pptiInfo^.dosPrinter := ppliInfo^.dosPrinter;
{---------------------------------------------------}
{ Create the thread to do the printing }
{---------------------------------------------------}
if VPBeginThread( PrintThread,
$8000,
pptiInfo) = -1 then
WinMessageBox( HWND_DESKTOP,
HWND_DESKTOP,
'An error occurred while trying to print',
'Error',
0,
MB_OK OR MB_INFORMATION );
end; { MI_PRINT }
MI_SETUP :
begin
{---------------------------------------------------}
{ Query the selected printer }
{---------------------------------------------------}
usSelect := WinQueryLboxSelectedItem(pciInfo^.hwndListbox);
ppliInfo :=
ptPrnListInfo(
pVoidFromMR(WinSendMsg(pciInfo^.hwndListbox,
LM_QUERYITEMHANDLE,
MPFROMSHORT(usSelect),
0)) );
DevPostDeviceModes( WinQueryAnchorBlock(hwndClient),
ppliInfo^.dosPrinter.pdriv,
ppliInfo^.dosPrinter.pszDriverName,
ppliInfo^.achDevice,
nil,
DPDM_POSTJOBPROP );
end; { MI_SETUP }
MI_REFRESH :
begin
destroyPrnList(pciInfo^.hwndListbox);
usResult := createPrnList(pciInfo^.hwndListbox);
case usResult of
CPL_ERROR :
WinMessageBox( HWND_DESKTOP,
HWND_DESKTOP,
'An error occurred while refreshing the list',
'Error',
0,
MB_OK OR MB_INFORMATION);
CPL_NOPRINTERS :
WinMessageBox( HWND_DESKTOP,
HWND_DESKTOP,
'There are no printers defined.',
'Warning',
0,
MB_OK OR MB_INFORMATION);
else
WinSendMsg( WinWindowFromID(hwndClient,WND_LISTBOX),
LM_SELECTITEM,
MPFROMSHORT(0),
ord(TRUE));
end; { Case usResult }
end; { MI_REFRESH }
MI_EXIT :
WinPostMsg( hwndClient,
WM_CLOSE,
0,
0 );
else
ClientWndProc := WinDefWindowProc( hwndClient,
ulMsg,
mpParm1,
mpParm2);
end; { WM_COMMAND }
WM_PAINT :
begin
hpsPaint := WinBeginPaint( hwndClient,
NULLHANDLE,
@rclPaint );
WinFillRect( hpsPaint,
rclPaint,
SYSCLR_WINDOW );
WinEndPaint(hpsPaint);
end; { WM_PAINT }
else
ClientWndProc := WinDefWindowProc( hwndClient,
ulMsg,
mpParm1,
mpParm2);
end; { Case ulMsg }
end;
Var
habAnchor : HAB;
hmqQueue : HMQ;
ulFlags : ULONG;
hwndFrame : HWND;
bLoop : BOOL;
qmMsg : QMSG;
begin
habAnchor := WinInitialize(0);
hmqQueue := WinCreateMsgQueue(habAnchor,
0);
WinRegisterClass( habAnchor,
CLS_CLIENT,
clientWndProc,
0,
sizeof(Pointer) );
ulFlags := FCF_SIZEBORDER OR FCF_TITLEBAR OR FCF_TASKLIST OR
FCF_SHELLPOSITION OR FCF_SYSMENU OR FCF_MENU OR FCF_ACCELTABLE;
hwndFrame := WinCreateStdWindow(HWND_DESKTOP,
WS_VISIBLE,
ulFlags,
CLS_CLIENT,
'Printing Sample Application',
0,
NULLHANDLE,
RES_CLIENT,
nil);
if hwndFrame <> NULLHANDLE then
begin
bLoop := WinGetMsg(habAnchor,
qmMsg,
NULLHANDLE,
0,
0);
while bLoop do
begin
WinDispatchMsg( habAnchor,
qmMsg);
bLoop := WinGetMsg( habAnchor,
qmMsg,
NULLHANDLE,
0,
0);
end;
WinDestroyWindow(hwndFrame);
end;
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
end.