C#实现的ActiveX截图打印控件

http://www.cnblogs.com/xuezhizhang/archive/2010/11/25/1887735.html

电子证书签名及CAB包制作工具下载

 

 C#开发ActiveX控件参考资料:

  http://www.cnblogs.com/zhf/archive/2009/03/02/1401299.html

  http://www.cnblogs.com/homer/archive/2005/01/08/88780.html

 

C#开发ActiveX的详细介绍见以上两篇文章,我只补充这两篇文章未说明的。

1.实现脚本调用ActiveX方法,必需将方法定义到一个接口中(该接口使用一个唯一的GUID,不能与ActiveX控件的GUID相同),然后在ActiveX方法即控件类中实现该接口中的方法。

2.在ActiveX中使用WebBrowser控件,不可直接从工具箱中拖出该控件使用,只需声明一个WebBrowser对象,在需要使用的时候实例化即可。不然在ActiveX中无法正常使用,即使在本地测试的时候没有问题,在页面中使用你会发现WebBrowser对象已经被释放。

3.在Windows 7下,必需以管理员权限运行VS,设置的COM自动注册才能顺利完成。

 

以我的代码为例:

   
   
代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices;
namespace EstatePrintMapActiveX {     //控件类,GUID可以在工具中生成,此处的GUID即为页面中引用控件时使用的CLASSID     //ComVisible(true)设置是否自动注册控件,设置项目属性中的“生成”项下的“为COM互操作注册”为勾选实现在本地自动注册(windows7需要以管理员运行该项目)     [Guid("1161E5A8-B698-4e57-8B56-B5B06B69FF4D"), ProgId("EstatePrintMapActiveX.UserControl1"),     ClassInterface(ClassInterfaceType.None), ComVisible(true)]     public partial class UserControl1 : UserControl, IMonitor, IObjectSafety     {         #region 实现IObjectSafety 成员
        public void GetInterfacceSafyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions)         {             pdwSupportedOptions = 1;             pdwEnabledOptions = 2;         }
        public void SetInterfaceSafetyOptions(int riid, int dwOptionsSetMask, int dwEnabledOptions)         {             throw new NotImplementedException();         }
        #endregion
        System.Windows.Forms.WebBrowser webBrowser;
        bool isDetailPrint = false;//默认打印多张         string stringUrl;//需要打印的页面的URL
        public UserControl1()         {             InitializeComponent();         }
        //供JS调用的方法(必需在IMonitor接口中定义,接口名可自定)         public void SetUrlText(string strUrl, string pra)         {             if (pra == "1")//多图页面,打印多张             {                 stringUrl = strUrl;                 btnCapture.Text = "启动楼层图打印控件";//通过脚本参数改变控件按钮文字                 SetWebBrowser();             }             else if (pra == "2")             {                 stringUrl = strUrl;                 SetWebBrowser();                 isDetailPrint = true;//详细页面,打印单张             }             else             {   //无平面图信息则隐藏控件                 this.Visible = false;                 this.Height = 0;                 this.Width = 0;                 Dispose(true);             }         }
        //声明webBrowserObj并初始化         private void SetWebBrowser()         {             try             {                 WebBrowser webBrowserObj = new WebBrowser();                 webBrowserObj.Dock = System.Windows.Forms.DockStyle.Fill;                 webBrowserObj.Location = new System.Drawing.Point(0, 0);                 webBrowserObj.MinimumSize = new System.Drawing.Size(20, 20);                 webBrowserObj.Name = "webBrowser";                 webBrowserObj.ScrollBarsEnabled = false;//不显示滚动条                 webBrowserObj.Size = new System.Drawing.Size(1050, 97);                 webBrowserObj.TabIndex = 0;                 webBrowserObj.Navigating += new System.Windows.Forms.WebBrowserNavigatingEventHandler(this.webBrowser_Navigating);//加载事件                 webBrowserObj.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webBrowser_DocumentCompleted);//加载完成事件
                webBrowser = webBrowserObj;
                webBrowser.Navigate(stringUrl);//加载页面             }             catch (Exception ex)             {                 MessageBox.Show(ex.Message);             }         }
        //截图方法         private void btnCapture_Click(object sender, EventArgs e)         {             try             {                 //截图                 Bitmap bmp = Snapshot.TakeSnapshot(this.webBrowser.ActiveXInstance, new Rectangle(new Point(), webBrowser.Size));
                生成图片的方法,imgName为图片完整路径(含文件名)                 //bmp.Save(imgName, System.Drawing.Imaging.ImageFormat.Bmp);                 //bmp.Dispose();
                Form printView = new Form1(bmp, isDetailPrint);                 printView.Show();//在弹出的Form中显示截图及“打印”、“关闭”按钮
            }             catch (Exception ex)             {                 MessageBox.Show(ex.Message);             }
        }
        //加载完成记录高度和宽度、启动按钮可用         private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)         {             btnCapture.Enabled = true;//“启动打印控件”按钮可用
            //设置WebBrowser的Size,之后调用截图方法时要使用该Size以保证截的图为完整的             webBrowser.Size = new Size(int.Parse(webBrowser.Document.Body.GetAttribute("scrollwidth")),                                       int.Parse(webBrowser.Document.Body.GetAttribute("scrollHeight")));
        }
        //开始加载页面         private void webBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)         {             btnCapture.Enabled = false;//初始加载时“启动打印控件”按钮不可用         }
    } }
 
