- Implement AddFontResource*, add stubs for RemoveFontResource*.
[wine] / dlls / gdi / printdrv.c
1 /*
2  * Implementation of some printer driver bits
3  *
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Huw Davies
6  * Copyright 1998 Andreas Mohr
7  * Copyright 1999 Klaas van Gend
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include "winbase.h"
34 #include "winuser.h"
35 #include "wine/winbase16.h"
36 #include "wine/wingdi16.h"
37 #include "winspool.h"
38 #include "winerror.h"
39 #include "winreg.h"
40 #include "wine/debug.h"
41 #include "gdi.h"
42 #include "heap.h"
43 #include "file.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(print);
46
47 static char PrinterModel[]      = "Printer Model";
48 static char DefaultDevMode[]    = "Default DevMode";
49 static char PrinterDriverData[] = "PrinterDriverData";
50 static char Printers[]          = "System\\CurrentControlSet\\Control\\Print\\Printers\\";
51
52 /******************************************************************
53  *                  StartDoc  [GDI.377]
54  *
55  */
56 INT16 WINAPI StartDoc16( HDC16 hdc, const DOCINFO16 *lpdoc )
57 {
58     DOCINFOA docA;
59
60     docA.cbSize = lpdoc->cbSize;
61     docA.lpszDocName = MapSL(lpdoc->lpszDocName);
62     docA.lpszOutput = MapSL(lpdoc->lpszOutput);
63
64     if(lpdoc->cbSize >= 14)
65         docA.lpszDatatype = MapSL(lpdoc->lpszDatatype);
66     else
67         docA.lpszDatatype = NULL;
68
69     if(lpdoc->cbSize >= 18)
70         docA.fwType = lpdoc->fwType;
71     else
72         docA.fwType = 0;
73
74     return StartDocA(hdc, &docA);
75 }
76
77 /******************************************************************
78  *                  StartDocA  [GDI32.@]
79  *
80  * StartDoc calls the STARTDOC Escape with the input data pointing to DocName
81  * and the output data (which is used as a second input parameter).pointing at
82  * the whole docinfo structure.  This seems to be an undocumented feature of
83  * the STARTDOC Escape.
84  *
85  * Note: we now do it the other way, with the STARTDOC Escape calling StartDoc.
86  */
87 INT WINAPI StartDocA(HDC hdc, const DOCINFOA* doc)
88 {
89     INT ret = 0;
90     DC *dc = DC_GetDCPtr( hdc );
91
92     TRACE("DocName = '%s' Output = '%s' Datatype = '%s'\n",
93           doc->lpszDocName, doc->lpszOutput, doc->lpszDatatype);
94
95     if(!dc) return SP_ERROR;
96
97     if (dc->funcs->pStartDoc) ret = dc->funcs->pStartDoc( dc->physDev, doc );
98     GDI_ReleaseObj( hdc );
99     return ret;
100 }
101
102 /*************************************************************************
103  *                  StartDocW [GDI32.@]
104  *
105  */
106 INT WINAPI StartDocW(HDC hdc, const DOCINFOW* doc)
107 {
108     DOCINFOA docA;
109     INT ret;
110
111     docA.cbSize = doc->cbSize;
112     docA.lpszDocName = doc->lpszDocName ?
113       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDocName ) : NULL;
114     docA.lpszOutput = doc->lpszOutput ?
115       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszOutput ) : NULL;
116     docA.lpszDatatype = doc->lpszDatatype ?
117       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDatatype ) : NULL;
118     docA.fwType = doc->fwType;
119
120     ret = StartDocA(hdc, &docA);
121
122     if(docA.lpszDocName)
123         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDocName );
124     if(docA.lpszOutput)
125         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszOutput );
126     if(docA.lpszDatatype)
127         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDatatype );
128
129     return ret;
130 }
131
132 /******************************************************************
133  *                  EndDoc  [GDI.378]
134  *
135  */
136 INT16 WINAPI EndDoc16(HDC16 hdc)
137 {
138     return EndDoc(hdc);
139 }
140
141 /******************************************************************
142  *                  EndDoc  [GDI32.@]
143  *
144  */
145 INT WINAPI EndDoc(HDC hdc)
146 {
147     INT ret = 0;
148     DC *dc = DC_GetDCPtr( hdc );
149     if(!dc) return SP_ERROR;
150
151     if (dc->funcs->pEndDoc) ret = dc->funcs->pEndDoc( dc->physDev );
152     GDI_ReleaseObj( hdc );
153     return ret;
154 }
155
156 /******************************************************************
157  *                  StartPage  [GDI.379]
158  *
159  */
160 INT16 WINAPI StartPage16(HDC16 hdc)
161 {
162     return StartPage(hdc);
163 }
164
165 /******************************************************************
166  *                  StartPage  [GDI32.@]
167  *
168  */
169 INT WINAPI StartPage(HDC hdc)
170 {
171     INT ret = 1;
172     DC *dc = DC_GetDCPtr( hdc );
173     if(!dc) return SP_ERROR;
174
175     if(dc->funcs->pStartPage)
176         ret = dc->funcs->pStartPage( dc->physDev );
177     else
178         FIXME("stub\n");
179     GDI_ReleaseObj( hdc );
180     return ret;
181 }
182
183 /******************************************************************
184  *                  EndPage  [GDI.380]
185  *
186  */
187 INT16 WINAPI EndPage16( HDC16 hdc )
188 {
189     return EndPage(hdc);
190 }
191
192 /******************************************************************
193  *                  EndPage  [GDI32.@]
194  *
195  */
196 INT WINAPI EndPage(HDC hdc)
197 {
198     INT ret = 0;
199     DC *dc = DC_GetDCPtr( hdc );
200     if(!dc) return SP_ERROR;
201
202     if (dc->funcs->pEndPage) ret = dc->funcs->pEndPage( dc->physDev );
203     GDI_ReleaseObj( hdc );
204     if (!QueryAbort16( hdc, 0 ))
205     {
206         EndDoc( hdc );
207         ret = 0;
208     }
209     return ret;
210 }
211
212 /******************************************************************************
213  *                 AbortDoc  [GDI.382]
214  */
215 INT16 WINAPI AbortDoc16(HDC16 hdc)
216 {
217     return AbortDoc(hdc);
218 }
219
220 /******************************************************************************
221  *                 AbortDoc  [GDI32.@]
222  */
223 INT WINAPI AbortDoc(HDC hdc)
224 {
225     INT ret = 0;
226     DC *dc = DC_GetDCPtr( hdc );
227     if(!dc) return SP_ERROR;
228
229     if (dc->funcs->pAbortDoc) ret = dc->funcs->pAbortDoc( dc->physDev );
230     GDI_ReleaseObj( hdc );
231     return ret;
232 }
233
234 /**********************************************************************
235  *           QueryAbort   (GDI.155)
236  *
237  *  Calls the app's AbortProc function if avail.
238  *
239  * RETURNS
240  * TRUE if no AbortProc avail or AbortProc wants to continue printing.
241  * FALSE if AbortProc wants to abort printing.
242  */
243 BOOL16 WINAPI QueryAbort16(HDC16 hdc, INT16 reserved)
244 {
245     BOOL ret = TRUE;
246     DC *dc = DC_GetDCPtr( hdc );
247     ABORTPROC abproc;
248
249     if(!dc) {
250         ERR("Invalid hdc %04x\n", hdc);
251         return FALSE;
252     }
253
254     abproc = dc->pAbortProc;
255     GDI_ReleaseObj( hdc );
256
257     if (abproc)
258         ret = abproc(hdc, 0);
259     return ret;
260 }
261
262 /* ### start build ### */
263 extern WORD CALLBACK PRTDRV_CallTo16_word_ww(FARPROC16,WORD,WORD);
264 /* ### stop build ### */
265
266 /**********************************************************************
267  *           call_abort_proc16
268  */
269 static BOOL CALLBACK call_abort_proc16( HDC hdc, INT code )
270 {
271     ABORTPROC16 proc16;
272     DC *dc = DC_GetDCPtr( hdc );
273
274     if (!dc) return FALSE;
275     proc16 = dc->pAbortProc16;
276     GDI_ReleaseObj( hdc );
277     if (proc16) return PRTDRV_CallTo16_word_ww( (FARPROC16)proc16, hdc, code );
278     return TRUE;
279 }
280
281
282 /**********************************************************************
283  *           SetAbortProc   (GDI.381)
284  */
285 INT16 WINAPI SetAbortProc16(HDC16 hdc, ABORTPROC16 abrtprc)
286 {
287     DC *dc = DC_GetDCPtr( hdc );
288
289     if (!dc) return FALSE;
290     dc->pAbortProc16 = abrtprc;
291     GDI_ReleaseObj( hdc );
292     return SetAbortProc( hdc, call_abort_proc16 );
293 }
294
295 /**********************************************************************
296  *           SetAbortProc   (GDI32.@)
297  *
298  */
299 INT WINAPI SetAbortProc(HDC hdc, ABORTPROC abrtprc)
300 {
301     DC *dc = DC_GetDCPtr( hdc );
302
303     if (!dc) return FALSE;
304     dc->pAbortProc = abrtprc;
305     GDI_ReleaseObj( hdc );
306     return TRUE;
307 }
308
309
310 /****************** misc. printer related functions */
311
312 /*
313  * The following function should implement a queing system
314  */
315 struct hpq
316 {
317     struct hpq  *next;
318     int          tag;
319     int          key;
320 };
321
322 static struct hpq *hpqueue;
323
324 /**********************************************************************
325  *           CreatePQ   (GDI.230)
326  *
327  */
328 HPQ16 WINAPI CreatePQ16(INT16 size)
329 {
330 #if 0
331     HGLOBAL16 hpq = 0;
332     WORD tmp_size;
333     LPWORD pPQ;
334
335     tmp_size = size << 2;
336     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
337        return 0xffff;
338     pPQ = GlobalLock16(hpq);
339     *pPQ++ = 0;
340     *pPQ++ = tmp_size;
341     *pPQ++ = 0;
342     *pPQ++ = 0;
343     GlobalUnlock16(hpq);
344
345     return (HPQ16)hpq;
346 #else
347     FIXME("(%d): stub\n",size);
348     return 1;
349 #endif
350 }
351
352 /**********************************************************************
353  *           DeletePQ   (GDI.235)
354  *
355  */
356 INT16 WINAPI DeletePQ16(HPQ16 hPQ)
357 {
358     return GlobalFree16((HGLOBAL16)hPQ);
359 }
360
361 /**********************************************************************
362  *           ExtractPQ   (GDI.232)
363  *
364  */
365 INT16 WINAPI ExtractPQ16(HPQ16 hPQ)
366 {
367     struct hpq *queue, *prev, *current, *currentPrev;
368     int key = 0, tag = -1;
369     currentPrev = prev = NULL;
370     queue = current = hpqueue;
371     if (current)
372         key = current->key;
373
374     while (current)
375     {
376         currentPrev = current;
377         current = current->next;
378         if (current)
379         {
380             if (current->key < key)
381             {
382                 queue = current;
383                 prev = currentPrev;
384             }
385         }
386     }
387     if (queue)
388     {
389         tag = queue->tag;
390
391         if (prev)
392             prev->next = queue->next;
393         else
394             hpqueue = queue->next;
395         HeapFree(GetProcessHeap(), 0, queue);
396     }
397
398     TRACE("%x got tag %d key %d\n", hPQ, tag, key);
399
400     return tag;
401 }
402
403 /**********************************************************************
404  *           InsertPQ   (GDI.233)
405  *
406  */
407 INT16 WINAPI InsertPQ16(HPQ16 hPQ, INT16 tag, INT16 key)
408 {
409     struct hpq *queueItem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct hpq));
410     if(queueItem == NULL) {
411         ERR("Memory exausted!\n");
412         return FALSE;
413     }
414     queueItem->next = hpqueue;
415     hpqueue = queueItem;
416     queueItem->key = key;
417     queueItem->tag = tag;
418
419     FIXME("(%x %d %d): stub???\n", hPQ, tag, key);
420     return TRUE;
421 }
422
423 /**********************************************************************
424  *           MinPQ   (GDI.231)
425  *
426  */
427 INT16 WINAPI MinPQ16(HPQ16 hPQ)
428 {
429     FIXME("(%x): stub\n", hPQ);
430     return 0;
431 }
432
433 /**********************************************************************
434  *           SizePQ   (GDI.234)
435  *
436  */
437 INT16 WINAPI SizePQ16(HPQ16 hPQ, INT16 sizechange)
438 {
439     FIXME("(%x %d): stub\n", hPQ, sizechange);
440     return -1;
441 }
442
443
444
445 /*
446  * The following functions implement part of the spooling process to
447  * print manager.  I would like to see wine have a version of print managers
448  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
449  * now.
450  */
451 typedef struct PRINTJOB
452 {
453     char        *pszOutput;
454     char        *pszTitle;
455     HDC16       hDC;
456     HANDLE16    hHandle;
457     int         nIndex;
458     int         fd;
459 } PRINTJOB, *PPRINTJOB;
460
461 #define MAX_PRINT_JOBS 1
462 #define SP_OK 1
463
464 PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
465
466
467 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
468 {
469     return gPrintJobsTable[0];
470 }
471
472 static int CreateSpoolFile(LPCSTR pszOutput)
473 {
474     int fd=-1;
475     char psCmd[1024];
476     char *psCmdP = psCmd;
477
478     /* TTD convert the 'output device' into a spool file name */
479
480     if (pszOutput == NULL || *pszOutput == '\0')
481       return -1;
482
483     if (!strncmp("LPR:",pszOutput,4))
484       sprintf(psCmd,"|lpr -P%s",pszOutput+4);
485     else
486     {
487         HKEY hkey;
488         /* default value */
489         psCmd[0] = 0;
490         if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\spooler", &hkey))
491         {
492             DWORD type, count = sizeof(psCmd);
493             RegQueryValueExA(hkey, pszOutput, 0, &type, psCmd, &count);
494             RegCloseKey(hkey);
495         }
496     }
497     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
498           psCmd, pszOutput);
499     if (!*psCmd)
500         psCmdP = (char *)pszOutput;
501     else
502     {
503         while (*psCmdP && isspace(*psCmdP))
504         {
505             psCmdP++;
506         };
507         if (!*psCmdP)
508             return -1;
509     }
510     if (*psCmdP == '|')
511     {
512         int fds[2];
513         if (pipe(fds))
514             return -1;
515         if (fork() == 0)
516         {
517             psCmdP++;
518
519             TRACE("In child need to exec %s\n",psCmdP);
520             close(0);
521             dup2(fds[0],0);
522             close (fds[1]);
523             system(psCmdP);
524             exit(0);
525
526         }
527         close (fds[0]);
528         fd = fds[1];
529         TRACE("Need to execute a cmnd and pipe the output to it\n");
530     }
531     else
532     {
533         char buffer[MAX_PATH];
534
535         TRACE("Just assume it's a file\n");
536
537         /**
538          * The file name can be dos based, we have to find its
539          * Unix correspondant file name
540          */
541         wine_get_unix_file_name(psCmdP, buffer, sizeof(buffer));
542
543         if ((fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY , 0600)) < 0)
544         {
545             ERR("Failed to create spool file '%s' ('%s'). (error %s)\n",
546                 buffer, psCmdP, strerror(errno));
547         }
548     }
549     return fd;
550 }
551
552 static int FreePrintJob(HANDLE16 hJob)
553 {
554     int nRet = SP_ERROR;
555     PPRINTJOB pPrintJob;
556
557     pPrintJob = FindPrintJobFromHandle(hJob);
558     if (pPrintJob != NULL)
559     {
560         gPrintJobsTable[pPrintJob->nIndex] = NULL;
561         HeapFree(GetProcessHeap(), 0, pPrintJob->pszOutput);
562         HeapFree(GetProcessHeap(), 0, pPrintJob->pszTitle);
563         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
564         HeapFree(GetProcessHeap(), 0, pPrintJob);
565         nRet = SP_OK;
566     }
567     return nRet;
568 }
569
570 /**********************************************************************
571  *           OpenJob   (GDI.240)
572  *           OpenJob16 (GDI32.@)
573  *
574  */
575 HPJOB16 WINAPI OpenJob16(LPCSTR lpOutput, LPCSTR lpTitle, HDC16 hDC)
576 {
577     HPJOB16 hHandle = (HPJOB16)SP_ERROR;
578     PPRINTJOB pPrintJob;
579
580     TRACE("'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
581
582     pPrintJob = gPrintJobsTable[0];
583     if (pPrintJob == NULL)
584     {
585         int fd;
586
587         /* Try and create a spool file */
588         fd = CreateSpoolFile(lpOutput);
589         if (fd >= 0)
590         {
591             pPrintJob = HeapAlloc(GetProcessHeap(), 0, sizeof(PRINTJOB));
592             if(pPrintJob == NULL) {
593                 WARN("Memory exausted!\n");
594                 return hHandle;
595             }
596
597             hHandle = 1;
598
599             pPrintJob->pszOutput = HeapAlloc(GetProcessHeap(), 0, strlen(lpOutput)+1);
600             strcpy( pPrintJob->pszOutput, lpOutput );
601             if(lpTitle)
602             {
603                 pPrintJob->pszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(lpTitle)+1);
604                 strcpy( pPrintJob->pszTitle, lpTitle );
605             }
606             pPrintJob->hDC = hDC;
607             pPrintJob->fd = fd;
608             pPrintJob->nIndex = 0;
609             pPrintJob->hHandle = hHandle;
610             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob;
611         }
612     }
613     TRACE("return %04x\n", hHandle);
614     return hHandle;
615 }
616
617 /**********************************************************************
618  *           CloseJob   (GDI.243)
619  *           CloseJob16 (GDI32.@)
620  *
621  */
622 INT16 WINAPI CloseJob16(HPJOB16 hJob)
623 {
624     int nRet = SP_ERROR;
625     PPRINTJOB pPrintJob = NULL;
626
627     TRACE("%04x\n", hJob);
628
629     pPrintJob = FindPrintJobFromHandle(hJob);
630     if (pPrintJob != NULL)
631     {
632         /* Close the spool file */
633         close(pPrintJob->fd);
634         FreePrintJob(hJob);
635         nRet  = 1;
636     }
637     return nRet;
638 }
639
640 /**********************************************************************
641  *           WriteSpool   (GDI.241)
642  *           WriteSpool16 (GDI32.@)
643  *
644  */
645 INT16 WINAPI WriteSpool16(HPJOB16 hJob, LPSTR lpData, INT16 cch)
646 {
647     int nRet = SP_ERROR;
648     PPRINTJOB pPrintJob = NULL;
649
650     TRACE("%04x %08lx %04x\n", hJob, (DWORD)lpData, cch);
651
652     pPrintJob = FindPrintJobFromHandle(hJob);
653     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
654     {
655         if (write(pPrintJob->fd, lpData, cch) != cch)
656           nRet = SP_OUTOFDISK;
657         else
658           nRet = cch;
659 #if 0
660         /* FIXME: We just cannot call 16 bit functions from here, since we
661          * have acquired several locks (DC). And we do not really need to.
662          */
663         if (pPrintJob->hDC == 0) {
664             TRACE("hDC == 0 so no QueryAbort\n");
665         }
666         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
667         {
668             CloseJob16(hJob); /* printing aborted */
669             nRet = SP_APPABORT;
670         }
671 #endif
672     }
673     return nRet;
674 }
675
676 typedef INT (WINAPI *MSGBOX_PROC)( HWND, LPCSTR, LPCSTR, UINT );
677
678 /**********************************************************************
679  *           WriteDialog   (GDI.242)
680  *
681  */
682 INT16 WINAPI WriteDialog16(HPJOB16 hJob, LPSTR lpMsg, INT16 cchMsg)
683 {
684     HMODULE mod;
685     MSGBOX_PROC pMessageBoxA;
686     INT16 ret = 0;
687
688     TRACE("%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
689
690     if ((mod = GetModuleHandleA("user32.dll")))
691     {
692         if ((pMessageBoxA = (MSGBOX_PROC)GetProcAddress( mod, "MessageBoxA" )))
693             ret = pMessageBoxA(0, lpMsg, "Printing Error", MB_OKCANCEL);
694     }
695     return ret;
696 }
697
698
699 /**********************************************************************
700  *           DeleteJob  (GDI.244)
701  *
702  */
703 INT16 WINAPI DeleteJob16(HPJOB16 hJob, INT16 nNotUsed)
704 {
705     int nRet;
706
707     TRACE("%04x\n", hJob);
708
709     nRet = FreePrintJob(hJob);
710     return nRet;
711 }
712
713 /*
714  * The following two function would allow a page to be sent to the printer
715  * when it has been processed.  For simplicity they havn't been implemented.
716  * This means a whole job has to be processed before it is sent to the printer.
717  */
718
719 /**********************************************************************
720  *           StartSpoolPage   (GDI.246)
721  *
722  */
723 INT16 WINAPI StartSpoolPage16(HPJOB16 hJob)
724 {
725     FIXME("StartSpoolPage GDI.246 unimplemented\n");
726     return 1;
727
728 }
729
730
731 /**********************************************************************
732  *           EndSpoolPage   (GDI.247)
733  *
734  */
735 INT16 WINAPI EndSpoolPage16(HPJOB16 hJob)
736 {
737     FIXME("EndSpoolPage GDI.247 unimplemented\n");
738     return 1;
739 }
740
741
742 /**********************************************************************
743  *           GetSpoolJob   (GDI.245)
744  *
745  */
746 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
747 {
748     DWORD retval = 0;
749     TRACE("In GetSpoolJob param 0x%lx noption %d\n",param, nOption);
750     return retval;
751 }
752
753
754 /******************************************************************
755  *                  DrvGetPrinterDataInternal
756  *
757  * Helper for DrvGetPrinterData
758  */
759 static DWORD DrvGetPrinterDataInternal(LPSTR RegStr_Printer,
760 LPBYTE lpPrinterData, int cbData, int what)
761 {
762     DWORD res = -1;
763     HKEY hkey;
764     DWORD dwType, cbQueryData;
765
766     if (!(RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey))) {
767         if (what == INT_PD_DEFAULT_DEVMODE) { /* "Default DevMode" */
768             if (!(RegQueryValueExA(hkey, DefaultDevMode, 0, &dwType, 0, &cbQueryData))) {
769                 if (!lpPrinterData)
770                     res = cbQueryData;
771                 else if ((cbQueryData) && (cbQueryData <= cbData)) {
772                     cbQueryData = cbData;
773                     if (RegQueryValueExA(hkey, DefaultDevMode, 0,
774                                 &dwType, lpPrinterData, &cbQueryData))
775                         res = cbQueryData;
776                 }
777             }
778         } else { /* "Printer Driver" */
779             cbQueryData = 32;
780             RegQueryValueExA(hkey, "Printer Driver", 0,
781                         &dwType, lpPrinterData, &cbQueryData);
782             res = cbQueryData;
783         }
784     }
785     if (hkey) RegCloseKey(hkey);
786     return res;
787 }
788
789 /******************************************************************
790  *                DrvGetPrinterData     (GDI.282)
791  *                DrvGetPrinterData16   (GDI32.@)
792  *
793  */
794 DWORD WINAPI DrvGetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
795                                LPDWORD lpType, LPBYTE lpPrinterData,
796                                int cbData, LPDWORD lpNeeded)
797 {
798     LPSTR RegStr_Printer;
799     HKEY hkey = 0, hkey2 = 0;
800     DWORD res = 0;
801     DWORD dwType, PrinterAttr, cbPrinterAttr, SetData, size;
802
803     if (HIWORD(lpPrinter))
804             TRACE("printer %s\n",lpPrinter);
805     else
806             TRACE("printer %p\n",lpPrinter);
807     if (HIWORD(lpProfile))
808             TRACE("profile %s\n",lpProfile);
809     else
810             TRACE("profile %p\n",lpProfile);
811     TRACE("lpType %p\n",lpType);
812
813     if ((!lpPrinter) || (!lpProfile) || (!lpNeeded))
814         return ERROR_INVALID_PARAMETER;
815
816     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
817                                strlen(Printers) + strlen(lpPrinter) + 2);
818     strcpy(RegStr_Printer, Printers);
819     strcat(RegStr_Printer, lpPrinter);
820
821     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
822     (!strcmp(lpProfile, DefaultDevMode)))) {
823         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
824                                          INT_PD_DEFAULT_DEVMODE);
825         if (size+1) {
826             *lpNeeded = size;
827             if ((lpPrinterData) && (*lpNeeded > cbData))
828                 res = ERROR_MORE_DATA;
829         }
830         else res = ERROR_INVALID_PRINTER_NAME;
831     }
832     else
833     if (((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
834     (!strcmp(lpProfile, PrinterModel)))) {
835         *lpNeeded = 32;
836         if (!lpPrinterData) goto failed;
837         if (cbData < 32) {
838             res = ERROR_MORE_DATA;
839             goto failed;
840         }
841         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
842                                          INT_PD_DEFAULT_MODEL);
843         if ((size+1) && (lpType))
844             *lpType = REG_SZ;
845         else
846             res = ERROR_INVALID_PRINTER_NAME;
847     }
848     else
849     {
850         if ((res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)))
851             goto failed;
852         cbPrinterAttr = 4;
853         if ((res = RegQueryValueExA(hkey, "Attributes", 0,
854                         &dwType, (LPBYTE)&PrinterAttr, &cbPrinterAttr)))
855             goto failed;
856         if ((res = RegOpenKeyA(hkey, PrinterDriverData, &hkey2)))
857             goto failed;
858         *lpNeeded = cbData;
859         res = RegQueryValueExA(hkey2, lpProfile, 0,
860                 lpType, lpPrinterData, lpNeeded);
861         if ((res != ERROR_CANTREAD) &&
862          ((PrinterAttr &
863         (PRINTER_ATTRIBUTE_ENABLE_BIDI|PRINTER_ATTRIBUTE_NETWORK))
864         == PRINTER_ATTRIBUTE_NETWORK))
865         {
866             if (!(res) && (*lpType == REG_DWORD) && (*(LPDWORD)lpPrinterData == -1))
867                 res = ERROR_INVALID_DATA;
868         }
869         else
870         {
871             SetData = -1;
872             RegSetValueExA(hkey2, lpProfile, 0, REG_DWORD, (LPBYTE)&SetData, 4); /* no result returned */
873         }
874     }
875
876 failed:
877     if (hkey2) RegCloseKey(hkey2);
878     if (hkey) RegCloseKey(hkey);
879     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
880     return res;
881 }
882
883
884 /******************************************************************
885  *                 DrvSetPrinterData     (GDI.281)
886  *                 DrvSetPrinterData16   (GDI32.@)
887  *
888  */
889 DWORD WINAPI DrvSetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
890                                DWORD lpType, LPBYTE lpPrinterData,
891                                DWORD dwSize)
892 {
893     LPSTR RegStr_Printer;
894     HKEY hkey = 0;
895     DWORD res = 0;
896
897     if (HIWORD(lpPrinter))
898             TRACE("printer %s\n",lpPrinter);
899     else
900             TRACE("printer %p\n",lpPrinter);
901     if (HIWORD(lpProfile))
902             TRACE("profile %s\n",lpProfile);
903     else
904             TRACE("profile %p\n",lpProfile);
905     TRACE("lpType %08lx\n",lpType);
906
907     if ((!lpPrinter) || (!lpProfile) ||
908     ((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
909     (!strcmp(lpProfile, PrinterModel))))
910         return ERROR_INVALID_PARAMETER;
911
912     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
913                         strlen(Printers) + strlen(lpPrinter) + 2);
914     strcpy(RegStr_Printer, Printers);
915     strcat(RegStr_Printer, lpPrinter);
916
917     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
918     (!strcmp(lpProfile, DefaultDevMode)))) {
919         if ( RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)
920              != ERROR_SUCCESS ||
921              RegSetValueExA(hkey, DefaultDevMode, 0, REG_BINARY,
922                               lpPrinterData, dwSize) != ERROR_SUCCESS )
923                 res = ERROR_INVALID_PRINTER_NAME;
924     }
925     else
926     {
927         strcat(RegStr_Printer, "\\");
928
929         if( (res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)) ==
930             ERROR_SUCCESS ) {
931
932             if (!lpPrinterData)
933                 res = RegDeleteValueA(hkey, lpProfile);
934             else
935                 res = RegSetValueExA(hkey, lpProfile, 0, lpType,
936                                        lpPrinterData, dwSize);
937         }
938     }
939
940     if (hkey) RegCloseKey(hkey);
941     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
942     return res;
943 }