Category : Files from Magazines
Archive   : DDJ-9103.ZIP
Filename : NET_WIN3.ASC

 
Output of file : NET_WIN3.ASC contained in archive : DDJ-9103.ZIP

_NETWORKING WITH WINDOWS 3_
by Mike Klein


[LISTING ONE]

/* MEGAPHON.H */

#define MAX_CONNECTIONS 100
#define MAX_MESSAGE_LEN 56

#define IDT_MESSAGETIMER 100

#define IDC_USERLISTBOXTITLE 100
#define IDC_USERLISTBOX 101
#define IDC_SERVERCOMBOBOXTITLE 102
#define IDC_SERVERCOMBOBOX 103
#define IDC_MESSAGEEDITBOX 104
#define IDC_SENDBUTTON 105
#define IDC_SETTINGSBUTTON 106
#define IDC_EXITBUTTON 107

#define IDC_ACCEPTMESSAGES 100
#define IDC_ICONIZEMESSAGES 101
#define IDC_ONLYATTACHEDUSERS 102
#define IDC_ALLUSERSINBINDERY 103

#define IDC_USERNAME 100
#define IDC_STATION 101
#define IDC_NODE 102
#define IDC_FULLNAME 103
#define IDC_LOGINTIME 104
#define IDC_NETWORK 105

#define IDC_REPLYEDITBOX 100
#define IDC_REPLYBUTTON 101
#define IDC_SAVEBUTTON 102

#define IDM_EXIT 200
#define IDM_ABOUT 201
#define IDM_REFRESH 220


int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);

