zoukankan      html  css  js  c++  java
  • word把对应图片变成嵌入的ActiveX控件。

    刚做一个小东东,做的过程还是走过些弯路,记录一下,客户提出word报表上的图太单调,提出把对应的报表上的图做成如程序里显示的Chart控件一样,能缩放,能做一些数据的转换运算然后显示.最开始我是准备用VSTO来完成这个,后来发现一些限制,如对应模版的与Word版本关系,并且要让客户说的嵌入显示在Word里也是有一些问题,所以改成用Activex控件与Office共享插件,因为一些原因,是用vs2010开发,但是.net的环境只能选择2.0 ,语言都是用的C#.

    我先描述一下做一个chart图的activex控件,chart控件就选用上次介绍的ZedGraph,新建的用户控件为ChartControlActivex.cs,里面包含一个ZedGraphControl控件。这个过程网上都有,二个要设置的地方,将为COM互操作注册勾选上,然后是ComVisible设置为true。但是只做这二项在IE里应该引用就可以调用了,但是如果在WORD里找对应的控件,会发现还是找不到,这需要我们来注册。

            [EditorBrowsable(EditorBrowsableState.Never)]
            [ComRegisterFunction()]
            public static void Register(Type t)
            {
                try
                {
                    // Open the CLSID key of the control
                    using (RegistryKey keyCLSID = Registry.ClassesRoot.OpenSubKey(
                        @"CLSID\" + t.GUID.ToString("B"), true))
                    {
                        RegistryKey subkey = null;
                        subkey = keyCLSID.OpenSubKey("InprocServer32", true);
                        if (subkey != null)                       
                            subkey.SetValue(null, Environment.SystemDirectory + @"\mscoree.dll");
                        using (subkey = keyCLSID.CreateSubKey("Control")) { };     
                        using (subkey = keyCLSID.CreateSubKey("TypeLib"))
                        {
                            Guid libId = Marshal.GetTypeLibGuidForAssembly(t.Assembly);
                            subkey.SetValue("", libId.ToString("B"), RegistryValueKind.String);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message); // Log the error
                    throw;  // Re-throw the exception
                }
            }

    我简单说明下这个函数的各个地方,前面特性保证注册时会调用这段代码,如果是我们自己在用VS2010编译,那么也是会在编译时调用的,首先会在注册表的类节点建一个存放自己信息的目录,我们这是用.net32写的,所以有点特殊,大家可以查找下mscoree.dll的一些信息,这个dll是.net组件的入口点,并且还负责选择.net版本的选择,以及这里我们需要的对COM的支持,这里我说下.net com组件被调用的过程,比如我的activex控件的GUID是[Guid("5158491A-B243-42A6-8863-A2AE82CFDFDC")],那么当我调用这个GUID的组件时,它会发现在对应的注册表里入口DLL为mscoree.dll,当加载这个DLL后,调用DllGetClassObject函数读取它的GUID,来找到它的类型名称,程序集的名称,相应版本等,然后调用CreateInstance来创建这个组件的实例,在这里后,我们打开word,查看,发现里面的activex控件还是没我们新建的控件,这里因为我们还没有创建Control子节点,这个用来表明我是一个activex control,果然,创建这个子节点后,我们能在word里的activex控件里选择它了,但是我们跟着发现,当我们创建这个节点时,会提示我们创建此对象的程序是word,您的计算机尚未安装此程序。晕,我明明安装了Word啊,这是因为我们的DLL没有包含相应的Ole信息,在WORD里,创建新的子节点TypeLib,注册这个组件的Lib信息,这个节点同时会让程序生成一份TLB文件,然后我们在找word在插入这个activex 控件,可以发现,终于能成功插入了,还有一个子节点的信息很重要,MiscStatus子节点,这个主要是activex 控件的相关显示状态,具体住处可以查看http://msdn.microsoft.com/en-us/library/ms678497.aspxhttp://msdn.microsoft.com/en-us/library/ms683733.aspx

    删除注册信息用RegasmUnregisterControl,就是把上面的注册信息删除掉,这个不多做说明。

    对应的注册表应该如下所示。

    abbb

    activex控件做完后,我们来看word插件的安装,这个word插件主要是查看一些图片有没额外的信息,如果有,它会试着看这些额外信息是否是我们activex控件要求的一些信号的数据,如果是,它会把这些信号传入activex控件,然后让activex控件来绘制出相关信号。

    我先说下在这二个组件之间互相调用我产生的一些问题,我们知道在C#里添加activex控件用如下方法 var chart = InlineShapes.AddOLEControl(typename,range),然后调用chart.OLEFormat.Object应该就是ChartControlActivex控件,但是这里是转化不成功的,反射相关属性我也设过,也是取不到的,不知是不是word的特殊性,如果不能转化,那我们信息也就传不过去,听起来好像是一个死胡同了,还好,经过我的尝试,可以把chart.OLEFormat.Object转化成ChartControlActivex后继承的com组件接口,然后通过接口来传递这对难兄难弟,代码如下。

    ChartControlActivex的相关实现
    namespace ZedGraphActiveX
    {
        [Guid("5158491A-B243-42A6-8863-A2AE82CFDFDC")]
        [ComVisible(true)]
        public partial class ChartControlActivex : UserControl, IChartInfo
        {
            public ChartControlActivex()
            {
                InitializeComponent();
            }
            const int OLEMISC_RECOMPOSEONRESIZE = 1;
            const int OLEMISC_CANTLINKINSIDE = 16;
            const int OLEMISC_INSIDEOUT = 128;
            const int OLEMISC_ACTIVATEWHENVISIBLE = 256;
            const int OLEMISC_SETCLIENTSITEFIRST = 131072;
            [EditorBrowsable(EditorBrowsableState.Never)]
            [ComRegisterFunction()]
            public static void Register(Type t)
            {
                try
                {
                    // Open the CLSID key of the control
                    using (RegistryKey keyCLSID = Registry.ClassesRoot.OpenSubKey(
                        @"CLSID\" + t.GUID.ToString("B"), true))
                    {
                        RegistryKey subkey = null;
                        subkey = keyCLSID.OpenSubKey("InprocServer32", true);
                        if (subkey != null)
                            subkey.SetValue(null, Environment.SystemDirectory + @"\mscoree.dll");
                        using (subkey = keyCLSID.CreateSubKey("Control")) { };
                        using (subkey = keyCLSID.CreateSubKey("TypeLib"))
                        {
                            Guid libId = Marshal.GetTypeLibGuidForAssembly(t.Assembly);
                            subkey.SetValue("", libId.ToString("B"), RegistryValueKind.String);
                        }
                        using (subkey = keyCLSID.CreateSubKey("MiscStatus"))
                        {
                            int nMiscStatus = OLEMISC_RECOMPOSEONRESIZE +
                                OLEMISC_CANTLINKINSIDE + OLEMISC_INSIDEOUT +
                                OLEMISC_ACTIVATEWHENVISIBLE + OLEMISC_SETCLIENTSITEFIRST;
    
                            subkey.SetValue("", nMiscStatus.ToString(), RegistryValueKind.String);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message); // Log the error
                    throw;  // Re-throw the exception
                }
            }
    
            [EditorBrowsable(EditorBrowsableState.Never)]
            [ComUnregisterFunction()]
            public static void Unregister(Type t)
            {
                try
                {
                    Registry.ClassesRoot.DeleteSubKeyTree(@"CLSID\" + t.GUID.ToString("B"));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message); // Log the error
                    throw; // Re-throw the exception
                }
            }
    
            private string chartXml;
    
            public string ChartXml
            {
                get
                {
                    return chartXml;
                }
                set
                {
                    chartXml = value;
                    SetChartInfo(value);
                }
            }
    
            public bool IsSuccess { get; set; }
    
            public string ErrorMessage { get; set; }
    
            public void MouseWheelChange(int x, int y, int count)
            {
                MouseEventArgs args = new MouseEventArgs(System.Windows.Forms.MouseButtons.None, 0, x, y, count);
                object chartGraphic = this.zedGraphControl1;
                chartGraphic.InvokeMethod("ChartControl_MouseWheel", new object[] { null, args });
            }
    
            public void SetChartInfo(string value)
            {
                
                if (string.IsNullOrEmpty(value))
                    return;
                try
                {
                    XmlDocument xdoc = new XmlDocument();
                    xdoc.LoadXml(value);
                    XmlNode node = xdoc.ChildNodes[0];
                    //AddTraceToChart(node, zedGraphControl1);               
                    this.zedGraphControl1.AxisChange();
                    this.IsSuccess = true;
    
                }
                catch (Exception e)
                {
                    this.IsSuccess = false;
                    ErrorMessage = "Message:" + e.Message + "\n";
                    ErrorMessage += "StackTrace:" + e.StackTrace;
                    //Utility.Log(e);
                }
    
            }
        }
        public static class Helper
        {
            public static void SetValeuField(this object obj, string name, object setValue)
            {
                var field = obj.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                field.SetValue(obj, setValue);
            }
    
            public static object GetValeuField(this object obj, string name)
            {
                var field = obj.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                var result = field.GetValue(obj);
                return result;
            }
    
            public static object InvokeMethod(this object obj, string name, object[] parms)
            {
                var method = obj.GetType().GetMethod(name, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                var result = method.Invoke(obj, parms);
                return result;
            }
        }
    
        [Guid("FDE85CDF-3ER9-AS13-8B1A-DWE3A9930864")]
        public interface IChartInfo
        {
            #region Properties
    
            string ChartXml { get; set; }
    
            bool IsSuccess { get; set; }
    
            string ErrorMessage { get; set; }
    
            #endregion
    
            #region Methods
    
            void Refresh();                    // Typical control method  
    
            void MouseWheelChange(int x, int y, int count);
    
            #endregion
        }
    }

    word插件功能相关实现。

    	[GuidAttribute("5CCDCEDC-247B-436A-910F-B792738EC966"), ProgId("WordChartAddIn.Connect")]
    	public class Connect : Object, Extensibility.IDTExtensibility2
    	{
    		/// <summary>
    		///		Implements the constructor for the Add-in object.
    		///		Place your initialization code within this method.
    		/// </summary>
    		public Connect()
    		{
    		}
    
    		/// <summary>
    		///      Implements the OnConnection method of the IDTExtensibility2 interface.
    		///      Receives notification that the Add-in is being loaded.
    		/// </summary>
    		/// <param term='application'>
    		///      Root object of the host application.
    		/// </param>
    		/// <param term='connectMode'>
    		///      Describes how the Add-in is being loaded.
    		/// </param>
    		/// <param term='addInInst'>
    		///      Object representing this Add-in.
    		/// </param>
    		/// <seealso class='IDTExtensibility2' />
            public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
            {
                applicationObject = application;
                addInInstance = addInInst;
                App = application as Word.Application;
                if (App != null)
                {
                    App.WindowSelectionChange += new Word.ApplicationEvents4_WindowSelectionChangeEventHandler(app_WindowSelectionChange);
                    //App.DocumentBeforeClose += new Word.ApplicationEvents4_DocumentBeforeCloseEventHandler(App_DocumentBeforeClose);
                    //App.DocumentBeforeSave += new Word.ApplicationEvents4_DocumentBeforeSaveEventHandler(App_DocumentBeforeSave);
                    MouseHookProcedure = new HookProc(MouseHookProc);
                    hHook = SetWindowsHookEx(WH_MOUSE,//WH_MOUSE_LL,
                        MouseHookProcedure,
                        (IntPtr)0,
                        AppDomain.GetCurrentThreadId());
                }
            }
            private IChartInfo selectActiveXCtrl;
    
            void app_WindowSelectionChange(Word.Selection Sel)
            {
                selectActiveXCtrl = null;
                if (Sel.Type == Word.WdSelectionType.wdSelectionInlineShape)
                {
                    //Thread.Sleep(100);            
                    Word.InlineShape shape = Sel.InlineShapes[1];
                    IChartInfo cxc = null;
                    if (shape == null)
                        return;
                    if (shape.OLEFormat != null)
                    {
                        cxc = shape.OLEFormat.Object as IChartInfo;
                        if (cxc != null)
                        {
                            selectActiveXCtrl = cxc;
                            return;
                        }
                    }
                    string panes = shape.AlternativeText;
                    if (!string.IsNullOrEmpty(panes))
                    {
                       // SettingGlobe(panes);
                        Word.InlineShape chart = Sel.InlineShapes.AddOLEControl("ZedGraphActiveX.ChartControlActivex", shape.Range);
                        object oChart = chart.OLEFormat.Object;
                        cxc = oChart as IChartInfo;
                        if (cxc != null)
                        {
                            selectActiveXCtrl = cxc;
                            cxc.ChartXml = panes;
                            if (cxc.IsSuccess)
                            {
                                shape.ConvertToShape().Visible = Office.MsoTriState.msoFalse;
                            }
                            else
                            {
                                //System.Windows.Forms.MessageBox.Show(cxc.ErrorMessage);
                                System.Windows.Forms.MessageBox.Show("请检查是否已经安装相关插件或者联系相关人员.");
                                chart.Delete();
                            }
                        }
                    }
    
                }
            }
            public Word.Application App { get; set; }
    		/// <summary>
    		///     Implements the OnDisconnection method of the IDTExtensibility2 interface.
    		///     Receives notification that the Add-in is being unloaded.
    		/// </summary>
    		/// <param term='disconnectMode'>
    		///      Describes how the Add-in is being unloaded.
    		/// </param>
    		/// <param term='custom'>
    		///      Array of parameters that are host application specific.
    		/// </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)
    		{
                if (hHook == 0)
                    return;
                bool ret = UnhookWindowsHookEx(hHook);
                hHook = 0;
    		}
    
    		/// <summary>
    		///      Implements the OnAddInsUpdate method of the IDTExtensibility2 interface.
    		///      Receives notification that the collection of Add-ins has changed.
    		/// </summary>
    		/// <param term='custom'>
    		///      Array of parameters that are host application specific.
    		/// </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnAddInsUpdate(ref System.Array custom)
    		{
    		}
    
    		/// <summary>
    		///      Implements the OnStartupComplete method of the IDTExtensibility2 interface.
    		///      Receives notification that the host application has completed loading.
    		/// </summary>
    		/// <param term='custom'>
    		///      Array of parameters that are host application specific.
    		/// </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnStartupComplete(ref System.Array custom)
    		{
    		}
    
    		/// <summary>
    		///      Implements the OnBeginShutdown method of the IDTExtensibility2 interface.
    		///      Receives notification that the host application is being unloaded.
    		/// </summary>
    		/// <param term='custom'>
    		///      Array of parameters that are host application specific.
    		/// </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnBeginShutdown(ref System.Array custom)
    		{
    		}
    		
    		private object applicationObject;
    		private object addInInstance;
    
            #region HOOK
    
            public int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                if (nCode > 0)
                {
                    int message = wParam.ToInt32();
                    if (message == WM_MOUSEWHEEL && selectActiveXCtrl != null)
                    {
                        MouseLLHookStruct mousellHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
                        int hwnd = mousellHookStruct.mouseData;
                        RECT rect = new RECT();
                        GetWindowRect(hwnd, ref rect);
                        if (mousellHookStruct.pt.X > rect.Left && mousellHookStruct.pt.X < rect.Right &&
                            mousellHookStruct.pt.Y > rect.Top && mousellHookStruct.pt.Y < rect.Bottom)
                        {
                            StringBuilder sbTitle = new StringBuilder(265);
                            GetWindowText(hwnd, sbTitle, 256);
                            StringBuilder sbClass = new StringBuilder(265);
                            RealGetWindowClass(hwnd, sbClass, 256);
    
                            int count = (short)((mousellHookStruct.dwExtraInfo >> 16) & 0xffff);
                            string str_Title = sbTitle.ToString();
                            string str_Class = sbClass.ToString();
                            if (string.IsNullOrEmpty(str_Title) &&
                                str_Class.Contains("WindowsForms10.Window.8.app"))
                            {
                                int x = mousellHookStruct.pt.X - rect.Left;
                                int y = mousellHookStruct.pt.Y - rect.Top;
                                selectActiveXCtrl.MouseWheelChange(x, y, count);
                                return 1;
                            }
                        }
    
                    }
                }
                return CallNextHookEx(hHook, nCode, wParam, lParam);
            }
    
            public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
    
            //Declare the hook handle as an int.
            private int hHook = 0;
    
            //Declare the mouse hook constant.
            //For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
            private const int WH_MOUSE = 7;
    
            private const int WM_MOUSEWHEEL = 0x020A;
    
            //Declare MouseHookProcedure as a HookProc type.
            HookProc MouseHookProcedure;
    
            //Declare the wrapper managed POINT class.
            [StructLayout(LayoutKind.Sequential)]
            public class POINT
            {
                public POINT(int _x, int _y)
                {
                    X = _x;
                    Y = _y;
                }
                public int X;
                public int Y;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public RECT(int _left, int _top, int _right, int _bottom)
                {
                    Left = _left;
                    Top = _top;
                    Right = _right;
                    Bottom = _bottom;
                }
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
            //Declare the wrapper managed MouseHookStruct class.
            [StructLayout(LayoutKind.Sequential)]
            public class MouseHookStruct
            {
                public POINT pt;
                public int hwnd;
                public int wHitTestCode;
                public int dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            private class MouseLLHookStruct
            {
                public POINT pt;
                public int mouseData;
                public int flags;
                public int time;
                public int dwExtraInfo;
            }
            //This is the Import for the SetWindowsHookEx function.
            //Use this function to install a thread-specific hook.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
             CallingConvention = CallingConvention.StdCall)]
            public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
            IntPtr hInstance, int threadId);
    
            //This is the Import for the UnhookWindowsHookEx function.
            //Call this function to uninstall the hook.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
             CallingConvention = CallingConvention.StdCall)]
            public static extern bool UnhookWindowsHookEx(int idHook);
    
            //This is the Import for the CallNextHookEx function.
            //Use this function to pass the hook information to the next hook procedure in chain.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
             CallingConvention = CallingConvention.StdCall)]
            public static extern int CallNextHookEx(int idHook, int nCode,
            IntPtr wParam, IntPtr lParam);
    
            //根据点查找窗口
            [DllImport("user32.dll", CharSet = CharSet.Auto,
             CallingConvention = CallingConvention.StdCall)]
            public static extern int WindowFromPoint(
                POINT Point
                );
    
            [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
            public static extern int GetWindowText(
                int hWnd,
                StringBuilder lpString,
                int nMaxCount
                );
    
            //获得窗口类名称,返回值为字符串的字符数量
            [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
            public static extern uint RealGetWindowClass(
                int hWnd,
                StringBuilder pszType,		//缓冲区
                uint cchType);				//缓冲区长度
    
            [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
            public static extern bool GetWindowRect(
                int hWnd,
                ref RECT lpRect
                );
            #endregion
    	}

    我是把信号的信息放进XML文件里,并保存在图片的AlternativeText属性上,然后点击图片时,会查看是否有信息,会尝试生成一个activex chart控件,设置它ChartXml值,这个值会调用SetChartInfo方法来给zedGraphControl的各个信息来设置属性,然后刷新,填加新的一些处理,比如信号的转换什么的,有一点要注意的是,如果我们写一些接口,只是在activex里用,但是用到.net dll的一些控件,可能会提示我们编译不过,尝试在对应接口加上[ComVisible(false)]特性。现在的效果图如下。

    bbbbb

    好像有点长了,有时间我再说下activex 控件安装打包要注意的地方与上述的一个钩子函数,这个钩子函数主要是为了让鼠标在activex 控件里滚动时,不要让word也跟着滚动。

  • 相关阅读:
    PTA(Advanced Level)1037.Magic Coupon
    PTA(Advanced Level)1033.To Fill or Not to Fill
    PTA(Basic Level)1020.月饼
    PTA(Advanced Level)1048.Find Coins
    PTA(Advanced Level)1050.String Subtraction
    PTA(Advanced Level)1041.Be Unique
    PTA(Basci Level)1043.输出PATest
    PTA(Basic Level)1039.到底买不买
    PTA(Basic Level)1033.旧键盘打字
    PTA(Advanced Level)1083.List Grades
  • 原文地址:https://www.cnblogs.com/zhouxin/p/2531438.html
Copyright © 2011-2022 走看看