1 /*
2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions giving information about DEVMODE structures
5 * COPYRIGHT: Copyright 2016-2017 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 typedef struct _MINIMUM_SIZE_TABLE
11 {
12 DWORD dwField;
13 WORD wSize;
14 }
15 MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
16
17 /**
18 * Minimum required DEVMODEA structure size based on the used fields. Must be in descending order!
19 */
20 static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
21 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
22 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
23 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
24 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
25 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
26 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
27 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
28 { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
29 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
30 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
31 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
32 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
33 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
34 { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
35 { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
36 { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
37 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
38 { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
39 { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
40 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
41 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
42 { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
43 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
44 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
45 { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
46 { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
47 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
48 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
49 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
50 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
51 { 0, 0 }
52 };
53
54 /**
55 * Minimum required DEVMODEW structure size based on the used fields. Must be in descending order!
56 */
57 static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
58 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
59 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
60 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
61 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
62 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
63 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
64 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
65 { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
66 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
67 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
68 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
69 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
70 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
71 { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
72 { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
73 { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
74 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
75 { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
76 { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
77 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
78 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
79 { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
80 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
81 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
82 { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
83 { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
84 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
85 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
86 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
87 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
88 { 0, 0 }
89 };
90
91 /**
92 * Replace the last character by a null terminator if the given ANSI string is not null-terminated.
93 */
94 static __inline void
_FixStringA(PBYTE String,DWORD cbString)95 _FixStringA(PBYTE String, DWORD cbString)
96 {
97 const PBYTE pLastCharacter = &String[cbString / sizeof(BYTE) - 1];
98 PBYTE p = String;
99
100 while (*p)
101 {
102 if (p == pLastCharacter)
103 {
104 *p = 0;
105 break;
106 }
107
108 p++;
109 }
110 }
111
112 /**
113 * Replace the last character by a null terminator if the given Unicode string is not null-terminated.
114 */
115 static __inline void
_FixStringW(PWSTR String,DWORD cbString)116 _FixStringW(PWSTR String, DWORD cbString)
117 {
118 const PWSTR pLastCharacter = &String[cbString / sizeof(WCHAR) - 1];
119 PWSTR p = String;
120
121 while (*p)
122 {
123 if (p == pLastCharacter)
124 {
125 *p = 0;
126 break;
127 }
128
129 p++;
130 }
131 }
132
133 BOOL WINAPI
IsValidDevmodeA(PDEVMODEA pDevmode,size_t DevmodeSize)134 IsValidDevmodeA(PDEVMODEA pDevmode, size_t DevmodeSize)
135 {
136 PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
137 WORD wRequiredSize;
138
139 TRACE("IsValidDevmodeA(%p, %lu)\n", pDevmode, DevmodeSize);
140
141 // Check if a Devmode was given at all.
142 if (!pDevmode)
143 goto Failure;
144
145 // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
146 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
147 goto Failure;
148
149 // If the structure has private members, the public structure must be 32-bit packed.
150 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
151 goto Failure;
152
153 // Now determine the minimum possible dmSize based on the given fields in dmFields.
154 wRequiredSize = FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields);
155
156 while (pTable->dwField)
157 {
158 if (pDevmode->dmFields & pTable->dwField)
159 {
160 wRequiredSize = pTable->wSize;
161 break;
162 }
163
164 pTable++;
165 }
166
167 // Verify that the value in dmSize is big enough for the used fields.
168 if (pDevmode->dmSize < wRequiredSize)
169 goto Failure;
170
171 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
172 // Fix this if they aren't.
173 _FixStringA(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
174 if (pDevmode->dmFields & DM_FORMNAME)
175 _FixStringA(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
176
177 // Return success without setting the error code.
178 return TRUE;
179
180 Failure:
181 SetLastError(ERROR_INVALID_DATA);
182 return FALSE;
183 }
184
185 BOOL WINAPI
IsValidDevmodeW(PDEVMODEW pDevmode,size_t DevmodeSize)186 IsValidDevmodeW(PDEVMODEW pDevmode, size_t DevmodeSize)
187 {
188 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
189 WORD wRequiredSize;
190
191 TRACE("IsValidDevmodeW(%p, %lu)\n", pDevmode, DevmodeSize);
192
193 // Check if a Devmode was given at all.
194 if (!pDevmode)
195 goto Failure;
196
197 // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
198 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
199 goto Failure;
200
201 // If the structure has private members, the public structure must be 32-bit packed.
202 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
203 goto Failure;
204
205 // Now determine the minimum possible dmSize based on the given fields in dmFields.
206 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
207
208 while (pTable->dwField)
209 {
210 if (pDevmode->dmFields & pTable->dwField)
211 {
212 wRequiredSize = pTable->wSize;
213 break;
214 }
215
216 pTable++;
217 }
218
219 // Verify that the value in dmSize is big enough for the used fields.
220 if (pDevmode->dmSize < wRequiredSize)
221 goto Failure;
222
223 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
224 // Fix this if they aren't.
225 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
226 if (pDevmode->dmFields & DM_FORMNAME)
227 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
228
229 // Return success without setting the error code.
230 return TRUE;
231
232 Failure:
233 SetLastError(ERROR_INVALID_DATA);
234 return FALSE;
235 }
236
237 BOOL WINAPI
IsValidDevmodeNoSizeW(PDEVMODEW pDevmode)238 IsValidDevmodeNoSizeW(PDEVMODEW pDevmode)
239 {
240 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
241 WORD wRequiredSize;
242
243 TRACE("IsValidDevmodeNoSizeW(%p)\n", pDevmode);
244
245 // Check if a Devmode was given at all.
246 if (!pDevmode)
247 goto Failure;
248
249 // If the structure has private members, the public structure must be 32-bit packed.
250 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
251 goto Failure;
252
253 // Now determine the minimum possible dmSize based on the given fields in dmFields.
254 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
255
256 while (pTable->dwField)
257 {
258 if (pDevmode->dmFields & pTable->dwField)
259 {
260 wRequiredSize = pTable->wSize;
261 break;
262 }
263
264 pTable++;
265 }
266
267 // Verify that the value in dmSize is big enough for the used fields.
268 if (pDevmode->dmSize < wRequiredSize)
269 goto Failure;
270
271 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
272 // Fix this if they aren't.
273 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
274 if (pDevmode->dmFields & DM_FORMNAME)
275 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
276
277 // Return success without setting the error code.
278 return TRUE;
279
280 Failure:
281 SetLastError(ERROR_INVALID_DATA);
282 return FALSE;
283 }
284
RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput,PDEVMODEW * pDevModeOutput)285 void RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput, PDEVMODEW *pDevModeOutput)
286 {
287 // FIXME: This function should become ConvertAnsiDevModeToUnicodeDevmode when its parameters are known!
288
289 // Check if a pDevModeInput and pDevModeOutput are both not NULL.
290 if (!pDevModeInput || !pDevModeOutput)
291 return;
292
293 *pDevModeOutput = GdiConvertToDevmodeW(pDevModeInput);
294 }
295
296 // Internal counterpart to GdiConvertToDevmodeW from gdi32
297 static __inline DEVMODEA*
_ConvertToDevmodeA(const DEVMODEW * dmW)298 _ConvertToDevmodeA(const DEVMODEW *dmW)
299 {
300 DEVMODEA *dmA;
301 WORD dmA_size, dmW_size;
302 size_t BytesToCopy;
303
304 dmW_size = dmW->dmSize;
305
306 /* this is the minimal dmSize that XP accepts */
307 if (dmW_size < FIELD_OFFSET(DEVMODEW, dmFields))
308 return NULL;
309
310 // Guard against callers that set dmSize incorrectly.
311 if (dmW_size > sizeof(DEVMODEW))
312 dmW_size = sizeof(DEVMODEW);
313
314 // dmA_size must become dmW_size without the additional 1 byte per character for each Unicode string (dmDeviceName and dmFormName).
315 dmA_size = dmW_size - CCHDEVICENAME;
316 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
317 dmA_size -= CCHFORMNAME;
318
319 // Allocate the required bytes, that is dmSize for the ANSI DEVMODEA structure plus any extra bytes requested through dmDriverExtra.
320 dmA = HeapAlloc(GetProcessHeap(), 0, dmA_size + dmW->dmDriverExtra);
321 if (!dmA) return NULL;
322
323 // Every valid DEVMODEW has a dmDeviceName, which we convert to ANSI here.
324 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
325
326 // Copy everything up to dmFormName or the remaining dmW_size, whatever is smaller.
327 BytesToCopy = min(FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion), dmW_size - CCHDEVICENAME * sizeof(WCHAR));
328 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion, BytesToCopy);
329
330 // Handle dmFormName if the input DEVMODEW is large enough to contain one.
331 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
332 {
333 if (dmW->dmFields & DM_FORMNAME)
334 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
335 else
336 dmA->dmFormName[0] = 0;
337
338 // Copy the remaining fields.
339 if (dmW_size > FIELD_OFFSET(DEVMODEW, dmLogPixels))
340 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW_size - FIELD_OFFSET(DEVMODEW, dmLogPixels));
341 }
342
343 // Append dmDriverExtra if required.
344 if (dmW->dmDriverExtra)
345 memcpy((char *)dmA + dmA_size, (const char *)dmW + dmW_size, dmW->dmDriverExtra);
346
347 // Set the corrected dmSize and we are done.
348 dmA->dmSize = dmA_size;
349
350 return dmA;
351 }
352
RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput,PDEVMODEA pDevModeOutput)353 void RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput, PDEVMODEA pDevModeOutput)
354 {
355 PDEVMODEA pTmp;
356
357 // FIXME: This function should become ConvertUnicodeDevModeToAnsiDevmode when its parameters are known!
358
359 // Check if a pDevModeInput and pDevModeOutput are both not NULL.
360 if (!pDevModeInput || !pDevModeOutput)
361 return;
362
363 pTmp = _ConvertToDevmodeA(pDevModeInput);
364 memcpy( pDevModeOutput, pTmp, pTmp->dmSize + pTmp->dmDriverExtra); // Copy into a Wide char (Larger) buffer.
365 HeapFree(hProcessHeap, 0, pTmp);
366 }
367
368 VOID
369 WINAPI
DeviceMode(PVOID param_1,PVOID param_2,PCHAR param_3,PVOID param_4)370 DeviceMode(
371 PVOID param_1,
372 PVOID param_2,
373 PCHAR param_3,
374 PVOID param_4)
375 {
376 UNIMPLEMENTED;
377 }
378
379 LONG
380 WINAPI
ExtDeviceMode(HWND hWnd,HANDLE hInst,LPDEVMODEA pDevModeOutput,LPSTR pDeviceName,LPSTR pPort,LPDEVMODEA pDevModeInput,LPSTR pProfile,DWORD fMode)381 ExtDeviceMode(
382 HWND hWnd,
383 HANDLE hInst,
384 LPDEVMODEA pDevModeOutput,
385 LPSTR pDeviceName,
386 LPSTR pPort,
387 LPDEVMODEA pDevModeInput,
388 LPSTR pProfile,
389 DWORD fMode)
390 {
391 UNIMPLEMENTED;
392 return 0;
393 }
394