LONG FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
LONG FAR PASCAL UserInfo(HWND, unsigned, WORD, LONG);
LONG FAR PASCAL MessageHandler(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL About(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL Settings(HWND, unsigned, WORD, LONG);

BOOL PASCAL InitNetStuff(VOID);
VOID PASCAL EnableOrDisableSendButton(VOID);
VOID PASCAL SendNetWareMessageToUsers(VOID);
VOID PASCAL ShowUserInformation(VOID);




[LISTING TWO]

/*****************************************************************************

PROGRAM: Megaphone
AUTHOR : Mike Klein
VERSION: 1.0
FILE : megaphon.exe
CREATED: 10-25-90

REQUIREMENTS: Windows 3.x and a Novell NetWare 2.1x or compatible network

PURPOSE : Messaging and simple information system for Novell NetWare.
Allows quick replies to messages from coworkers and
miscellaneous login information about them.

*****************************************************************************/


#define NOCOMM


#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#include "megaphon.h"


HANDLE hInstMegaphone; /* Original instance of Megaphone */

/* These are handles to commonly accessed controls in different dialogs */

HWND hWndCurrent; /* handle to currently active window on screen */
HWND hDlgMegaphone; /* handle to Megaphone dialog window */
HWND hWndUserListBox;
HWND hWndServerComboBox;
HWND hWndMessageEditBox;
HWND hWndSendButton;

/* Set up some additional global variables to commonly used NetWare stuff */

WORD DefaultConnectionID;
BYTE ServerName[48];

WORD UserConnectionNum;
BYTE UserName[48];
BYTE UserLoginTime[7];

WORD SelUserConnectionNum;
BYTE SelUserNetworkAddr[4];
BYTE SelUserNodeAddr[6];
BYTE SelUserName[48];
BYTE SelUserFullName[48];
BYTE SelUserLoginTime[7];

BOOL AcceptMessages = TRUE;
BOOL IconizeMessages = FALSE;
BOOL AllUsers = FALSE;

BYTE Text[100];


/*****************************************************************************

FUNCTION: WinMain

PURPOSE : Calls initialization function, processes message loop

*****************************************************************************/

int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
MSG msg;

if(!hPrevInstance) /* Other instances of app running? */
{
hInstMegaphone = hInstance; /* Remember original instance */

/* Fill in window class structure with parameters that describe the */
/* main window. */

wc.style = CS_DBLCLKS; /* Process double click msgs */
wc.lpfnWndProc = MainWndProc; /* Function to retrieve msgs for */
/* windows of this class. */
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = DLGWINDOWEXTRA; /* Set becuase we used the CLASS */
/* statement in dialog box */
wc.hInstance = hInstance; /* Application that owns the class.*/
wc.hIcon = LoadIcon(hInstance, "Megaphone");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "Megaphone";
wc.lpszClassName = "Megaphone";

if(!RegisterClass(&wc))
return(FALSE);

/* Fill in window class structure with parameters that describe the */
/* message window. */

wc.style = NULL; /* Process double click msgs */
wc.lpfnWndProc = MessageHandler;
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = DLGWINDOWEXTRA; /* Set becuase we used the CLASS */
/* statement in dialog box */
wc.hInstance = hInstance; /* Application that owns the class.*/
wc.hIcon = LoadIcon(hInstance, "Message");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "Message";

if(!RegisterClass(&wc))
return(FALSE);

/* Fill in window class structure with parameters that describe the */
/* userinfo window. */

wc.style = NULL; /* Process double click msgs */
wc.lpfnWndProc = UserInfo;
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = DLGWINDOWEXTRA; /* Set becuase we used the CLASS */
/* statement in dialog box */
wc.hInstance = hInstance; /* Application that owns the class.*/
wc.hIcon = LoadIcon(hInstance, "User");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "User";

if(!RegisterClass(&wc))
return(FALSE);

/* Create the main dialog window Megaphone, get window handles to */
/* several of the controls, send the message edit box a message to */
/* limit itself to MAX_MESSAGE_LEN characters, and set the system */
/* font (mono-spaced) for server combo box and user list box. */

hDlgMegaphone = CreateDialog(hInstance, "Megaphone", NULL, 0L);

hWndUserListBox = GetDlgItem(hDlgMegaphone, IDC_USERLISTBOX);
hWndServerComboBox = GetDlgItem(hDlgMegaphone, IDC_SERVERCOMBOBOX);
hWndMessageEditBox = GetDlgItem(hDlgMegaphone, IDC_MESSAGEEDITBOX);
hWndSendButton = GetDlgItem(hDlgMegaphone, IDC_SENDBUTTON);

SendMessage(hWndMessageEditBox, EM_LIMITTEXT, MAX_MESSAGE_LEN - 1, 0L);
SendMessage(hWndUserListBox, WM_SETFONT,
GetStockObject(SYSTEM_FIXED_FONT), FALSE);
SendMessage(hWndServerComboBox, WM_SETFONT,
GetStockObject(SYSTEM_FIXED_FONT), FALSE);

/* Finally, show the Megaphone dialog box */

ShowWindow(hDlgMegaphone, nCmdShow);
UpdateWindow(hDlgMegaphone);

/* Initialize the network stuff, and fill in list boxes */

InitNetStuff();
}
else
{
/* If there was another instance of Megaphone running, then switch to */
/* it by finding any window of class = "Megaphone". Then, if it's an */
/* icon, open the window, otherwise just make it active. */

hDlgMegaphone = FindWindow("Megaphone", NULL);
if(IsIconic(hDlgMegaphone))
ShowWindow(hDlgMegaphone, SW_SHOWNORMAL);
SetActiveWindow(hDlgMegaphone);

return(FALSE);
}

/* Acquire and dispatch messages until a WM_QUIT message is received. */
/* The window handle hWndCurrent points to the currently active window, */
/* and is used to identify and process keystrokes going to any modeless */
/* dialog box. */

while(GetMessage(&msg, NULL, NULL, NULL))
if(hWndCurrent == NULL || !IsDialogMessage(hWndCurrent, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}


/*****************************************************************************

FUNCTION: MainWndProc

PURPOSE : Processes messages for Megaphone dialog box

*****************************************************************************/

long FAR PASCAL MainWndProc(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
{
FARPROC lpProc; /* Far procedure ptr to be used for About box */

HWND hWndTemp;
HWND hDlgMessage;

BYTE *ptr;
int Index = 100;

BYTE MessageCaption[100];
BYTE MessageDate[9];
BYTE MessageTime[9];

switch(wMsg)
{
case WM_COMMAND :

switch(wParam)
{
/* When wParam == 1, return was pressed in either the list box, */
/* combo box, check boxes, or edit control. */

case 1 :

/* Find out the current control (window) and process ENTER */
/* key accordingly. */

switch(GetDlgCtrlID(GetFocus()))
{
case IDC_USERLISTBOX :

ShowUserInformation();
break;

case IDC_SERVERCOMBOBOX :

SendMessage(hWndServerComboBox, WM_KILLFOCUS, NULL, 0L);
SendMessage(hWndServerComboBox, WM_SETFOCUS, NULL, 0L);
break;

case IDC_MESSAGEEDITBOX :

if(IsWindowEnabled(hWndSendButton))
{
SendMessage(hWndSendButton, WM_LBUTTONDOWN, 0, 0L);
SendMessage(hWndSendButton, WM_LBUTTONUP, 0, 0L);
SendMessage(hDlgMegaphone, WM_NEXTDLGCTL,
hWndMessageEditBox, TRUE);
}
else
{
MessageBeep(0);
MessageBox(hDlgMegaphone, "You need a message and a user(s)",
"ERROR", MB_ICONEXCLAMATION | MB_OK);
}
break;

default :

break;
}
break;

case IDC_USERLISTBOX :

if(HIWORD(lParam) == LBN_DBLCLK)
{
/* Restore the list box item's selection state. If it */
/* isn't flagged, then flag it, and vice versa. */

Index = (int) SendMessage(hWndUserListBox, LB_GETCURSEL, 0, 0L);
if(SendMessage(hWndUserListBox, LB_GETSEL, Index, 0L))
SendMessage(hWndUserListBox, LB_SETSEL, FALSE, Index);
else
SendMessage(hWndUserListBox, LB_SETSEL, TRUE, Index);

ShowUserInformation();
}
else
EnableOrDisableSendButton();

break;

case IDC_SERVERCOMBOBOX :

if(HIWORD(lParam) == CBN_SELCHANGE)
{
if((Index = (int) SendMessage(hWndServerComboBox, CB_GETCURSEL,
0, 0L)) == CB_ERR)
break;

SendMessage(hWndServerComboBox, CB_GETLBTEXT, Index,
(LONG) (LPSTR) ServerName);

if(!GetConnectionID(ServerName, &DefaultConnectionID))
{
SetPreferredConnectionID(DefaultConnectionID);
InitNetStuff();
}
}
break;

case IDC_MESSAGEEDITBOX :

EnableOrDisableSendButton();
break;

case IDC_SENDBUTTON :

if(HIWORD(lParam) == BN_CLICKED)
SendNetWareMessageToUsers();
break;

case IDM_EXIT :
case IDC_EXITBUTTON :

SendMessage(hDlgMegaphone, WM_CLOSE, 0, 0L);
break;

case IDM_ABOUT :

lpProc = MakeProcInstance(About, hInstMegaphone);
DialogBox(hInstMegaphone, "About", hWnd, lpProc);
FreeProcInstance(lpProc);
break;

case IDM_REFRESH :

InitNetStuff();
break;

case IDC_SETTINGSBUTTON :

lpProc = MakeProcInstance(Settings, hInstMegaphone);
DialogBox(hInstMegaphone, "Settings", hWnd, lpProc);
FreeProcInstance(lpProc);
break;

default :

break;
}

break;

case WM_TIMER :

/* This is the Windows timer for retrieving messages that goes off */
/* every five seconds. */

GetBroadcastMessage(Text);

if(*Text)
{
/* Create the message reply dialog box and limit the edit box */
/* to NetWare's limit of 56 or so characters. */

hDlgMessage = CreateDialog(hInstMegaphone, "Message",
hDlgMegaphone, 0L);

SendDlgItemMessage(hDlgMessage, IDC_REPLYEDITBOX, EM_LIMITTEXT,
MAX_MESSAGE_LEN - 1, 0L);

/* Parse the incoming string of 'username[station#]message' */

if((ptr = strchr(Text, '[')) == NULL)
{
/* If the incoming message isn't formatted by NetWare's */
/* SEND command, SESSION program, or Megaphone, then we */
/* can't use the REPLY button, so disable it. */

SelUserName[0] = '\0';
SelUserConnectionNum = 0;
EnableWindow(GetDlgItem(hDlgMessage, IDC_REPLYBUTTON), FALSE);
}
else
{
/* Pull up the user name and connection#, and message, which */
/* is right after the ']'. */

strncpy(SelUserName, Text, ptr - Text);
SelUserName[ptr - Text] = '\0';
SelUserConnectionNum = atoi(ptr + 1);
if((ptr = strchr(Text, ']')) != NULL)
lstrcpy((LPSTR) Text, (LPSTR) (ptr + 1));

/* Check again to see if we pulled up a valid Conn#. If we */
/* didn't, then disable the REPLY button. */

if(SelUserConnectionNum < 1 || SelUserConnectionNum > 255)
EnableWindow(GetDlgItem(hDlgMessage, IDC_REPLYBUTTON), FALSE);
}

/* Put the retrieved message in the dialog's edit box */

SetDlgItemText(hDlgMessage, IDC_REPLYEDITBOX, Text);

/* Record the date and time that the message came in at and */
/* make it reflected in the message caption. */

_strdate(MessageDate);
_strtime(MessageTime);
wsprintf(MessageCaption, "%s %s %s", (LPSTR) SelUserName,
(LPSTR) MessageDate, (LPSTR) MessageTime);
SetWindowText(hDlgMessage, MessageCaption);

/* Finally, show (or minimize) the completed message dialog box */

if(IconizeMessages)
ShowWindow(hDlgMessage, SW_SHOWMINNOACTIVE);
else
ShowWindow(hDlgMessage, SW_SHOWNORMAL);

MessageBeep(0);
}

return(0L);

case WM_CLOSE :

/* Check before closing the main window if there are any */
/* outstanding messages that haven't been closed. */

if(hWndTemp = FindWindow((LPSTR) "Message", NULL))
{
if(MessageBox(hDlgMegaphone,
"Quit without disposing of/reading messages?",
"Messages Outstanding", MB_YESNO | MB_APPLMODAL |
MB_ICONEXCLAMATION | MB_DEFBUTTON2) == IDYES)
{
DestroyWindow(hDlgMegaphone);
}
else
{
ShowWindow(hWndTemp, SW_SHOWNORMAL);
SetActiveWindow(hWndTemp);
}
}
else
DestroyWindow(hDlgMegaphone);

return(0L);

case WM_SETFOCUS :

if(IsWindowEnabled(hWndMessageEditBox))
SendMessage(hDlgMegaphone, WM_NEXTDLGCTL, hWndMessageEditBox, TRUE);
else
SendMessage(hDlgMegaphone, WM_NEXTDLGCTL,
GetDlgItem(hDlgMegaphone, IDC_EXITBUTTON), TRUE);

return(0L);

case WM_ACTIVATE :

hWndCurrent = (wParam == NULL) ? NULL : hWnd;
break;

case WM_DESTROY :

PostQuitMessage(0);
return(0L);

default :

break;

}
return(DefDlgProc(hWnd, wMsg, wParam, lParam));
}


/*****************************************************************************

FUNCTION: SendNetWareMessageToUsers

PURPOSE : Do I really need to explain this one?

*****************************************************************************/

VOID PASCAL SendNetWareMessageToUsers(VOID)
{
BYTE Message[MAX_MESSAGE_LEN];
WORD ConnectionsToSend[MAX_CONNECTIONS];
BYTE ResultList[MAX_CONNECTIONS];
int NumUsers;
int i, j;

/* Get text inside message edit box and format message so it includes */
/* the username, connection#, and message. The first two fields are */
/* needed for replying back since there's nothing in NetWare's messaging */
/* facility to tell you who sent the message. */

GetDlgItemText(hDlgMegaphone, IDC_MESSAGEEDITBOX, (LPSTR) Text, MAX_MESSAGE_LEN);

wsprintf(Message, "%s[%d]%s", (LPSTR) UserName, UserConnectionNum,
(LPSTR) Text);

/* Get total number of users in list box and check to see if they've */
/* been selected or not. If they have, get their Connection# and put it */
/* in the ConnectionsToSend array. */

NumUsers = (int) SendMessage(hWndUserListBox, LB_GETCOUNT, 0, 0L);

for(i = j = 0; i < NumUsers; i++)
if(SendMessage(hWndUserListBox, LB_GETSEL, i, 0L))
{
SendMessage(hWndUserListBox, LB_GETTEXT, i, (LONG) (LPSTR) Text);
ConnectionsToSend[j++] = atoi(&Text[18]);
}

/* Send the message to users in the array. */

SendBroadcastMessage(Message, ConnectionsToSend, ResultList, j);

/* Scan through the ResultList array checking for messages that had */
/* problems. Selecting OK will continue to check the status of the other */
/* messages, where selecting CANCEL from the message box will abort the */
/* send status checking altogether. */

for(i = 0; i < j; i++)
switch(ResultList[i])
{
case 0xfc :

wsprintf(Text, "Message to Connection %d", ConnectionsToSend[i]);
if(MessageBox(hDlgMegaphone,
"Message not sent - User already has message pending",
Text, MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
{
i = j;
}
break;

case 0xfd :

wsprintf(Text, "Message to Connection %d", ConnectionsToSend[i]);
if(MessageBox(hDlgMegaphone,
"Message not sent - Invalid connection number",
Text, MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
{
i = j;
}
break;

case 0xff :

wsprintf(Text, "Message to Connection %d", ConnectionsToSend[i]);
if(MessageBox(hDlgMegaphone,
"Message not sent - User has blocking turned on",
Text, MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
{
i = j;
}
break;

default :

break;
}
}


/*****************************************************************************

FUNCTION: EnableOrDisableSendButton

PURPOSE : Based on a message being in the edit box and at least one
selected user, the send button is enabled or disabled

*****************************************************************************/

VOID PASCAL EnableOrDisableSendButton(VOID)
{
/* Check to see if at least one user is selected and at least a one */
/* character message in the edit box. If there is, then enable the SEND */
/* button and thicken it to make it the default response when ENTER is */
/* pressed. */

if(SendMessage(hWndUserListBox, LB_GETSELCOUNT, 0, 0L) &&
SendMessage(hWndMessageEditBox, EM_LINELENGTH, -1, 0L))
{
EnableWindow(hWndSendButton, TRUE);
}
else
{
EnableWindow(hWndSendButton, FALSE);
}
}


/*****************************************************************************

FUNCTION: InitNetStuff

PURPOSE : Initialize network connections and fill in combo and list boxes

*****************************************************************************/

BOOL PASCAL InitNetStuff(VOID)
{
HCURSOR hOldCursor;

WORD NumberOfConnections;
WORD NumberOfServers;

int Index;
BYTE DirHandle;

BYTE TempServerName[48];
WORD ObjectType;
WORD ConnID;
WORD ConnectionList[MAX_CONNECTIONS];
BYTE SearchObjectName[48] = "*";
BYTE ObjectName[48];

long ObjectID;
BYTE ObjectHasProperties;
BYTE ObjectFlag;
BYTE ObjectSecurity;

/* Check to see if a connection has been made to any server */

if(UserConnectionNum = GetConnectionNumber())
if(!GetConnectionInformation(UserConnectionNum, UserName, &ObjectType,
&ObjectID, UserLoginTime))
if(*UserName)
{
/* If we have a preferred connection ID, then were supposed to */
/* use it for all of our requests. If we don't, then check to */
/* see if we're sitting on a local drive (bit 0 or 1 not set). */
/* If we are, then set the default connection ID to that of the */
/* primary server. If we're sitting on a network drive, then */
/* requests go to the associated server. */

if(GetPreferredConnectionID())
DefaultConnectionID = GetPreferredConnectionID();
else
{
if(!(GetDriveInformation((BYTE) (_getdrive() - 1), &ConnID,
&DirHandle) & 3))
{
DefaultConnectionID = GetPrimaryConnectionID();
}
SetPreferredConnectionID(DefaultConnectionID);
}

/* Set NetWare's message mode so that Megaphone can poll for */
/* messages instead of automatically having them sent to the */
/* station. */

EnableBroadcasts();
SetBroadcastMode(3);

/* Set up a Windows timer so that every 5 seconds, the server is */
/* polled for waiting messages. */

SetTimer(hDlgMegaphone, IDT_MESSAGETIMER, 5000, NULL);

EnableWindow(GetDlgItem(hDlgMegaphone, IDC_SETTINGSBUTTON), TRUE);
}

if(!UserConnectionNum)
{
EnableWindow(GetDlgItem(hDlgMegaphone, IDC_SETTINGSBUTTON), FALSE);
MessageBox(hDlgMegaphone, "Must be logged into a NetWare server",
"ERROR - NO USERS", MB_ICONSTOP | MB_OK);
return(FALSE);
}

/* Now that we've established a network connection, let's fill in the */
/* drop-down combo box with file servers and the list box with users of */
/* whatever the node's preferred server is. */

/* Turn off re-drawing of the list box so it doesn't flicker, reset the */
/* contents of both boxes, capture and intercept all mouse activity, and */
/* turn the cursor into an hourglass. */

SendMessage(hWndUserListBox, WM_SETREDRAW, FALSE, 0L);
SendMessage(hWndUserListBox, LB_RESETCONTENT, 0, 0L);
SendMessage(hWndServerComboBox, CB_RESETCONTENT, 0, 0L);
SetCapture(hDlgMegaphone);
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

/* Scan through the possible ConnectionID#'s (1-8) and see what file */
/* servers are attached, if any are, and put them in the combo box. */

for(ConnID = 1; ConnID < 9; ++ConnID)
{
GetFileServerName(ConnID, TempServerName);
if(*TempServerName)
SendMessage(hWndServerComboBox, CB_ADDSTRING, NULL,
(LONG) (LPSTR) TempServerName);
}

/* Get default server */

GetFileServerName(DefaultConnectionID, ServerName);

/* Search the NetWare bindery for active user connections, putting */
/* them into the list box */

ObjectID = -1;
while(!ScanBinderyObject(SearchObjectName, OT_USER, &ObjectID, ObjectName,
&ObjectType, &ObjectHasProperties, &ObjectFlag, &ObjectSecurity))
{
GetObjectConnectionNumbers(ObjectName, OT_USER, &NumberOfConnections,
ConnectionList, MAX_CONNECTIONS);

/* If there are multiple connections for a single user then we */
/* have to make sure and get all of them. */

if(!NumberOfConnections)
{
if(AllUsers)
{
wsprintf(Text, "[%s]", (LPSTR) ObjectName);
SendMessage(hWndUserListBox, LB_ADDSTRING, NULL, (LONG) (LPSTR) Text);
}
}
else
for(Index = 0; Index < (int) NumberOfConnections; ++Index)
{
if(UserConnectionNum == ConnectionList[Index])
wsprintf(Text, "%-16.16s *%3d", (LPSTR) ObjectName, ConnectionList[Index]);
else
wsprintf(Text, "%-17.17s %3d", (LPSTR) ObjectName, ConnectionList[Index]);
SendMessage(hWndUserListBox, LB_ADDSTRING, NULL, (LONG) (LPSTR) Text);
}
}

/* Turn re-drawing for the list box back on and make the first item in */
/* the server combo box and user list box the default. */

InvalidateRect(hWndUserListBox, NULL, TRUE);
SendMessage(hWndUserListBox, LB_SETSEL, 0, 0L);
SendMessage(hWndUserListBox, WM_SETREDRAW, TRUE, 0L);

/* Select the default server in the server combo box */

SendMessage(hWndServerComboBox, CB_SELECTSTRING, -1, (LONG) (LPSTR) ServerName);

/* Add the # of servers and users to the caption on the server and list */
/* boxes. */

NumberOfConnections = (int) SendMessage(hWndUserListBox, LB_GETCOUNT, 0, 0L);
wsprintf(Text, "%d &Users on %s", NumberOfConnections, (LPSTR) ServerName);
SetDlgItemText(hDlgMegaphone, IDC_USERLISTBOXTITLE, (LPSTR) Text);

NumberOfServers = (int) SendMessage(hWndServerComboBox, CB_GETCOUNT, 0, 0L);
wsprintf(Text, "%d Ser&vers", NumberOfServers);
SetDlgItemText(hDlgMegaphone, IDC_SERVERCOMBOBOXTITLE, (LPSTR) Text);

/* Restore mouse activity, set the cursor back to normal, and initially */
/* disable the Send button. */

ReleaseCapture();
SetCursor(hOldCursor);
EnableOrDisableSendButton();

return(TRUE);
}


/*****************************************************************************

FUNCTION: About

PURPOSE : Processes messages for About box

*****************************************************************************/

BOOL FAR PASCAL About(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
{
switch(wMsg)
{
case WM_INITDIALOG :

return(TRUE);

case WM_COMMAND :

if(wParam == IDOK || wParam == IDCANCEL)
{
EndDialog(hWnd, TRUE);
return(TRUE);
}
break;

default :

break;
}
return(FALSE);
}


/*****************************************************************************

FUNCTION: Settings

PURPOSE : Processes messages for Settings window

*****************************************************************************/

BOOL FAR PASCAL Settings(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
{
switch(wMsg)
{
case WM_INITDIALOG :

CheckDlgButton(hWnd, IDC_ACCEPTMESSAGES, AcceptMessages);
CheckDlgButton(hWnd, IDC_ICONIZEMESSAGES, IconizeMessages);

if(AllUsers)
CheckRadioButton(hWnd, IDC_ALLUSERSINBINDERY,
IDC_ALLUSERSINBINDERY, IDC_ALLUSERSINBINDERY);
else
CheckRadioButton(hWnd, IDC_ONLYATTACHEDUSERS,
IDC_ONLYATTACHEDUSERS, IDC_ONLYATTACHEDUSERS);

break;

case WM_COMMAND :

switch(wParam)
{
case IDC_ACCEPTMESSAGES :

if(IsDlgButtonChecked(hWnd, IDC_ACCEPTMESSAGES))
{
EnableBroadcasts();
SetTimer(hDlgMegaphone, IDT_MESSAGETIMER, 5000, NULL);
AcceptMessages = TRUE;
}
else
{
DisableBroadcasts();
KillTimer(hDlgMegaphone, IDT_MESSAGETIMER);
AcceptMessages = FALSE;
}

break;

case IDC_ICONIZEMESSAGES :

if(IsDlgButtonChecked(hWnd, IDC_ICONIZEMESSAGES))
IconizeMessages = TRUE;
else
IconizeMessages = FALSE;

break;

case IDC_ONLYATTACHEDUSERS :

AllUsers = FALSE;
InitNetStuff();
break;

case IDC_ALLUSERSINBINDERY :

AllUsers = TRUE;
InitNetStuff();
break;

case IDOK :
case IDCANCEL :

EndDialog(hWnd, TRUE);
return(TRUE);

default :

break;
}
}
return(FALSE);
}


/*****************************************************************************

FUNCTION: MessageHandler

PURPOSE : Processes messages for user message dialog box/window

*****************************************************************************/

long FAR PASCAL MessageHandler(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
{
BYTE Message[MAX_MESSAGE_LEN];
WORD ConnectionsToSend[1];
BYTE ResultList[1];

switch(wMsg)
{
case WM_COMMAND :

switch(wParam)
{
case 1 :

/* A '1' is generated when ENTER is pressed while in a */
/* control in a dialog box and there's no default button */
/* defined. This is to trap the ENTER key when in the reply */
/* edit box. We'll simulate the pressing of the REPLY button */

if(IsWindowEnabled(GetDlgItem(hWnd, IDC_REPLYBUTTON)))
{
SendMessage(GetDlgItem(hWnd, IDC_REPLYBUTTON),
WM_LBUTTONDOWN, 0, 0L);
SendMessage(GetDlgItem(hWnd, IDC_REPLYBUTTON),
WM_LBUTTONUP, 0, 0L);
}
else
{
MessageBeep(0);
MessageBox(hWnd, "You cannot reply to this message",
"ERROR", MB_ICONEXCLAMATION | MB_OK);
}
break;

case IDC_SAVEBUTTON :

/* "Save" the message by iconizing it */

CloseWindow(hWnd);
break;

case IDC_REPLYBUTTON :

/* Get text from edit box located in message dialog box */

GetDlgItemText(hWnd, IDC_REPLYEDITBOX, (LPSTR) Text,
MAX_MESSAGE_LEN);

/* Set up my connection# to send to, format the message, and */
/* send it to the user. */

ConnectionsToSend[0] = SelUserConnectionNum;
wsprintf(Message, "%s[%d]%s", (LPSTR) SelUserName,
SelUserConnectionNum, (LPSTR) Text);

SendBroadcastMessage(Message, ConnectionsToSend, ResultList, 1);

/* Possible results of sending a message */

switch(ResultList[0])
{
case 0xfc :

wsprintf(Text, "Message to Connection %d",
ConnectionsToSend[0]);
MessageBox(hDlgMegaphone,
"Message not sent - User already has message pending",
Text, MB_OK | MB_ICONEXCLAMATION);
break;

case 0xfd :

wsprintf(Text, "Message to Connection %d",
ConnectionsToSend[0]);
MessageBox(hDlgMegaphone,
"Message not sent - Invalid connection number",
Text, MB_OK | MB_ICONEXCLAMATION);

break;

case 0xff :

wsprintf(Text, "Message to Connection %d",
ConnectionsToSend[0]);
MessageBox(hDlgMegaphone,
"Message not sent - User has blocking turned on",
Text, MB_OK | MB_ICONEXCLAMATION);

break;

default :

break;
}

/* Get rid of the message reply dialog box/window */

DestroyWindow(hWnd);
return(0L);

case IDCANCEL :

DestroyWindow(hWnd);
return(0L);

default :

break;
}
break;

case WM_CLOSE :

DestroyWindow(hWnd);
return(0L);

case WM_SETFOCUS :

/* Set the focus to the edit control. */

SendMessage(hWnd, WM_NEXTDLGCTL, GetDlgItem(hWnd, IDC_REPLYEDITBOX),
TRUE);

return(0L);

case WM_ACTIVATE :

hWndCurrent = (wParam == NULL) ? NULL : hWnd;
break;

default :

break;
}
return(DefDlgProc(hWnd, wMsg, wParam, lParam));
}


/*****************************************************************************

FUNCTION: ShowUserInformation

PURPOSE : Shows user information on current or double-clicked entry

*****************************************************************************/

VOID PASCAL ShowUserInformation(VOID)
{
int Index;
BYTE *ptr;

HWND hDlgUserInfo;

WORD ObjectType;
long ObjectID;
WORD SocketNum;
BYTE PropertyValue[128];
BYTE MoreSegments;
BYTE PropertyFlags;

/* Get an index to the user name underneath the cursor, */
/* and then get the user's connection number so we can */
/* retrieve NetWare information on him/her. */

Index = (int) SendMessage(hWndUserListBox, LB_GETCURSEL, 0, 0L);
SendMessage(hWndUserListBox, LB_GETTEXT, Index, (LONG) (LPSTR) Text);

/* If entry in list box doesn't have a connection #, then we need to get */
/* the login name by parsing the list box string, we can't use a call to */
/* GetConnectionInformation(). */

memset(SelUserLoginTime, '\0', 7);
memset(SelUserNetworkAddr, '\0', 4);
memset(SelUserNodeAddr, '\0', 6);
SelUserFullName[0] = '\0';
SelUserConnectionNum = 0;

if(Text[0] == '[')
{
ptr = strchr(Text, ']');
strncpy(SelUserName, Text + 1, ptr - Text - 1);
SelUserName[ptr - Text - 1] = '\0';
}
else
{
ptr = strchr(Text, ' ');
strncpy(SelUserName, Text, ptr - Text);
SelUserConnectionNum = atoi(&Text[18]);
SelUserName[ptr - Text] = '\0';

/* We can get connection info only for users that are logged in to a */
/* server. So, get the user name and login time, network and node */
/* address, and full name for specified connection#. */

GetConnectionInformation(SelUserConnectionNum, SelUserName, &ObjectType,
&ObjectID, SelUserLoginTime);

GetInternetAddress(SelUserConnectionNum, SelUserNetworkAddr,
SelUserNodeAddr, &SocketNum);
}

if(!ReadPropertyValue(SelUserName, OT_USER, "IDENTIFICATION", 1,
PropertyValue, &MoreSegments, &PropertyFlags))
{
wsprintf(SelUserFullName, "%s", (LPSTR) PropertyValue);
}

/* Create userinfo dialog box and change caption to the */
/* user's login name. */

hDlgUserInfo = CreateDialog(hInstMegaphone, "UserInfo", hDlgMegaphone, 0L);
SetWindowText(hDlgUserInfo, SelUserName);

/* Initialize the user info dialog box by changing */
/* the values of different static text fields to */
/* reflect acquired user information */

wsprintf(Text, ": %s", (LPSTR) SelUserName);
SetDlgItemText(hDlgUserInfo, IDC_USERNAME, (LPSTR) Text);

wsprintf(Text, ": %d", SelUserConnectionNum);
SetDlgItemText(hDlgUserInfo, IDC_STATION, (LPSTR) Text);

wsprintf(Text, ": %s", (LPSTR) SelUserFullName);
SetDlgItemText(hDlgUserInfo, IDC_FULLNAME, (LPSTR) Text);

wsprintf(Text, ": %02d/%02d/%02d %02d:%02d:%02d",
SelUserLoginTime[1], SelUserLoginTime[2], SelUserLoginTime[0],
SelUserLoginTime[3], SelUserLoginTime[4], SelUserLoginTime[5]);
SetDlgItemText(hDlgUserInfo, IDC_LOGINTIME, (LPSTR) Text);

wsprintf(Text, ": %02X%02X%02X%02X",
SelUserNetworkAddr[0], SelUserNetworkAddr[1],
SelUserNetworkAddr[2], SelUserNetworkAddr[3]);
SetDlgItemText(hDlgUserInfo, IDC_NETWORK, (LPSTR) Text);

wsprintf(Text, ": %02X%02X%02X%02X%02X%02X",
SelUserNodeAddr[0], SelUserNodeAddr[1], SelUserNodeAddr[2],
SelUserNodeAddr[3], SelUserNodeAddr[4], SelUserNodeAddr[5]);
SetDlgItemText(hDlgUserInfo, IDC_NODE, (LPSTR) Text);

ShowWindow(hDlgUserInfo, SW_SHOWNORMAL);
}


/*****************************************************************************

FUNCTION: UserInfo

PURPOSE : Processes messages for user info box

*****************************************************************************/

long FAR PASCAL UserInfo(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
{
switch(wMsg)
{
case WM_COMMAND :

if(wParam == IDOK || wParam == IDCANCEL)
{
DestroyWindow(hWnd);
return(0L);
}
break;

case WM_CLOSE :

DestroyWindow(hWnd);
return(0L);

case WM_ACTIVATE :

hWndCurrent = (wParam == NULL) ? NULL : hWnd;
break;

default :

break;
}
return(DefDlgProc(hWnd, wMsg, wParam, lParam));
}


[LISTING THREE]

#include "windows.h"
#include "megaphon.h"


Megaphone ICON MEGAPHON.ICO
Message ICON MESSAGE.ICO
User ICON USER.ICO


Megaphone MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Exit", IDM_EXIT
MENUITEM SEPARATOR
MENUITEM "&About ...", IDM_ABOUT
END
MENUITEM "&Refresh!", IDM_REFRESH
END


Megaphone DIALOG 65, 75, 165, 139
CLASS "Megaphone"
CAPTION "Megaphone - NetWare Intercom"
STYLE WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX
BEGIN
CONTROL "Megaphone", -1, "static",
SS_ICON | WS_CHILD, 24, 6, 0, 0
CONTROL "&Users", IDC_USERLISTBOXTITLE, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 65, 2, 95, 9
CONTROL "", IDC_USERLISTBOX, "listbox",
LBS_STANDARD | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP |
WS_CHILD,
65, 12, 95, 71
CONTROL "Ser&vers", IDC_SERVERCOMBOBOXTITLE, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 5, 23, 55, 9
CONTROL "", IDC_SERVERCOMBOBOX, "combobox",
CBS_HASSTRINGS | CBS_SORT | CBS_DROPDOWNLIST |
WS_VSCROLL | WS_TABSTOP | WS_CHILD,
5, 33, 55, 63
CONTROL "&Message", -1, "static", SS_LEFT | WS_CHILD,
5, 80, 55, 9
CONTROL "", IDC_MESSAGEEDITBOX, "edit",
ES_LEFT | ES_AUTOHSCROLL | ES_UPPERCASE | WS_BORDER | WS_TABSTOP | WS_CHILD,
5, 90, 155, 12
CONTROL "&Send", IDC_SENDBUTTON, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD,
5, 110, 45, 15
CONTROL "Se&ttings", IDC_SETTINGSBUTTON, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD,
60, 110, 45, 15
CONTROL "&Exit", IDC_EXITBUTTON, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD,
115, 110, 45, 15
END


Message DIALOG 100, 100, 170, 55
CAPTION "Message"
CLASS "Message"
STYLE WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX
BEGIN
CONTROL "", IDC_REPLYEDITBOX, "edit",
ES_LEFT | ES_AUTOHSCROLL | ES_UPPERCASE | WS_BORDER |
WS_TABSTOP | WS_CHILD, 5, 10, 155, 12
CONTROL "&Reply", IDC_REPLYBUTTON, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 5, 30, 45, 15
CONTROL "&Cancel", IDCANCEL, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 60, 30, 45, 15
CONTROL "&Save", IDC_SAVEBUTTON, "button",
BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 115, 30, 45, 15
END


UserInfo DIALOG 68, 54, 145, 79
CAPTION "User Information"
CLASS "User"
STYLE WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX
BEGIN
CONTROL "User", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 3, 18, 9
CONTROL ":", IDC_USERNAME, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 60, 3, 82, 9
CONTROL "Stn", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 12, 18, 9
CONTROL ":", IDC_STATION, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 60, 12, 77, 9
CONTROL "Node", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 21, 18, 9
CONTROL ":", IDC_NODE, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 60, 21, 77, 9
CONTROL "Full Name", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 3, 30, 39, 9
CONTROL ":", IDC_FULLNAME, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 30, 95, 9
CONTROL "Login Time", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 3, 39, 39, 9
CONTROL ":", IDC_LOGINTIME, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 39, 95, 9
CONTROL "Network", -1, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 3, 48, 39, 9
CONTROL ":", IDC_NETWORK, "static",
SS_LEFTNOWORDWRAP | WS_CHILD, 42, 48, 95, 9
CONTROL "User", -1, "static",
SS_ICON | WS_CHILD, 11, 6, 15, 15
DEFPUSHBUTTON "OK", IDOK, 39, 60, 45, 15
END


Settings DIALOG 11, 21, 175, 65
CAPTION "Settings"
STYLE WS_POPUPWINDOW | WS_CAPTION
BEGIN
CONTROL "&Incoming Messages", -1, "button",
BS_GROUPBOX | WS_CHILD, 5, 5, 75, 35
CONTROL "Accept", IDC_ACCEPTMESSAGES, "button",
BS_AUTOCHECKBOX | WS_TABSTOP | WS_CHILD, 10, 15, 60, 10
CONTROL "Iconize", IDC_ICONIZEMESSAGES, "button",
BS_AUTOCHECKBOX | WS_TABSTOP | WS_CHILD, 10, 25, 60, 10
CONTROL "&Users to scan", -1, "button",
BS_GROUPBOX | WS_CHILD, 85, 5, 85, 35
CONTROL "Attached users only", IDC_ONLYATTACHEDUSERS, "button",
BS_AUTORADIOBUTTON | WS_CHILD, 90, 15, 75, 10
CONTROL "Include unattached", IDC_ALLUSERSINBINDERY, "button",
BS_AUTORADIOBUTTON | WS_CHILD, 90, 25, 75, 10
CONTROL "&OK", IDOK, "button",
BS_DEFPUSHBUTTON | WS_TABSTOP | WS_CHILD, 55, 45, 55, 15
END


About DIALOG 22, 17, 110, 80
CAPTION "About"
STYLE WS_POPUPWINDOW | WS_CAPTION
BEGIN
CTEXT "Megaphone - NetWare Intercom" -1, 0, 5, 110, 8
CTEXT "Version 1.0" -1, 0, 14, 110, 8
CTEXT "by Mike Klein" -1, 0, 22, 110, 8
ICON "User" -1, 13, 35, 0, 0
ICON "Megaphone" -1, 48, 35, 0, 0
ICON "Message" -1, 82, 35, 0, 0
DEFPUSHBUTTON "OK" IDOK, 33, 60, 45, 15
END



[LISTING FOUR]

; module-definition file for Megaphone -- used by LINK.EXE

NAME Megaphone ; application's module name
DESCRIPTION 'Megaphone - NetWare Intercom'
EXETYPE WINDOWS ; required for all Windows applications
STUB 'WINSTUB.EXE' ; Generates error message if application
; is run without Windows

;CODE can be moved in memory and discarded/reloaded
CODE PRELOAD MOVEABLE DISCARDABLE

;DATA must be MULTIPLE if program can be invoked more than once
DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024
STACKSIZE 5120 ; recommended minimum for Windows applications

; All functions that will be called by any Windows routine
; MUST be exported.

EXPORTS
MainWndProc @1
About @2
UserInfo @3
MessageHandler @4
Settings @5



[LISTING FIVE]

# Standard Windows make file. The utility MAKE.EXE compares the
# creation date of the file to the left of the colon with the file(s)
# to the right of the colon. If the file(s) on the right are newer
# then the file on the left, Make will execute all of the command lines
# following this line that are indented by at least one tab or space.
# Any valid MS-DOS command line may be used.

# This line allows NMAKE to work as well

all: megaphon.exe

# Update the resource if necessary

megaphon.res: megaphon.rc megaphon.h megaphon.ico
rc -r megaphon.rc

# Update the object file if necessary

megaphon.obj: megaphon.c megaphon.h
cl -W4 -c -AS -Gsw -Oad -Zp megaphon.c

# Update the executable file if necessary, and if so, add the resource back in.

megaphon.exe: megaphon.obj megaphon.def
link /NOD megaphon,,, libw slibcew snit, megaphon.def
rc megaphon.res

# If the .res file is new and the .exe file is not, update the resource.
# Note that the .rc file can be updated without having to either
# compile or link the file.

megaphon.exe: megaphon.res
rc megaphon.res