其中脚本方法接口和安全接口定义如下: 
 
代码
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Runtime.InteropServices;
namespace EstatePrintMapActiveX {     [Guid("8FF612BC-4386-4629-B81A-E84AD7CB7AE7"), InterfaceType(ComInterfaceType.InterfaceIsDual), ComVisible(true)]     public interface IMonitor     {         void SetUrlText(string strUrl, string pra);     }
    [Serializable, ComVisible(true)]     public enum ObjectSafetyOptions     {         INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,         INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,         INTERFACE_USES_DISPEX = 0x00000004,         INTERFACE_USES_SECURITY_MANAGER = 0x00000008     };
    MS IObjectSafety Interface definition     //[ComImport(), Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]     //public interface IObjectSafety     //{     //    [PreserveSig]     //    long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);
    //    [PreserveSig]     //    long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);     //};
    [ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]     public interface IObjectSafety     {         [PreserveSig]         void GetInterfacceSafyOptions(             int riid,             out int pdwSupportedOptions,             out int pdwEnabledOptions);
        [PreserveSig]         void SetInterfaceSafetyOptions(             int riid,             int dwOptionsSetMask,             int dwEnabledOptions);     }
}
 

截图方法(引自随飞:http://chinasf.cnblogs.com):  

代码
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes;
namespace EstatePrintMapActiveX {     /// <summary>     /// ActiveX 组件快照类     /// AcitveX 必须实现 IViewObject 接口     /// 作者:随飞     /// http://chinasf.cnblogs.com     /// chinasf@hotmail.com     /// </summary>     public static class Snapshot     {         /// <summary>         /// 取快照         /// </summary>         /// <param name="pUnknown">Com 对象</param>         /// <param name="bmpRect">图象大小</param>         /// <returns></returns>         public static Bitmap TakeSnapshot(object pUnknown, Rectangle bmpRect)         {             if (pUnknown == null)                 return null;
            //必须为com对象             if (!Marshal.IsComObject(pUnknown))                 return null;
            //IViewObject 接口             UnsafeNativeMethods.IViewObject ViewObject = null;             IntPtr pViewObject = IntPtr.Zero;             //内存图             Bitmap pPicture = new Bitmap(bmpRect.Width, bmpRect.Height);             Graphics hDrawDC = Graphics.FromImage(pPicture);             //获取接口             object hret = Marshal.QueryInterface(Marshal.GetIUnknownForObject(pUnknown),                 ref UnsafeNativeMethods.IID_IViewObject, out pViewObject);
            try             {                 ViewObject = Marshal.GetTypedObjectForIUnknown(pViewObject, typeof(UnsafeNativeMethods.IViewObject)) as UnsafeNativeMethods.IViewObject;                 //调用Draw方法                 ViewObject.Draw((int)DVASPECT.DVASPECT_CONTENT,                     -1,                     IntPtr.Zero,                     null,                     IntPtr.Zero,                     hDrawDC.GetHdc(),                     new NativeMethods.COMRECT(bmpRect),                     null,                     IntPtr.Zero, );                 Marshal.Release(pViewObject);             }             catch (Exception ex)             {                 Console.WriteLine(ex.Message);                 throw ex;             }
            //释放             hDrawDC.Dispose();             return pPicture;         }     }
}
 

打印方法: 

代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms;
namespace EstatePrintMapActiveX {     public partial class Form1 : Form     {         int PageCount;         int pageCountTmp;         bool isDetailPrint = false;         public Form1(Bitmap bmp, bool isDetailPrintPar)         {             InitializeComponent();
            isDetailPrint = isDetailPrintPar;
            Image imgObj = (Image)bmp;             pictureBox1.Height = imgObj.Height;             pictureBox1.Width = imgObj.Width;
            panel1.Width = pictureBox1.Width;             panel1.Height = pictureBox1.Height;
            this.Width = pictureBox1.Width;             this.Height = pictureBox1.Height;
            panel1.Visible = true;
            pictureBox1.Image = imgObj;             PageCount = pictureBox1.Image.Height / 1500;             pageCountTmp = PageCount;
            btn.Visible = true;             btnClose.Visible = true;         }
        //打印过程中调用         private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)         {             if (!isDetailPrint)//多页             {                 if (PageCount >= 1)                 {                     e.HasMorePages = true;//分页设置                     e.Graphics.DrawImage(pictureBox1.Image,                                         new RectangleF(new Point(0, 20),                                             new Size(1050, 1500)),                                         new RectangleF(new Point(0, (pageCountTmp - PageCount) * 1500),                                             new Size(1050, 1500)),                                         GraphicsUnit.Pixel);
                    if (PageCount == 1)                     {                         e.HasMorePages = false;//结束分页及打印                     }                 }                 PageCount--;//计数             }             else//单页             {                 e.Graphics.DrawImage(pictureBox1.Image, new Rectangle(new Point(0, 20),                     new Size(int.Parse(Math.Round(pictureBox1.Image.Width * 0.8).ToString()),                             int.Parse(Math.Round(pictureBox1.Image.Height * 0.8).ToString()))));             }         }
        //关闭按钮事件         private void btnClose_Click(object sender, EventArgs e)         {             this.Close();             this.Dispose();         }
        //打印按钮事件:调用打印方法         private void btn_Click(object sender, EventArgs e)         {             printDocument1.Print();             this.Visible = false;             this.Height = 0;             this.Width = 0;             Dispose(true);         }     } }
 
 如果想不使用ActiveX实现客户端截图打印,而是在页面后台接收需要打印的页面URL,使用WebBrowser的DrawToBitmap方法实现截图,会遇到两个问题。

即:“GDI+中发生一般性错误”、“对COM 组件的调用返回了错误 HRESULT E_FAIL”。

  详见我的CSDN问题贴:http://topic.csdn.net/u/20101012/18/a750440e-f472-497f-b666-d1b420ae8f7c.html

  另外,当你需要打印的页面中有VML图或者AJAX效果,或者在页面加载完成之后使用脚本修改过页面的效果的时候,后台方法中的Complete状态判断并不能保证截图时页面已经是最终的显示效果,所以截图效果会与页面的实践显示效果有较大差异,甚至截图为空白。

  而使用ActiveX控件,当页面加载完时“启动控件按钮”可用,单击按钮之后触发截图方法,在客户端完成截图和打印(不生成图片且截图与页面实际显示的效果一致),同时还可可避免以上问题。

  补充:

  被截图的页面必需是不需要登录即可查看的页面;

  该控件在客户端运行,需要.NET Framework的支持, 最好做成基于.NET Framework2.0,因为高版本的.NET Framework文件很大;

  本控件的实现初衷是为了打印页面中VML实现的图,因为VML不支持web打印,且实际验证发现jQuery的区域打印即类似$("div#printAreaDiv").printArea();这样的jQuery方法打印的是整个页面,且与页面实际显示的效果有差异(页面中有AJAX或者页面加载完之后有使用脚本对页面进行过修改的情况);

  关于ActiveX调用脚本方法,可参考:http://www.cnblogs.com/xiaoshatian/archive/2008/09/02/1281786.html

  关于怎么制作安装文件,本篇文章给出的第一个链接里“5.发布”部分有详细的介绍;  

  控件使用示例:

HTML:

< div > < object id ="UserControlObj" name ="UserControlObj" classid ="clsid:1161E5A8-B698-4E57-8B56-B5B06B69FF4D" width ="950" height ="55" > </ object > </ div >
 
 

脚本:

var strUrl = "" ; var strPar = " 2 " ; strUrl = window.location.href.split( ' = ' )[ 0 ].replace( " RoomInfoDetail.aspx?&RM_ID " , " RoomInfoPrint.aspx?rid= " + window.location.search.split( ' = ' )[ 1 ]); document.all.UserControlObj.SetUrlText(strUrl, strPar);
 
 控件代码已经打包上传,下载链接:EstatePrintMapActiveX.rar

 

  文章结尾分享一下CSDN上ActiveX控件开发的讨论帖:

  http://topic.csdn.net/u/20101028/11/085a5e12-ab46-4de5-8860-07234638b57b.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值