summaryrefslogtreecommitdiffstats
path: root/private/ole2ui32/utility.cpp
blob: 3a663585d70eaa7d8ce3fdb5f227ec0ff0f6e83a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
/*
 * UTILITY.CPP
 *
 * Utility routines for functions inside OLEDLG.DLL
 *
 *  General:
 *  ----------------------
 *  HourGlassOn             Displays the hourglass
 *  HourGlassOff            Hides the hourglass
 *
 *  Misc Tools:
 *  ----------------------
 *  Browse                  Displays the "File..." or "Browse..." dialog.
 *  ReplaceCharWithNull     Used to form filter strings for Browse.
 *  ErrorWithFile           Creates an error message with embedded filename
 *  OpenFileError           Give error message for OpenFile error return
 *  ChopText                Chop a file path to fit within a specified width
 *  DoesFileExist           Checks if file is valid
 *
 *
 * Copyright (c)1992 Microsoft Corporation, All Right Reserved
 */

#include "precomp.h"
#include "common.h"
#include <stdlib.h>
#include <commdlg.h>
#include <memory.h>
#include <cderr.h>
#include "utility.h"

OLEDBGDATA

/*
 * HourGlassOn
 *
 * Purpose:
 *  Shows the hourglass cursor returning the last cursor in use.
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  HCURSOR         Cursor in use prior to showing the hourglass.
 */

HCURSOR WINAPI HourGlassOn(void)
{
        HCURSOR     hCur;

        hCur=SetCursor(LoadCursor(NULL, IDC_WAIT));
        ShowCursor(TRUE);

        return hCur;
}


/*
 * HourGlassOff
 *
 * Purpose:
 *  Turns off the hourglass restoring it to a previous cursor.
 *
 * Parameters:
 *  hCur            HCURSOR as returned from HourGlassOn
 *
 * Return Value:
 *  None
 */

void WINAPI HourGlassOff(HCURSOR hCur)
{
        ShowCursor(FALSE);
        SetCursor(hCur);
        return;
}


/*
 * Browse
 *
 * Purpose:
 *  Displays the standard GetOpenFileName dialog with the title of
 *  "Browse."  The types listed in this dialog are controlled through
 *  iFilterString.  If it's zero, then the types are filled with "*.*"
 *  Otherwise that string is loaded from resources and used.
 *
 * Parameters:
 *  hWndOwner       HWND owning the dialog
 *  lpszFile        LPSTR specifying the initial file and the buffer in
 *                  which to return the selected file.  If there is no
 *                  initial file the first character of this string should
 *                  be NULL.
 *  lpszInitialDir  LPSTR specifying the initial directory.  If none is to
 *                  set (ie, the cwd should be used), then this parameter
 *                  should be NULL.
 *  cchFile         UINT length of pszFile
 *  iFilterString   UINT index into the stringtable for the filter string.
 *  dwOfnFlags      DWORD flags to OR with OFN_HIDEREADONLY
 *  nBrowseID
 *  lpfnHook        Callback Hook Proceedure.  Set if OFN_ENABLE_HOOK is
 *                  in dwOfnFlags else it should be NULL.
 *
 * Return Value:
 *  BOOL            TRUE if the user selected a file and pressed OK.
 *                  FALSE otherwise, such as on pressing Cancel.
 */

