zoukankan      html  css  js  c++  java
  • C#实现的ActiveX截图打印控件

     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,
    0);
    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

  • 相关阅读:
    无法启动IIS Express Web服务器 端口"1025"正在使用
    mysql查看是否锁表并解除锁
    C#的栈
    面向对象VS面向过程
    Visual Studio包管理器NuGet 依赖管理
    Vue中的Promise.all()
    【1024练习】Mybatis练习三
    【1023练习】MyBatis练习二
    【练习】mybatis
    【练习】json数组,以及json对象数组循环在页面的选择下拉框,ul列表,table等中显示出来
  • 原文地址:https://www.cnblogs.com/xuezhizhang/p/1887735.html
Copyright © 2011-2022 走看看