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”。
详见我的优快云问题贴:http://topic.youkuaiyun.com/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
转载于:http://www.cnblogs.com/xuezhizhang/archive/2010/11/25/1887735.html
文章结尾分享一下优快云上ActiveX控件开发的讨论帖:
http://topic.youkuaiyun.com/u/20101028/11/085a5e12-ab46-4de5-8860-07234638b57b.html
本文介绍如何使用C#开发ActiveX控件实现网页截图与打印功能,包括实现步骤、注意事项以及代码示例。重点在于解决在页面后台实现截图打印时遇到的问题,如GDI+错误和页面动态内容影响截图效果,并通过ActiveX控件提供更稳定的截图与打印解决方案。

5527

被折叠的 条评论
为什么被折叠?



