您的位置:首页 > 其它

直接使用默认设置进行打印:(3)使用VC开发ActiveX控件实现打印中文字符串

2016-07-19 18:18 676 查看
        使用过ScriptX的用户,都会对这个强大好用的IE加载项留下深刻的印象。但是ScriptX毕竟是一个商业软件,在使用时会受到很多限制。我们能否自行开发一个类似的打印控件呢?虽然对打印技术缺乏深入的了解,但还是有可能开发出一个很简单的打印控件。本文在上一篇博文的基础上,介绍一个简单的ActiveX打印字符串控件的实现方法。尽管这里实现的打印控件在功能上远不及ScriptX那么全面,但是通过实现它,可以让我们对通过ActiveX控件方式实现打印增加一些了解。

        对于使用VC开发ActiveX控件,网上有以下博文讲解得很清晰,适合入门者阅读:
http://blog.csdn.net/longhuahaha/article/details/8556964
http://www.cnblogs.com/qguohog/archive/2013/01/22/2871805.html

        本文也是在参考这些博文的基础上,照猫画虎,实现了一个简单的ActiveX打印字符串控件,在这里把过程记录如下:

        在VS 2010中,新建项目,类型为Visual C++下面的MFC、MFC ActiveX控件,这里假定要创建的控件名称为 myPrintOcx,为它选择一个保存位置,点击“确定”按钮,如下图:



        随后会弹出MFC ActiveX控件向导,接下来的操作如下图:









        项目创建完毕后,在“解决方案资源管理器”子窗口中,可以看到以下内容:

 


        为了让生成的控件能够在没有安装VS 2010动态库的用户计算机上也能运行,用鼠标右键单击项目名称“myPrintOcx”,在弹出菜单中选择“属性”,在属性页中,将“MFC的使用”设置为“在静态库中使用MFC”,如下图:



        接下来切换到“类视图”,在“_DmyPrintOcx”上点击鼠标右键,在弹出菜单中选择“添加”->“添加方法”,如下图:



       

        “添加方法向导”对话框的显示如下图:

 


        点击“添加”按钮,依次增加 PrintData( ) 定义中包含的参数,如下图:

 


        注意PrintData( ) 包含的最后一个参数 strToBePrinted 类型是 LPCTSTR,在下拉列表框中并没有这个类型,此时应将该参数的类型设为 BSTR。全部参数输入完毕后,点击“完成”按钮。切换到“解决方案资源管理器”,双击“源文件”下面的“myPrintOcxCtrl.cpp”,打开该文件,找到方法定义

void CmyPrintOcxCtrl::PrintData (LONG left, LONG top, LONG right, LONG bottom, LPCTSTR strToBePrinted)

        如下图所示:

 


        在“// TODO: 在此添加调度处理程序代码”这一行后面,把上一篇博文中介绍的 PrintData ( )函数的实现部分代码拷贝过来。如下图:

 


        在“解决方案资源管理器”子窗口的“myPrintOcx”上点击右键,在弹出菜单中选择“生成”,如下图:

 


        结果报错,原因在于权限不足。关闭Visual Studio 2010,然后以管理员权限运行它,打开解决方案“myPrintOcx”,再次尝试生成,这次就能成功了。

        接下来实现安全接口,都是按照博客:
http://www.cnblogs.com/qguohog/archive/2013/01/22/2871805.html

上面介绍的方法来实现的。先在项目中添加Cathelp.h和Cathelp.cpp两个文件。

Cathelp.h 文件内容:

#include "comcat.h"

// Helper function to create a component category and associated
// description
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);

// Helper function to register a CLSID as belonging to a component
// category
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

// HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

Cathelp.cpp 文件内容:

#include "stdafx.h"
#include "comcat.h"
#include "strsafe.h"
#include "objsafe.h"

// HRESULT CreateComponentCategory - Used to register ActiveX control as safe
HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription)
{
ICatRegister *pcr = NULL ;
HRESULT hr = S_OK ;

hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (FAILED(hr))
return hr;

// Make sure the HKCR\Component Categories\{..catid...}
// key is registered.
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // english
size_t len;
// Make sure the provided description is not too long.
// Only copy the first 127 characters if it is.
// The second parameter of StringCchLength is the maximum
// number of characters that may be read into catDescription.
// There must be room for a NULL-terminator. The third parameter
// contains the number of characters excluding the NULL-terminator.
hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, &len);
if (SUCCEEDED(hr))
{
if (len>127)
{
len = 127;
}
}
else
{
// TODO: Write an error handler;
}
// The second parameter of StringCchCopy is 128 because you need
// room for a NULL-terminator.
hr = StringCchCopy(catinfo.szDescription, len + 1, catDescription);
// Make sure the description is null terminated.
catinfo.szDescription[len + 1] = '\0';

