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