随笔 - 61  文章 - 0  评论 - 405 

   使用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的源码

http://www.microsoft.com/downloads/details.aspx?familyid=CE2CA4FD-2169-4FAC-82AF-770AA9B60D77&displaylang=en

下面进行我们的改造过程

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;
 }
}

 

还有一些遗留问题:

   1.菜单合并时候,可以指定位置,这样有利于我们控制,这个暂时没做。

   2.文件菜单中,当Excel卸载后,文件菜单的部分不能自动置为灰色的,这里还没搞清楚是什么原因

 

好了如果你有兴趣,而且要使用类似的功能,可以找我,我可以提供源码,不过如果你改的更好了,别忘记给我一份,呵呵。

 

 

 

 

 

 

 

posted on 2006-04-19 08:53 caidehui 阅读(...) 评论(...) 编辑 收藏