虽然说使在IE上直接调用打印插件打印已经不常用,但是有时候还是会用到,这里就记录一下。
首先我们列出来我们的打印类
1 public class PrintService
2 {
3 //打印机名称
4 private static readonly string _printerName = "";
5 //业务名称
6 private static string _printStr = "";
7
8 /// <summary>
9 /// 打印
10 /// </summary>
11 /// <param name="printStr"></param>
12 /// <returns>0:success;1:未找到打印机;2:fail</returns>
13 public static int PrintNo(string printStr)
14 {
15 try
16 {
17 _printStr = printStr;
18 PrintDocument pd = new PrintDocument();
19 //取消打印弹窗(PrintDocment默认的PrintController 是PrintControllerWithStatusDialog)
20 pd.PrintController = new StandardPrintController();
21 //pd.PrinterSettings.PrinterName = "打印机的名字";
22 //打印机名称可以不配置,取默认打印机
23 pd.DocumentName = string.Format("取号{0:yyyyMMddHHmmss}", DateTime.Now);
24 pd.OriginAtMargins = true;
25 pd.DefaultPageSettings.Margins = new Margins(0, 0, 0, 20);
26 //打印开始前
27 pd.BeginPrint += new PrintEventHandler(pd_BeginPrint);
28 //打印输出(过程)
29 pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
30 //打印结束
31 pd.EndPrint += new PrintEventHandler(pd_EndPrint);
32 pd.Print();
33
34 return 0;
35 }
36 catch (Exception ex)
37 {
38 NLog.Error(ex.ToString());
39 return 2;
40 }
41 }
42
43 private static void pd_EndPrint(object sender, PrintEventArgs e)
44 {
45 //打印结束后相关操作
46 }
47
48 private static void pd_PrintPage(object sender, PrintPageEventArgs e)
49 {
50 try
51 {
52 int pagewidth = e.MarginBounds.Width;
53 float y = 0;
54 _title = _title ?? string.Empty;
55 int titlerowspan = (_title.Length / 11) + 1;
56 y = CreateRectangleF(0, y, pagewidth, e.Graphics.MeasureString("-", new Font("微软雅黑", 14, FontStyle.Bold)).Height, "------------------------------------", new Font("微软雅黑", 14, FontStyle.Bold), e);
57 y = CreateRectangleF(0, y, pagewidth, e.Graphics.MeasureString("中A1", new Font("宋体", 11, FontStyle.Bold)).Height, _printStr, new Font("宋体", 11, FontStyle.Bold), e);
58 }
59 catch (InvalidPrinterException ex)
60 {
61 return;
62 }
63 }
64
65 private static void pd_BeginPrint(object sender, PrintEventArgs e)
66 {
67 //也可以把一些打印的参数放在此处设置;
68 }
69
70 /// <summary>
71 /// 绘制一个矩形区域文本文字
72 /// </summary>
73 private static float CreateRectangleF(float x, float y, float width, float height, string str, Font font, PrintPageEventArgs e)
74 {
75 StringFormat sf = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
76 //RectangleF rect = new RectangleF(x, y, width, height);
77 var size = MeasureStringExtend(e.Graphics, str, font, (int)width);
78 RectangleF rect = new RectangleF(x,y,size.Width, size.Height);
79 e.Graphics.DrawString(str, font, System.Drawing.Brushes.Black, rect, sf);
80 return rect.Bottom;
81 }
82 //多行文本自动换行
83 public static SizeF MeasureStringExtend(Graphics g, string text, Font font, int desWidth)
84 {
85 string tempString = text;
86 string workString = "";//当前打印的文本
87 int npos = 1;//文字个数
88 int sp_pos = 0;//
89 int line = 0;//所在行数
90 int nWidth = 0;
91
92 //获取文字的Size对象
93 SizeF size = g.MeasureString(text, font);
94 //多行文本:文本的宽度大于纸张的宽度
95 if (size.Width > desWidth)
96 {
97 while (tempString.Length > 0)
98 {
99 //判断最后一行
100 if (npos > tempString.Length)
101 {
102 line++;
103 break;
104 }
105 //截取npos个文字,用于检测是否超出纸张宽度
106 workString = tempString.Substring(0, npos);
107 //获取当前文本的宽度
108 nWidth = (int)g.MeasureString(workString, font).Width;
109 //仍然大于纸张宽度
110 if (nWidth > desWidth)
111 {
112 //最后一个空格的位置
113 sp_pos = workString.LastIndexOf(" ");
114 if (sp_pos > 0)
115 {
116 //从空格后一个字符作为起点,继续循环,可用于去除空格。这里并没有这么做。
117 tempString = tempString.Substring((sp_pos + 1), tempString.Length - (sp_pos + 1));
118 line++;
119 npos = 0;
120 }
121 else //没有空格
122 {
123 tempString = tempString.Substring(npos, tempString.Length - npos);
124 line++;
125 npos = 0;
126 }
127 }
128 npos++;
129 }
130 return new SizeF(desWidth, (line * size.Height));
131 }
132 else
133 return size;
134
135 }
136 }
View Code
调用的时候
PrintSrivce.PrintNo(“这里是打印的文字内容,可以写很多,它会自动换行哦”);
为了做成Acivex插件,所以这里我们这么做:
1 [Guid("F83174A0-087D-4634-B6AE-0094CD392FD1")]//IE浏览器通过这个GUID可以查到打印方法,调用打印方法,打印。
2 public class PrinterHelper:ActiveXControl
3 {
4 public int Printing(string printStr)
5 {
6 return PrintService.PrintNo(printStr);
7 }
8 }
下面是打印的HTML页面
<object id="printer-ActiveX" classid="clsid:F83174A0-087D-4634-B6AE-0094CD392FD1" codebase="Printer.CAB#version=1,0,0" style="display: none;"></object>
1 var printerActiveX = document.getElementById('printer-ActiveX');
2 printerActiveX.Printing('这里是打印的文字内容,可以写很多,它会自动换行哦');
为什么这样就可以打印了呢?
其实之前省略了如何制作Acivex的步骤,下面介绍一下:
一、IObjectSafety接口
为了让ActiveX控件获得客户端的信任,控件类还需要实现一个名为“IObjectSafety”的接口。先创建该接口(注意,不能修改该接口的GUID值),接口内容如下:
1 ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
2 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
3 public interface IObjectSafety
4 {
5 [PreserveSig]
6 int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);
7
8 [PreserveSig()]
9 int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
10 }
二、ActiveXControl控件基类
1 public abstract class ActiveXControl : IObjectSafety
2 {
3 #region IObjectSafety 成员
4
5 private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
6 private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
7 private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
8 private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
9 private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
10
11 private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
12 private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
13 private const int S_OK = 0;
14 private const int E_FAIL = unchecked((int)0x80004005);
15 private const int E_NOINTERFACE = unchecked((int)0x80004002);
16
17 private bool _fSafeForScripting = true;
18 private bool _fSafeForInitializing = true;
19
20
21 public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
22 {
23 int Rslt = E_FAIL;
24
25 string strGUID = riid.ToString("B");
26 pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
27 switch (strGUID)
28 {
29 case _IID_IDispatch:
30 case _IID_IDispatchEx:
31 Rslt = S_OK;
32 pdwEnabledOptions = 0;
33 if (_fSafeForScripting == true)
34 pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
35 break;
36 case _IID_IPersistStorage:
37 case _IID_IPersistStream:
38 case _IID_IPersistPropertyBag:
39 Rslt = S_OK;
40 pdwEnabledOptions = 0;
41 if (_fSafeForInitializing == true)
42 pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
43 break;
44 default:
45 Rslt = E_NOINTERFACE;
46 break;
47 }
48
49 return Rslt;
50 }
51
52 public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
53 {
54 int Rslt = E_FAIL;
55
56 string strGUID = riid.ToString("B");
57 switch (strGUID)
58 {
59 case _IID_IDispatch:
60 case _IID_IDispatchEx:
61 if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
62 (_fSafeForScripting == true))
63 Rslt = S_OK;
64 break;
65 case _IID_IPersistStorage:
66 case _IID_IPersistStream:
67 case _IID_IPersistPropertyBag:
68 if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
69 (_fSafeForInitializing == true))
70 Rslt = S_OK;
71 break;
72 default:
73 Rslt = E_NOINTERFACE;
74 break;
75 }
76
77 return Rslt;
78 }
79
80 #endregion
81 }
View Code
PrinterHelper类,并在这个类库的属性里设置“使程序集COM可见”。
四、使用VS2012发布
具体步骤如下:
1、
2、
3、
1)设置为简体中文,否则安装路径有中文的话就会出问题
2)设置默认安装路径
3)修改默认字体
4)每次升级,重新打包,只需要点击这一行右侧的“...”按钮,就会重新生成Code,安装时就会自动覆盖老版本
5)我们把INSTALLDIR里面改写为[ProgramFilesFolder],方便后面我们使用CAB打包
4、
5、这一步很重要,在主输出上点右键属性
6、点击“6 Prepare for Release”下方的Release
注意点:该解决方案需要以管理员身份运行。
生成两个文件:
PrinterActiveX.MSI和Data1.cab
剩下一步就是签名了。
makecab.exe /f "cab.ddf"
signtool sign -f mantishell.pfx -p mantishell PrinterActiveX.CAB
.OPTION EXPLICIT
.Set Cabinet=on
.Set Compress=on
.Set MaxDiskSize=CDROM
.Set ReservePerCabinetSize=6144
.Set DiskDirectoryTemplate="."
.Set CompressionType=MSZIP
.Set CompressionLevel=7
.Set CompressionMemory=21
.Set CabinetNameTemplate="PrinterActiveX.CAB"
"installer.inf"
"PrinterActiveX.MSI"
"Data1.cab"
1、生成公钥文件
makecert -# 01 -r -n "CN=xxxx公司,O=xxxx,OU=xxxx,C=China,S=NanJing" -e 08/07/2019 -sky signature -sv mantishell.pvk d:\mantishell.cer
-e表示截至时间:如果去掉截至时间到2039年
私钥密码设置为:mantishell
2、生成证书
pvk2pfx.exe /pvk mantishell.pvk /pi mantishell /spc mantishell.cer /pfx mantishell.pfx /f
makecert -# 01 -r -n "CN=xxxx公司,O=xxxx公司,OU=xxxx公司,C=China,S=Nanjing" -sky signature -sv d:\ActiveX\CAB\mantishell.pvk d:\ActiveX\PrinterActiveX\CAB\mantishell.cer