BOOL WINAPI Browse(HWND hWndOwner, LPTSTR lpszFile, LPTSTR lpszInitialDir, UINT cchFile,
        UINT iFilterString, DWORD dwOfnFlags, UINT nBrowseID, LPOFNHOOKPROC lpfnHook)
{
        UINT    cch;
        TCHAR   szFilters[256];
        TCHAR   szDlgTitle[128];  // that should be big enough

        if (NULL == lpszFile || 0 == cchFile)
                return FALSE;

        /*
         * Exact contents of the filter combobox is TBD.  One idea
         * is to take all the extensions in the RegDB and place them in here
         * with the descriptive class name associate with them.  This has the
         * extra step of finding all extensions of the same class handler and
         * building one extension string for all of them.  Can get messy quick.
         * UI demo has only *.* which we do for now.
         */

        if (0 != iFilterString)
        {
                cch = LoadString(_g_hOleStdResInst, iFilterString, szFilters,
                        sizeof(szFilters)/sizeof(TCHAR));
        }
        else
        {
                szFilters[0] = 0;
                cch = 1;
        }

        if (0 == cch)
                return FALSE;

        ReplaceCharWithNull(szFilters, szFilters[cch-1]);

        // Prior string must also be initialized, if there is one.
        OPENFILENAME ofn;
        memset(&ofn, 0, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner   = hWndOwner;
        ofn.lpstrFile   = lpszFile;
        ofn.nMaxFile    = cchFile;
        ofn.lpstrFilter = szFilters;
        ofn.nFilterIndex = 1;
        ofn.lpfnHook = lpfnHook;
        if (LoadString(_g_hOleStdResInst, IDS_BROWSE, szDlgTitle, sizeof(szDlgTitle)/sizeof(TCHAR)))
                ofn.lpstrTitle  = szDlgTitle;
        ofn.hInstance = _g_hOleStdResInst;
        if (NULL != lpszInitialDir)
                ofn.lpstrInitialDir = lpszInitialDir;
        ofn.Flags = OFN_HIDEREADONLY | dwOfnFlags;
        if (bWin4)
            ofn.Flags |= OFN_EXPLORER;

        // Lastly, parent to tweak OFN parameters
        if (hWndOwner != NULL)
                SendMessage(hWndOwner, uMsgBrowseOFN, nBrowseID, (LPARAM)&ofn);

        // On success, copy the chosen filename to the static display
        BOOL bResult = StandardGetOpenFileName((LPOPENFILENAME)&ofn);
        return bResult;
}

/*
 * ReplaceCharWithNull
 *
 * Purpose:
 *  Walks a null-terminated string and replaces a given character
 *  with a zero.  Used to turn a single string for file open/save
 *  filters into the appropriate filter string as required by the
 *  common dialog API.
 *
 * Parameters:
 *  psz             LPTSTR to the string to process.
 *  ch              int character to replace.
 *
 * Return Value:
 *  int             Number of characters replaced.  -1 if psz is NULL.
 */

int WINAPI ReplaceCharWithNull(LPTSTR psz, int ch)
{
        int cChanged = 0;

        if (psz == NULL)
                return -1;

        while ('\0' != *psz)
        {
                if (ch == *psz)
                {
                        *psz++ = '\0';
                        cChanged++;
                        continue;
                }
                psz = CharNext(psz);
        }
        return cChanged;
}

/*
 * ErrorWithFile
 *
 * Purpose:
 *  Displays a message box built from a stringtable string containing
 *  one %s as a placeholder for a filename and from a string of the
 *  filename to place there.
 *
 * Parameters:
 *  hWnd            HWND owning the message box.  The caption of this
 *                  window is the caption of the message box.
 *  hInst           HINSTANCE from which to draw the idsErr string.
 *  idsErr          UINT identifier of a stringtable string containing
 *                  the error message with a %s.
 *  lpszFile        LPSTR to the filename to include in the message.
 *  uFlags          UINT flags to pass to MessageBox, like MB_OK.
 *
 * Return Value:
 *  int             Return value from MessageBox.
 */

int WINAPI ErrorWithFile(HWND hWnd, HINSTANCE hInst, UINT idsErr,
        LPTSTR pszFile, UINT uFlags)
{
        int             iRet=0;
        HANDLE          hMem;
        const UINT      cb = (2*MAX_PATH);
        LPTSTR          psz1, psz2, psz3;

        if (NULL == hInst || NULL == pszFile)
                return iRet;

        // Allocate three 2*MAX_PATH byte work buffers
        hMem=GlobalAlloc(GHND, (DWORD)(3*cb)*sizeof(TCHAR));

        if (NULL==hMem)
                return iRet;

        psz1 = (LPTSTR)GlobalLock(hMem);
        psz2 = psz1+cb;
        psz3 = psz2+cb;

        if (0 != LoadString(hInst, idsErr, psz1, cb))
        {
                wsprintf(psz2, psz1, pszFile);

                // Steal the caption of the dialog
                GetWindowText(hWnd, psz3, cb);
                iRet=MessageBox(hWnd, psz2, psz3, uFlags);
        }

        GlobalUnlock(hMem);
        GlobalFree(hMem);
        return iRet;
}

// returns width of line of text. this is a support routine for ChopText
static LONG GetTextWSize(HDC hDC, LPTSTR lpsz)
{
        SIZE size;

        if (GetTextExtentPoint(hDC, lpsz, lstrlen(lpsz), (LPSIZE)&size))
                return size.cx;
        else
                return 0;
}

LPTSTR FindChar(LPTSTR lpsz, TCHAR ch)
{
        while (*lpsz != 0)
        {
                if (*lpsz == ch)
                        return lpsz;
                lpsz = CharNext(lpsz);
        }
        return NULL;
}

LPTSTR FindReverseChar(LPTSTR lpsz, TCHAR ch)
{
        LPTSTR lpszLast = NULL;
        while (*lpsz != 0)
        {
                if (*lpsz == ch)
                        lpszLast = lpsz;
                lpsz = CharNext(lpsz);
        }
        return lpszLast;
}

static void WINAPI Abbreviate(HDC hdc, int nWidth, LPTSTR lpch, int nMaxChars)
{
        /* string is too long to fit; chop it */
        /* set up new prefix & determine remaining space in control */
        LPTSTR lpszFileName = NULL;
        LPTSTR lpszCur = CharNext(CharNext(lpch));
        lpszCur = FindChar(lpszCur, TEXT('\\'));

        // algorithm will insert \... so allocate extra 4
        LPTSTR lpszNew = (LPTSTR)OleStdMalloc((lstrlen(lpch)+5) * sizeof(TCHAR));
        if (lpszNew == NULL)
                return;

        if (lpszCur != NULL)  // at least one backslash
        {
                *lpszNew = (TCHAR)0;
                *lpszCur = (TCHAR)0;
                lstrcpy(lpszNew, lpch);
                *lpszCur = TEXT('\\');
                // lpszNew now contains c: or \\servername
                lstrcat(lpszNew, TEXT("\\..."));
                // lpszNew now contains c:\... or \\servername\...
                LPTSTR lpszEnd = lpszNew;
                while (*lpszEnd != (TCHAR)0)
                        lpszEnd = CharNext(lpszEnd);
                // lpszEnd is now at the end of c:\... or \\servername\...

                // move down directories until it fits or no more directories
                while (lpszCur != NULL)
                {
                        *lpszEnd = (TCHAR)0;
                        lstrcat(lpszEnd, lpszCur);
                        if (GetTextWSize(hdc, lpszNew) <= nWidth &&
                                lstrlen(lpszNew) < nMaxChars)
                        {
                                lstrcpyn(lpch, lpszNew, nMaxChars);
                                OleStdFree(lpszNew);
                                return;
                        }
                        lpszCur = CharNext(lpszCur);    // advance past backslash
                        lpszCur = FindChar(lpszCur, TEXT('\\'));
                }

                // try just ...filename and then shortening filename
                lpszFileName = FindReverseChar(lpch, TEXT('\\'));
        }
        else
                lpszFileName = lpch;

        while (*lpszFileName != (TCHAR)0)
        {
                lstrcpy(lpszNew, TEXT("..."));
                lstrcat(lpszNew, lpszFileName);
                if (GetTextWSize(hdc, lpszNew) <= nWidth && lstrlen(lpszNew) < nMaxChars)
                {
                        lstrcpyn(lpch, lpszNew, nMaxChars);
                        OleStdFree(lpszNew);
                        return;
                }
                lpszFileName = CharNext(lpszFileName);
        }

        OleStdFree(lpszNew);

        // not even a single character fit
        *lpch = (TCHAR)0;
}

/*
 * ChopText
 *
 * Purpose:
 *  Parse a string (pathname) and convert it to be within a specified
 *  length by chopping the least significant part
 *
 * Parameters:
 *  hWnd            window handle in which the string resides
 *  nWidth          max width of string in pixels
 *                  use width of hWnd if zero
 *  lpch            pointer to beginning of the string
 *  nMaxChars       maximum allowable number of characters (0 ignore)
 *
 * Return Value:
 *  pointer to the modified string
 */
LPTSTR WINAPI ChopText(HWND hWnd, int nWidth, LPTSTR lpch, int nMaxChars)
{
        HDC     hdc;
        HFONT   hfont;
        HFONT   hfontOld = NULL;
        RECT    rc;

        if (!hWnd || !lpch)
                return NULL;

        if (nMaxChars == 0)
                nMaxChars = 32768; // big number

        /* Get length of static field. */
        if (!nWidth)
        {
                GetClientRect(hWnd, (LPRECT)&rc);
                nWidth = rc.right - rc.left;
        }

        /* Set up DC appropriately for the static control */
        hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
        hfont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0L);

        if (NULL != hfont)   // WM_GETFONT returns NULL if window uses system font
                hfontOld = (HFONT)SelectObject(hdc, hfont);

        /* check horizontal extent of string */
        if (GetTextWSize(hdc, lpch) > nWidth || lstrlen(lpch) >= nMaxChars)
                Abbreviate(hdc, nWidth, lpch, nMaxChars);

        if (NULL != hfont)
                SelectObject(hdc, hfontOld);
        DeleteDC(hdc);

        return lpch;
}

