关键字: 打印 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/ 星绽紫辉
程序截图如下:


1
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