将Excel嵌入你的.Net程序-使用dsoFramer (转载)
使用AxWebBrowser或者WebBrowser的方法将Office嵌入我们的.Net系统问题有几个,1是WebBrowser控件是一个比较重的控件,2是通过WebBrowser去控制Office如果出现问题没有办法进行调试与判断,也无法修改,3是Office对应的菜单没有办法控制。为此我们决定需求新的解决方案,使用微软提供的dsoFramer控件例子,这个例子使用VC++编写,本身是可以使用的,可以参见:
http://support.microsoft.com/default.aspx?scid=kb;en-us;311765
在这里下载dsoFramer的源码
下面进行我们的改造过程
1.使用VisualStudio2005打开该VC工程,按系统提示进行转换
2.转换完成后,发现该控件缺少OLB文件,这个文件是控件的对象库,在引用该控件的时候要用到,编译该对象库需要mktyplib.exe但该工具在VS2005中已经不再提供,使用MIDL代替,但MIDL又不支持这种转换过来的工程,没想到第一步就遇到了挫折,为此想了很多办法,后来从VS2003中找到旧的mktyplib.exe,拷入VS2005\common\tools\bin下面,呵呵可以了,(这个应该算VS2005的一个Bug吧)
3.编译好ocx,通过Regsvr32 注册到系统中
4.创建一个C#工程,引入刚才的ocx
5.发现该控件,自带了菜单,工具栏,并且我们基本不能控制该控件,晕倒
6.决定自己将它的菜单栏放到我们的.Net Form上,发现不能将 C++的菜单直接放过来,C++的有太多的控制了,如果要重写这些控制,就要吐血了,并且发现新的MenuStrip控件根本就不是一个菜单,传入C++后,不认识。费了好大的劲,将Form的MainMenu给传了进去,呵呵,这下行了,看看运行的效果:
怎么样,感觉还不错吧,现在这个Excel就可以好好的控制了,最主要的是, 如果你想增加什么新的东西,出现什么问题就可以自己调试解决了。
如果你要在OCX中增加接口,你必须同时修改三个地方,一个是ODL,OCX的接口定义文件,并且按照规范,一个是你的控件实现的对应的.H文件,一个实现的.CPP文件,这些都简单了。
6.菜单出来了,发现不能控制,又再次晕倒,不知所措了
解决办法,是通过OVerride窗体的WndProc方法,将菜单的事件,除了你自己的菜单以外的,都给传到OCX中。
在OCX中增加一个接口方法:ExecuteMenuMessage,它直接调用OnMenuMessage,呵呵,这样就行了,代码如下:
protected override void WndProc(ref Message m)
{
IntPtr lWParam=IntPtr.Zero ;
switch (m.Msg)
{
case WindowsMessages.WM_INITMENU :
case WindowsMessages.WM_ENTERIDLE:
case WindowsMessages.WM_MENUSELECT:
case WindowsMessages.WM_INITMENUPOPUP:
case WindowsMessages.WM_COMMAND :
ExecuteMenuMessage(m.Msg, m.WParam, m.LParam);
//Console.Write(m.Msg.ToString("X"));
//Console.Write(" ");
//Console.Write(lWParam.ToString("X"));
//Console.Write(" ");
//Console.WriteLine(m.LParam.ToString("X"));
break;
}
base.WndProc(ref m);
}
现在菜单信息也传入了,哦,创建菜单的程序如下,酷吧:
private void Form1_Load(object sender, EventArgs e)
{
//if (this.Menu == null)
{
this.Menu = new MainMenu();
CreateFileMenu();
IntPtr hMainMenu = this.Menu.Handle;
this.axFramerControl1.MainFormMenu = hMainMenu;
this.axFramerControl1.MainForm = this.Handle;
}
}
private void CreateFileMenu()
{
IntPtr pFileMenu = MenuHelper.CreatePopupMenu();
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_NEW, "新建(&N)...\tCtrl+N");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_OPEN, "打开(&O)...\tCtrl+O");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_CLOSE, "关闭(&C)");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_SAVE, "保存(&S)\tCtrl+S");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_SAVEAS, "另存为(&A)...");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PGSETUP, "页面设置(&U)...");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PRINTPV, "打印预览(&V)");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PRINT, "打印(&P)...");
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PROPS, "属性(&I)");
MenuHelper.AppendMenu(this.Menu.Handle, MenuFlags.MF_BYPOSITION | MenuFlags.MF_POPUP, (uint)pFileMenu.ToInt32() , "文件(&F)");
MenuHelper.DrawMenuBar(this.Handle);
}
7.当关闭Excel后会怎么样,靠,菜单还在,并且每个都报错,肯定报错,OLE已经卸载了,只好跑去修改OCX控件中docObject的ClearMergeMenu,让它清除掉Excel的菜单,明白原理就简单了。
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::ClearMergedMenu (protected)
//
// Frees the merged menu set by host.
//
STDMETHODIMP_(void) CDsoDocObject::ClearMergedMenu()
{
if (m_hMenuMerged)
{
int cbMenuCnt = GetMenuItemCount(m_hMenuMerged);
int iMainMenuCnt=0;
if(this->m_hMenuMainForm)
iMainMenuCnt=GetMenuItemCount(m_hMenuMainForm);
for (int i = cbMenuCnt-1; i >0; --i)
{
if(this->m_hMenuMainForm)
{
RemoveMenu(m_hMenuMainForm,--iMainMenuCnt,MF_BYPOSITION);
}
RemoveMenu(m_hMenuMerged, i, MF_BYPOSITION);
}
if(m_hwndMainForm)
DrawMenuBar(m_hwndMainForm);
DestroyMenu(m_hMenuMerged);
m_hMenuMerged = NULL;
}
}