hr = pcr->RegisterCategories(1, &catinfo);
pcr->Release();

return hr;
}

// HRESULT RegisterCLSIDInCategory -
// Register your component categories information
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
ICatRegister *pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
// Register this category as being "implemented" by the class.
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
}

if (pcr != NULL)
pcr->Release();

return hr;
}

// HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
ICatRegister *pcr = NULL ;
HRESULT hr = S_OK ;

hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
// Unregister this category as being "implemented" by the class.
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
}

if (pcr != NULL)
pcr->Release();

return hr;
}

        在“解决方案资源管理器”子窗口中,双击“源文件”下面的“myPrintOcxCtrl.cpp”,打开该文件后,找到下面的内容:

 


        将下面一段内容复制出来:

// 初始化类工厂和 guid

IMPLEMENT_OLECREATE_EX(CmyPrintOcxCtrl, "MYPRINTOCX.myPrintOcxCtrl.1",

 0x4eb4fb67, 0xc431, 0x4e95, 0xa9, 0x5c, 0xfe, 0x6b, 0x21, 0x97, 0xa0, 0x34)

        在“解决方案资源管理器”子窗口中,双击“源文件”下面的“myPrintOcx.cpp”,打开该文件,编写下面一段代码:

const CATID CLSID_SafeItem = {0x4eb4fb67, 0xc431, 0x4e95,{0xa9, 0x5c, 0xfe, 0x6b, 0x21, 0x97, 0xa0, 0x34}};

        注意:花括号中的数值来源于刚才复制的内容,并加以修改,即前三个值不变,后面8个值再放入到一个花括号中。将这一段代码粘贴到“myPrintOcx.cpp”文件中 CmyPrintOcxApp theApp; 这一行后面,如下图:



        在“myPrintOcx.cpp”文件起始处,加入下面的代码:

#include "Cathelp.h"

#include "objsafe.h"

        如下图:

 


        在“myPrintOcx.cpp”文件中,找到 DllRegisterServer( ) 和 DllUnregisterServer( )两个函数,删除它们。粘贴下面的代码:

// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
HRESULT hr; // HResult used by Safety Functions

AFX_MANAGE_STATE(_afxModuleAddrThis);

if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return ResultFromScode(SELFREG_E_TYPELIB);

if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);

// Mark the control as safe for initializing.

hr = CreateComponentCategory(CATID_SafeForInitializing,
L"Controls safely initializable from persistent data!");
if (FAILED(hr))
return hr;

hr = RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForInitializing);
if (FAILED(hr))
return hr;

// Mark the control as safe for scripting.

hr = CreateComponentCategory(CATID_SafeForScripting,
L"Controls safely scriptable!");
if (FAILED(hr))
return hr;

hr = RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForScripting);
if (FAILED(hr))
return hr;

return NOERROR;
}

// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);

// 删除控件初始化安全入口.
HRESULT hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);

if (FAILED(hr))
return hr;

// 删除控件脚本安全入口
hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);

if (FAILED(hr))
return hr;

if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
return ResultFromScode(SELFREG_E_TYPELIB);

if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return ResultFromScode(SELFREG_E_CLASS);

return NOERROR;
}

        在“解决方案资源管理器”子窗口中,右键单击“资源文件”下面的“myPrintOcx.rc”,在弹出菜单中选择“查看代码”,如下图所示:



        找到“BLOCK "StringFileInfo"”这一行,从这一行开始向下看,

1)将BLOCK的值设为:"040904e4"

2)将OLESelfRegister的值设为:"\0"

3)"Translation"后面改为:0x0409, 1252。修改如下图所示:



        以管理员身份运行Visual Stuido 2010,重新生成整个解决方案。

        在“解决方案资源管理器”子窗口中,双击打开文件“myPrintOcx.idl”,找到以下内容:

