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