/*
 * OpenFileError
 *
 * Purpose:
 *  display message for error returned from OpenFile
 *
 * Parameters:
 *  hDlg            HWND of the dialog.
 *  nErrCode        UINT error code returned in OFSTRUCT passed to OpenFile
 *  lpszFile        LPSTR file name passed to OpenFile
 *
 * Return Value:
 *  None
 */
void WINAPI OpenFileError(HWND hDlg, UINT nErrCode, LPTSTR lpszFile)
{
        switch (nErrCode)
        {
        case 0x0005:    // Access denied
                ErrorWithFile(hDlg, _g_hOleStdResInst, IDS_CIFILEACCESS, lpszFile,
                        MB_OK | MB_ICONEXCLAMATION);
                break;

        case 0x0020:    // Sharing violation
                ErrorWithFile(hDlg, _g_hOleStdResInst, IDS_CIFILESHARE, lpszFile,
                        MB_OK | MB_ICONEXCLAMATION);
                break;

        case 0x0002:    // File not found
        case 0x0003:    // Path not found
                ErrorWithFile(hDlg, _g_hOleStdResInst, IDS_CIINVALIDFILE, lpszFile,
                        MB_OK | MB_ICONEXCLAMATION);
                break;

        default:
                ErrorWithFile(hDlg, _g_hOleStdResInst, IDS_CIFILEOPENFAIL, lpszFile,
                        MB_OK | MB_ICONEXCLAMATION);
                break;
        }
}

