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

        使用过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" οnclick="JavaScript:MyPrint()">打印</button>
</BODY>
</HTML>

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



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


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值