windows-nt/Source/XPSP1/NT/ds/nw/svcdlls/nwwks/client/nwshui.cxx
2020-09-26 16:20:57 +08:00

1643 lines
49 KiB
C++

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
nwshui.cxx
Abstract:
This module implements the context menu actions of shell extension classes.
Author:
Yi-Hsin Sung (yihsins) 25-Oct-1995
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>
#define DONT_WANT_SHELLDEBUG
#include <shlobjp.h>
#include <winnetwk.h>
#include <npapi.h>
#include <ntddnwfs.h>
#include <ndsapi32.h>
#include <nwapi.h>
#include <nwwks.h>
#include <nwmisc.h>
//extern "C"
//{
#include "nwutil.h"
//}
#include "nwshcmn.h"
#include "nwshrc.h"
extern "C"
{
NTSTATUS
NwNdsOpenRdrHandle(
OUT PHANDLE phandleRdr );
}
#define MAX_ONE_CONN_INFORMATION_SIZE 512
#define NW_ENUM_EXTRA_BYTES 256
#define GLOBAL_WHOAMI_REFRESH_INTERVAL 30000 // in milliseconds, Win95 uses 10000
DWORD
LogoutFromServer(
LPWSTR pszServer,
PBOOL pfDisconnected
);
BOOL
CALLBACK
GlobalWhoAmIDlgProc(
HWND hwndDlg,
UINT msg,
WPARAM wParam,
LPARAM lParam );
VOID
GetConnectionStatusString(
PCONN_STATUS pConnStatus,
LPBYTE Buffer,
DWORD nSize
);
HRESULT
NWUIWhoAmI(
HWND hParent,
LPNETRESOURCE pNetRes
)
{
DWORD err = NO_ERROR;
DWORD_PTR ResumeKey = 0;
LPBYTE pBuffer = NULL;
DWORD EntriesRead = 0;
DWORD dwMessageId;
WCHAR szUserName[MAX_PATH+1] = L"";
WCHAR szConnType[128];
WCHAR szRemoteName[MAX_PATH + 1];
szConnType[0] = 0;
if ( pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
{
// Need to extract the server name from full UNC path
NwExtractServerName( pNetRes->lpRemoteName, szRemoteName );
dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
}
else // NDS container name
{
// Need to extract the tree name from the full UNC path
szRemoteName[0] = TREECHAR;
NwExtractTreeName( pNetRes->lpRemoteName, szRemoteName+1);
dwMessageId = IDS_MESSAGE_NOT_ATTACHED_TO_TREE;
}
err = NwGetConnectionStatus( szRemoteName,
&ResumeKey,
&pBuffer,
&EntriesRead );
if ( err == NO_ERROR && EntriesRead > 0 )
// For trees, we'll get more than one entry
{
PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
LPWSTR pszStart = szConnType;
DWORD nSize = sizeof(szConnType)/sizeof(WCHAR);
if ( EntriesRead > 1 && szRemoteName[0] == TREECHAR )
{
// If there is more than one entry for trees,
// then we need to find one entry where username is not null
// and the login type is NDS.
// If we cannot find one, then just use the first one.
DWORD i;
PCONN_STATUS pConnStatusTmp = pConnStatus;
PCONN_STATUS pConnStatusUser = NULL;
PCONN_STATUS pConnStatusNoUser = NULL;
for ( i = 0; i < EntriesRead ; i++ )
{
if ( pConnStatusTmp->fNds )
{
pConnStatusNoUser = pConnStatusTmp;
if ( ( pConnStatusTmp->pszUserName != NULL )
&& ( ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
|| ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
)
)
{
// Found it
pConnStatusUser = pConnStatusTmp;
break;
}
}
// Continue with the next item
pConnStatusTmp = (PCONN_STATUS) ( (DWORD_PTR) pConnStatusTmp
+ pConnStatusTmp->dwTotalLength);
}
if ( pConnStatusUser ) // found one nds entry with a user name
pConnStatus = pConnStatusUser;
else if ( pConnStatusNoUser ) // use an nds entry with no user name
pConnStatus = pConnStatusNoUser;
// else use the first entry
}
if ( szRemoteName[0] == TREECHAR // A tree
|| !pConnStatus->fPreferred // A server but not preferred
)
{
// Show this conneciton only if this is a tree or if this is
// not a implicit connection to the preferred server.
dwMessageId = pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER ?
IDS_MESSAGE_ATTACHED : IDS_MESSAGE_ATTACHED_TO_TREE;
if ( pConnStatus->pszUserName )
{
wcscpy( szUserName, pConnStatus->pszUserName );
}
if ( pConnStatus->fNds ) // NDS
{
LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, pszStart, nSize );
nSize -= wcslen( pszStart );
pszStart += wcslen( pszStart);
}
else // Bindery
{
LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, pszStart, nSize );
nSize -= wcslen( pszStart );
pszStart += wcslen( pszStart);
}
LoadString( ::hmodNW, IDS_LOGIN_STATUS_SEPARATOR, pszStart, nSize );
nSize -= wcslen( pszStart );
pszStart += wcslen( pszStart);
GetConnectionStatusString( pConnStatus, (LPBYTE) pszStart, nSize );
}
}
if ( err == NO_ERROR )
{
// Popup the message now.
::MsgBoxPrintf( hParent,
dwMessageId,
IDS_TITLE_WHOAMI,
MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
szRemoteName[0] == TREECHAR? szRemoteName + 1 : szRemoteName,
szUserName,
szConnType );
}
else // error occurred
{
::MsgBoxErrorPrintf( hParent,
IDS_MESSAGE_CONNINFO_ERROR,
IDS_TITLE_WHOAMI,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
NULL );
}
if ( pBuffer != NULL )
{
LocalFree( pBuffer );
pBuffer = NULL;
}
return NOERROR;
}
VOID
GetConnectionStatusString(
PCONN_STATUS pConnStatus,
LPBYTE Buffer,
DWORD nSize
)
{
LPWSTR pszStart = (LPWSTR) Buffer;
DWORD dwMessageId = 0;
if ( pConnStatus->fNds ) // NDS
{
if ( pConnStatus->dwConnType == NW_CONN_NOT_AUTHENTICATED )
{
dwMessageId = IDS_LOGIN_STATUS_NOT_AUTHENTICATED;
}
else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
{
dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
}
else // authenticated, licensed or unlicensed
{
LoadString( ::hmodNW, IDS_LOGIN_STATUS_AUTHENTICATED,
pszStart, nSize );
nSize -= wcslen( pszStart );
pszStart += wcslen( pszStart);
if ( pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
dwMessageId = IDS_LOGIN_STATUS_LICENSED;
else // NW_CONN_NDS_AUTHENTICATED_NO_LICENSE
dwMessageId = IDS_LOGIN_STATUS_NOT_LICENSED;
}
}
else // Bindery
{
if ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN )
dwMessageId = IDS_LOGIN_STATUS_LOGGED_IN;
else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
else
dwMessageId = IDS_LOGIN_STATUS_ATTACHED_ONLY;
}
LoadString( ::hmodNW, dwMessageId, pszStart, nSize );
}
HRESULT
NWUIGlobalWhoAmI(
HWND hParent
)
{
::DialogBoxParam( ::hmodNW,
MAKEINTRESOURCE(DLG_GLOBAL_WHOAMI),
hParent,
(DLGPROC) ::GlobalWhoAmIDlgProc,
NULL );
return NOERROR;
}
HRESULT
NWUILogOut(
HWND hParent,
LPNETRESOURCE pNetRes,
PBOOL pfDisconnected
)
{
DWORD err = NO_ERROR;
WCHAR szServer[MAX_PATH+1];
BOOL fAttached;
BOOL fAuthenticated;
DWORD dwMessageId;
*pfDisconnected = FALSE;
// Need to extract the server name from full UNC path
NwExtractServerName( pNetRes->lpRemoteName, szServer );
err = NwIsServerOrTreeAttached( szServer, &fAttached, &fAuthenticated );
if ( err == NO_ERROR && !fAttached )
{
dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
}
else if ( err == NO_ERROR ) // attached
{
int nRet = ::MsgBoxPrintf( hParent,
IDS_MESSAGE_LOGOUT_CONFIRM,
IDS_TITLE_LOGOUT,
MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION);
if ( nRet != IDYES )
return NOERROR;
err = LogoutFromServer( szServer, pfDisconnected );
if ( err == NO_ERROR )
dwMessageId = IDS_MESSAGE_DETACHED;
else
dwMessageId = IDS_MESSAGE_LOGOUT_FAILED;
}
else // error occurred
{
::MsgBoxErrorPrintf( hParent,
IDS_MESSAGE_CONNINFO_ERROR,
IDS_TITLE_LOGOUT,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
NULL );
return NOERROR;
}
::MsgBoxPrintf( hParent,
dwMessageId,
IDS_TITLE_LOGOUT,
MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION );
return NOERROR;
}
DWORD
LogoutFromServer(
LPWSTR pszServer,
PBOOL pfDisconnected
)
{
DWORD err = NO_ERROR;
HANDLE EnumHandle = (HANDLE) NULL;
LPNETRESOURCE NetR = NULL;
LPNETRESOURCEW SavePtr;
DWORD BytesNeeded = MAX_ONE_NETRES_SIZE;
DWORD EntriesRead = 0;
DWORD i;
*pfDisconnected = FALSE;
err = NPOpenEnum( RESOURCE_CONNECTED,
0,
0,
NULL,
&EnumHandle );
if ( err != NO_ERROR)
{
EnumHandle = (HANDLE) NULL;
goto CleanExit;
}
//
// Allocate buffer to get server list.
//
if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
do {
EntriesRead = 0xFFFFFFFF; // Read as many as possible
err = NwEnumConnections( EnumHandle,
&EntriesRead,
(LPVOID) NetR,
&BytesNeeded,
TRUE );
if ( err == WN_SUCCESS)
{
SavePtr = NetR;
for (i = 0; i < EntriesRead; i++, NetR++)
{
BYTE Buffer[MAX_ONE_CONN_INFORMATION_SIZE];
BOOL fImplicit;
LPWSTR pszCurrentServer;
fImplicit = FALSE;
if ( NwIsNdsSyntax( NetR->lpRemoteName))
{
// For Nds name, the server name might not be in the full UNC name.
// Hence we need to get the server name from the Rdr.
DWORD err1 = NwGetConnectionInformation(
NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
Buffer,
sizeof(Buffer));
if ( err1 != NO_ERROR )
continue; // continue with the next entry if error occurred
pszCurrentServer = ((PCONN_INFORMATION) Buffer)->HostServer;
// Need to NULL terminate the server name, this will probably used the space
// occupied by UserName but since we are not using it, it is probably ok.
LPWSTR pszTemp = (LPWSTR) ((DWORD_PTR) pszCurrentServer
+ ((PCONN_INFORMATION) Buffer)->HostServerLength );
*pszTemp = 0;
}
else // in the form \\server\sys
{
LPWSTR pszTemp;
wcscpy( (LPWSTR) Buffer, NetR->lpRemoteName + 2 ); // go past two backslashes
if ( pszTemp = wcschr( (LPWSTR) Buffer, L'\\' ))
*pszTemp = 0;
else
{
// The remote name contains only \\server, hence if the local name
// is null, this is a implicit connection
if ( NetR->lpLocalName == NULL )
fImplicit = TRUE;
}
pszCurrentServer = (LPWSTR) Buffer;
}
if ( _wcsicmp( pszCurrentServer, pszServer ) == 0 )
{
do {
// for implicit connections, we need to try and disconnect until
// we deleted all the implicit connections, i.e. until we
// get the invalid handle error
// NOTE: If we don't pass in CONNECT_UPDATE_PROFILE, shell won't update
// the windows that got disconnected. What do we want to do here?
err = WNetCancelConnection2(
NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
0, // CONNECT_UPDATE_PROFILE,
TRUE );
if ( err == NO_ERROR )
*pfDisconnected = TRUE;
} while ( fImplicit && ( err == NO_ERROR));
if ( err == ERROR_INVALID_HANDLE )
{
// implicit connection will sometimes return this if the explicit connection
// is already disconnected
err = NO_ERROR;
}
if ( err != NO_ERROR )
{
NetR = SavePtr;
goto CleanExit;
}
}
}
NetR = SavePtr;
}
else if ( err != WN_NO_MORE_ENTRIES)
{
if ( err == WN_MORE_DATA)
{
//
// Original buffer was too small. Free it and allocate
// the recommended size and then some to get as many
// entries as possible.
//
(void) LocalFree((HLOCAL) NetR);
BytesNeeded += NW_ENUM_EXTRA_BYTES;
if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
}
else
{
goto CleanExit;
}
}
} while (err != WN_NO_MORE_ENTRIES);
if ( err == WN_NO_MORE_ENTRIES)
err = NO_ERROR;
CleanExit:
if (EnumHandle != (HANDLE) NULL)
(void) NPCloseEnum( EnumHandle);
if (NetR != NULL)
{
(void) LocalFree( (HLOCAL) NetR);
NetR = NULL;
}
return err;
}
HRESULT
NWUIAttachAs(
HWND hParent,
LPNETRESOURCE pNetRes
)
{
DWORD err = NO_ERROR;
WCHAR szServerName[MAX_PATH+1];
BOOL fAttached;
BOOL fAuthenticated;
// First, see if we are attached to the server.
// Note, Attach as menu will be disabled on the NDS servers that we are already logged into.
// Need to extract the server name from full UNC path
szServerName[0] = szServerName[1] = L'\\';
NwExtractServerName( pNetRes->lpRemoteName, szServerName + 2 );
err = NwIsServerOrTreeAttached( szServerName + 2, &fAttached, &fAuthenticated );
if ( err == NO_ERROR && fAttached && fAuthenticated )
{
// Already attached and authenticated to the server.
// So, ask the user if he wants to log out first.
int nRet = ::MsgBoxPrintf( hParent,
IDS_MESSAGE_LOGOUT_QUESTION,
IDS_TITLE_LOGOUT,
MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION );
if ( nRet != IDYES )
return NOERROR;
BOOL fDisconnected = FALSE; // can be used to refresh the shell if needed
err = LogoutFromServer( szServerName + 2, &fDisconnected );
if ( err != NO_ERROR )
{
::MsgBoxPrintf( hParent,
IDS_MESSAGE_LOGOUT_FAILED,
IDS_TITLE_LOGOUT,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP );
return NOERROR;
}
}
// If error occurred, just assume we are not attached to the server and continue on
// to login to the server.
DWORD dwConnFlags = CONNECT_INTERACTIVE | CONNECT_PROMPT;
// See if the server is in the default context tree.
// If yes, then we don't need to prompt the password first before connecting
// in WNetAddConnection3.
BOOL fInDefaultTree = FALSE;
err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
if ( (err == NO_ERROR ) && fInDefaultTree )
dwConnFlags = CONNECT_INTERACTIVE;
//
// Now call WNetAddConnection3
//
// NOTE: net use \\mars_srv0 will succeed
// but net use \\marsdev\cn=mars_srv0... will failed
// Hence, just use the server name to connect.
LPWSTR pszSave = pNetRes->lpRemoteName;
pNetRes->lpRemoteName = szServerName;
err = WNetAddConnection3( hParent,
pNetRes,
NULL,
NULL,
dwConnFlags );
if ( err != WN_SUCCESS && err != WN_CANCEL )
{
::MsgBoxErrorPrintf( hParent,
IDS_MESSAGE_ADDCONN_ERROR,
IDS_NETWARE_TITLE,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
pNetRes->lpRemoteName );
}
pNetRes->lpRemoteName = pszSave; // restore the original remote name just in case
return NOERROR;
}
#if 0
HRESULT
NWUISetDefaultContext(
HWND hParent,
LPNETRESOURCE pNetRes
)
{
DWORD dwPrintOptions;
LPWSTR pszCurrentContext = NULL;
WCHAR szNewContext[MAX_PATH+1];
DWORD err = NO_ERROR;
HANDLE handleRdr = NULL;
UNICODE_STRING uTree;
UNICODE_STRING uContext;
LPWSTR pszContext = NULL;
// Open a handle to the redirector
err = RtlNtStatusToDosError( ::NwNdsOpenRdrHandle( &handleRdr ));
if ( err != NO_ERROR )
goto CleanExit;
// Get the print option so that we can use it later
err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
if ( err != NO_ERROR )
goto CleanExit;
wcscpy( szNewContext, pNetRes->lpRemoteName + 1 ); // get past 1st '\\'
szNewContext[0] = TREECHAR; // in the format "*TREE\CONTEXT"
if ( (pszContext = wcschr( szNewContext, L'\\' )) != NULL )
{
*pszContext = 0;
RtlInitUnicodeString( &uContext, pszContext + 1 );
}
else
RtlInitUnicodeString( &uContext, L"");
RtlInitUnicodeString( &uTree, szNewContext+1 );
if ( (err = RtlNtStatusToDosError( ::NwNdsSetTreeContext( handleRdr, &uTree, &uContext)))
== NO_ERROR )
{
*pszContext = L'\\';
if ((err = ::NwSetInfoInRegistry( dwPrintOptions, szNewContext )) == NO_ERROR )
{
::MsgBoxPrintf( hParent,
IDS_MESSAGE_CONTEXT_CHANGED,
IDS_NETWARE_TITLE,
MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
pszCurrentContext
? ( *pszCurrentContext == TREECHAR
? pszCurrentContext + 1
: pszCurrentContext )
: L"",
szNewContext+1 );
}
}
CleanExit:
if ( err != NO_ERROR )
{
::MsgBoxErrorPrintf( hParent,
IDS_MESSAGE_CONTEXT_ERROR,
IDS_NETWARE_TITLE,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
szNewContext+1);
}
if ( pszCurrentContext != NULL )
{
LocalFree( pszCurrentContext );
pszCurrentContext = NULL;
}
if ( handleRdr != NULL )
{
::NtClose( handleRdr );
handleRdr = NULL;
}
return NOERROR;
}
#endif
HRESULT
NWUIMapNetworkDrive(
HWND hParent,
LPNETRESOURCE pNetRes
)
{
HRESULT hres;
CONNECTDLGSTRUCT cds;
cds.cbStructure = sizeof(cds);
cds.hwndOwner = hParent;
cds.lpConnRes = pNetRes;
cds.dwFlags = CONNDLG_RO_PATH;
if ( (( hres = WNetConnectionDialog1( &cds )) == WN_SUCCESS )
&& ( g_pFuncSHChangeNotify )
)
{
WCHAR szPath[4];
szPath[0] = ((USHORT) cds.dwDevNum) - 1 + L'A';
szPath[1] = L':';
szPath[2] = L'\\';
szPath[3] = 0;
// Notify shell about added redirection
(*g_pFuncSHChangeNotify)( SHCNE_DRIVEADD,
SHCNF_FLUSH | SHCNF_PATH | SHCNF_FLUSHNOWAIT,
szPath,
NULL );
Sleep(1000); // short delay to make sure Shell has updated drive table
// And we need to open shell window on this path
if (g_pFuncSHExecuteEx)
{
SHELLEXECUTEINFO ExecInfo;
::memset(&ExecInfo,0,sizeof(ExecInfo));
ExecInfo.hwnd = hParent;
ExecInfo.lpVerb = 0;
ExecInfo.lpFile = szPath;
ExecInfo.lpParameters = NULL;
ExecInfo.lpDirectory = NULL;
ExecInfo.nShow = SW_NORMAL | SW_SHOW;
ExecInfo.fMask = SEE_MASK_CLASSNAME;
ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ExecInfo.lpClass = (LPWSTR) L"Folder";
ExecInfo.hkeyClass = NULL;
(*g_pFuncSHExecuteEx)(&ExecInfo);
}
}
return hres;
}
#define LB_PCT_NAME 25
#define LB_PCT_TYPE 11
#define LB_PCT_CONN 11
#define LB_PCT_USER 25
#define LB_PCT_STATUS 27
#define BITMAP_WIDTH 16
#define BITMAP_HEIGHT 16
#define MAPCOLOR RGB(0, 255, 0)
static UINT uiNDSIconIndex = 0;
static UINT uiServerIconIndex = 0;
/*
* FillConnectionsListView
* -----------------------
*
* Fill list box with information on active connections
*/
VOID
FillConnectionsListView(
HWND hwndDlg
)
{
HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
LV_ITEM lvI;
WCHAR szSubItemText[MAX_PATH+1];
UINT uiInsertedIndex;
DWORD_PTR ResumeKey = 0;
LPBYTE pConnBuffer = NULL;
DWORD EntriesRead = 0;
DWORD err = NO_ERROR;
// Prepare ListView structure
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvI.state = 0;
lvI.stateMask = 0;
do {
if ( pConnBuffer != NULL )
{
LocalFree( pConnBuffer );
pConnBuffer = NULL;
}
err = NwGetConnectionStatus( NULL,
&ResumeKey,
&pConnBuffer,
&EntriesRead );
if ( ( err != NO_ERROR )
|| ( EntriesRead == 0 )
)
{
goto CleanExit;
}
PCONN_STATUS pConnStatus = (PCONN_STATUS) pConnBuffer;
for ( DWORD i = 0; i < EntriesRead; i++)
{
// Allocate and initialize new item structure for use in the listbox
DWORD dwSize;
//
// Don't need to show preferred server with only implicit
// connections since we can't disconnect from it.
//
if ( pConnStatus->fPreferred )
{
// Continue with the next item
pConnStatus = (PCONN_STATUS) ( (DWORD_PTR) pConnStatus
+ pConnStatus->dwTotalLength);
continue;
}
//
// Allocate and copy the connection information to be store with
// the listbox
//
PCONN_STATUS pConnStatusKeep =
(PCONN_STATUS) LocalAlloc( LMEM_ZEROINIT, pConnStatus->dwTotalLength );
if ( pConnStatusKeep == NULL )
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
memcpy( pConnStatusKeep, pConnStatus, pConnStatus->dwTotalLength );
dwSize = sizeof(CONN_STATUS);
if ( pConnStatus->pszServerName )
{
pConnStatusKeep->pszServerName =
(LPWSTR) ((DWORD_PTR)pConnStatusKeep + dwSize );
NwMakePrettyDisplayName( pConnStatusKeep->pszServerName );
dwSize += (wcslen(pConnStatus->pszServerName)+1)*sizeof(WCHAR);
}
if ( pConnStatus->pszUserName )
{
pConnStatusKeep->pszUserName =
(LPWSTR) ((DWORD_PTR)pConnStatusKeep + dwSize );
dwSize += (wcslen(pConnStatus->pszUserName)+1) * sizeof(WCHAR);
NwAbbreviateUserName( pConnStatus->pszUserName,
pConnStatusKeep->pszUserName );
NwMakePrettyDisplayName( pConnStatusKeep->pszUserName );
}
if ( pConnStatus->pszTreeName )
{
pConnStatusKeep->pszTreeName =
(LPWSTR) ((DWORD_PTR)pConnStatusKeep + dwSize );
}
//
// Construct the item to add to the listbox
//
lvI.iItem = i;
lvI.iSubItem = 0;
lvI.pszText = szSubItemText;
lvI.cchTextMax = sizeof(szSubItemText);
// Select proper icon
lvI.iImage = pConnStatusKeep->fNds? uiNDSIconIndex
: uiServerIconIndex;
lvI.lParam = (LPARAM) pConnStatusKeep;
wcscpy( szSubItemText, pConnStatusKeep->pszServerName );
// Insert the item
uiInsertedIndex = ListView_InsertItem( hwndLV, &lvI);
if ( uiInsertedIndex != -1 )
{
// Added item itself - now for specific columns
// First, add the column indicating bindery or nds connection
if ( pConnStatusKeep->fNds )
{
LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, szSubItemText,
sizeof(szSubItemText)/sizeof(szSubItemText[0]));
}
else
{
LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, szSubItemText,
sizeof(szSubItemText)/sizeof(szSubItemText[0]));
}
ListView_SetItemText( hwndLV,
uiInsertedIndex,
1, // SubItem id
szSubItemText );
// Next, Add the column indicating the connection number
if ( ( pConnStatusKeep->pszServerName[0] != TREECHAR )
&& ( pConnStatusKeep->dwConnType != NW_CONN_DISCONNECTED )
)
{
// Add connection number only if it is a connection
// to a server and not a tree.
// A connection number only makes sense when not disconnected
::wsprintf(szSubItemText,L"%4d",pConnStatusKeep->nConnNum );
ListView_SetItemText( hwndLV,
uiInsertedIndex,
2, // SubItem id
szSubItemText );
}
// Then, add the column indicating the user name
*szSubItemText = L'\0';
if ( pConnStatusKeep->pszUserName )
{
wcscpy( szSubItemText, pConnStatusKeep->pszUserName );
ListView_SetItemText( hwndLV,
uiInsertedIndex,
3, // SubItem id
szSubItemText );
}
// Last of all, add the column indicating the connection status
*szSubItemText = L'\0';
GetConnectionStatusString( pConnStatusKeep,
(LPBYTE) szSubItemText,
sizeof(szSubItemText)/sizeof(szSubItemText[0]));
ListView_SetItemText( hwndLV,
uiInsertedIndex,
4, // SubItem id
szSubItemText );
}
else
{
// Failed inserting item in the list,
// need to delete the allocated CONN_STATUS
ASSERT( FALSE );
LocalFree( pConnStatusKeep );
pConnStatusKeep = NULL;
}
// Continue with the next item
pConnStatus = (PCONN_STATUS) ( (DWORD_PTR) pConnStatus
+ pConnStatus->dwTotalLength);
}
} while ( ResumeKey != 0 );
CleanExit:
if ( pConnBuffer != NULL )
{
LocalFree( pConnBuffer );
pConnBuffer = NULL;
}
if ( err != NO_ERROR )
{
// If error occurred, we will end the dialog
::MsgBoxErrorPrintf( hwndDlg,
IDS_MESSAGE_CONNINFO_ERROR,
IDS_NETWARE_TITLE,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
NULL );
::EndDialog( hwndDlg, FALSE);
}
}
/*
* CreateConnectionsListView
* -------------------------
*
* Initialize the column headers of the list box
*/
VOID
CreateConnectionsListView(
HWND hwndDlg
)
{
HWND hwndLV;
HIMAGELIST hSmall;
UINT uiLBoxWidth;
InitCommonControls();
// Get the handle of the listbox
hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
RECT rc;
::GetWindowRect( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST ), &rc );
uiLBoxWidth = rc.right - rc.left;
// Load image list
hSmall = ::ImageList_Create(BITMAP_WIDTH,BITMAP_HEIGHT,ILC_MASK,4,0);
// Load bitmap of the tree/server icon
HBITMAP hbm;
hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_TREE_ICON));
if ((uiNDSIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1) {
ASSERT(FALSE);
}
hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_SERVER_ICON));
if ((uiServerIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1) {
ASSERT(FALSE);
}
ImageList_SetBkColor(hSmall, CLR_NONE);
// Associate image list with list view control
ListView_SetImageList(hwndLV,hSmall,LVSIL_SMALL);
// Initialize columns in header
LV_COLUMN lvC;
UINT uiColumnIndex = 0;
WCHAR szColumnName[128];
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvC.fmt = LVCFMT_LEFT;
lvC.cx = (uiLBoxWidth*LB_PCT_NAME)/100;
lvC.pszText = szColumnName;
// Add the column header representing the server name
*szColumnName = L'\0';
::LoadString( ::hmodNW,
IDS_COLUMN_NAME,
szColumnName,
sizeof(szColumnName)/sizeof(szColumnName[0]));
if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1) {
ASSERT(FALSE);
}
// Add the column header representing the conneciton type
*szColumnName = L'\0';
lvC.cx = (uiLBoxWidth*LB_PCT_TYPE)/100;
::LoadString( ::hmodNW,
IDS_COLUMN_CONN_TYPE,
szColumnName,
sizeof(szColumnName)/sizeof(szColumnName[0]));
if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1) {
ASSERT(FALSE);
}
// Add the column header representing the connection number
*szColumnName = L'\0';
lvC.cx = (uiLBoxWidth*LB_PCT_CONN)/100;
lvC.fmt = LVCFMT_RIGHT;
::LoadString( ::hmodNW,
IDS_COLUMN_CONN_NUMBER,
szColumnName,
sizeof(szColumnName)/sizeof(szColumnName[0]));
if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1) {
ASSERT(FALSE);
}
// Add the column header representing the name of the user
*szColumnName = L'\0';
lvC.cx = (uiLBoxWidth*LB_PCT_USER)/100;
lvC.fmt = LVCFMT_LEFT;
::LoadString( ::hmodNW,
IDS_COLUMN_USER,
szColumnName,
sizeof(szColumnName)/sizeof(szColumnName[0]));
if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1) {
ASSERT(FALSE);
}
// Add the column header representing the status of the connection
*szColumnName = L'\0';
lvC.cx = (uiLBoxWidth*LB_PCT_STATUS)/100;
lvC.fmt = LVCFMT_LEFT;
::LoadString( ::hmodNW,
IDS_COLUMN_STATUS,
szColumnName,
sizeof(szColumnName)/sizeof(szColumnName[0]));
if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1) {
ASSERT(FALSE);
}
// Now fill list view window with connection information
FillConnectionsListView( hwndDlg );
} /* endproc CreateConnectionsListView */
/*
* GlobalWhoAmI_ListViewCompareProc
* --------------------------------
*
*/
LRESULT CALLBACK
GlobalWhoAmI_ListViewCompareProc(
LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort
)
{
PCONN_STATUS pConnItem1 = (PCONN_STATUS)lParam1;
PCONN_STATUS pConnItem2 = (PCONN_STATUS)lParam2;
int iResult = 0;
if ( pConnItem1 && pConnItem2 )
{
switch(lParamSort)
{
case 0:
iResult = ::_wcsicmp( pConnItem1->pszServerName,
pConnItem2->pszServerName);
break;
case 1:
iResult = pConnItem1->fNds - pConnItem2->fNds;
break;
case 2:
iResult = pConnItem1->nConnNum - pConnItem2->nConnNum;
break;
case 3:
{
// pszUserName might be NULL, so we need to be careful when
// comparing them.
if ( pConnItem1->pszUserName )
{
if ( pConnItem2->pszUserName )
{
iResult = ::_wcsicmp( pConnItem1->pszUserName,
pConnItem2->pszUserName);
}
else
{
iResult = 1;
}
}
else
{
iResult = -1;
}
break;
}
case 4:
iResult = pConnItem1->dwConnType - pConnItem2->dwConnType;
break;
default:
iResult = 0;
break;
}
}
return (iResult);
}
LRESULT
NotifyHandler(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
NM_LISTVIEW *lpNm = (NM_LISTVIEW *)lParam;
NMHDR *lphdr = &lpNm->hdr;
HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
switch(lphdr->code)
{
case LVN_ITEMCHANGED:
// Check for change in item state, make sure item has received focus
if (lpNm->uChanged & LVIF_STATE)
{
UINT uiSelectedItems = 0;
uiSelectedItems = ListView_GetSelectedCount(hwndLV);
EnableWindow( GetDlgItem(hwndDlg, IDD_DETACH),
uiSelectedItems? TRUE : FALSE);
return TRUE;
}
break;
case LVN_COLUMNCLICK:
ListView_SortItems( hwndLV,
GlobalWhoAmI_ListViewCompareProc,
(LPARAM)(lpNm->iSubItem));
return TRUE; /* we processed a message */
case LVN_DELETEITEM:
// Free memory
LocalFree( (HLOCAL) lpNm->lParam );
lpNm->lParam = NULL;
break;
default:
break;
}
return FALSE;
}
BOOL
DetachResourceProc(
HWND hwndDlg
)
{
BOOL fDetached = FALSE;
LV_ITEM lvitem;
int index;
DWORD err;
HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
index = -1; // Start at beginning of item list.
while ((index = ListView_GetNextItem(hwndLV, index, LVNI_SELECTED)) != -1)
{
lvitem.iItem = index;
lvitem.mask = LVIF_PARAM;
lvitem.iSubItem = 0;
if ( ListView_GetItem( hwndLV, &lvitem ))
{
PCONN_STATUS pConnStatus = (PCONN_STATUS) lvitem.lParam;
BOOL fDisconnected = FALSE; // Can be used to refresh
// the shell if needed
err = LogoutFromServer( pConnStatus->pszServerName,
&fDisconnected );
if ( err == NO_ERROR )
{
fDetached = TRUE;
}
else
{
NwMakePrettyDisplayName(pConnStatus->pszServerName);
::MsgBoxPrintf( hwndDlg,
IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED,
IDS_TITLE_LOGOUT,
MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
pConnStatus->pszServerName );
}
}
}
return fDetached;
}
static DWORD aWhoAmIIds[] = { IDC_LOGOFRAME, NO_HELP,
IDD_GLOBAL_SERVERLIST_T,IDH_GLOBAL_SERVERLIST,
IDD_GLOBAL_SERVERLIST, IDH_GLOBAL_SERVERLIST,
IDD_DETACH, IDH_GLOBAL_DETACH,
IDD_GLOBAL_SVRLIST_DESC,IDH_GLOBAL_SERVERLIST,
0, 0 };
/*
* GlobalWhoAmIDlgProc
* -------------------
*
* WhoAmI information for list of attached servers
*/
BOOL
CALLBACK
GlobalWhoAmIDlgProc(
HWND hwndDlg,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
switch (msg)
{
case WM_INITDIALOG:
{
LPWSTR pszCurrentContext = NULL;
DWORD dwPrintOptions;
// Get the current default tree or server name
DWORD err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
if ( err == NO_ERROR )
{
LPWSTR pszName;
WCHAR szUserName[MAX_PATH+1] = L"";
WCHAR szNoName[2] = L"";
DWORD_PTR ResumeKey = 0;
LPBYTE pBuffer = NULL;
DWORD EntriesRead = 0;
DWORD dwMessageId;
UNICODE_STRING uContext;
WCHAR szContext[MAX_PATH+1];
szContext[0] = 0;
uContext.Buffer = szContext;
uContext.Length = uContext.MaximumLength
= sizeof(szContext)/sizeof(szContext[0]);
if ( pszCurrentContext )
{
pszName = pszCurrentContext;
}
else
{
pszName = szNoName;
}
if ( pszName[0] == TREECHAR )
{
// Get the tree name from the full name *TREE\CONTEXT
LPWSTR pszTemp;
if ( pszTemp = wcschr( pszName, L'\\' ))
*pszTemp = 0;
dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_TREE;
}
else
{
dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_SERVER;
}
if ( pszName[0] != 0 ) // there is preferred server/tree
{
err = NwGetConnectionStatus( pszName,
&ResumeKey,
&pBuffer,
&EntriesRead );
}
if ( err == NO_ERROR && EntriesRead > 0 )
// For trees, we'll get more than one entry
{
PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
if ( EntriesRead > 1 && pszName[0] == TREECHAR )
{
// If there is more than one entry for trees,
// then we need to find one entry where username is not null.
// If we cannot find one, then just use the first one.
DWORD i;
PCONN_STATUS pConnStatusTmp = pConnStatus;
PCONN_STATUS pConnStatusUser = NULL;
PCONN_STATUS pConnStatusNoUser = NULL;
for ( i = 0; i < EntriesRead ; i++ )
{
if ( pConnStatusTmp->fNds )
{
pConnStatusNoUser = pConnStatusTmp;
if ( ( pConnStatusTmp->pszUserName != NULL )
&& ( ( pConnStatusTmp->dwConnType
== NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
|| ( pConnStatusTmp->dwConnType
== NW_CONN_NDS_AUTHENTICATED_LICENSED )
)
)
{
// Found it
pConnStatusUser = pConnStatusTmp;
break;
}
}
// Continue with the next item
pConnStatusTmp = (PCONN_STATUS)
( (DWORD_PTR) pConnStatusTmp
+ pConnStatusTmp->dwTotalLength);
}
if ( pConnStatusUser )
{
// found one nds entry with a user name
pConnStatus = pConnStatusUser;
}
else if ( pConnStatusNoUser )
{
// use an nds entry with no user name
pConnStatus = pConnStatusNoUser;
}
// else use the first entry
}
if ( ( pConnStatus->pszUserName )
&& ( pConnStatus->pszUserName[0] != 0 )
)
{
NwAbbreviateUserName( pConnStatus->pszUserName,
szUserName);
NwMakePrettyDisplayName( szUserName );
if ( pszName[0] != TREECHAR )
{
dwMessageId = IDS_MESSAGE_LOGGED_IN_SERVER;
}
else
{
dwMessageId = IDS_MESSAGE_LOGGED_IN_TREE;
}
}
if ( pszName[0] == TREECHAR )
{
// For trees, we need to get the current context
// Open a handle to the redirector
HANDLE handleRdr = NULL;
err = RtlNtStatusToDosError(
::NwNdsOpenRdrHandle( &handleRdr ));
if ( err == NO_ERROR )
{
UNICODE_STRING uTree;
RtlInitUnicodeString( &uTree, pszName+1 ); // get past '*'
// Get the current context in the default tree
err = RtlNtStatusToDosError(
::NwNdsGetTreeContext( handleRdr,
&uTree,
&uContext));
}
if ( handleRdr != NULL )
::NtClose( handleRdr );
}
}
if ( !err )
{
LPWSTR pszText = NULL;
err = ::LoadMsgPrintf( &pszText,
dwMessageId,
pszName[0] == TREECHAR?
pszName + 1 : pszName,
szUserName,
szContext );
if ( err == NO_ERROR )
{
::SetDlgItemText( hwndDlg, IDD_GLOBAL_SERVERLIST_T,
pszText);
::LocalFree( pszText );
}
}
if ( pBuffer != NULL )
{
LocalFree( pBuffer );
pBuffer = NULL;
}
}
if ( pszCurrentContext != NULL )
{
LocalFree( pszCurrentContext );
pszCurrentContext = NULL;
}
if ( err != NO_ERROR )
{
::MsgBoxErrorPrintf( hwndDlg,
IDS_MESSAGE_CONNINFO_ERROR,
IDS_NETWARE_TITLE,
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
err,
NULL );
::EndDialog( hwndDlg, FALSE);
return TRUE;
}
// Fill listview control with connection parameters
CreateConnectionsListView(hwndDlg);
UnHideControl( hwndDlg, IDD_DETACH);
// List view fill defaults to no selected server, disable Detach.
EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH), FALSE);
// Set up timer for automatic refresh interval
::SetTimer( hwndDlg, 1, GLOBAL_WHOAMI_REFRESH_INTERVAL, NULL);
// Set focus to list box
::SetFocus( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST));
return FALSE; /* we set the focus */
}
case WM_DESTROY:
::KillTimer( hwndDlg, 1);
break;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
case IDCANCEL:
::EndDialog( hwndDlg, FALSE);
return TRUE; /* we processed a message */
case IDD_DETACH:
// Attempt to detach server connection currently selected
if ( DetachResourceProc( hwndDlg ))
{
// If succeeded - refresh window
::SendMessage(hwndDlg,WM_COMMAND,IDD_REFRESH,0L);
}
return TRUE; /* we processed a message */
case IDD_REFRESH:
{
// Refresh connection listbox
HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
::SetFocus( hwndLV );
// Clear list
ListView_DeleteAllItems( hwndLV );
// Now refill list view window
FillConnectionsListView( hwndDlg );
// List view refill unsets selected server focus, disable Detach.
EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH ), FALSE );
return TRUE; /* we processed a message */
}
default:
break;
}
break;
case WM_NOTIFY:
// Handle notifications
if ( NotifyHandler( hwndDlg, msg, wParam, lParam))
return TRUE; /* we processed a message */
break;
case WM_TIMER:
::SendMessage( hwndDlg, WM_COMMAND, IDD_REFRESH, 0L);
break;
case WM_HELP:
::WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
NW_HELP_FILE,
HELP_WM_HELP,
(DWORD_PTR) (LPVOID) aWhoAmIIds );
return TRUE;
case WM_CONTEXTMENU:
::WinHelp( (HWND) wParam,
NW_HELP_FILE,
HELP_CONTEXTMENU,
(DWORD_PTR) (LPVOID) aWhoAmIIds );
return TRUE;
}
return FALSE; /* we didn't process the message */
}