/*
 * DoesFileExist
 *
 * Purpose:
 *  Determines if a file path exists
 *
 * Parameters:
 *  lpszFile        LPTSTR - file name
 *  cchMax          UINT - size of the lpszFile string buffer in characters.
 *
 * Return Value:
 *  BOOL            TRUE if file exists, else FALSE.
 *
 * NOTE: lpszFile may be changed as a result of this call to match the first
 *       matching file name found by this routine.
 *
 */
BOOL WINAPI DoesFileExist(LPTSTR lpszFile, UINT cchMax)
{
        static const TCHAR *arrIllegalNames[] =
        {
                TEXT("LPT1"), TEXT("LPT2"), TEXT("LPT3"),
                TEXT("COM1"), TEXT("COM2"), TEXT("COM3"), TEXT("COM4"),
                TEXT("CON"),  TEXT("AUX"),  TEXT("PRN")
        };

        // check is the name is an illegal name (eg. the name of a device)
        for (int i = 0; i < (sizeof(arrIllegalNames)/sizeof(arrIllegalNames[0])); i++)
        {
                if (lstrcmpi(lpszFile, arrIllegalNames[i])==0)
                        return FALSE;
        }

        // First try to find the file with an exact match

        // check the file's attributes
        DWORD dwAttrs = GetFileAttributes(lpszFile);
        if (dwAttrs == 0xFFFFFFFF)  // file wasn't found
        {
            // look in path for file
            TCHAR szTempFileName[MAX_PATH];
            LPTSTR lpszFilePart;
            BOOL fFound = SearchPath(NULL, lpszFile, NULL, MAX_PATH, szTempFileName, &lpszFilePart) != 0;
            if (!fFound)
            {
                // File wasn't found in the search path
                // Try to append a .* and use FindFirstFile to try for a match in the current directory
                UINT cchFile = lstrlen(lpszFile);
                if (cchFile + 4 < MAX_PATH)
                {
                    WIN32_FIND_DATA sFindFileData;
                    lstrcpy(szTempFileName,lpszFile);
                    lstrcpy(&szTempFileName[cchFile], TEXT("*.*"));
                    HANDLE hFindFile = FindFirstFile(szTempFileName, &sFindFileData);
                    if (INVALID_HANDLE_VALUE != hFindFile)
                    {
                        // found something
                        while (0 != sFindFileData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_TEMPORARY))
                        {
                            // found a directory or a temporary file try again
                            if (!FindNextFile(hFindFile, &sFindFileData))
                            {
                                // Could only match a directory or temporary file.
                                FindClose(hFindFile);
                                return(FALSE);
                            }
                        }
                        // Copy the name of the found file into the end of the path in the
                        // temporary buffer (if any).
                        // First scan back for the last file separator.
                        UINT cchPath = lstrlen(szTempFileName);
                        while (cchPath)
                        {
                            if (_T('\\') == szTempFileName[cchPath - 1]
                                || _T('/') == szTempFileName[cchPath - 1])
                            {
                                break;
                            }
                            cchPath--;
                        }
                        lstrcpyn(&szTempFileName[cchPath], sFindFileData.cFileName, MAX_PATH - cchPath);
                        fFound = TRUE;
                        FindClose(hFindFile);
                    }
                }
            }
            if (fFound)
            {
                // copy the temporary buffer into szFile
                lstrcpyn(lpszFile, szTempFileName, cchMax -1);
            }
            return(fFound);
        }
        else if (dwAttrs & (FILE_ATTRIBUTE_DIRECTORY|
                FILE_ATTRIBUTE_TEMPORARY))
        {
                return FALSE;
        }
        return TRUE;
}

