/*++ 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 #include #include #include #include #include #include #define DONT_WANT_SHELLDEBUG #include #include #include #include #include #include #include #include //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 ); INT_PTR 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, ::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 */ INT_PTR 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 */ }