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

13 篇文章 0 订阅

 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

 

 

  本文章首发于我的博客园博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值