C++的连接打印机显示图片的问题

HDC hdcPrint; //打印机直流手柄

TCHAR szDevString [120]; //数组为WIN.INI数据

TCHAR * szPrinter,* szDriver; //打印机和驱动程序名称

TCHAR * szPort; //端口名称

TCHAR * nextChar;

//检索打印机,打印机驱动程序和  
//从WIN.INI输出端口名称。  

GetProfileString(_T(“windows”),_T(“device”),_T(“,,,”), 
    szDevString,120); 

//解析名字串,根据需要设置ptrs  
//如果字符串包含所需的名称,请使用它们  
//创建设备上下文。  

if((szPrinter = _tcstok_s(szDevString, 
           _T(“,”),&nextChar)) 
        &&(szDriver = _tcstok_s(NULL, 
           _T(“,”),&nextChar)) 
        &&(szPort = _tcstok_s(NULL, 
           _T(“,”),&nextChar)))
{
    hdcPrint = CreateDC(szDriver,szPrinter, 
        szPort,NULL); 
}

//打印包含该字符串的测试页  
//“打印机测试”在左上角。  

Escape(hdcPrint,STARTDOC,8,“Test-Doc”,NULL); 
TextOut(hdcPrint,50,50,_T(“PRINTER TEST”),12); 
Escape(hdcPrint,NEWFRAME,0,NULL,NULL); 
Escape(hdcPrint,ENDDOC,0,NULL,NULL); 

//删除打印机DC。  
DeleteDC(hdcPrint); 

    这是我查到的资料,已经测试完毕,但是现在的结果就是我只能打出一行文字,但是打印不出图像,希望有大神能来执教一下  我QQ568997743 如果解决定有重谢

4个回答

你这个肯定打印不出图形,你用的是esc/pos指令集,这种底层的方式只能打印文本。
如果用这种方式要打印图像,需要自己计算像素点,转换为打印指令,再输出,不是一般的麻烦。

正确的做法是安装好打印机驱动,用windows的图形打印方式打印。

qq_41956328
qq_41956328 重新安装一下驱动,不行做下电脑系统
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: 有具体点的代码吗我们公司电脑没有FLASH,看不了文档,可以帮忙写下吗,可以额外付费
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: http://www.docin.com/p-643347308.html
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: 使用gdi api的图形方式下,文字、直线和图形都是图形。你可以用BitBlt把图片画上去。
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: 你说的这个我会阿,但这个不也是文字和直线吗,我想打印出图片阿
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_40827007: 不过图形方式也要注意,如果是热敏或者针打,分辨率低的话,文字不要太小,不要用颜色,也不要抗锯齿,否则会不清晰。
一年多之前 回复
qq_40827007
qq_40827007 嗯嗯 学到了 谢谢
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: https://blog.csdn.net/zxxssdsd/article/details/17073761
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: 用图形方式很简单。
一年多之前 回复
qq_38482920
qq_38482920 那用代码可以实现吗 我安装了打印机驱动啊
一年多之前 回复

添加一个bitmap资源叫做IDB_BITMAP1,放一个图片

然后用下面的代码:

 // Q695292.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "windows.h"
#include "resource.h"

int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR   szDriver[16] = _T("WINSPOOL");
    TCHAR   szPrinter[256];
    DWORD   cchBuffer = 255;
    HDC     hdcPrint = NULL;
    HANDLE  hPrinter = NULL;
    PRINTER_INFO_2  *pPrinterData;
    BYTE    pdBuffer[16384];
    BOOL    bReturn = FALSE;

    DWORD   cbBuf = sizeof (pdBuffer);
    DWORD   cbNeeded = 0;
    pPrinterData = (PRINTER_INFO_2 *)&pdBuffer[0];

    // get the default printer name
    bReturn = GetDefaultPrinter(
        szPrinter,
        &cchBuffer);

    if (bReturn) {
        // open the default printer
        bReturn = OpenPrinter(
            szPrinter,
            &hPrinter,
            NULL);
    }

    if (bReturn) {
        // get the printer port name
        bReturn =  GetPrinter(
            hPrinter,
            2,
            &pdBuffer[0],
            cbBuf,
            &cbNeeded);

           // this handle is no longer needed
        ClosePrinter(hPrinter);
    }

    if (bReturn) {
       // create the Print DC
       hdcPrint = CreateDC(szDriver, szPrinter, 
            pPrinterData->pPortName, NULL); 
    }

    if (hdcPrint) {

        Escape(hdcPrint, STARTDOC, 8, "Test-Doc", NULL); 

        HBITMAP hBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP1));

        HDC hMemDC = CreateCompatibleDC(hdcPrint);
        SelectObject(hMemDC, hBitmap);
        BitBlt(hdcPrint, 0, 0, 3650 * 15, 2710 * 15, hMemDC, 0, 0, SRCCOPY); //用bitblt画图
        DeleteDC(hMemDC);
        DeleteObject(hBitmap);

        Escape(hdcPrint, NEWFRAME, 0, NULL, NULL); 
        Escape(hdcPrint, ENDDOC, 0, NULL, NULL); 

        // Delete the printer DC.  
        DeleteDC(hdcPrint); 
    }
}


