/[winpt]/trunk/Src/wptCryptdisk.cpp
ViewVC logotype

Annotation of /trunk/Src/wptCryptdisk.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 77 - (hide annotations)
Mon Nov 14 15:01:01 2005 UTC (19 years, 3 months ago) by twoaday
File size: 15822 byte(s)
2005-11-12  Timo Schulz  <ts@g10code.com>
 
        Fix more GCC warnings.
 
2005-11-10  Timo Schulz  <ts@g10code.com>
 
        * wptClipSignDlg.cpp (one_key_proc): Use
        release_gpg_passphrase_cb() to free the context.
        * wptListView.cpp (listview_deselect_all): New.
        * wptMAPI.cpp (mapi_send_pubkey): Works again.
        * wptFileManagerDlg.cpp (file_manager_dlg_proc): Support encrypt &
        zip.
        * wptPassphraseCB.cpp (passphrase_callback_proc): Fix passphrase
        caching for signing operations.
        * wptKeyManager.cpp (km_send_to_mail_recipient): Works again.
        * wptFileManager.cpp (fm_send_file): Likewise.
        (fm_encrypt_into_zip): New.
         

1 werner 36 /* wptCryptdisk.cpp
2     * Copyright (C) 2004 Timo Schulz
3     *
4     * This file is part of WinPT.
5     *
6     * WinPT is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * WinPT is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with WinPT; if not, write to the Free Software Foundation,
18     * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19     */
20     #ifdef HAVE_CONFIG_H
21     #include <config.h>
22     #endif
23    
24     #include <windows.h>
25     #include <winioctl.h>
26     #include <stdio.h>
27     #include <ctype.h>
28    
29 werner 47 #include "resource.h"
30 werner 36 #include "wptCryptdisk.h"
31     #include "wptTypes.h"
32     #include "wptW32API.h"
33     #include "wptNLS.h"
34     #include "wptErrors.h"
35    
36     /*DWORD SHFormatDrive(HWND hwnd, UINT drive, UINT fmtID, UINT options);*/
37     typedef DWORD (WINAPI *sh_format_drive_p) (HWND, UINT, UINT, UINT);
38    
39     static mount_list_t mounted=NULL;
40     static HMODULE shell32=NULL;
41     static sh_format_drive_p shfmt=NULL;
42    
43    
44     static int
45     is_nt4 (void)
46     {
47     static int nt_flag = -1;
48     OSVERSIONINFO osinf;
49    
50     if (nt_flag != -1)
51     return nt_flag;
52    
53     memset (&osinf, 0, sizeof osinf);
54     osinf.dwOSVersionInfoSize = sizeof osinf;
55     GetVersionEx (&osinf);
56     if (osinf.dwPlatformId == VER_PLATFORM_WIN32_NT) {
57     nt_flag = 1;
58     shell32 = LoadLibrary ("SHELL32.DLL");
59     shfmt = (sh_format_drive_p)GetProcAddress (shell32, "SHFormatDrive");
60     if (!shfmt)
61     BUG (0);
62     }
63     else
64     nt_flag = 0;
65     return nt_flag;
66     }
67    
68     static int
69     check_filedisk (void)
70     {
71     HKEY hk;
72     int rc = -1;
73    
74     rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
75     "SYSTEM\\CurrentControlSet\\Services\\FileDisk",
76     0, KEY_READ, &hk);
77     if (rc == ERROR_SUCCESS) {
78     RegCloseKey (hk);
79     rc = 0;
80     }
81     return rc;
82     }
83    
84    
85     int
86     cryptdisk_available (void)
87     {
88     return (is_nt4 () == 1 && check_filedisk () == 0)? 1 : 0;
89     }
90    
91    
92     void
93     cryptdisk_cleanup (void)
94     {
95     if (shell32)
96     FreeLibrary (shell32);
97     shell32 = NULL;
98     }
99    
100    
101     static int
102     num_of_devices (void)
103     {
104     HKEY hk;
105     BYTE buf[32];
106     DWORD n=0, type=0;
107     int rc;
108    
109     rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
110     "SYSTEM\\CurrentControlSet\\Services\\FileDisk\\Parameters",
111     0, KEY_READ, &hk);
112     if (rc != ERROR_SUCCESS)
113     return -1;
114    
115     n = sizeof buf-1;
116     memset (buf, 0, sizeof buf);
117     rc = RegQueryValueEx (hk, "NumberOfDevices", 0, &type, buf, &n);
118     if (rc != ERROR_SUCCESS || type != REG_DWORD)
119     n=0;
120     else
121     n = *buf;
122     RegCloseKey (hk);
123     return n;
124     }
125    
126    
127     static HWND
128     start_cryptdisk_server (HANDLE * r_proc)
129     {
130     STARTUPINFO si;
131     PROCESS_INFORMATION pi;
132    
133     memset (&si, 0, sizeof si);
134     memset (&pi, 0, sizeof pi);
135     si.cb = sizeof si;
136    
137     if (CreateProcess (NULL, "CryptdiskSrv", NULL, NULL, FALSE, 0,
138     NULL, NULL, &si, &pi) == FALSE) {
139     msg_box (NULL, _("Could not execute Cryptdisk Server."),
140     _("Cryptdisk Error"), MB_ERR);
141     return NULL;
142     }
143     *r_proc = pi.hProcess;
144     Sleep (300);
145     return FindWindow ("CryptdiskSrv", NULL);
146     }
147    
148    
149     static int
150     cryptdisk_mount (int devnum,
151     const char * imgfile,
152     u32 size,
153     const char * hdlet)
154     {
155     HWND h;
156     HANDLE proc, wait_ev;
157     COPYDATASTRUCT cds;
158     struct private_ofi_s ofi;
159    
160     h = start_cryptdisk_server (&proc);
161     if (!h)
162     return WPTERR_GENERAL;
163     memset (&ofi, 0, sizeof ofi);
164     ofi.cmd = CMD_MOUNT;
165     ofi.devnum = devnum;
166     ofi.filesize.LowPart = size;
167     ofi.drvlet = *hdlet;
168     ofi.readonly = 0;
169     strcpy ((char *)ofi.fname, imgfile);
170     ofi.flen = strlen (imgfile);
171    
172     wait_ev = OpenEvent (SYNCHRONIZE, FALSE, "cryptdisksrv.wait");
173     if (!wait_ev)
174     BUG (0);
175     memset (&cds, 0, sizeof cds);
176     cds.cbData = sizeof ofi;
177     cds.lpData = &ofi;
178     SendMessage (h, WM_COPYDATA, (WPARAM)GetDesktopWindow (), (LPARAM)&cds);
179     WaitForSingleObject (wait_ev, INFINITE);
180     CloseHandle (wait_ev);
181     CloseHandle (proc);
182     return 0;
183     }
184    
185    
186    
187     int
188     cryptdisk_unmount (char drivelet, int forced)
189     {
190     HWND h;
191     HANDLE proc;
192     COPYDATASTRUCT cds;
193     struct private_ofi_s ofi;
194    
195     h = start_cryptdisk_server (&proc);
196     if (!h)
197     return WPTERR_GENERAL;
198    
199     memset (&ofi, 0, sizeof ofi);
200     ofi.cmd = CMD_UMOUNT;
201     ofi.drvlet = drivelet;
202     ofi.forced = forced;
203    
204     memset (&cds, 0, sizeof cds);
205     cds.lpData = &ofi;
206     cds.cbData = sizeof ofi;
207     SendMessage (h, WM_COPYDATA, (WPARAM)GetDesktopWindow (), (LPARAM)&cds);
208     CloseHandle (proc);
209    
210     return 0;
211     }
212    
213     static void
214     print_lasterr (char * prefix)
215     {
216     LPVOID msg;
217    
218     FormatMessage(
219     FORMAT_MESSAGE_ALLOCATE_BUFFER |
220     FORMAT_MESSAGE_FROM_SYSTEM |
221     FORMAT_MESSAGE_IGNORE_INSERTS,
222     NULL,
223     GetLastError(),
224     0,
225     (LPTSTR) &msg,
226     0,
227     NULL
228     );
229     MessageBox (NULL, (LPTSTR)msg, prefix, MB_ICONERROR|MB_OK);
230     LocalFree (msg);
231     }
232    
233    
234     static int
235     filedisk_status (char drivelet, LARGE_INTEGER * size, int * rdonly)
236     {
237     char VolumeName[] = "\\\\.\\ :";
238     HANDLE Device;
239     POPEN_FILE_INFORMATION ofi;
240     DWORD BytesReturned;
241     int n= sizeof (OPEN_FILE_INFORMATION)+ MAX_PATH;
242    
243     VolumeName[4] = drivelet;
244    
245     Device = CreateFile (VolumeName,
246     GENERIC_READ,
247     FILE_SHARE_READ | FILE_SHARE_WRITE,
248     NULL,
249     OPEN_EXISTING,
250     FILE_FLAG_NO_BUFFERING,
251     NULL);
252    
253     if (Device == INVALID_HANDLE_VALUE) {
254     print_lasterr(&VolumeName[4]);
255     return WPTERR_CDISK_OPEN;
256     }
257    
258     ofi = (POPEN_FILE_INFORMATION)malloc (n);
259    
260     if (!DeviceIoControl (Device,
261     IOCTL_FILE_DISK_QUERY_FILE,
262     NULL,
263     0,
264     ofi,
265     sizeof(OPEN_FILE_INFORMATION) + MAX_PATH,
266     &BytesReturned, NULL)) {
267     print_lasterr (&VolumeName[4]);
268     return WPTERR_CDISK_QUERY;
269     }
270    
271     if (BytesReturned < sizeof(OPEN_FILE_INFORMATION)) {
272     SetLastError (ERROR_INSUFFICIENT_BUFFER);
273     print_lasterr (&VolumeName[4]);
274     return WPTERR_CDISK_QUERY;
275     }
276    
277     if (size)
278     *size = ofi->FileSize;
279     if (rdonly)
280     *rdonly = ofi->ReadOnly;
281    
282     return 0;
283     }
284    
285    
286     static mount_list_t
287     new_mount_list (const char * drive, int devnum)
288     {
289     mount_list_t n;
290    
291     n = (mount_list_t)calloc (1, sizeof *n);
292     if (n) {
293     strcpy (n->drive, drive);
294     n->devnum = devnum;
295     n->ttl = 1;
296     filedisk_status (*drive, &n->size, &n->rdonly);
297     }
298     return n;
299     }
300    
301    
302     static mount_list_t
303     find_mount_list (mount_list_t root, const char * drive)
304     {
305     mount_list_t n;
306    
307     for (n=root; n; n=n->next) {
308     if (n->ttl == 0)
309     continue;
310     if (!strncmp (n->drive, drive, strlen (drive)))
311     return n;
312     }
313     return NULL;
314     }
315    
316    
317     static void
318     add_mount_list (mount_list_t root, mount_list_t node)
319     {
320     mount_list_t n;
321    
322     if (find_mount_list (root, node->drive))
323     return;
324    
325     for (n=root; n->next; n=n->next)
326     ;
327     n->next = node;
328     }
329    
330    
331     static void
332     remove_mount_list (mount_list_t root, mount_list_t node)
333     {
334     mount_list_t n;
335    
336     n = find_mount_list (root, node->drive);
337     if (n)
338     n->ttl = 0;
339     }
340    
341    
342     static void
343     free_mount_list (mount_list_t root)
344     {
345     mount_list_t n;
346    
347     while (root) {
348     n = root->next;
349     free (root);
350     root = n;
351     }
352     }
353    
354    
355     static int
356     last_devnum (mount_list_t root)
357     {
358     mount_list_t n;
359     int devnum=0;
360    
361     if (!root)
362     return 0;
363    
364     for (n=root; n; n=n->next) {
365     if (devnum < n->devnum)
366     devnum=n->devnum;
367     }
368     return devnum+1;
369     }
370    
371    
372     static char
373     init_drives (HWND dlg, int ctlid)
374     {
375     char buf[512], drv[5];
376     DWORD n=0, i=0;
377    
378     n=GetLogicalDriveStrings (sizeof buf-1, buf);
379     for (i=0; i < n; i++) {
380     memcpy (drv, buf+i, 3); drv[3]=0;
381     i += 3;
382     }
383     if (!dlg && ctlid == -1)
384     return ++drv[0];
385    
386     for (++drv[0]; drv[0] < 'Z'; drv[0]++)
387     SendDlgItemMessage (dlg, ctlid, CB_ADDSTRING, 0,
388     (LPARAM)(const char *)drv);
389     SendDlgItemMessage (dlg, ctlid, CB_SETCURSEL, 0, 0);
390     return 0;
391     }
392    
393    
394     static int
395     check_freespace (const char * imgfile, u32 size)
396     {
397     ULARGE_INTEGER tocaller, total, totfree;
398     char buf[128] = {0};
399    
400     if (!strchr (imgfile, ':'))
401     GetCurrentDirectory (sizeof buf-1, buf);
402     else
403     strncpy (buf, imgfile, sizeof buf-1);
404     buf[3] = '\0';
405     GetDiskFreeSpaceEx (buf, &tocaller, &total, &totfree);
406     if (size > tocaller.LowPart)
407     return -1;
408     return 0;
409     }
410    
411    
412     static void
413     do_check (HWND dlg)
414     {
415     if (!cryptdisk_serv_run ()) { /* xxx */
416     msg_box (dlg, _("The Cryptdisk service seems to be available but "
417     "it is not started yet.\nPlease start the service "
418     "and try again."), _("Cryptdisk Error"), MB_ERR);
419     EndDialog (dlg, TRUE);
420     }
421     }
422    
423    
424     BOOL CALLBACK
425     cryptdisk_new_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
426     {
427     const char * ciphers [] = {
428     "twofish",
429     NULL
430     };
431     const char * s;
432     char imgfile[128], key[128], drv[3];
433     int i;
434     u32 size=0;
435    
436     switch (msg) {
437     case WM_INITDIALOG:
438     do_check (dlg);
439     SetDlgItemInt (dlg, IDC_CDNEW_SIZE, DISK_DEFSIZE, FALSE);
440     for (i=0; (s=ciphers[i]); i++)
441     SendDlgItemMessage (dlg, IDC_CDNEW_CIPHERS, CB_ADDSTRING,
442     0, (LPARAM)(const char *) s);
443     SendDlgItemMessage (dlg, IDC_CDNEW_CIPHERS, CB_SETCURSEL, 0, 0);
444     center_window (dlg, NULL);
445     SetForegroundWindow (dlg);
446     break;
447    
448     case WM_COMMAND:
449     switch (LOWORD (wparam)) {
450     case IDOK:
451     i = GetDlgItemText (dlg, IDC_CDNEW_IMGFILE, imgfile, sizeof imgfile-1);
452     if (!i) {
453     msg_box (dlg, _("Please enter a name for the image file."),
454     _("Cryptdisk"), MB_ERR);
455     return FALSE;
456     }
457     if (file_exist_check (imgfile) == 0) {
458     i = msg_box (dlg, _("This volume file already exists.\n"
459     "Do you want to overwrite it?"),
460     _("Cryptdisk Warning"), MB_YESNO|MB_WARN);
461     if (i == IDNO)
462     return FALSE;
463     }
464     for (i=0; i < (int)strlen (imgfile); i++)
465     imgfile[i] = toupper (imgfile[i]);
466     size = GetDlgItemInt (dlg, IDC_CDNEW_SIZE, NULL, FALSE);
467     if (!size) {
468     msg_box (dlg, _("Please enter the size for the volume"),
469     _("Cryptdisk"), MB_INFO);
470     return FALSE;
471     }
472     size *= 1024; /*KB*/
473     if (check_freespace (imgfile, (size*1024))) {
474     msg_box (dlg, _("There is not enough free disk space to "
475     "store the volume."), _("Cryptdisk"), MB_ERR);
476     return FALSE;
477     }
478     i = GetDlgItemText (dlg, IDC_CDNEW_PASS, key, sizeof key-1);
479     if (!i) {
480     msg_box (dlg, _("Please enter a passphrase for the volume."),
481     _("Cryptdisk"), MB_ERR);
482     return FALSE;
483     }
484     strcpy (drv, "?:\\");
485     drv[0] = init_drives (NULL, -1);
486     i = cryptdisk_mount (last_devnum (mounted), imgfile, size, drv);
487     if (!i)
488     shfmt (dlg, drv[0] - 65, 0, 0);
489     break;
490     case IDCANCEL:
491     EndDialog (dlg, FALSE);
492     break;
493     }
494     break;
495    
496     }
497    
498     return FALSE;
499     }
500    
501    
502     BOOL CALLBACK
503     cryptdisk_mount_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
504     {
505     const char * s_imgfile;
506     char buf[8], drive[8], passwd[64], imgfile[128];
507     int n, i, devnum=0;
508     int rc;
509    
510     switch (msg) {
511     case WM_INITDIALOG:
512     do_check (dlg);
513     s_imgfile = (const char *)lparam;
514     if (s_imgfile)
515     SetDlgItemText (dlg, IDC_CDMOUNT_IMGFILE, s_imgfile);
516     init_drives (dlg, IDC_CDMOUNT_DRV);
517     n = num_of_devices ();
518     if (n == -1) {
519     msg_box (dlg, _("Cannot determine the number of drives."),
520     _("Cryptdisk Error"), MB_ERR);
521     EndDialog (dlg, FALSE);
522     }
523     for (i=last_devnum (mounted); i < n; i++) {
524     sprintf (buf, "%d", i);
525     SendDlgItemMessage (dlg, IDC_CDMOUNT_ID, CB_ADDSTRING, 0,
526     (LPARAM)(const char *)buf);
527     }
528     SendDlgItemMessage (dlg, IDC_CDMOUNT_ID, CB_SETCURSEL, 0, 0);
529     center_window (dlg, NULL);
530     SetForegroundWindow (dlg);
531     break;
532    
533     case WM_COMMAND:
534     switch (LOWORD (wparam)) {
535     case IDC_CDMOUNT_SELFILE:
536     s_imgfile = get_fileopen_dlg (dlg, _("Select Crypdisk Volume"),
537     NULL, NULL);
538     if (s_imgfile != NULL)
539     SetDlgItemText (dlg, IDC_CDMOUNT_IMGFILE, s_imgfile);
540     break;
541    
542     case IDOK:
543     n = item_get_text_length (dlg, IDC_CDMOUNT_IMGFILE);
544     if (!n) {
545     msg_box (dlg, _("Please enter the name of the image file."),
546     _("Cryptdisk Error"), MB_ERR);
547     return FALSE;
548     }
549     n = item_get_text_length (dlg, IDC_CDMOUNT_PASS);
550     if (!n) {
551     msg_box (dlg, _("Please enter a password."),
552     _("Cryptdisk Error"), MB_ERR);
553     return FALSE;
554     }
555     devnum = GetDlgItemInt (dlg, IDC_CDMOUNT_ID, NULL, FALSE);
556     GetDlgItemText (dlg, IDC_CDMOUNT_DRV, drive, sizeof drive-1);
557     GetDlgItemText (dlg, IDC_CDMOUNT_PASS, passwd, sizeof passwd-1);
558     GetDlgItemText (dlg, IDC_CDMOUNT_IMGFILE, imgfile, sizeof imgfile-1);
559     if (file_exist_check (imgfile)) {
560     msg_box (dlg, _("Image file does not exist or could not be accessed."),
561     _("Cryptdisk Error"), MB_ERR);
562     return FALSE;
563     }
564     rc = cryptdisk_mount (devnum, imgfile, 0, drive);
565     if (!rc) {
566 twoaday 77 mount_list_t t = new_mount_list (drive, devnum);
567 werner 36 if (!mounted)
568 twoaday 77 mounted = t;
569 werner 36 else
570 twoaday 77 add_mount_list (mounted, t);
571 werner 36 EndDialog (dlg, TRUE);
572     }
573     else
574     msg_box (dlg, winpt_strerror (rc), _("Cryptdisk Error"), MB_ERR);
575     break;
576    
577     case IDCANCEL:
578     EndDialog (dlg, FALSE);
579     break;
580     }
581     break;
582     }
583     return FALSE;
584     }
585    
586    
587     static void
588     load_devs_from_mountlist (HWND dlg, int ctlid)
589     {
590     mount_list_t n;
591     char buf[128];
592    
593     if (!mounted)
594     return;
595    
596     for (n=mounted; n; n=n->next) {
597     if (n->ttl == 0)
598     continue;
599     _snprintf (buf, sizeof buf-1, _("Drive %s (ID %d); Size %d MB, Readonly=%s"),
600     n->drive, n->devnum, n->size.LowPart/1024/1024,
601     n->rdonly? "true" : "false");
602     SendDlgItemMessage (dlg, ctlid, LB_ADDSTRING, 0, (LPARAM)(const char *)buf);
603     }
604    
605     }
606    
607    
608     static void
609     do_reaping (void)
610     {
611     mount_list_t n;
612     int ndevs=0, numount=0;
613    
614     for (n=mounted; n; n=n->next) {
615     if (n->ttl == 0)
616     numount++;
617     ndevs++;
618     }
619     if (ndevs == numount) {
620     free_mount_list (mounted);
621     mounted = NULL;
622     }
623     }
624    
625    
626     BOOL CALLBACK
627     cryptdisk_umount_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
628     {
629     mount_list_t vol;
630     char buf[128];
631     int n, rc=0;
632    
633     switch (msg) {
634     case WM_INITDIALOG:
635     do_check (dlg);
636     load_devs_from_mountlist (dlg, IDC_CDUMOUNT_LIST);
637     center_window (dlg, NULL);
638     SetForegroundWindow (dlg);
639     break;
640    
641     case WM_DESTROY:
642     do_reaping ();
643     break;
644    
645     case WM_COMMAND:
646     switch (LOWORD (wparam)) {
647     case IDOK:
648     n = SendDlgItemMessage (dlg, IDC_CDUMOUNT_LIST, LB_GETCURSEL, 0, 0);
649     if (n == LB_ERR) {
650     msg_box (dlg, _("Please select one drive to umount."), _("Cryptdisk"), MB_INFO);
651     return FALSE;
652     }
653     SendDlgItemMessage (dlg, IDC_CDUMOUNT_LIST, LB_GETTEXT, n,
654     (LPARAM)(char *)buf);
655     strcpy (buf, buf+6);
656     buf[2] = '\0';
657     vol = find_mount_list (mounted, buf);
658     if (!vol)
659     BUG (0);
660     rc = cryptdisk_unmount (buf[0], 0);
661     //if (rc == WPTERR_CDISK_LOCK)
662     // rc = cryptdisk_unmount (buf[0], 0);
663     if (!rc) {
664     SendDlgItemMessage (dlg, IDC_CDUMOUNT_LIST, LB_DELETESTRING, (WPARAM)n, 0);
665     remove_mount_list (mounted, vol);
666     }
667     else
668     msg_box (dlg, winpt_strerror (rc), _("Cryptdisk Error"), MB_ERR);
669     break;
670     case IDCANCEL:
671     EndDialog (dlg, FALSE);
672     break;
673     }
674     break;
675     }
676    
677     return FALSE;
678     }
679    
680    
681     int
682     cryptdisk_serv_run (void)
683     {
684     SC_HANDLE hd;
685     ENUM_SERVICE_STATUS ess[4096];
686     DWORD nservs=0, dummy=0;
687     DWORD i;
688     int rc=0;
689    
690     hd = OpenSCManager (NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
691     if (!hd)
692     return 0;
693    
694     EnumServicesStatus (hd, SERVICE_DRIVER, SERVICE_ACTIVE,
695     (LPENUM_SERVICE_STATUS)&ess,
696     sizeof ess-1, &dummy, &nservs, &dummy);
697    
698     for (i=0; i < nservs; i++) {
699     if (!strnicmp ("FileDisk", ess[i].lpDisplayName, 8)) {
700     rc = 1;
701     break;
702     }
703     }
704    
705     CloseServiceHandle (hd);
706     return rc;
707     }
708 twoaday 77

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26