一、背景
Eclipse中,对于JAVA项目,可在项目或者文件上的右键菜单Show in ->System Explorer中,直接在资源管理器中显示当前项目或当前文件所在文件夹,以便于在Windows资源管理器中对文件的操作管理。同样的,在Visual Studio 2010、2013、2015、2017等版本也有类似功能,VC6为早期版本,并无相应功能。本文通过对VC6 IDE插件的开发,实现了在VC6环境中直接打开资源管理器至当前项目所在文件夹,也可以打开命令行窗口,且路径直接切换至当前工作目录。先看效果:
二、实现过程
1、VC6中新建“DevStudio Add-in Wizard”插件工程。工程名为“OpenInExplorer”,插件项目向导中均使用默认选项,点击完成。向导完成后,生成的项目代码实际上为一COM组件。
2、生成项目中各类简要介绍(略),详见Microsoft参考文档
各个文件和类的解释参考微软Understanding the Results of the Add-in Wizard
3、在ResourceView视图中修改Bitmap资源:IDR_TOOLBAR_LARGE和IDR_TOOLBAR_MEDIUM文件。
4、在ResourceView视图中修改字串表,如下表所示:
ID |
值 |
标题 |
IDS_CMD_STRING | \n在Windows资源管理器中打开-1\n在Windows资源浏览器中打开工程-2\n在Windows资源管理器中打开 | |
IDS_CMD_STRING2 | \n使用记事本打开1\n使用记事本打开2 \n使用记事本打开 | |
IDS_CMD_STRING3 | \n打开命令窗口1\n打开CMD窗口2\n打开命令窗口 |
5、在CDSAddin类的OnConnection函数中添加自己的工具栏按钮及要执行的命令,完整函数如下所示:
STDMETHODIMP CDSAddIn::OnConnection(IApplication* pApp, VARIANT_BOOL bFirstTime,
long dwCookie, VARIANT_BOOL* OnConnection)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// Store info passed to us
IApplication* pApplication = NULL;
if (FAILED(pApp->QueryInterface(IID_IApplication, (void**) &pApplication))
|| pApplication == NULL)
{
*OnConnection = VARIANT_FALSE;
return S_OK;
}
m_dwCookie = dwCookie;
// Create command dispatch, send info back to DevStudio
CCommandsObj::CreateInstance(&m_pCommands);
m_pCommands->AddRef();
// The QueryInterface above AddRef'd the Application object. It will
// be Release'd in CCommand's destructor.
m_pCommands->SetApplicationObject(pApplication);
// (see stdafx.h for the definition of VERIFY_OK)
VERIFY_OK(pApplication->SetAddInInfo((long) AfxGetInstanceHandle(),
(LPDISPATCH) m_pCommands, IDR_TOOLBAR_MEDIUM, IDR_TOOLBAR_LARGE, m_dwCookie));
// Inform DevStudio of the commands we implement
// TODO: Replace the AddCommand call below with a series of calls,
// one for each command your add-in will add.
// The command name should not be localized to other languages. The
// tooltip, command description, and other strings related to this
// command are stored in the string table (IDS_CMD_STRING) and should
// be localized.
LPCTSTR szCommand = _T("OpenInExplorerCommand");
VARIANT_BOOL bRet;
CString strCmdString;
strCmdString.LoadString(IDS_CMD_STRING);
strCmdString = szCommand + strCmdString;
CComBSTR bszCmdString(strCmdString);
CComBSTR bszMethod(_T("OpenInExplorerCommandMethod"));
CComBSTR bszCmdName(szCommand);
VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));
if (bRet == VARIANT_FALSE)
{
// AddCommand failed because a command with this name already
// exists. You may try adding your command under a different name.
// Or, you can fail to load as we will do here.
*OnConnection = VARIANT_FALSE;
return S_OK;
}
// Add toolbar buttons only if this is the first time the add-in
// is being loaded. Toolbar buttons are automatically remembered
// by Developer Studio from session to session, so we should only
// add the toolbar buttons once.
if (bFirstTime == VARIANT_TRUE)
{
VERIFY_OK(pApplication->
AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
}
/******添加自定义工具栏按钮 Start************************************************************/
// 添加第二个工具栏按钮
LPCTSTR szOpenNotepadCommand = _T("OpenInNotepad");
CString strOpenNotepadCmdString;
strOpenNotepadCmdString.LoadString(IDS_CMD_STRING2);
strOpenNotepadCmdString = szOpenNotepadCommand + strOpenNotepadCmdString;
CComBSTR bszOpenNotepadCmdString(strOpenNotepadCmdString);
CComBSTR bszOpenNotepadMethod(_T("OpenInNotepad"));
CComBSTR bszOpenNotepadCmdName(szOpenNotepadCommand);
VERIFY_OK(pApplication->AddCommand(bszOpenNotepadCmdString, bszOpenNotepadMethod, 1, m_dwCookie, &bRet));
if (bRet == VARIANT_FALSE)
{
*OnConnection = VARIANT_FALSE;
return S_OK;
}
if (bFirstTime == VARIANT_TRUE)
{
VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszOpenNotepadCmdName, m_dwCookie));
}
// 添加第三个工具栏按钮
LPCTSTR szConsoleCommand = _T("OpenInCommand");
CComBSTR bszConsoleCmdName(szConsoleCommand);
CString strConsoleCmdString;
strConsoleCmdString.LoadString(IDS_CMD_STRING3);
strConsoleCmdString = szConsoleCommand + strConsoleCmdString;
CComBSTR bszConsoleCmdString(strConsoleCmdString);
CComBSTR bszConsoleMethod(_T("OpenInCommand"));
VERIFY_OK(pApplication->AddCommand(bszConsoleCmdString, bszConsoleMethod, 4, m_dwCookie, &bRet));
if (bRet == VARIANT_FALSE)
{
*OnConnection = VARIANT_FALSE;
return S_OK;
}
if (bFirstTime == VARIANT_TRUE)
{
VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszConsoleCmdName, m_dwCookie));
}
/**********添加自定义工具栏按钮 End*************************************************************/
*OnConnection = VARIANT_TRUE;
return S_OK;
}
6、ClassView视图中,在CCommands类的接口上添加方法OpenInNotepad()和OpenInCommand()。并分别在CCommands类中实现这些方法。具体代码如下:
STDMETHODIMP CCommands::OpenInExplorerCommandMethod()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Replace this with the actual code to execute this command
// Use m_pApplication to access the Developer Studio Application object,
// and VERIFY_OK to see error strings in DEBUG builds of your add-in
// (see stdafx.h)
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
//获得DevStudio当前工作目录
BSTR bstrCurrentDirectory;
m_pApplication->get_CurrentDirectory(&bstrCurrentDirectory);
char* lpszDir = _com_util::ConvertBSTRToString(bstrCurrentDirectory);
// operation params:"open", "runas", "print", "edit", "explorer", "find"
::ShellExecute(NULL,"open",lpszDir,NULL,lpszDir,SW_SHOWNORMAL);
//释放资源
::SysFreeString(bstrCurrentDirectory);
delete[] lpszDir;
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
return S_OK;
}
以上使用了com工具,故需要引入相应头文件和依赖库文件。
//_com_util::ConvertBSTRToString()函数将要用到的头文件和库依赖文件
#include "comutil.h"
#pragma comment(lib, "comsupp.lib")
OpenInNotepad()
STDMETHODIMP CCommands::OpenInNotepad()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
CHAR path[MAX_PATH] = {NULL};
::GetSystemDirectory(path,MAX_PATH);
HINSTANCE hinst = NULL;
// hinst = ::ShellExecute(NULL, NULL, "notepad.exe", NULL, path, SW_SHOWNORMAL);
// hinst = ::ShellExecute(NULL,"open","C:\\Windows\\System32\\notepad.exe",NULL,NULL,SW_SHOWNORMAL);
UINT ret = ::WinExec("notepad.exe", SW_SHOWNORMAL);
if( SE_ERR_NOASSOC == (int)hinst){
::MessageBox(NULL, "Call notepad.exe Exception!", "OpenInNotepad", MB_OK | MB_ICONWARNING);
}
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
return S_OK;
}
OpenInCommand()
STDMETHODIMP CCommands::OpenInCommand()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
HINSTANCE hinst = NULL;
// 执行外部命令的几个函数:WinExec(), ShellExecute(), system(), CreateProcess()
// 打开IDE当前项目路径
hinst = ::ShellExecute(NULL,"open","c:\\windows\\system32\\cmd.exe",NULL,NULL,SW_SHOWNORMAL);
// 打开IDE当前项目路径
// hinst = ::ShellExecute(NULL,"open","cmd.exe",NULL,NULL,SW_SHOWNORMAL);
// 打开C:\Windows\System32目录
// hinst = ::ShellExecute(NULL, NULL, "cmd.exe", NULL, "C:\\Windows\\System32\\", SW_SHOWNORMAL);
//::WinExec("cmd.exe", SW_SHOWNORMAL);
// system("c:\\windows\\system32\\cmd.exe");
// system("notepad.exe");
// 使用浏览器打开网页
// hinst = ::ShellExecute(NULL,"open","http://localhost",NULL,NULL,SW_SHOWNORMAL);
// 使用邮件工具编辑邮件
// hinst = ::ShellExecute(NULL,"open","mailto:[email protected]?subject=Hello&Body=this is a test",NULL,NULL,SW_SHOWNORMAL);
if( SE_ERR_NOASSOC == (int)hinst){
::MessageBox(NULL, "Call cmd.exe Exception!", "OpenInNotepad", MB_OK | MB_ICONWARNING);
}
VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
return S_OK;
}
7、编译链接完成后实现自动注册组件。VC6IDE中->菜单“工程”->设置->Post-Build步骤,新增命令,命令行如下
regsvr32.exe $(OutDir)\OpenInExplorer.dll /s
/s参数表示 无声,不显示注册成功与否的消息框。
7、编译、注册组件后,VC6IDE中->菜单“工具”->定制->附加项和宏文件,点击该界面的“浏览”按钮,选中本项目的OpenInExplorer.dll文件。选中插件“OpenInExplorer Developer Studio Add-in”。关闭后弹出工具栏对话框。具体也可参考CmdWnd: Issue IDE Command in a Command Window
然后IDE可增加带3个命令按钮的工具栏对话框。在任意打开的项目中点击自定义的工具栏按钮,发现已经实现目标。直接点击按钮1,可在打开的资源管理器中直接浏览本项目文件或对项目文件进行IDE之外的操作。
三、参考文档
Visual C++ Developer Studio Add-in Samples
Overview: Add-ins for the Visual C++ Developer Studio
How to: Create Add-in
微软实现步骤示例Creating Add-ins Using Visual C++
四、注意事项
如果在windows 7以上编译,请使用管理员权限打开vc6,否则因权限不足,会导致组件注册失败。
五、完整源代码下载:OpenInExplorer