/*
 * FormatStrings
 *
 * Purpose:
 *  Simple message formatting API compatible w/ different languages
 *
 * Note:
 *  Shamelessly stolen/modified from MFC source code
 *
 */

void WINAPI FormatStrings(LPTSTR lpszDest, LPCTSTR lpszFormat,
        LPCTSTR* rglpsz, int nString)
{
        LPCTSTR pchSrc = lpszFormat;
        while (*pchSrc != '\0')
        {
                if (pchSrc[0] == '%' && (pchSrc[1] >= '1' && pchSrc[1] <= '9'))
                {
                        int i = pchSrc[1] - '1';
                        pchSrc += 2;
                        if (i >= nString)
                                *lpszDest++ = '?';
                        else if (rglpsz[i] != NULL)
                        {
                                lstrcpy(lpszDest, rglpsz[i]);
                                lpszDest += lstrlen(lpszDest);
                        }
                }
                else
                {
#ifndef _UNICODE
                        if (IsDBCSLeadByte(*pchSrc))
                                *lpszDest++ = *pchSrc++; // copy first of 2 bytes
#endif
                        *lpszDest++ = *pchSrc++;
                }
        }
        *lpszDest = '\0';
}

void WINAPI FormatString1(LPTSTR lpszDest, LPCTSTR lpszFormat, LPCTSTR lpsz1)
{
        FormatStrings(lpszDest, lpszFormat, &lpsz1, 1);
}