图片说明
运行效果(我用的是pdf虚拟打印机,如果你默认打印机设置为你的物理打印机,就可以打印输出了)

qq_38482920
qq_38482920 回复caozhy:谢谢老哥了 终于做出来了 不过打印出来的图片是横向的 下面的纸是空白的 我想让他纵向覆盖整个屏幕要怎么做呢
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: 我现在是把生成的bitmap存到本地 但是要读取本地birmap文件除了添加资源还有什么办法呢
一年多之前 回复
qq_38482920
qq_38482920 我已经把image转换为bitmap 但是我不是从本地加载的图片 所以不知道怎么使用你的select object函数
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: 这个基于opencv的是可以写入文件的 https://blog.csdn.net/ilyhlf5201314/article/details/8182474 写入文件就可以用LoadBitmap 读取了
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 wxImage是第三方库吧,你要转换为 HBITMAP,如果它不支持,你看看它能不能写入bmp文件,然后再通过文件读取。
一年多之前 回复
qq_38482920
qq_38482920 老哥实在是辛苦了我现在可以实现你这个效果 我现在的问题是为了把生成的屏幕截图进行颜色反转 就把bitmap的图像格式转换成image了 然后我就不知道怎么获取stretch函数的源设备句柄了
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: wxImage imgBuff =img->copy(); 我是首先获取了一个bitmap格式屏幕截图,然后将其转换为image格式进行颜色反转,现在需要将这个image图像打印出来,而且要占满整个打印纸
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: wxImage *img =new wxImage(bmp->converttoimage())
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: 把你生成image的代码贴出来看看
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: 而且打印出来的不能布满整个屏幕
一年多之前 回复
qq_38482920
qq_38482920 回复caozhy: 我生成的是image的图片 不知道怎么获取句柄
一年多之前 回复
caozhy
贵阳老马马善福专业维修游泳池堵漏防水工程 回复qq_38482920: 你看我的代码HBITMAP hBitmap = LoadBitmap,如果你生成的图片,有HBitmap句柄,这里是一样的。
一年多之前 回复
qq_38482920
qq_38482920 谢谢老哥了 我看懂了 我现在想问的是我已经在其他函数中生成了一个image 我要如何获取他的句柄呢 我已经采纳你之前的回答了 感谢老哥 真的十分感谢 可否再帮忙解决下这个问题呢
一年多之前 回复
 // Q695292.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "windows.h"
#include "resource.h"

HBITMAP CaptureImage(HWND hwnd)
{
    HANDLE hDIB;
    DWORD dwBmpSize;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;
    BITMAPINFOHEADER bi;
    CHAR *lpbitmap;
    INT width = GetSystemMetrics(SM_CXSCREEN);  // 屏幕宽
    INT height = GetSystemMetrics(SM_CYSCREEN);  // 屏幕高
    HDC hdcScreen = GetDC(NULL); // 全屏幕DC
    HDC hdcMemDC = CreateCompatibleDC(hdcScreen); // 创建兼容内存DC

    if (!hdcMemDC)
    {
        //echo(TEXT("CreateCompatibleDC has failed"));
        goto done;
    }

    // 通过窗口DC 创建一个兼容位图
    hbmScreen = CreateCompatibleBitmap(hdcScreen, width, height);

    if (!hbmScreen)
    {
        //echo(TEXT("CreateCompatibleBitmap Failed"));
        goto done;
    }

    // 将位图块传送到我们兼容的内存DC中
    SelectObject(hdcMemDC, hbmScreen);
    if (!BitBlt(
                hdcMemDC,    // 目的DC
                0, 0,        // 目的DC的 x,y 坐标
                width, height, // 目的 DC 的宽高
                hdcScreen,   // 来源DC
                0, 0,        // 来源DC的 x,y 坐标
                SRCCOPY))    // 粘贴方式
    {
        //echo(TEXT("BitBlt has failed"));
        goto done;
    }

    // 获取位图信息并存放在 bmpScreen 中
    GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmpScreen.bmWidth;
    bi.biHeight = bmpScreen.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // 在 32-bit Windows 系统上, GlobalAlloc 和 LocalAlloc 是由 HeapAlloc 封装来的
    // handle 指向进程默认的堆. 所以开销比 HeapAlloc 要大
    hDIB = GlobalAlloc(GHND, dwBmpSize);
    lpbitmap = (char *)GlobalLock(hDIB);

    // 获取兼容位图的位并且拷贝结果到一个 lpbitmap 中.
    GetDIBits(
        hdcScreen,  // 设备环境句柄
        hbmScreen,  // 位图句柄
        0,          // 指定检索的第一个扫描线
        (UINT)bmpScreen.bmHeight, // 指定检索的扫描线数
        lpbitmap,   // 指向用来检索位图数据的缓冲区的指针
        (BITMAPINFO *)&bi, // 该结构体保存位图的数据格式
        DIB_RGB_COLORS // 颜色表由红、绿、蓝(RGB)三个直接值构成
    );

    // 解锁堆内存并释放
    GlobalUnlock(hDIB);
    GlobalFree(hDIB);

    // 清理资源
done:
    //DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    ReleaseDC(NULL, hdcScreen);

    return hbmScreen;
}


