关键字: 打印 SPL EMF 文件格式
问题:
Windows的假脱机打印会在Windows/System32/spool/PRINERS目录下生成.spl和.shd文件,其中的打印内
容存贮在.spl文件中,但是.spl文件格式似乎未公开,那么如何才能将未知的.spl文件剥离成.emf文件呢?
首先,让我们了解一下Windows打印机制:
这是微软的官网的一副打印流程图片:
其中ISV是应用软件接口,IHV是硬件接口,左边是XP的打印模型,右边是Vista最新的XPS打印模
型,但两者可以互相转换,具有良好的兼容性。不过,这里暂时只关心XP系统的打印过程。
网络打印过程图:
但是这些图似乎还不够详细,那么请看下面一副:(摘录于论文:《基于关键字匹配的打印数据截获
系统》):
基本的思路是: 打印过程发生时,GDI模块和打印驱动(由打印机厂商提供)进行基本的数据交换,在假
脱机设置环境下,生成打印机命令文件:.spl或.emf文件,作为一个打印池的作业,然后Windows后台打印线
程处理打印作业,将数据文件送至打印机打印,打印完删除该打印文件。
好,现在回到正题:.spl文件该如何剥离成.emf呢?看一个例子:
在WinHex中打开一个.spl文件:
参考: http://www.undocprint.org/formats/winspool/spl 中一些打印结构的定义。
首先,.spl文件都是以0x00010000签名开头,然后一个DWORD 是emf相关区的文件偏移,第3个
DWORD是文档描述字符串(UNICODE)的文件偏移,第4个DWORD 描述的是端口说明字符串(UNICODE)。大
致结构如下:
文件尾就是这个样子:
当定位到0x50的文件位置,读取2个DWORD数据之后,就是.emf文件开始了。.emf文件格式是公开的,而
且非常简单,是一系列EMR_XXX开口结构的紧密排列,通常以EMR_HEADER(0x01)开头,以EMR_EOF
(0x0E)结尾。其实我们根本没有必要去解析.emf文件格式,Windows SDK有专门显示.emf文件的API,3个函数就
搞定:
HENHMETAFILE hEMF = GetEnhMetaFile("EMF_DumpOK.emf");
PlayEnhMetaFile (dc.m_hDC, hEMF, &rc) ;
DeleteEnhMetaFile (hEMF) ;
然后.spl文件还有一些东西,我现在还没有解析出来,但是.emf文件已经剥离出来了,后面的可以先不理它。
然后,开始写程序喽,因为比较简单,所以代码有点随便哦~~:)
http://www.cppblog.com/rawdata/ 星绽紫辉
程序截图如下:
2
3 #include <windows.h>
4 #include <winspool.h>
5 #include <stdio.h>
6 #include <locale.h>
7 #include <tchar.h>
8 #include <iostream>
9 using namespace std;
10
11
12 BOOL AnalyseFile(const char* pszFileName);
13
14 void PFT(const char* pszInfo,DWORD dwData)
15 {
16 printf("%s: 0x%08X/n",pszInfo,dwData);
17}
18
19 void PFM(const char* pszInfo)
20 {
21 printf("%s/n",pszInfo);
22}
23
24 void UPFM(const wchar_t pszInfo[])
25 {
26 wprintf(L"%s/n",pszInfo);
27}
28
29 static char* ID_Func[] =
30 {
31"EMR_HEADER",
32"EMR_POLYBEZIER",
33"EMR_POLYGON",
34"EMR_POLYLINE",
35"EMR_POLYBEZIERTO",
36"EMR_POLYLINETO",
37"EMR_POLYPOLYLINE",
38"EMR_POLYPOLYGON",
39"EMR_SETWINDOWEXTEX",
40"EMR_SETWINDOWORGEX",
41"EMR_SETVIEWPORTEXTEX",
42"EMR_SETVIEWPORTORGEX",
43"EMR_SETBRUSHORGEX",
44"EMR_EOF",
45"EMR_SETPIXELV",
46"EMR_SETMAPPERFLAGS",
47"EMR_SETMAPMODE",
48"EMR_SETBKMODE",
49"EMR_SETPOLYFILLMODE",
50"EMR_SETROP2",
51"EMR_SETSTRETCHBLTMODE",
52"EMR_SETTEXTALIGN",
53"EMR_SETCOLORADJUSTMENT",
54"EMR_SETTEXTCOLOR",
55"EMR_SETBKCOLOR",
56"EMR_OFFSETCLIPRGN",
57"EMR_MOVETOEX",
58"EMR_SETMETARGN",
59"EMR_EXCLUDECLIPRECT",
60"EMR_INTERSECTCLIPRECT",
61"EMR_SCALEVIEWPORTEXTEX",
62"EMR_SCALEWINDOWEXTEX",
63"EMR_SAVEDC",
64"EMR_RESTOREDC",
65"EMR_SETWORLDTRANSFORM",
66"EMR_MODIFYWORLDTRANSFORM",
67"EMR_SELECTOBJECT",
68"EMR_CREATEPEN",
69"EMR_CREATEBRUSHINDIRECT",
70"EMR_DELETEOBJECT",
71"EMR_ANGLEARC",
72"EMR_ELLIPSE",
73"EMR_RECTANGLE",
74"EMR_ROUNDRECT",
75"EMR_ARC",
76"EMR_CHORD",
77"EMR_PIE",
78"EMR_SELECTPALETTE",
79"EMR_CREATEPALETTE",
80"EMR_SETPALETTEENTRIES",
81"EMR_RESIZEPALETTE",
82"EMR_REALIZEPALETTE",
83"EMR_EXTFLOODFILL",
84"EMR_LINETO",
85"EMR_ARCTO",
86"EMR_POLYDRAW",
87"EMR_SETARCDIRECTION",
88"EMR_SETMITERLIMIT",
89"EMR_BEGINPATH",
90"EMR_ENDPATH",
91"EMR_CLOSEFIGURE",
92"EMR_FILLPATH",
93"EMR_STROKEANDFILLPATH",
94"EMR_STROKEPATH",
95"EMR_FLATTENPATH",
96"EMR_WIDENPATH",
97"EMR_SELECTCLIPPATH",
98"EMR_ABORTPATH",
99"69--Unknown",
100
101"EMR_GDICOMMENT",
102"EMR_FILLRGN",
103"EMR_FRAMERGN",
104"EMR_INVERTRGN",
105"EMR_PAINTRGN ",
106"EMR_EXTSELECTCLIPRGN",
107"EMR_BITBLT ",
108"EMR_STRETCHBLT",
109"EMR_MASKBLT",
110"EMR_PLGBLT",
111"EMR_SETDIBITSTODEVICE",
112"EMR_STRETCHDIBITS",
113"EMR_EXTCREATEFONTINDIRECTW",
114"EMR_EXTTEXTOUTA ",
115"EMR_EXTTEXTOUTW",
116"EMR_POLYBEZIER16",
117"EMR_POLYGON16 ",
118"EMR_POLYLINE16 ",
119"EMR_POLYBEZIERTO16",
120"EMR_POLYLINETO16 ",
121"EMR_POLYPOLYLINE16",
122"EMR_POLYPOLYGON16",
123"EMR_POLYDRAW16 ",
124"EMR_CREATEMONOBRUSH ",
125"EMR_CREATEDIBPATTERNBRUSHPT",
126"EMR_EXTCREATEPEN",
127"EMR_POLYTEXTOUTA ",
128"EMR_POLYTEXTOUTW",
129"EMR_SETICMMODE ",
130"EMR_CREATECOLORSPACE",
131"EMR_SETCOLORSPACE ",
132"EMR_DELETECOLORSPACE ",
133"EMR_GLSRECORD ",
134"EMR_GLSBOUNDEDRECORD",
135"EMR_PIXELFORMAT",
136"EMR_RESERVED_105 ",
137"EMR_RESERVED_106 ",
138"EMR_RESERVED_107",
139"EMR_RESERVED_108 ",
140"EMR_RESERVED_109",
141"EMR_RESERVED_110 ",
142"EMR_COLORCORRECTPALETTE",
143"EMR_SETICMPROFILEA ",
144"EMR_SETICMPROFILEW ",
145"EMR_ALPHABLEND",
146"EMR_SETLAYOUT ",
147"EMR_TRANSPARENTBLT",
148"EMR_RESERVED_117 ",
149"EMR_GRADIENTFILL",
150"EMR_RESERVED_119 ",
151"EMR_RESERVED_120",
152"EMR_COLORMATCHTOTARGETW",
153"EMR_CREATECOLORSPACEW"
154};
155
156 int main()
157 {
158 setlocale(LC_ALL,"");
159
160 const char* pszFileName = "C://Documents and Settings//joe//桌面//1//00053.SPL";
161
162 if(!AnalyseFile(pszFileName))
163 PFM("Analyse File Failed!");
164 else
165 PFM("Analyse File Successed Completed!");
166
167 return 0;
168}
169
170
171 BOOL AnalyseFile(const char* pszFileName)
172 {
173 BOOL bRet = FALSE;
174
175 DWORD dwStartPos = 0;
176
177 FILE* pFile = fopen(pszFileName,"rb");
178
179 if(!pFile)
180 {
181 PFM("Open File Failed!");
182 return bRet;
183 }
184
185 PFM("Begin Analyse");
186
187 PFM("[1] Begin to read SPL HeaderInfo:");
188
189 /**//* =======================Headers================================ */
190 DWORD dwTmp = 0;
191
192 fseek(pFile,0,0);
193
194 fread(&dwTmp,sizeof(DWORD),1,pFile);
195
196 PFT("签名",dwTmp);
197
198
199 fread(&dwTmp,sizeof(DWORD),1,pFile);
200
201 dwStartPos = dwTmp;
202
203 PFT("正文信息偏移:",dwTmp);
204
205 fread(&dwTmp,sizeof(DWORD),1,pFile);
206
207 PFT("文档信息偏移(UNICODE):",dwTmp);
208
209 long pos = ftell(pFile);
210
211 fseek(pFile,dwTmp,SEEK_SET);
212
213 wchar_t pszInfo[256] = {0};
214 pszInfo[0] = L'(';
215
216 WORD wTmp;
217 for(int i = 1;;i++)
218 {
219 fread(&wTmp,sizeof(wTmp),1,pFile);
220
221 if(!wTmp)
222 break;
223
224 memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
225 }
226 pszInfo[i] = L')';
227 UPFM(pszInfo);
228
229 fseek(pFile,pos,SEEK_SET);
230
231 fread(&dwTmp,sizeof(DWORD),1,pFile);
232
233 PFT("打印端口信息偏移(UNICODE):",dwTmp);
234
235 fseek(pFile,dwTmp,SEEK_SET);
236
237 memset(pszInfo,0,sizeof(wchar_t)*256);
238 pszInfo[0] = L'(';
239 for(i = 1;;i++)
240 {
241 fread(&wTmp,sizeof(wTmp),1,pFile);
242
243 if(!wTmp)
244 break;
245
246 memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
247 }
248 pszInfo[i] = L')';
249 UPFM(pszInfo);
250
251 /**//* ======================== Unknown datas ================================= */
252 PFM("[2] Begin to read SPL Unknown Datas:");
253
254 fseek(pFile,dwStartPos,SEEK_SET);
255
256 fread(&dwTmp,sizeof(DWORD),1,pFile);
257
258 PFT("未知数据",dwTmp);
259
260 fread(&dwTmp,sizeof(DWORD),1,pFile);
261
262 PFT("未知数据",dwTmp);
263
264 /**//* ======================== Record datas ================================= */
265 PFM("[3] Begin to read Record Datas:");
266
267 DWORD dwTmp2 = 0;
268 for(int i=0;;i++)
269 {
270 pos = ftell(pFile);
271
272 fread(&dwTmp,sizeof(DWORD),1,pFile);
273
274 fread(&dwTmp2,sizeof(DWORD),1,pFile);
275
276
277 printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (%s)/n",i,dwTmp,dwTmp2,pos,ID_Func[dwTmp-1]);
278
279// printf("位置: %08X",pos);
280
281// printf("(%s)/n",ID_Func[dwTmp]);
282
283 if(dwTmp == 0x0E)
284 {
285// printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (End)/n",i,dwTmp,dwTmp2,pos,);
286 PFM("End of Record Datas.");
287 break;
288 }
289
290 fseek(pFile,pos+dwTmp2,SEEK_SET);
291 }
292
293 if(pFile) fclose(pFile);
294 bRet = TRUE;
295
296 return bRet;
297}
298
299
300
301
302
303
304
305
306
307
308
309
有了以上的分析,你应该很容易写一个spl To EMF 文件格式的程序了。
如果代码有什么谬误或者Bug,请留言或者EmailToMe: xiaolu69soft@yahoo.com.cn
祝你好运~
rawdata