//*************************************************************************** // // Library: // HOMEDRV.DLL // // // Author: // Joev Dubach // // // Purpose: // HOMEDRV is a File Manager extension DLL. An extension DLL adds // a menu to File Manager, contains entry point that processes menu // commands and notification messages sent by File Manager, and // queries data and information about the File Manager windows. The // purpose of an extension DLL is to add administration support // features to File Manager, for example, file and disk utilities. // Up to five extension DLLs may be installed at any one time. // // HOMEDRV adds a menu called "HomeDrive" to File Manager and // processes all the messages that are sent by File Manager to the // HOMEDRV DLL. In particular, it allows the user to select // "Connect to Home Drive" from the HomeDrive menu. Using RPC, the // DLL asks a HomeDrive server running on NT for the home share of // the given user. If it receives this information, it redirects // a network drive to this share. Then it tells File Manager to // refresh its windows, so as to bring the new connection up. // // Usage: // File Manager installs the extensions that have entries in the // [AddOns] section of the WINFILE.INI initialization file. An entry // consists of a tag and a value. To load HOMEDRV.DLL as a File // Manager extension, add the following to WINFILE.INI (assuming the // DLL resides in c:\win\system32): // // [AddOns] // HomeDrive Extension=c:\win\system32\homedrv.dll // // // Menu Options: // Following menu items belong to the "Extension" menu that is added // to File Manager: // // Connect to Home Drive - Redirects a drive to a given user's share // About HomeDrive... - Displays About dialog // // // More Info: // Query on-line help on: FMExtensionProc, File Manager Extensions, RPC // // //*************************************************************************** // // Inclusions // #include #include #include #include #include #include #include #include #include #include // RPC data structures and APIs #include #include #include "homedrv.h" #include "home.h" // header file generated by MIDL compiler #include "..\homedir.h" // Server/client error codes // // Global variables // HANDLE ghDllInst; // DLL's instance handle HMENU ghMenu; // Extension's menu handle WORD gwMenuDelta; // Delta for extension's menu items char gOldAddress[STRINGSIZE]; // Server network address BOOL gRefreshNecessary; // Tells if FM needs to refresh windows // // Function prototypes // HMENU FAR PASCAL FMExtensionProc ( HWND hwndExtension, WORD wMesssage, LONG lParam ); BOOL FAR PASCAL AboutDlgProc ( HWND hDlg, unsigned uMessage, WORD wParam, LONG lParam ); BOOL FAR PASCAL MenuADG( HWND hDlg, unsigned message, WORD wParam, LONG lParam ); void Connect( char * drive, char *dir ); void CheckWinStatus( UINT status ); void CheckRpcStatus( char *ProcName, RPC_STATUS status ); void CheckMyStatus( char *ProcName, MY_STATUS status ); void CreateBinding( char *Address ); void DestroyBinding( void ); //*************************************************************************** // // LibMain() // // Purpose: // // LibMain is called by LibEntry. LibEntry is called by Windows // when the DLL is loaded. The LibEntry routine is provided // in the LIBENTRY.OBJ in the SDK Link Libraries disk. (The // source LIBENTRY.ASM is also provided.) // // LibEntry initializes the DLL's heap if a HEAPSIZE value is // specified in the DLL's DEF file. After this, LibEntry calls // LibMain. The LibMain function below satisfies that call. // // The LibMain function should perform additional initialization // tasks required by the DLL. LibMain should return a value of // TRUE if the initialization is successful. // // // Parameters: // // IN: // hLibInst - DLLs instance handle // wDataSeg - Data segment // cbHeapSize - Size of the DLL's heap // lpszCmdLine - Command line // // OUT: // N/A // // Return Value: // // TRUE if the initialization is successful; FALSE otherwise. // //*************************************************************************** int FAR PASCAL LibMain ( HANDLE hLibInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine ) { char server[STRINGSIZE]; if(GetPrivateProfileString("HomeDrive", // Section of winfile.ini "Server", // Variable "", // Default server, // Where to put it STRINGSIZE, "winfile.ini")) { CreateBinding(server); strcpy(gOldAddress,server); // save the address } ghDllInst = hLibInst; return (TRUE); } // LibMain() //*************************************************************************** // // WEP() // // Purpose: // // Performs cleanup tasks when the .DLL is unloaded; in this case, // it destroys the binding to the RPC HomeDrive server. The WEP() // is called automatically by Windows when the DLL is unloaded. // // Make sure that the WEP() is @1 RESIDENTNAME in the EXPORTS // section of the .DEF file. This ensures that the WEP() can // be called as quickly as possible. Incidently, this is why // the WEP() is called the WEP() instead of WindowsExitProcedure(). // It takes up the minimum amount of space and is quickly located. // // // Parameters: // // IN: // bSystemExit - Type of exit // // OUT: // N/A // // Return Value: // // TRUE. // //*************************************************************************** int FAR PASCAL WEP( int bSystemExit ) { DestroyBinding(); return (TRUE); } // WEP() //*************************************************************************** // // FMExtensionProc() // // Purpose: // // This is an application-defined callback function. It processes menu // commands and messages sent to HOMEDRV.DLL. // // // Parameters: // // IN: // hwndExtension - Identifies the File Manager window // wMessage - Message sent to extension DLL // lParam - Message information // // OUT: // N/A // // Return Value: // // When the wMessage is FMEVENT_INITMENU, handle to extension's menu // should be returned; otherwise a NULL value. // //*************************************************************************** HMENU FAR PASCAL FMExtensionProc ( HWND hwndExtension, WORD wMessage, LONG lParam ) { LPFMS_LOAD lpLoad; FARPROC lpDialogProc; FARPROC lpProc; // pointer to the chosen function switch (wMessage) { // ****************** File Manager Events case FMEVENT_LOAD: lpLoad = (LPFMS_LOAD) lParam; // Assign the menu handle from the DLL's resource ghMenu = LoadMenu (ghDllInst, "ExtensionMenu"); lpLoad->hMenu = ghMenu; // This is the delta we are being assigned. gwMenuDelta = lpLoad->wMenuDelta; // Size of the load structure lpLoad->dwSize = sizeof (FMS_LOAD); // Assign the popup menu name for this extension lstrcpy (lpLoad->szMenuName, "Ho&meDrive"); // Return that handle return (ghMenu); // ****************** Extension menu commands case IDM_CONNECT: gRefreshNecessary=FALSE; lpProc = MakeProcInstance(MenuADG, ghDllInst); DialogBox(ghDllInst, // current instance "CONNECTBOX", // resource to use hwndExtension, // parent handle lpProc); // About() instance address if (gRefreshNecessary) { SendMessage(hwndExtension, FM_REFRESH_WINDOWS, // To bring the drive up 1, // Non-zero refreshes all windows 0L); } FreeProcInstance(AboutDlgProc); break; case IDM_ABOUTEXT: lpDialogProc = MakeProcInstance(AboutDlgProc, ghDllInst); DialogBox (ghDllInst, "ABOUTBOX", hwndExtension, lpDialogProc); FreeProcInstance(AboutDlgProc); break; } return (NULL); } // FMExtensionProc() //*************************************************************************** // // AboutDlgProc() // // Purpose: // // Procedure to handle About dialog messages. This dialog displays // copyright and help information. // // // Parameters: // // IN: // hDlg - Dialog window handle // uMessage - Dialog message // wParam - Message information // lParam - Additional message information // // OUT: // N/A // // Return Value: // // Appropriate value for the dialog message. // //*************************************************************************** BOOL FAR PASCAL AboutDlgProc( HWND hDlg, unsigned uMessage, WORD wParam, LONG lParam ) { #define ICONLEFTCOORD 8 #define ICONTOPCOORD 8 #define ICONRIGHTCOORD 40 #define ICONBOTTOMCOORD 40 #define ICONXLOC 9 #define ICONYLOC 8 HDC hdc; int i; DWORD temp; int x; int y; static HWND hIcon[16]; volatile int j; static BOOL FirstTime=TRUE; static BOOL CookieOn=FALSE; switch (uMessage) { case WM_INITDIALOG: for(i=0;i<16;i++) hIcon[i]=LoadIcon(ghDllInst,MAKEINTRESOURCE(ANIM1+i)); return (TRUE); case WM_COMMAND: switch (wParam) { case IDOK: case IDCANCEL: for(i=0;i<16;i++) DestroyIcon(hIcon[i]); EndDialog (hDlg, TRUE); return (TRUE); default: break; } // switch (wParam) case WM_LBUTTONUP: case WM_RBUTTONUP: if((CookieOn != (uMessage==WM_LBUTTONUP)) && (wParam & MK_CONTROL)) { hdc=GetDC(hDlg); temp=GetCurrentPosition(hdc); x=LOWORD(lParam)-LOWORD(temp); y=HIWORD(lParam)-HIWORD(temp); if((x>ICONLEFTCOORD)&&(xICONTOPCOORD)&&(y0);) { DrawIcon(hdc,ICONXLOC,ICONYLOC,hIcon[i]); if(!FirstTime) { for(j=0;j<30000;j++); } if(uMessage==WM_LBUTTONUP) { i++; } else { i--; } } DrawIcon(hdc,ICONXLOC,ICONYLOC,hIcon[0]); FirstTime=FALSE; if(uMessage==WM_LBUTTONUP) { ShowWindow(GetDlgItem(hDlg,ID_OPAQUERECT),SW_SHOWNORMAL); ShowWindow(GetDlgItem(hDlg,ID_CREDITS),SW_SHOWNORMAL); CookieOn=TRUE; } else { ShowWindow(GetDlgItem(hDlg,ID_OPAQUERECT),SW_HIDE); ShowWindow(GetDlgItem(hDlg,ID_CREDITS),SW_HIDE); CookieOn=FALSE; } ReleaseDC(hDlg,hdc); return(TRUE); } else { ReleaseDC(hDlg,hdc); } } break; case WM_DESTROY: CookieOn=FALSE; } // switch (message) return (FALSE); } // AboutDlgProc /**************************************************************************** FUNCTION: MenuADG(HWND, unsigned, WORD, LONG) PURPOSE: Processes messages for the "Connect" dialog box. MESSAGES: WM_INITDIALOG - initialize dialog box WM_COMMAND - Input received COMMENTS: ****************************************************************************/ BOOL FAR PASCAL MenuADG( HWND hDlg, // window handle of the dialog box unsigned message, // type of message WORD wParam, // message-specific information LONG lParam ) { char name[STRINGSIZE]; char dir[STRINGSIZE]; char server[STRINGSIZE]; char drive[3]="E:"; // It would be a shame to change 3 to STRINGSIZE. HWND hWndListBox; MY_STATUS TempStat; switch (message) { case WM_INITDIALOG: // message: initialize dialog box // Initialize Drive list box hWndListBox = GetDlgItem(hDlg, IDDRIVE); while(drive[0]<='Z') { SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) ((char _far *) drive)); (drive[0])++; } // Get defaults from stored data in winfile.ini if(GetPrivateProfileString("HomeDrive", // Section of winfile.ini "Name", // Variable "", // Default name, // Where to put it STRINGSIZE, "winfile.ini")) { SetDlgItemText(hDlg, IDNAME, name); } SetDlgItemText(hDlg, IDSERVER, gOldAddress); if(GetPrivateProfileString("HomeDrive", // Section of winfile.ini "Drive", // Variable "", // Default drive, // Where to put it 3, "winfile.ini")) { SendMessage(hWndListBox, LB_SELECTSTRING, (UINT) -1, (LONG) ((char _far *) drive)); } return (TRUE); case WM_COMMAND: // message: received a command switch (wParam) { case IDOK: // "OK" box selected? hWndListBox = GetDlgItem(hDlg, IDDRIVE); GetDlgItemText(hDlg,IDNAME,name,STRINGSIZE); GetDlgItemText(hDlg,IDSERVER,server,STRINGSIZE); if (strcmp(gOldAddress,server)) // server has changed { DestroyBinding(); CreateBinding(server); strcpy(gOldAddress,server); } SendMessage(hWndListBox, LB_GETTEXT, (WORD) SendMessage(hWndListBox, LB_GETCURSEL, 0, 0L), (LONG) ((char _far *) drive)); // Save defaults WritePrivateProfileString("HomeDrive","Name",name, "winfile.ini"); WritePrivateProfileString("HomeDrive","Server",server, "winfile.ini"); WritePrivateProfileString("HomeDrive","Drive",drive, "winfile.ini"); RpcTryExcept { CheckMyStatus( "RPC Get call", TempStat = RpcHomesvrGet(name,dir) ); if (!TempStat) { Connect(drive,dir); } } RpcExcept(1) { CheckRpcStatus("Runtime library reported an exception", RpcExceptionCode() ); } RpcEndExcept if (!TempStat) { EndDialog(hDlg, TRUE); } return (TRUE); case IDCANCEL: EndDialog(hDlg, TRUE); return (TRUE); case IDDRIVE: // If the user double clicks the list box, treat it as OK. if (HIWORD(lParam)==LBN_DBLCLK) { SendMessage(hDlg,WM_COMMAND,IDOK,0L); } } break; } return (FALSE); // Didn't process a message } void Connect( char *Drive, char *Dir ) { // I assume that the share is valid. The syntax is checked by the // HomeDrive administrative application when it's entered into the // database, so it's a good bet. char Share[STRINGSIZE]; char Subdir[STRINGSIZE]; char CurrConnect[STRINGSIZE]; UINT BuffSize=STRINGSIZE; UINT status; int i=0; int j=0; int k; // Parse the share information. for(k=1;k<=4;k++) { while(Dir[i]!='\\' && Dir[i]!='\0') { Share[i]=Dir[i]; i++; } Share[i]=Dir[i]; i++; } Share[--i]='\0'; while(Dir[i]!='\0') { Subdir[j]=Dir[i]; i++; j++; } Subdir[j]='\0'; switch(status=WNetGetConnection(Drive,CurrConnect,&BuffSize)) { case WN_NOT_CONNECTED: gRefreshNecessary=TRUE; status=WNetAddConnection(Share,"",Drive); CheckWinStatus(status); // If the File Manager had more hooks, I'd actually do this. //if (status==WN_SUCCESS) // { // change directory // change drive // } break; case WN_CONNECTION_CLOSED: gRefreshNecessary=TRUE; status=WNetCancelConnection(Drive,TRUE); // TRUE _forces_ close CheckWinStatus(status); if (status==WN_SUCCESS) { status=WNetAddConnection(Share,"",Drive); CheckWinStatus(status); //if (status==WN_SUCCESS) // { // change directory // change drive // } } break; case WN_SUCCESS: if(strcmp(_strlwr(CurrConnect),Share)!=0) // Share is lowercase { gRefreshNecessary=TRUE; status=WNetCancelConnection(Drive,TRUE); // TRUE _forces_ close CheckWinStatus(status); if (status==WN_SUCCESS) { status=WNetAddConnection(Share,"",Drive); CheckWinStatus(status); //if (status==WN_SUCCESS) // { // change directory // change drive // } } } //else // already connected // { // change directory // change drive // } break; default: CheckWinStatus(status); } } void CheckWinStatus( UINT status ) { char temp[60]; switch (status) { case WN_SUCCESS: break; case WN_NOT_SUPPORTED: MessageBox(HWND_DESKTOP, "That function is not supported by your network.", "Error", MB_OK); break; case WN_OUT_OF_MEMORY: MessageBox(HWND_DESKTOP, "Out of memory.", "Error", MB_OK); break; case WN_NET_ERROR: MessageBox(HWND_DESKTOP, "An error occured on the network.", "Error", MB_OK); break; case WN_BAD_NETNAME: MessageBox(HWND_DESKTOP, "Your network share could not be reached.", "Error", MB_OK); break; case WN_BAD_PASSWORD: case WN_ACCESS_DENIED: MessageBox(HWND_DESKTOP, "Access denied. Check your password, or see your system administrator.", "Error", MB_OK); break; case WN_BAD_VALUE: case WN_BAD_LOCALNAME: MessageBox(HWND_DESKTOP, "Invalid drive specification.", "Error", MB_OK); break; case WN_OPEN_FILES: MessageBox(HWND_DESKTOP, "Could not delete previous connection. Try closing files open on that drive.", "Error", MB_OK); break; case WN_BAD_POINTER: case WN_MORE_DATA: default: wsprintf(temp, "Internal error %u; contact your system administrator.", status); MessageBox(HWND_DESKTOP, temp, "Error", MB_OK); } } void CheckRpcStatus( char *ProcName, MY_STATUS status ) { char buffer[10]; char temp[100] = "RPC Exception in "; switch (status) { case RPC_S_INVALID_STRING_BINDING: case RPC_S_SERVER_UNAVAILABLE: case RPC_S_CALL_FAILED_DNE: case RPC_S_ACCESS_DENIED: MessageBox(HWND_DESKTOP, "Check that the HomeDrive server is running on an accessible server.", strcat( temp, ProcName ), MB_OK); break; case RPC_S_OK: break; default: MessageBox(HWND_DESKTOP, _itoa(status,buffer,10), strcat( temp, ProcName ), MB_OK); } } void CheckMyStatus( char *ProcName, MY_STATUS status ) { char buffer[10]; switch (status) { case HOMEDIR_S_OK: break; case HOMEDIR_S_ENTRY_ALREADY_EXISTS: MessageBox(HWND_DESKTOP, "That name is already in the database.", "Error", MB_OK); break; case HOMEDIR_S_ENTRY_NOT_FOUND: MessageBox(HWND_DESKTOP, "That name is not in the database.", "Error", MB_OK); break; default: // This should never happen MessageBox(HWND_DESKTOP, _itoa( status, buffer, 10 ), "Fatal Error", MB_OK); } } // ==================================================================== // MIDL allocate and free // ==================================================================== void * MIDL_user_allocate( size_t len ) { return(malloc(len)); } void MIDL_user_free( void * ptr ) { free(ptr); } void CreateBinding( char *pszNetworkAddress ) { char * pszUuid = "12345678-1234-1234-1234-123456789ABC"; char * pszProtocolSequence = "ncacn_np"; char * pszEndpoint = "\\pipe\\home"; char * pszOptions = NULL; char * pszStringBinding = NULL; // FUTURE ENHANCEMENT: use the locator to find the server. /* RPC_NS_HANDLE ImportContext; CheckRpcStatus("RpcNsBindingImportBegin", RpcNsBindingImportBegin( RPC_C_NS_SYNTAX_DEFAULT, "/.:/Home", home_ClientIfHandle, NULL, &ImportContext ) ); CheckRpcStatus("RpcNsBindingImportNext", RpcNsBindingImportNext( ImportContext, &home_BindingHandle ) ); CheckRpcStatus("RpcNsBindingImportDone", RpcNsBindingImportDone(&ImportContext) ); */ // CheckRpcStatus("RpcStringBindingCompose", RpcStringBindingCompose( (unsigned char *) pszUuid, (unsigned char *) pszProtocolSequence, (unsigned char *) pszNetworkAddress, (unsigned char *) pszEndpoint, (unsigned char *) pszOptions, (unsigned char * *) &pszStringBinding ) ); // Set the binding handle that will be used to bind to the server. CheckRpcStatus("RpcBindingFromStringBinding", RpcBindingFromStringBinding( (unsigned char *) pszStringBinding, &home_BindingHandle ) ); CheckRpcStatus("RpcStringFree", RpcStringFree((unsigned char * *) &pszStringBinding) ); // } void DestroyBinding( void ) { CheckRpcStatus("RpcBindingFree", RpcBindingFree(&home_BindingHandle)); }