int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR   szDriver[16] = _T("WINSPOOL");
    TCHAR   szPrinter[256];
    DWORD   cchBuffer = 255;
    HDC     hdcPrint = NULL;
    HANDLE  hPrinter = NULL;
    PRINTER_INFO_2  *pPrinterData;
    BYTE    pdBuffer[16384];
    BOOL    bReturn = FALSE;

    DWORD   cbBuf = sizeof (pdBuffer);
    DWORD   cbNeeded = 0;
    pPrinterData = (PRINTER_INFO_2 *)&pdBuffer[0];

    // get the default printer name
    bReturn = GetDefaultPrinter(
        szPrinter,
        &cchBuffer);

    if (bReturn) {
        // open the default printer
        bReturn = OpenPrinter(
            szPrinter,
            &hPrinter,
            NULL);
    }

    if (bReturn) {
        // get the printer port name
        bReturn =  GetPrinter(
            hPrinter,
            2,
            &pdBuffer[0],
            cbBuf,
            &cbNeeded);

           // this handle is no longer needed
        ClosePrinter(hPrinter);
    }

    if (bReturn) {
       // create the Print DC
       hdcPrint = CreateDC(szDriver, szPrinter, 
            pPrinterData->pPortName, NULL); 
    }

    if (hdcPrint) {

        Escape(hdcPrint, STARTDOC, 8, "Test-Doc", NULL); 

        HBITMAP hBitmap = CaptureImage(GetDesktopWindow()); //LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP1));

        HDC hMemDC = CreateCompatibleDC(hdcPrint);
        SelectObject(hMemDC, hBitmap);
        //BitBlt(hdcPrint, 0, 0, 3650 * 15, 2710 * 15, hMemDC, 0, 0, SRCCOPY);
        StretchBlt(hdcPrint, 300, 300, 1680 * 8 / 3, 1050 * 8 / 3, hMemDC, 0, 0, 1680, 1050, SRCCOPY);

        DeleteDC(hMemDC);
        DeleteObject(hBitmap);

        Escape(hdcPrint, NEWFRAME, 0, NULL, NULL); 
        Escape(hdcPrint, ENDDOC, 0, NULL, NULL); 

        // Delete the printer DC.  
        DeleteDC(hdcPrint); 
    }
}