void WINAPI FormatString2(LPTSTR lpszDest, LPCTSTR lpszFormat, LPCTSTR lpsz1,
        LPCTSTR lpsz2)
{
        LPCTSTR rglpsz[2];
        rglpsz[0] = lpsz1;
        rglpsz[1] = lpsz2;
        FormatStrings(lpszDest, lpszFormat, rglpsz, 2);
}

// Replacement for stdlib atol,
// which didn't work and doesn't take far pointers.
// Must be tolerant of leading spaces.
//
//
LONG WINAPI Atol(LPTSTR lpsz)
{
        signed int sign = +1;
        UINT base = 10;
        LONG l = 0;

        if (NULL==lpsz)
        {
                OleDbgAssert (0);
                return 0;
        }
        while (*lpsz == ' ' || *lpsz == '\t' || *lpsz == '\n')
                lpsz++;

        if (*lpsz=='-')
        {
                lpsz++;
                sign = -1;
        }
        if (lpsz[0] == TEXT('0') && lpsz[1] == TEXT('x'))
        {
                base = 16;
                lpsz+=2;
        }

        if (base == 10)
        {
                while (*lpsz >= '0' && *lpsz <= '9')
                {
                        l = l * base + *lpsz - '0';
                        lpsz++;
                }
        }
        else
        {
                OleDbgAssert(base == 16);
                while (*lpsz >= '0' && *lpsz <= '9' ||
                        *lpsz >= 'A' && *lpsz <= 'F' ||
                        *lpsz >= 'a' && *lpsz <= 'f')
                {
                        l = l * base;
                        if (*lpsz >= '0' && *lpsz <= '9')
                                l += *lpsz - '0';
                        else if (*lpsz >= 'a' && *lpsz <= 'f')
                                l += *lpsz - 'a' + 10;
                        else
                                l += *lpsz - 'A' + 10;
                        lpsz++;
                }
        }
        return l * sign;
}

BOOL WINAPI IsValidClassID(REFCLSID clsid)
{
        return clsid != CLSID_NULL;
}

/* PopupMessage
 * ------------
 *
 *  Purpose:
 *      Popup messagebox and get some response from the user. It is the same
 *      as MessageBox() except that the title and message string are loaded
 *      from the resource file.
 *
 *  Parameters:
 *      hwndParent      parent window of message box
 *      idTitle         id of title string
 *      idMessage       id of message string
 *      fuStyle         style of message box
 */
int WINAPI PopupMessage(HWND hwndParent, UINT idTitle, UINT idMessage, UINT fuStyle)
{
        TCHAR szTitle[256];
        TCHAR szMsg[256];

        LoadString(_g_hOleStdResInst, idTitle, szTitle, sizeof(szTitle)/sizeof(TCHAR));
        LoadString(_g_hOleStdResInst, idMessage, szMsg, sizeof(szMsg)/sizeof(TCHAR));
        return MessageBox(hwndParent, szMsg, szTitle, fuStyle);
}

/* DiffPrefix
 * ----------
 *
 *  Purpose:
 *      Compare (case-insensitive) two strings and return the prefixes of the
 *      the strings formed by removing the common suffix string from them.
 *      Integrity of tokens (directory name, filename and object names) are
 *      preserved. Note that the prefixes are converted to upper case
 *      characters.
 *
 *  Parameters:
 *      lpsz1           string 1
 *      lpsz2           string 2
 *      lplpszPrefix1   prefix of string 1
 *      lplpszPrefix2   prefix of string 2
 *
 *  Returns:
 *
 */
