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也跟着滾動。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM