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