void WINAPI DiffPrefix(LPCTSTR lpsz1, LPCTSTR lpsz2, TCHAR FAR* FAR* lplpszPrefix1, TCHAR FAR* FAR* lplpszPrefix2)
{
        LPTSTR  lpstr1;
        LPTSTR  lpstr2;
        TCHAR   szTemp1[MAX_PATH];
        TCHAR   szTemp2[MAX_PATH];

        OleDbgAssert(lpsz1);
        OleDbgAssert(lpsz2);
        OleDbgAssert(*lpsz1);
        OleDbgAssert(*lpsz2);
        OleDbgAssert(lplpszPrefix1);
        OleDbgAssert(lplpszPrefix2);

        // need to copy into temporary for case insensitive compare
        lstrcpy(szTemp1, lpsz1);
        lstrcpy(szTemp2, lpsz2);
        CharLower(szTemp1);
        CharLower(szTemp2);

        // do comparison
        lpstr1 = szTemp1 + lstrlen(szTemp1);
        lpstr2 = szTemp2 + lstrlen(szTemp2);

        while ((lpstr1 > szTemp1) && (lpstr2 > szTemp2))
        {
                lpstr1 = CharPrev(szTemp1, lpstr1);
                lpstr2 = CharPrev(szTemp2, lpstr2);
                if (*lpstr1 != *lpstr2)
                {
                        lpstr1 = CharNext(lpstr1);
                        lpstr2 = CharNext(lpstr2);
                        break;
                }
        }

        // scan forward to first delimiter
        while (*lpstr1 && *lpstr1 != '\\' && *lpstr1 != '!')
                lpstr1 = CharNext(lpstr1);
        while (*lpstr2 && *lpstr2 != '\\' && *lpstr2 != '!')
                lpstr2 = CharNext(lpstr2);

        *lpstr1 = '\0';
        *lpstr2 = '\0';

        // initialize in case of failure
        *lplpszPrefix1 = NULL;
        *lplpszPrefix2 = NULL;

        // allocate memory for the result
        *lplpszPrefix1 = (LPTSTR)OleStdMalloc((lstrlen(lpsz1)+1) * sizeof(TCHAR));
        if (!*lplpszPrefix1)
                return;

        *lplpszPrefix2 = (LPTSTR)OleStdMalloc((lstrlen(lpsz2)+1) * sizeof(TCHAR));
        if (!*lplpszPrefix2)
        {
                OleStdFree(*lplpszPrefix1);
                *lplpszPrefix1 = NULL;
                return;
        }

        // copy result
        lstrcpyn(*lplpszPrefix1, lpsz1, lstrlen(szTemp1)+1);
        lstrcpyn(*lplpszPrefix2, lpsz2, lstrlen(szTemp2)+1);
}

UINT WINAPI GetFileName(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax)
{
        // always capture the complete file name including extension (if present)
        LPTSTR lpszTemp = (LPTSTR)lpszPathName;
        for (LPCTSTR lpsz = lpszPathName; *lpsz != '\0'; lpsz = CharNext(lpsz))
        {
                // remember last directory/drive separator
                if (*lpsz == '\\' || *lpsz == '/' || *lpsz == ':')
                        lpszTemp = CharNext(lpsz);
        }

        // lpszTitle can be NULL which just returns the number of bytes
        if (lpszTitle == NULL)
                return lstrlen(lpszTemp)+1;

        // otherwise copy it into the buffer provided
        lstrcpyn(lpszTitle, lpszTemp, nMax);
        return 0;
}

BOOL WINAPI IsValidMetaPict(HGLOBAL hMetaPict)
{
    BOOL fReturn = FALSE;
    LPMETAFILEPICT pMF = (LPMETAFILEPICT) GlobalLock(hMetaPict);
    if (pMF != NULL)
    {
        if (!IsBadReadPtr( pMF, sizeof(METAFILEPICT)))
        {
            if (GetMetaFileBitsEx(pMF->hMF, 0, 0))
            {
                fReturn = TRUE;
            }
        }
        GlobalUnlock(hMetaPict);
    }
    return(fReturn);
}

/////////////////////////////////////////////////////////////////////////////