图片说明

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
C++的连接打印机显示图片的问题
-
Thinkpad E480 通过IP地址连接不上公司的共享打印机(可以看到共享打印机就是连接不上)
-
android 连接蓝牙打印机 打印图片
-
Android wifi连接打印机打印
-
iOS如何连接打印机,打印一段字符串
-
bitmap的打印机打印问题
-
android 连接打印机打印?
-
windows ce 的pad 连接蓝牙打印机问题
-
微信小程序蓝牙连接打印机写入数据,打印机没反应,求帮助
-
QT连接打印机打印是如何设置打印机打印出固定格式的内容
-
关于发送ZPL指令到斑马打印机的问题
-
C# 热敏打印机打印二维码,不是打印条形码
-
关于C#调用打印机异常问题
-
c# 设置打印机的默认属性
-
android蓝牙连接LW-600P蓝牙打印机可以连接成功但是打印没反应
-
labview 连接斑马条码打印机
-
C# 热敏打印机打印条形码
-
c#如何禁用指定打印机
-
python 连接打印机打印无法import win32print
-
求ZDesigner LP 2844打印机用指令打印的C#代码
-
学会了这些技术,你离BAT大厂不远了
每一个程序员都有一个梦想,梦想着能够进入阿里、腾讯、字节跳动、百度等一线互联网公司,由于身边的环境等原因,不知道 BAT 等一线互联网公司使用哪些技术?或者该如何去学习这些技术?或者我该去哪些获取这些技术资料?没关系,平头哥一站式服务,上面统统不是问题。平头哥整理了 BAT 等一线大厂的必备技能,并且帮你准备了对应的资料。对于整理出来的技术,如果你掌握的不牢固,那就赶快巩固,如果你还没有涉及,现在...
程序员真是太太太太太有趣了!!!
网络上虽然已经有了很多关于程序员的话题,但大部分人对这个群体还是很陌生。我们在谈论程序员的时候,究竟该聊些什么呢?各位程序员大佬们,请让我听到你们的声音!不管你是前端开发...
史上最详细的IDEA优雅整合Maven+SSM框架(详细思路+附带源码)
网上很多整合SSM博客文章并不能让初探ssm的同学思路完全的清晰,可以试着关掉整合教程,摇两下头骨,哈一大口气,就在万事具备的时候,开整,这个时候你可能思路全无 ~中招了咩~ ,还有一些同学依旧在使用eclipse或者Myeclipse开发,我想对这些朋友说IDEA 的编译速度很快,人生苦短,来不及解释了,直接上手idea吧。这篇文章每一步搭建过程都测试过了,应该不会有什么差错。本文章还有个比较优秀的特点,就是idea的使用,基本上关于idea的操作都算是比较详细的,所以不用太担心不会撸idea!最后,本文
知乎上 40 个有趣回复,很精辟很提神
点击蓝色“五分钟学算法”关注我哟加个“星标”,天天中午 12:15,一起学算法作者 |佚名来源 |网络整理,版权归原作者所有,侵删。1交朋友的标准是什么?- Ques...
武汉为什么进不了互联网第一梯队?
作者 |盛佳莹、张帆 本文经授权转自猎云网(ID:ilieyun) 从2011年以前双创在武汉萌芽,到现在力争互联网第四极,看武汉的互联网企业如何成长。 艰难中萌芽 2011年高考前夕,付小龙坐在宿舍里用人生第一台智能手机听广播,电台里正在讲述姚欣休学创业做出了用户量逾4亿的视频平台PPTV。 夜深人静时,仿佛有什么东西击中了付小龙的内心,“像姚欣一样‘开公司,...
我花了一夜用数据结构给女朋友写个H5走迷宫游戏
起因 又到深夜了,我按照以往在csdn和公众号写着数据结构!这占用了我大量的时间!我的超越妹妹严重缺乏陪伴而 怨气满满! 而女朋友时常埋怨,认为数据结构这么抽象难懂的东西没啥作用,常会问道:天天写这玩意,有啥作用。而我答道:能干事情多了,比如写个迷宫小游戏啥的! 当我码完字准备睡觉时:写不好别睡觉! 分析 如果用数据结构与算法造出东西来呢? ...
别再翻了,面试二叉树看这 11 个就够了~
写在前边 数据结构与算法: 不知道你有没有这种困惑,虽然刷了很多算法题,当我去面试的时候,面试官让你手写一个算法,可能你对此算法很熟悉,知道实现思路,但是总是不知道该在什么地方写,而且很多边界条件想不全面,一紧张,代码写的乱七八糟。如果遇到没有做过的算法题,思路也不知道从何寻找。面试吃了亏之后,我就慢慢的做出总结,开始分类的把数据结构所有的题型和解题思路每周刷题做出的系统性总结写在了 Github...
接班马云的为何是张勇?
上海人、职业经理人、CFO 背景,集齐马云三大不喜欢的张勇怎么就成了阿里接班人? 作者|王琳 本文经授权转载自燃财经(ID:rancaijing) 9月10日,张勇转正了,他由阿里巴巴董事局候任主席正式成为阿里巴巴董事局主席,这也意味着阿里巴巴将正式开启“逍遥子时代”。 从2015年接任CEO开始,张勇已经将阿里巴巴股价拉升了超过200%。但和马云强大的个人光环比,张勇显得尤其...
让程序员崩溃的瞬间(非程序员勿入)
今天给大家带来点快乐,程序员才能看懂。 来源:https://zhuanlan.zhihu.com/p/47066521 1. 公司实习生找 Bug 2.在调试时,将断点设置在错误的位置 3.当我有一个很棒的调试想法时 4.偶然间看到自己多年前写的代码 5.当我第一次启动我的单元测试时 ...
用Python分析2000款避孕套,得出这些有趣的结论
到现在为止,我们的淘宝教程已经写到了第四篇,前三篇分别是: 第一篇:Python模拟登录淘宝,详细讲解如何使用requests库登录淘宝pc端。 第二篇:淘宝自动登录2.0,新增Cookies序列化,教大家如何将cookies保存起来。 第三篇:Python爬取淘宝商品避孕套,教大家如何爬取淘宝pc端商品信息。 今天,我们来看看淘宝系列的第四篇 我们在上一篇的时候已经将淘宝数据爬取下来了,...
IPv6 带来的反欺诈难题,程序员该如何破解?
作者 |威胁猎人 本文转载自威胁猎人(ID:ThreatHunter) IP是互联网最基础的身份标识,也是黑灰产业发展不可或缺的底层资源支撑。如果说IPv4是一颗星球,那IPv6就是一整个宇宙,它的地址空间接近无限。本文将揭露目前黑灰产对IPv6资源的利用情况,并剖析在IPv4向IPv6升级的过程中,业务场景下的安全将面临的挑战。 黑灰产采用IPv6发起攻击的趋势不可逆转...
分享靠写代码赚钱的一些门路
作者 mezod,译者 josephchang10如今,通过自己的代码去赚钱变得越来越简单,不过对很多人来说依然还是很难,因为他们不知道有哪些门路。今天给大家分享一个精彩...
技术人员要拿百万年薪,必须要经历这9个段位
很多人都问,技术人员如何成长,每个阶段又是怎样的,如何才能走出当前的迷茫,实现自我的突破。所以我结合我自己10多年的从业经验,总结了技术人员成长的9个段位,希望对大家的职...
面试官:兄弟,说说基本类型和包装类型的区别吧
Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double。基本类型和包装类型的区别主要有以下 4 点。
C语言这么厉害,它自身又是用什么语言写的?
这是来自我的星球的一个提问:“C语言本身用什么语言写的?”换个角度来问,其实是:C语言在运行之前,得编译才行,那C语言的编译器从哪里来? 用什么语言来写的?如果是用C语...
面试官,不要再问我三次握手和四次挥手
三次握手和四次挥手是各个公司常见的考点,也具有一定的水平区分度,也被一些面试官作为热身题。很多小伙伴说这个问题刚开始回答的挺好,但是后面越回答越冒冷汗,最后就歇菜了。 见过比较典型的面试场景是这样的: 面试官:请介绍下三次握手 求职者:第一次握手就是客户端给服务器端发送一个报文,第二次就是服务器收到报文之后,会应答一个报文给客户端,第三次握手就是客户端收到报文后再给服务器发送一个报文,三次握手就...
一些实用的GitHub项目
最近整理了一些在GitHub上比较热门的开源项目关于GitHub,快速了解请戳这里其中涵盖了:学习教程,面试总结,实用工具,框架,机器学习等东西比较杂,要学的东西也比较多...
八大排序(C语言)
void BubbleSort();//冒泡 void SelectSort();//选择 void InsertSort();//直接插入 void ShellSort();//希尔 void HeapSort();//堆排 void QuickSort();//快排 void MegerSort();//归并 void RadixSort();//基数(桶排序) 冒泡: 1)...
为什么说 Web 开发永远不会退出历史舞台?
早在 PC 崛起之际,Web 从蹒跚学步一路走到了主导市场的地位,但是随着移动互联网时代的来临,业界曾有不少人猜测,“Web 应该被杀死,App 才是未来”。不过时间是检...
动画:用动画给面试官解释 TCP 三次握手过程
作者 | 小鹿 来源 | 公众号:小鹿动画学编程 写在前边 TCP 三次握手过程对于面试是必考的一个,所以不但要掌握 TCP 整个握手的过程,其中有些小细节也更受到面试官的青睐。 对于这部分掌握以及 TCP 的四次挥手,小鹿将会以动画的形式呈现给每个人,这样将复杂的知识简单化,理解起来也容易了很多,尤其对于一个初学者来说。 学习导图 一、TCP 是什么? TCP(Transmissio...
相关热词 c# mvc 上传 文件 c#扫描软件 c# 文字打印左右反转 c#byte转换成数字 c# 音量调节组件 c# wpf 界面 c# 读取证书文件的内容 c# 单例模式 工厂模式 c# dgv 树结构 c#继承 反序列化