//  CmyPrintOcxCtrl 的类信息

 [

  uuid(4EB4FB67-C431-4E95-A95C-FE6B2197A034)

 ]

 coclass myPrintOcx

 {

  [default] dispinterface _DmyPrintOcx;

  [default, source] dispinterface _DmyPrintOcxEvents;

 };

        记录下uuid的内容,后面会用到。

        接下来对生成的 myPrintOcx.ocx 进行数字签名,这涉及到购买数字证书服务提供商(比如VeriSign)提供的代码签名证书服务,在这里不做介绍,有兴趣的读者可以到国内的CA服务提供商网站上了解详情。在计算机上创建一个临时目录,这里假定取名为 myPrintFolder,将签过名的 myPrintOcx.ocx 拷贝到该目录下。然后在该目录下,使用文本编辑器创建一个名为 myPrintOcx.inf 的文件,打开该文件,写入以下内容:

[version]
signature="$CHICAGO$"
AdvancedINF=2.0

[Add.Code]
myPrintOcx.ocx=myPrintOcx.ocx

[myPrintOcx.ocx]
file=thiscab
clsid={4EB4FB67-C431-4E95-A95C-FE6B2197A034}
FileVersion=1,0,0,1
RegisterServer=yes
DestDir=10

        注意:clsid这一项的取值就是我们前面记录的uuid,保存文件后退出。

        按快捷键 Win + R ,将会弹出“运行”对话框,在其中输入要执行的命令:iexpress 。将会弹出微软的CAB制作工具向导 IExpress Wizard,如下图:

 


        选择第一项“Create new Self Extraction Directive file”,点击“下一步”按钮,显示如下:

 


        选择第三项“Create compressed files only(ActiveX Installs)”,点击“下一步”按钮,如下图:

 


        点击“Add”按钮,把 myPrintOcx.inf 和 myPrintOcx.ocx 添加进去,显示如下:

 


        然后点击下一步,显示如下:



        在“Package Name and Options”下方的输入框中输入待生成的 CAB 文件名 myPrintOcx.CAB,勾选“Store files using Long File Name inside Package”,点击“下一步”按钮。这时会弹出错误提示框,提示对于待生成的CAB文件,其文件名必须符合 8.3 格式,即文件名不超过 8 个字符长度,扩展名不超过 3 个字符长度。这里将 CAB 文件名改为“PrintOcx.CAB”,如下图:



        点击“下一步”按钮,显示将如下图:


 

        对于是否需要保存自解压指令,选择第二项“Don't save.”,点击“下一步”按钮,接下来的显示如下:


 

        再点击“下一步”按钮,显示如下:

 


        到这里创建CAB压缩文件的过程就结束了。对 PrintOcx.CAB文件进行数字签名,签名完成后,我们可以将PrintOcx.CAB文件部署到网站上,试验一下打印控件能否工作。要架设 Web 服务器,可以使用前面博文中介绍过的 Abyss Web Server X1 软件,这里我们将本机作为 Web 服务器。编写一个网页,假定取名为  PrintTest.html,其内容如下:

<HTML>
<HEAD>
<TITLE>Test Print ActiveX Ocx</TITLE>
</HEAD>
<BODY>
<OBJECT id="myPrintOcx"
classid="clsid:4EB4FB67-C431-4E95-A95C-FE6B2197A034"
codebase="http://127.0.0.1/PrintOcx.CAB#version=1,0,0,1"
width="0"
height="0">
</OBJECT>

<SCRIPT Language="JavaScript">
function MyPrint()
{
var myOcx = document.getElementById("myPrintOcx");
if (myOcx)
{
myOcx.PrintData(10, -50, 300, -180, "[\"打印测试\"]\r\n你好,中国!\r\n 你好,西安。");
}
else
{
alert("控件不存在!");
}
}
</SCRIPT>

<button type="button" onclick="JavaScript:MyPrint()">打印</button>
</BODY>
</HTML>

        将 PrintTest.html 和 PrintOcx.CAB 两个文件拷贝到 Abyss Web Server 安装目录下面的 htdocs 子目录下,启动 Abyss Web Server ,在 IE 浏览器中输入网址:http://localhost/PrintTest.html,页面显示如下:



        点击页面上的“打印”按钮,可以调用默认打印机打印出中文字符串,不会弹出“打印”设置对话框。为了简单,我们这里打印的是网页代码中事先给定的文字。如果服务器端能够动态生成一个页面,针对不同用户在页面上显示不同的文字内容,那么就可以调用打印控件打印动态生成的文字内容,比如为用户打印文字凭据或收条。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  VC