关键字: 打印 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
阅读更多