[CNewFileMenu : A wrapper around the New-File Menu within Windows Explorer] |
Environment: Win 95/98 VC6 SP4
This article assumes you are femiliar with the Registry . It uses some wrapper classes I wrote disscussing
the Registry .(Look for CRegistry + CRegistryShell classes' documentation)
CRegistry wraps registry functions
CRegistryShell implements some specific actions related to the shell
(searching for certain keys etc.
This picture is a representation of the new file menu : The funny letters on top are Hebrew script (New Folder, New Link) (Since it turned out well , I hopefully believe that it works on diffenet languages with special characters ... I havent checked yet whether it works !!! Please tell mail me ... (I'm interested especially in Latin + Slavic languages . Thanks.
This class is usefull for those of you who write Explorer like
application or want your application to have a generic ability of Creating new documents
the way Explorer does. Using it is simple . instantiate , call Create() , if it return TRUE
you can get a menu handle by calling GetMenu() what you do with that menu is your concern :
The companion application instatiates a CNewFileMenu object within
the Frame , Then it gets the main menu and appends the CNewFileMenu menu-handle ( got from GetMenu())
to the File sub menu. Since the menu handle is under your responsibily you need to delete
it yourself (here appended to main menu so the frame , deletes it)
Commands of the menu start with ...(you define the starting value) therefore you need command handlers for these :
a good practice is ON_COMMAND_RANGE macro : I assume that 50 id handlers are quite sufficient on any machine.
Creating the menu in OnCreate handler , you see that I used a wait cursor , Unfortunately Create() takes a long time : You should consider calling Create from a secondary Thread that is created in CWinApp::InitInstance() and storing returned result (of Create) in a boolean value accessible from OnCreate() of CFrameWnd , there when main menu is already created call GetMenu() and insert the handle to ... wherever you want.
CWaitCursor wait; if (menu.Create()) { HMENU hMenu = menu.GetMenu(WM_USER+1); CMenu *pMenu = GetMenu(); //getting the main menu if (pMenu) pMenu = pMenu->GetSubMenu(0); if (pMenu) pMenu->AppendMenu( MF_POPUP, (UINT)hMenu, _T("New")); }
Creating a handler for that range (of 50 id's) in .cpp file
ON_COMMAND_RANGE( WM_USER+1, WM_USER+50, OnNewFileMenu) ON_UPDATE_COMMAND_UI_RANGE(WM_USER+1, WM_USER+50, OnUpdateNewFileMenu )
In .h file
afx_msg void OnNewFileMenu( UINT uID); afx_msg void OnUpdateNewFileMenu(CCmdUI *pCmdUI);
Most importent is to call methods : database entries always begin with 1
Therefore any returned command (which begins with WM_USER+1 needs to drop that value.
Invoke has 2 parameters : the first is command id : (id you got - first id in menu)
the second is your reference direcrtory. (or current directory or any ...)
BOOL Invoke( int iCommand, LPCTSTR lpszDirectory = NULL ) { return CreateItem( iCommand, lpszDirectory ); } BOOL CreateItem( int iCommand, LPCTSTR lpszDirectory = NULL );
For information on command uncomment the first 3 lines of "OnNewFileMenu" function:
void CMainFrame::OnNewFileMenu( UINT uID) { // CString s; // s.Format("got id=%d",uID-WM_USER-1); // CMessage::msg(s); menu.Invoke(uID-WM_USER-1,"c:\\"); } void CMainFrame::OnUpdateNewFileMenu(CCmdUI *pCmdUI) //just to enable { }
How it works?
The function Create() builds a database :
it first enumerates all file extensions (for example : *.bmp , *jpeg)
into a CStringArray and then delete those that don't contain a key named
"ShellNew" in their branch . Most keys are now being deleted , and few are left:
this is done in members of CRegistryShell object:
EnumRecursive( strKey, &arrTmp) : enumerates all sub keys of branch. DeletePathWhichNotIncludes(...) : deletes Registry paths that dont include some key ...Then for every path of key a function "EnumShellNewOfSingleKey( strKey, &arrTmp)" is called with 2 parameters : the first : path to file extension (example: "HKEY_CURRENR_ROOT\\*.doc") the second : all paths to shellnew keys , In this example of *.doc extension , which is generally related to Microsoft Winword , there may be several paths , (and several types of documents). Explorer menu should contain only the default file type .
You see here the code of "AddCommand" it accepts 7 parameters :
1) iNewCommand = index in array
2) iCode = code of file craetion (if "Command" , "NullFile" ...)
3) lpszDescription = Caption to be presented in menu
4) lpszExtension = file extension (such as .bmp .cpp ...)
5) lpszData = additional data (in case of "Template" : template name , or shell command ("COMMND"))
6) pData = pointer to the data (only in case of "DATA")
7) cbData = length of data (" ...)
BOOL CNewFileMenu::AddCommand(int iNewCommand, int iCode , LPCTSTR lpszDescription, LPCTSTR lpszExtension, LPCTSTR lpszData, BYTE *pData , DWORD cbData) { if ((iCode<=0) || (iCode>5)) //invalid code return FALSE;//checking to be sure CNewFileInformation *pInfo = new CNewFileInformation; if (!pInfo) return FALSE; if (!pInfo->SetInfo( iNewCommand, lpszDescription, lpszExtension)) { delete pInfo; return FALSE; } //1=NULLFILE 2=FILENAME (template) //3=COMMAND 4=DATA 5=New Directory switch (iCode) { case 1: pInfo->SetFileOfTypeNull(); break; case 2: pInfo->SetFileOfTypeTemplate(lpszData); break; case 3: pInfo->SetFileOfTypeCommand(lpszData); break; case 4: pInfo->SetFileOfTypeData( pData, cbData); break; case 5: pInfo->SetFileOfTypeDirectory(); break; } m_NewFiles.Add(pInfo); return TRUE; }
Notice
Documentation isn't complete for more , read source code and comments ,
There is some more ...
The code requires more multi-lingual testing , I mean need to
be check if works well on veriety of languages , Please tell me if
captions are OK and extraction of file templates (and template directory).
This code was tested on Hebrew-English machines . (works ok)