File: w32ole.cpp

package info (click to toggle)
vile 9.8za-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,644 kB
  • sloc: ansic: 120,894; lex: 14,981; sh: 4,478; perl: 3,511; cpp: 3,180; makefile: 1,425; awk: 271
file content (1613 lines) | stat: -rw-r--r-- 43,911 bytes parent folder | download | duplicates (2)
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
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
/*
 * w32ole.cpp:  Winvile's OLE implementation (the editor currently
 *              supports only OLE Automation).
 *
 * Note:  A great deal of the code included in this file was copied from
 * the Microsoft platform sdk samples directory, specifically from:
 *
 *    samples\com\oleaut\hello\hello .
 *
 * However, this implementation handles the Release() methods quite
 * differently and so, I wouldn't use this code as a guide for writing
 * any other automation servers :-) .
 *
 * Caveats
 * =======
 * - Due to a conflict between estruct.h and MS names, the Windows macros
 *   "FAILED" may not be used to test an OLE return code.  Use SUCCEEDED
 *   instead.
 *
 * $Id: w32ole.cpp,v 1.37 2022/08/21 23:37:31 tom Exp $
 */

#include "w32vile.h"

#include <ole2.h>
#include <comutil.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>

extern "C"
{
    #include "estruct.h"
    #undef FAILED          /* Don't want this defn of FAILED */
    #include "edef.h"
}
#include "w32ole.h"
#include "w32reg.h"

#include <initguid.h>

#if (_MSC_VER >= 1200) && (_MSC_VER < 1300)
#define VC6_ADDIN 1             /* Visual C++ 6.0 */
#else
#define VC6_ADDIN 0
#endif

#if (_MSC_VER >= 1300) && (_MSC_VER < 1400)
#define VC7_ADDIN 1             /* Visual C++ 7.0 */
#else
#define VC7_ADDIN 0
#endif

#if VC6_ADDIN
#include <objmodel/appguid.h>
#include <objmodel/textguid.h>
#include <objmodel/appauto.h>
#include <objmodel/textauto.h>
#endif

#if VC7_ADDIN
#include <objbase.h>
#endif

static size_t   ansibuf_len,   /* scaled in bytes   */
                olebuf_len;    /* scaled in wchar_t */
static char     *ansibuf;
static DWORD    dwRegisterCF, dwRegisterActiveObj;
static vile_oa  *g_pvile_oa;
static int      oleauto_server;
static OLECHAR  *olebuf;
static HWND     redirect_hwnd;
static DWORD    redirect_tid, winvile_tid;   // tid == thread id

/* ------------------------ C Linkage Helpers ---------------------- */

extern "C"
{

#if OPT_TRACE
#undef TRACE
#define Trace MyTrace
static void
Trace(const char *fmt, ...)
{
    FILE *fp = fopen("c:\\temp\\w32ole.log", "a");
    if (fp != 0)
    {
        va_list ap;
        va_start(ap, fmt);
        vfprintf(fp, fmt, ap);
        va_end(ap);
        fclose(fp);
    }
}
#define TRACE(params) Trace params
#else
#define OPT_TRACE 0
#define TRACE(params) /* nothing */
#endif

int
oleauto_init(OLEAUTO_OPTIONS *opts)
{
    HRESULT        hr;
    LPCLASSFACTORY pcf = NULL;

    olebuf_len = ansibuf_len = 512;
    ansibuf    = typeallocn(char, ansibuf_len);
    olebuf     = typeallocn(OLECHAR, olebuf_len);
    if (! (olebuf && ansibuf))
        return (FALSE);

    hr = OleInitialize(NULL);   // Initialize OLE
    if (! SUCCEEDED(hr))
    {
        disp_win32_error(hr, NULL);
        return (FALSE);
    }

    oleauto_server = TRUE;

    // Create an instance of the Vile Application object. Object is
    // created with refcount 0.
    if (! SUCCEEDED(vile_oa::Create(&g_pvile_oa, ! opts->invisible)))
        return (FALSE);

    // Expose class factory for application object.
    pcf = new vile_oa_cf;
    if (! pcf)
        return (FALSE);
    pcf->AddRef();
    hr = CoRegisterClassObject(CLSID_VileAuto,
                               pcf,
                               CLSCTX_LOCAL_SERVER,
                    (opts->multiple) ? REGCLS_SINGLEUSE : REGCLS_MULTIPLEUSE,
                               &dwRegisterCF);
    pcf->Release();
    if (! SUCCEEDED(hr))
    {
        disp_win32_error(hr, NULL);
        return (FALSE);
    }

    /*
     * Register Vile application object in the Running Object Table (ROT).
     * This allows controllers to connect to a running application object
     * instead of creating a new instance.  Use weak registration so that
     * the ROT releases its reference when all external references are
     * released.  If strong registration is used, the ROT will not release
     * its reference until RevokeActiveObject is called and so will keep
     * the object alive even after all external references have been
     * released.
     */
    hr = RegisterActiveObject(g_pvile_oa,
                              CLSID_VileAuto,
                              ACTIVEOBJECT_WEAK,
                              &dwRegisterActiveObj);
    if (! SUCCEEDED(hr))
    {
        disp_win32_error(hr, NULL);
        return (FALSE);
    }
    if (! opts->invisible)
    {
        hr = CoLockObjectExternal(g_pvile_oa, TRUE, TRUE);
        if (! SUCCEEDED(hr))
        {
            disp_win32_error(hr, NULL);
            return (FALSE);
        }
    }
    return (TRUE);
}

void
oleauto_exit(int code)
{
    if (ansibuf)
        free(ansibuf);
    if (olebuf)
        free(olebuf);
    if (oleauto_server)
    {
        if (g_pvile_oa)
        {
            /*
             * For a local server, CoDisconnectObject will disconnect the
             * object from external connections.  So the controller will
             * get an RPC error if it accesses the object after calling
             * Quit and the controller will not GP fault.
             */

            CoDisconnectObject(g_pvile_oa, 0);
            delete g_pvile_oa;
        }
        if (dwRegisterCF != 0)
            CoRevokeClassObject(dwRegisterCF);
        if (dwRegisterActiveObj != 0)
            RevokeActiveObject(dwRegisterActiveObj, NULL);
        OleUninitialize();
    }
#ifdef VILE_ERROR_ABORT
#undef ExitProgram              /* use VILE_ERROR_ABORT code in main.c */
    ExitProgram(code);
#else
    exit(code);
#endif
}

} /* Extern "C" */

/* ----------------------- C++ Helper Functions --------------------- */

#if OPT_TRACE && !(defined(_UNICODE) || defined(UNICODE))
static char *visible_wcs(OLECHAR *source)
{
    static char *result = 0;
    static unsigned len = 0;
    char *target;
    unsigned need = 0;
    unsigned n;
    if (source != 0)
    {
	for (n = 0; source[n] != 0; ++n)
	    ;
	need = 10 * (n + 2);
	if (need > len)
	{
	    len = need;
            result = (char *) realloc(result, need * sizeof(OLECHAR));
	}
	for (n = 0, target = result; source[n] != 0; ++n)
	{
	    switch (source[n])
	    {
	    case ' ':
		strcpy(target, "\\s");
		break;
	    case '\\':
		strcpy(target, "\\\\");
		break;
	    case '\n':
		strcpy(target, "\\n");
		break;
	    case '\r':
		strcpy(target, "\\r");
		break;
	    case '\t':
		strcpy(target, "\\t");
		break;
	    case '\f':
		strcpy(target, "\\f");
		break;
	    default:
		if (source[n] >= ' '&& source[n] < 256)
		{
		    sprintf(target, "%c", (char) source[n]);
		}
		else
		{
		    sprintf(target, "\\%03o", (unsigned char) source[n]);
		}
		break;
	    }
	    target += strlen(target);
	}
    }
    return result;
}
#endif

/*
 * Quick & Dirty ANSI/Unicode conversion routines.  These routines use a
 * dynamic buffer to hold the converted string so it may be any arbitrary
 * size.  However, the same dynamic buffer is reused when the routine is
 * called a second time.  So make sure that the converted string is
 * used/copied before the conversion routine is called again.
 *
 */
#if 0 // !(defined(_UNICODE) || defined(UNICODE))

static char *
ConvertToAnsi(OLECHAR *szW)
{
    size_t len;

    len = wcstombs(NULL, szW, 1) + 1;
    if ((len + 1) > ansibuf_len)
    {
        if (ansibuf)
            free(ansibuf);
        ansibuf_len = ansibuf_len * 2 + len;
        if ((ansibuf = typeallocn(char, ansibuf_len)) == NULL)
            return (ansibuf);  /* We're gonna' die */
    }
    if (len > 0)
    {
	wcstombs(ansibuf, szW, len);
    }
    else
    {
	*ansibuf = '\0';
    }
    TRACE(("ConvertToAnsi %d->%d:%s\n", len, strlen(ansibuf), ansibuf));
    return (ansibuf);
}
#endif

#if !(defined(_UNICODE) || defined(UNICODE))
static OLECHAR *
ConvertToUnicode(const char *szA)
{
    size_t len;

    len = strlen(szA) + 1;
    TRACE(("ConvertToUnicode %d:%s\n", strlen(szA), szA));
    if (len > olebuf_len)
    {
        if (olebuf)
            free(olebuf);
        olebuf_len = olebuf_len * 2 + len;
        if ((olebuf = typeallocn(OLECHAR, olebuf_len)) == NULL)
            return (olebuf);  /* We're gonna' die */
    }
    mbstowcs(olebuf, szA, len);
    TRACE(("Result %s\n", visible_wcs(olebuf)));
    return (olebuf);
}
#endif

HRESULT
LoadTypeInfo(ITypeInfo **pptinfo, REFCLSID clsid)
{
    HRESULT hr;
    LPTYPELIB ptlib = NULL;
    LPTYPEINFO ptinfo = NULL;

    *pptinfo = NULL;

    hr = LoadRegTypeLib(LIBID_VileAuto, VTL_MAJOR, VTL_MINOR, 0x09, &ptlib);
    if (! SUCCEEDED(hr))
        return hr;

    // Get type information for interface of the object.
    hr = ptlib->GetTypeInfoOfGuid(clsid, &ptinfo);
    ptlib->Release();
    if (! SUCCEEDED(hr))
        return hr;

    *pptinfo = ptinfo;
    return NOERROR;
}

#if VC6_ADDIN
static int
ole_err_to_msgline(HRESULT hr, char *msg)
{
    char          *lclbuf = NULL, tmp[256], outstr[256];
    unsigned char *src, *dst;
    int           print_high, print_low;

    print_high = global_g_val(GVAL_PRINT_HIGH);
    print_low  = global_g_val(GVAL_PRINT_LOW);
    fmt_win32_error(hr, &lclbuf, 0);
    sprintf(tmp, "[%s, system msg: %s]", msg, lclbuf);

    /*
     * Strip out unprintable data from Win32 err desc (assume ASCII char set).
     * Why is garbage included in the returned error string?
     */
    src = (unsigned char *)tmp;
    dst = (unsigned char *)outstr;
    while (*src)
    {
        if (*src == _TAB_)
            *dst++ = _SPC_;
        else if ((*src >= _SPC_ && *src <= _TILDE_) ||
                                          (*src > _TILDE_ && PASS_HIGH(*src)))
        {
            *dst++ = *src;
        }
        *src++;
    }
    *dst = '\0';

    mlforce(outstr);
    LocalFree(lclbuf);
    return (FALSE);
}
#endif

/* ------------------------ Class Factory -------------------------- */

vile_oa_cf::vile_oa_cf(void)
{
    m_cRef = 0;
}

STDMETHODIMP
vile_oa_cf::QueryInterface(REFIID iid, void **ppv)
{
    *ppv = NULL;

    if (iid == IID_IUnknown || iid == IID_IClassFactory)
        *ppv = this;
    else
        return E_NOINTERFACE;

    AddRef();
    return NOERROR;
}

STDMETHODIMP_(ULONG)
vile_oa_cf::AddRef(void)
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG)
vile_oa_cf::Release(void)
{
    if(--m_cRef == 0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

STDMETHODIMP
vile_oa_cf::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
    HRESULT hr;

    *ppv = NULL;

    // This implementation doesn't allow aggregation
    if (punkOuter)
        return CLASS_E_NOAGGREGATION;

    /*
     * If this is a REGCLS_SINGLEUSE class factory, CreateInstance will be
     * called at most once.  The global application object has already been
     * created when CreateInstance is called.  A REGCLS_MULTIPLEUSE class
     * factory's CreateInstance would be called multiple times and would
     * create a new object each time.
     */
    hr = g_pvile_oa->QueryInterface(riid, ppv);
    if (!  SUCCEEDED(hr))
    {
        g_pvile_oa->Quit();
        return hr;
    }
    return (NOERROR);
}

STDMETHODIMP
vile_oa_cf::LockServer(BOOL fLock)
{
    HRESULT hr;

    hr = CoLockObjectExternal(g_pvile_oa, fLock, TRUE);
    if (! SUCCEEDED(hr))
        disp_win32_error(hr, NULL);
    return (hr);
}

/* ------------------------------- vile_oa ------------------------------ */

HRESULT
vile_oa::Create(vile_oa **ppvile, BOOL visible)
{
    HRESULT hr;
    vile_oa *pvile = NULL;
    OLECHAR *tmp;

    *ppvile = NULL;

    // Create Application object
    pvile = new vile_oa();
    if (pvile == NULL)
        return E_OUTOFMEMORY;

    pvile->m_hwnd     = (HWND) winvile_hwnd();
    pvile->m_bVisible = (visible) ? VARIANT_TRUE : VARIANT_FALSE;

    // Name
    tmp = reinterpret_cast<OLECHAR *>(TO_OLE_STRING(prognam));
    if (! (tmp && (pvile->m_bstrName = SysAllocString(tmp)) != 0))
        return E_OUTOFMEMORY;

    // Load type information from type library.
    hr = LoadTypeInfo(&pvile->m_ptinfo, IID_IVileAuto);
    if (! SUCCEEDED(hr))
    {
        w32_message_box(pvile->m_hwnd,
			"Cannot load type library.\r\r Register type info using winvile's '-Or' command line switch",
			MB_OK | MB_ICONSTOP);
        return (hr);
    }

    *ppvile = pvile;
    return NOERROR;
}

vile_oa::vile_oa()
{
    m_bstrFullName = NULL;
    m_bstrName     = NULL;
    m_ptinfo       = NULL;
    m_cRef         = 0;
}

vile_oa::~vile_oa()
{
    if (m_bstrFullName) SysFreeString(m_bstrFullName);
    if (m_bstrName)     SysFreeString(m_bstrName);
    if (m_ptinfo)       m_ptinfo->Release();
}

STDMETHODIMP
vile_oa::QueryInterface(REFIID iid, void **ppv)
{
    *ppv = NULL;

    if (iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IVileAuto)
        *ppv = this;
    else
        return E_NOINTERFACE;

    AddRef();
    return NOERROR;
}


STDMETHODIMP_(ULONG)
vile_oa::AddRef(void)
{
    return (++m_cRef);
}

STDMETHODIMP_(ULONG)
vile_oa::Release(void)
{
    if (--m_cRef == 0)
        PostQuitMessage(0);
    return (m_cRef);
}

STDMETHODIMP
vile_oa::GetTypeInfoCount(UINT *pctinfo)
{
    *pctinfo = 1;
    return NOERROR;
}

STDMETHODIMP
vile_oa::GetTypeInfo(
      UINT itinfo,
      LCID lcid,
      ITypeInfo **pptinfo)
{
    *pptinfo = NULL;

    if(itinfo != 0)
        return DISP_E_BADINDEX;

    m_ptinfo->AddRef();
    *pptinfo = m_ptinfo;

    return NOERROR;
}

STDMETHODIMP
vile_oa::GetIDsOfNames(
      REFIID riid,
      OLECHAR **rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID *rgdispid)
{
    return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
}

STDMETHODIMP
vile_oa::Invoke(
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS *pdispparams,
      VARIANT *pvarResult,
      EXCEPINFO *pexcepinfo,
      UINT *puArgErr)
{
    return DispInvoke(
        this, m_ptinfo,
        dispidMember, wFlags, pdispparams,
        pvarResult, pexcepinfo, puArgErr);
}

STDMETHODIMP
vile_oa::get_Application(IVileAuto **ppvile)
{
    HRESULT hr;

    *ppvile = NULL;

    hr = QueryInterface(IID_IDispatch, (void **)ppvile);
    return ((SUCCEEDED(hr)) ? NOERROR : E_UNEXPECTED);
}

STDMETHODIMP
vile_oa::get_FullName(BSTR *pbstr)
{
    if (! m_bstrFullName)
    {
        char    *cp, key[NFILEN];
        HKEY    hk;
        long    rc;
        OLECHAR *tmp;
        char    value[NFILEN * 2];

        *pbstr  = NULL;

        /* Extract server path from registry. */
        sprintf(key, "CLSID\\%s\\LocalServer32", CLSID_VILEAUTO_KEY);
        if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
                         reinterpret_cast<LPTSTR>(w32_charstring(key)),
                         0,
                         KEY_QUERY_VALUE,
                         &hk) != ERROR_SUCCESS)
        {
            disp_win32_error(W32_SYS_ERROR, m_hwnd);
            return (E_UNEXPECTED);
        }
        rc = w32_get_reg_sz(hk, NULL, value, sizeof(value));
        RegCloseKey(hk);
        if (rc != ERROR_SUCCESS)
        {
            disp_win32_error(W32_SYS_ERROR, m_hwnd);
            return (E_UNEXPECTED);
        }
        if ((cp = strchr(value, ' ')) != NULL)
            *cp = '\0';
        tmp = reinterpret_cast<OLECHAR *>(TO_OLE_STRING(value));
        if (! (tmp && (m_bstrFullName = SysAllocString(tmp)) != 0))
            return (E_OUTOFMEMORY);
    }
    *pbstr = SysAllocString(m_bstrFullName);
    return ((*pbstr) ? NOERROR : E_OUTOFMEMORY);
}

STDMETHODIMP
vile_oa::get_Name(BSTR *pbstr)
{
    *pbstr = SysAllocString(m_bstrName);
    return ((*pbstr) ? NOERROR : E_OUTOFMEMORY);
}

STDMETHODIMP
vile_oa::get_Parent(IVileAuto **ppvile)
{
    HRESULT hr;

    *ppvile = NULL;

    hr = QueryInterface(IID_IDispatch, (void **)ppvile);
    return ((SUCCEEDED(hr)) ? NOERROR : E_UNEXPECTED);
}

STDMETHODIMP
vile_oa::Quit()
{
    PostQuitMessage(0);
    return NOERROR;
}

STDMETHODIMP
vile_oa::VileKeys(BSTR keys)
{
    HRESULT hr   = NOERROR;
    char    *msg = _com_util::ConvertBSTRToString(keys);

    TRACE(("VileKeys %d bytes:\n%s\n", strlen(msg), msg));
    while (*msg)
    {
        if (! PostMessage(m_hwnd, WM_CHAR, *msg, 0))
        {
            disp_win32_error(W32_SYS_ERROR, m_hwnd);
            hr = E_UNEXPECTED;
            break;
        }
        msg++;
    }
    return (hr);
}

STDMETHODIMP
vile_oa::put_Visible(VARIANT_BOOL bVisible)
{
    if (bVisible != m_bVisible)
    {
        HRESULT hr;

        m_bVisible = bVisible;
        hr = CoLockObjectExternal(this, (m_bVisible) ? TRUE : FALSE, TRUE);
        if (! SUCCEEDED(hr))
        {
            disp_win32_error(hr, m_hwnd);
            return (hr);
        }
        ::ShowWindow(m_hwnd, bVisible ? SW_SHOW : SW_HIDE);
    }
    return (NOERROR);
}

STDMETHODIMP
vile_oa::get_Visible(VARIANT_BOOL *pbool)
{
    *pbool = m_bVisible;
    return NOERROR;
}

STDMETHODIMP
vile_oa::get_IsMinimized(VARIANT_BOOL *pbool)
{
    WINDOWPLACEMENT wp;

    wp.length = sizeof(wp);
    if (! GetWindowPlacement(m_hwnd, &wp))
        return (GetLastError());
    *pbool = (wp.showCmd == SW_MINIMIZE ||
              wp.showCmd == SW_SHOWMINIMIZED ||
              wp.showCmd == SW_SHOWMINNOACTIVE) ? VARIANT_TRUE : VARIANT_FALSE;
    return NOERROR;
}

STDMETHODIMP
vile_oa::get_InsertMode(VARIANT_BOOL *pbool)
{
    *pbool = (insertmode) ? VARIANT_TRUE : VARIANT_FALSE;
    return NOERROR;
}

STDMETHODIMP
vile_oa::get_GlobMode(VARIANT_BOOL *pbool)
{
    *pbool = (global_g_val(GMDGLOB)) ? VARIANT_TRUE : VARIANT_FALSE;
    return NOERROR;
}

// ForegroundWindow() is not very useful on Win2K or XP...
// See MSDN's description of SetForegroundWindow() for further details.
// As an alternative, use vile_oa::get_MainHwnd() to obtain a handle
// to the editor's main window and call SetForegoundWindow() directly.
// See wvwrap.cpp for an example.
STDMETHODIMP
vile_oa::ForegroundWindow()
{
    if (! m_bVisible)
        put_Visible(TRUE);   /* Force window to be visible, first. */
    ::SetForegroundWindow(m_hwnd);
    return NOERROR;
}

STDMETHODIMP
vile_oa::get_MainHwnd(LONG *phwnd /* for VB compatibility */)
{
    *phwnd = (LONG) m_hwnd;   // ugh
    return NOERROR;
}

STDMETHODIMP
vile_oa::Minimize()
{
    ::ShowWindow(m_hwnd, SW_MINIMIZE);
    if (! m_bVisible)
    {
        /*
         * Minimizing an invisible window will make it visible, so keep the
         * m_bVisible bookkeeping up-to-date.
         */

        put_Visible(TRUE);
    }
    return (NOERROR);
}

STDMETHODIMP
vile_oa::Restore()
{
    ::ShowWindow(m_hwnd, SW_RESTORE);
    if (! m_bVisible)
    {
        /*
         * Restoring an invisible window will make it visible, so keep the
         * m_bVisible bookkeeping up-to-date.
         */

        put_Visible(TRUE);
    }
    return (NOERROR);
}

STDMETHODIMP
vile_oa::WindowRedirect(DWORD hwnd /* Null HANDLE => don't redirect */)
{
    redirect_hwnd = (HWND) hwnd;
    ntwinio_redirect_hwnd(redirect_hwnd != NULL);
    redirect_tid = GetWindowThreadProcessId(redirect_hwnd, NULL);
    winvile_tid  = GetWindowThreadProcessId(m_hwnd, NULL);
    return (NOERROR);
}

/* ----------- C Linkage To OLE-specific Editor Functions ---------------- */

extern "C"
{

/*
 * Wait for a file to spring into existence with > 0 bytes of data.  This
 * editor function is used to work around a bug in DevStudio 5, which
 * creates its build log _after_ notifying visvile that a build is complete.
 */
int
waitfile(int f, int n)
{
#define SLEEP_INCR 100

    char         fname[NFILEN];  /* wait for this file */
    DWORD        how_long, curr_sleep;
    int          s;              /* status return */
    struct _stat statdata;

    fname[0] = '\0';
    how_long = (f) ? n : 2000;  /* If wait period not specified as argument,
                                 * wait up to 2 seconds.
                                 */
    if ((s = mlreply_no_opts("Wait for file: ", fname, sizeof(fname))) == TRUE)
    {
        curr_sleep = 0;
        for (;;)
        {
            if (_stat(fname, &statdata) == 0)
            {
                if (statdata.st_size > 0)
                    break;
            }
            Sleep(SLEEP_INCR);
            curr_sleep += SLEEP_INCR;
            if (curr_sleep > how_long)
            {
                mlwrite("timed out waiting for \"%s\"", fname);
                break;
            }
        }
    }
    return (s);
}



/* Synchronize the current buffer (at its current line) in DevStudio. */
int
syncfile(int f, int n)
{
#if VC6_ADDIN
    static CLSID     ds_clsid;   /* dev studio class id */
    HRESULT          hr;
    static int       initialized;
    IApplication     *pApp;
    IUnknown         *punk;
    IDispatch        *pDisp;
    IDocuments       *pDocuments;
    IGenericDocument *pGenDoc;
    ITextDocument    *pTextDoc;
    ITextSelection   *pTextSel;
    int              rc = TRUE;
    BSTR             path, doc_type;
    VARIANT_BOOL     vb;
    variant_t        vtype,   // initialized to VT_EMPTY by default
                     vfalse;

    if (b_is_temporary(curbp) || isInternalName(curbp->b_bname))
    {
        mlforce("[Can't synchronize scratch/invisible buffer]");
        return (FALSE);
    }
    if (! initialized)
    {
        hr = CLSIDFromProgID(L"MSDEV.APPLICATION", &ds_clsid);
        if (! SUCCEEDED(hr))
        {
            mlforce("[Unable to read DevStudio's CLSID from the registry.]");
            return (FALSE);
        }
        initialized = TRUE;
    }
    if (! oleauto_server)
    {
        /*
         * This instance of winvile was not invoked in automation mode.
         * deal with this.
         */

        olebuf_len = ansibuf_len = 512;
        ansibuf    = typeallocn(char, ansibuf_len);
        olebuf     = typeallocn(OLECHAR, olebuf_len);
        if (! (olebuf && ansibuf))
        {
            no_memory("syncfile()");
            return (FALSE);
        }
        hr = CoInitialize(NULL); // Fire up COM.
        if (! SUCCEEDED(hr))
        {
            // we're dead.

            (void) ole_err_to_msgline(hr, "CoInitialize() failed");
            return (FALSE);
        }
        oleauto_server = TRUE;
    }

    /*
     * Connect to an instance of DevStudio.   Note that no attempt is
     * made to start an instance of DevStudio if one is not already running.
     * Reason:  visvile key redirection won't work in that case.
     */
    hr = GetActiveObject(ds_clsid, 0, &punk);
    if (! SUCCEEDED(hr))
    {
        (void) ole_err_to_msgline(hr, "DevStudio not active");
        return (FALSE);
    }
    hr = punk->QueryInterface(IID_IApplication, (void **) &pApp);
    punk->Release();
    if (! SUCCEEDED(hr))
    {
        (void) ole_err_to_msgline(hr, "DevStudio App ptr load failed");
        return (FALSE);
    }
    hr = pApp->get_Visible(&vb);
    if (! SUCCEEDED(hr))
    {
        rc = ole_err_to_msgline(hr, "get_Visible() failed");
        goto syncfile_exit;
    }
    if (vb != VARIANT_TRUE)
    {
        hr = pApp->put_Visible(VARIANT_TRUE);
        if (! SUCCEEDED(hr))
        {
            rc = ole_err_to_msgline(hr, "put_Visible() failed");
            goto syncfile_exit;
        }
    }
    hr = pApp->get_Documents(&pDisp);
    if (! SUCCEEDED(hr))
    {
        rc = ole_err_to_msgline(hr, "get_Documents() failed");
        goto syncfile_exit;
    }
    hr = pDisp->QueryInterface(IID_IDocuments, (void **) &pDocuments);
    pDisp->Release();
    if (! SUCCEEDED(hr))
    {
        rc = ole_err_to_msgline(hr, "DevStudio Docs ptr load failed");
        goto syncfile_exit;
    }
    path = SysAllocString(TO_OLE_STRING(sl_to_bsl(curbp->b_fname)));
    if (! path)
    {
        no_memory("syncfile()");
        pDocuments->Release();
        rc = FALSE;
        goto syncfile_exit;
    }
    vfalse = VARIANT_FALSE;
    hr     = pDocuments->Open(path, vtype, vfalse, &pDisp);
    pDocuments->Release();
    SysFreeString(path);
    if (! SUCCEEDED(hr))
    {
        rc = ole_err_to_msgline(hr, "DevStudio document open failed");
        goto syncfile_exit;
    }
    hr = pDisp->QueryInterface(IID_IGenericDocument, (void **) &pGenDoc);
    pDisp->Release();
    if (! SUCCEEDED(hr))
    {
        rc = ole_err_to_msgline(hr, "DevStudio gen doc ptr load failed");
        goto syncfile_exit;
    }
    pGenDoc->get_Type(&doc_type);
    if (doc_type && wcscmp(L"Text", (OLECHAR *) doc_type) == 0)
    {
        /*
         * This is a text document -- it's possible to sync the editor's
         * current buffer position with DevStudio's text editor line posn.
         */

        hr = pGenDoc->QueryInterface(IID_ITextDocument, (void **) &pTextDoc);
        pGenDoc->Release();
        if (! SUCCEEDED(hr))
        {
            rc = ole_err_to_msgline(hr, "Text Document ptr load failed");
            goto syncfile_exit;
        }
        hr = pTextDoc->get_Selection(&pDisp);
        pTextDoc->Release();
        if (! SUCCEEDED(hr))
        {
            rc = ole_err_to_msgline(hr, "get_Selection() failed");
            goto syncfile_exit;
        }
        hr = pDisp->QueryInterface(IID_ITextSelection, (void **) &pTextSel);
        pDisp->Release();
        if (! SUCCEEDED(hr))
        {
            rc = ole_err_to_msgline(hr, "Text Slctn ptr load failed");
            goto syncfile_exit;
        }
        vtype = (long) dsMove;
        pTextSel->GoToLine(getcline(), vtype);
        pTextSel->Release();
    }
    else
    {
        /* Else can't manipulate a "Generic" DevStudio document. */

        pGenDoc->Release();
        mlforce("[Cannot position document w/in DevStudio]");
    }

syncfile_exit:
    pApp->Release();
    return (rc);
#else
    return 0;			// FIXME
#endif
}

/* ------------------- Key Redirection Implementation -------------------- */

#define FLUSH_BUFFERS 0x1
#define SWITCH_FOCUS  0x2
#define SYNC_BUFFER   0x4

static int rebuild_cache_tbl = TRUE;
                              /* Boolean, T -> the GVAL_REDIRECT_KEYS list
                               * has either changed or has not been initially
                               * parsed.  In either case, parse this list and
                               * change it into a dynamic data structure
                               * stored in "cache_tbl".
                               */
typedef struct redir_key_struct
{
    unsigned vk;              /* virtual key code */
    unsigned modifier;        /* vk modifier, possible values are:
                               *
                               *   (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED),
                               *   (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED),
                               *   SHIFT_PRESSED, or
                               *   0                      (no modifier)
                               */
    unsigned action;          /* action taken before key is pressed.  Possible
                               * values are:
                               *
                               *   FLUSH_BUFFERS, SWITCH_FOCUS, SYNC_BUFFER
                               */
} REDIR_KEY;

static REDIR_KEY *cache_tbl;      /* User-specified list of redirected keys. */
static unsigned  cache_tbl_nelem; /* # elements in cache_tbl.                */

/*
 * Win32 virtual key code -> string mapping table.  Mappings are only
 * provided for keys which DevStudio supports as shortcuts _and_ which
 * can be found on my keyboard :-) .  A mapping string of "" is unsupported
 * because it fails either of the previously mentioned criteria or would
 * be stupid to redirect (cf ESC).
 *
 * CONTROL, SHIFT, and MENU (aka ALT) are not explicitly redirected--they
 * are handled as separate key modifiers.
 */
static char *keymap[] =
{
    /* ??            0x0  */ "",
    /* VK_LBUTTON    0x01 */ "",
    /* VK_RBUTTON    0x02 */ "",
    /* VK_CANCEL     0x03 */ "",        /* ctrl-break, nuh-uh */
    /* VK_MBUTTON    0x04 */ "",
    /* ??            0x05 */ "",
    /* ??            0x06 */ "",
    /* ??            0x07 */ "",
    /* VK_BACK       0x08 */ "BACK",
    /* VK_TAB        0x09 */ "TAB",
    /* ??            0x0A */ "",
    /* ??            0x0B */ "",
    /* VK_CLEAR      0x0C */ "",
    /* VK_RETURN     0x0D */ "",        /* I wouldn't redirect this key :-) */
    /* ??            0x0E */ "",
    /* ??            0x0F */ "",
    /* VK_SHIFT      0x10 */ "",
    /* VK_CONTROL    0x11 */ "",
    /* VK_MENU       0x12 */ "",
    /* VK_PAUSE      0x13 */ "PAUSE",
    /* VK_CAPITAL    0x14 */ "",        /* caps lock...I don't think so */
    /* ??            0x15 */ "",
    /* ??            0x16 */ "",
    /* ??            0x17 */ "",
    /* ??            0x18 */ "",
    /* ??            0x19 */ "",
    /* ??            0x1A */ "",
    /* VK_ESCAPE     0x1B */ "",        /* I wouldn't redirect this key :-) */
    /* ??            0x1C */ "",
    /* ??            0x1D */ "",
    /* ??            0x1E */ "",
    /* ??            0x1F */ "",
    /* VK_SPACE      0x20 */ "SPACE",
    /* VK_PRIOR      0x21 */ "PRIOR",
    /* VK_NEXT       0x22 */ "NEXT",
    /* VK_END        0x23 */ "END",
    /* VK_HOME       0x24 */ "HOME",
    /* VK_LEFT       0x25 */ "LEFT",
    /* VK_UP         0x26 */ "UP",
    /* VK_RIGHT      0x27 */ "RIGHT",
    /* VK_DOWN       0x28 */ "DOWN",
    /* VK_SELECT     0x29 */ "",
    /* VK_PRINT      0x2A */ "",
    /* VK_EXECUTE    0x2B */ "",
    /* VK_SNAPSHOT   0x2C */ "SNAPSHOT",  /* print screen */
    /* VK_INSERT     0x2D */ "INSERT",
    /* VK_DELETE     0x2E */ "DELETE",
    /* VK_HELP       0x2F */ "",
    /* VK_0          0x30 */ "0",
    /* VK_1          0x31 */ "1",
    /* VK_2          0x32 */ "2",
    /* VK_3          0x33 */ "3",
    /* VK_4          0x34 */ "4",
    /* VK_5          0x35 */ "5",
    /* VK_6          0x36 */ "6",
    /* VK_7          0x37 */ "7",
    /* VK_8          0x38 */ "8",
    /* VK_9          0x39 */ "9",
    /* ??            0x3A */ "",
    /* ??            0x3B */ "",
    /* ??            0x3C */ "",
    /* ??            0x3D */ "",
    /* ??            0x3E */ "",
    /* ??            0x3F */ "",
    /* ??            0x40 */ "",
    /* VK_A          0x41 */ "A",
    /* VK_B          0x42 */ "B",
    /* VK_C          0x43 */ "C",
    /* VK_D          0x44 */ "D",
    /* VK_E          0x45 */ "E",
    /* VK_F          0x46 */ "F",
    /* VK_G          0x47 */ "G",
    /* VK_H          0x48 */ "H",
    /* VK_I          0x49 */ "I",
    /* VK_J          0x4A */ "J",
    /* VK_K          0x4B */ "K",
    /* VK_L          0x4C */ "L",
    /* VK_M          0x4D */ "M",
    /* VK_N          0x4E */ "N",
    /* VK_O          0x4F */ "O",
    /* VK_P          0x50 */ "P",
    /* VK_Q          0x51 */ "Q",
    /* VK_R          0x52 */ "R",
    /* VK_S          0x53 */ "S",
    /* VK_T          0x54 */ "T",
    /* VK_U          0x55 */ "U",
    /* VK_V          0x56 */ "V",
    /* VK_W          0x57 */ "W",
    /* VK_X          0x58 */ "X",
    /* VK_Y          0x59 */ "Y",
    /* VK_Z          0x5A */ "Z",
    /* VK_LWIN       0x5B */ "",
    /* VK_RWIN       0x5C */ "",
    /* VK_APPS       0x5D */ "APPS",      /* properties key  (Win95 kybd) */
    /* ??            0x5E */ "",
    /* ??            0x5F */ "",
    /* VK_NUMPAD0    0x60 */ "",
    /* VK_NUMPAD1    0x61 */ "",
    /* VK_NUMPAD2    0x62 */ "",
    /* VK_NUMPAD3    0x63 */ "",
    /* VK_NUMPAD4    0x64 */ "",
    /* VK_NUMPAD5    0x65 */ "",
    /* VK_NUMPAD6    0x66 */ "",
    /* VK_NUMPAD7    0x67 */ "",
    /* VK_NUMPAD8    0x68 */ "",
    /* VK_NUMPAD9    0x69 */ "",
    /* VK_MULTIPLY   0x6A */ "MULTIPLY",  /* keypad */
    /* VK_ADD        0x6B */ "ADD",       /* keypad */
    /* VK_SEPARATOR  0x6C */ "",
    /* VK_SUBTRACT   0x6D */ "SUBTRACT",  /* keypad */
    /* VK_DECIMAL    0x6E */ "DECIMAL",   /* keypad */
    /* VK_DIVIDE     0x6F */ "DIVIDE",    /* keypad */
    /* VK_F1         0x70 */ "F1",
    /* VK_F2         0x71 */ "F2",
    /* VK_F3         0x72 */ "F3",
    /* VK_F4         0x73 */ "F4",
    /* VK_F5         0x74 */ "F5",
    /* VK_F6         0x75 */ "F6",
    /* VK_F7         0x76 */ "F7",
    /* VK_F8         0x77 */ "F8",
    /* VK_F9         0x78 */ "F9",
    /* VK_F10        0x79 */ "F10",
    /* VK_F11        0x7A */ "F11",
    /* VK_F12        0x7B */ "F12",
    /* VK_F13        0x7C */ "F13",
    /* VK_F14        0x7D */ "F14",
    /* VK_F15        0x7E */ "F15",
    /* VK_F16        0x7F */ "F16",
    /* VK_F17        0x80 */ "F17",
    /* VK_F18        0x81 */ "F18",
    /* VK_F19        0x82 */ "F19",
    /* VK_F20        0x83 */ "F20",
    /* VK_F21        0x84 */ "F21",
    /* VK_F22        0x85 */ "F22",
    /* VK_F23        0x86 */ "F23",
    /* VK_F24        0x87 */ "F24",
    /* ??            0x88 */ "",
    /* ??            0x89 */ "",
    /* ??            0x8A */ "",
    /* ??            0x8B */ "",
    /* ??            0x8C */ "",
    /* ??            0x8D */ "",
    /* ??            0x8E */ "",
    /* ??            0x8F */ "",
    /* VK_NUMLOCK    0x90 */ "NUMLOCK",
    /* VK_SCROLL     0x91 */ "SCROLL",
};



int
chgd_redir_keys(VALARGS *unused1, int unused2, int testing)
{
    if (! testing)
        rebuild_cache_tbl = TRUE;
    return (TRUE);
}



/* Return FALSE, which indicates redirection has been disabled. */
static int
invalid_keylist(void)
{
    ntwinio_redirect_hwnd(FALSE);

    /* Killed keyboard redirection, so make sure user sees error */
    w32_message_box((HWND) winvile_hwnd(),
               "invalid keylist syntax, redirection disabled",
               MB_OK|MB_ICONSTOP);
    return (FALSE);
}



/* Return FALSE, which indicates redirection has been disabled. */
static int
unsupported_key(char *key)
{
    char msg[128];

    ntwinio_redirect_hwnd(FALSE);
    sprintf(msg, "VK_%s redir unsupported, redirection disabled", key);

    /* Killed keyboard redirection, so make sure user sees error */
    w32_message_box((HWND) winvile_hwnd(), msg, MB_OK|MB_ICONSTOP);
    return (FALSE);
}



static int
execute_action(int action)
{
    int rc = TRUE;

    if (action & FLUSH_BUFFERS)
    {
        rc = writeall(FALSE, 1, FALSE, FALSE, FALSE, FALSE);
        update(FALSE);  /* move cursor out of mini-buffer */

        /*
         * I've noticed that when flushing modified files to a networked,
         * NTFS server, a race condition exists between the time when the
         * flushed files' timestamps change and DevStudio receives a
         * redirected keystroke.  This race condition is particularly
         * noticeable when F7 is redirected (tells DevStudio to rebuild a
         * project) and nothing happens because DevStudio doesn't think any
         * files are out of date.  Avoid the race condition by giving the
         * network a chance to completely flush data to disk.
         */
        Sleep(500);
    }
    if (rc && (action & SWITCH_FOCUS))
        (void) SetForegroundWindow(redirect_hwnd);
    if (rc && (action & SYNC_BUFFER))
        rc = syncfile(0, 0);
    return (rc);
}



/*
 * FUNCTION
 *   build_keylist(void)
 *
 * DESCRIPTION
 *   Extract a list of redirection keys extracted from a vile "mode" and
 *   turn same into a dynamic, binary data structure.  The redirection list
 *   must be in the following format:
 *
 *      <keyspec>,...
 *
 *   where
 *
 *      <keyspec>     :== <virt_key>:[<modifier>...]:[<action>...],
 *      <virt_key>    :== virtual keycode macro name sans "VK_".  Refer to
 *                        include file winuser.h for a list of virtual keycode
 *                        names, or refer to the keymap array above.
 *      <modifier>    :== S|C|A         <-- mnemonics for shift, control, alt
 *      <action>      :== <flush>|<switch>|<sync>
 *      <Flush>       :== F             <-- flush all modified buffers to
 *                                          disk prior to redirecting key
 *      <Switch>      :== S             <-- switch focus to redirected window
 *                                          prior to redirecting key
 *      <sYnc>        :== Y             <-- syncrhronize current buffer (at
 *                                          its current line) within the
 *                                          DevStudio editor.  Very useful
 *                                          when setting breakpoints.
 * RETURNS
 *   Boolean, T -> list successfully parsed and built.
 */

static int
build_keylist(void)
{
    char      *redirlist, *tmp;
    REDIR_KEY *lcl_tbl;

    if (cache_tbl)
    {
        free(cache_tbl);      /* Blow away old table. */
        cache_tbl = NULL;
    }

    /* Count number of items to place in table. */
    cache_tbl_nelem = 1;
    redirlist       = tmp = global_g_val_ptr(GVAL_REDIRECT_KEYS);
    do
    {
        if ((tmp = strchr(tmp, ',')) != NULL)
        {
            cache_tbl_nelem++;
            tmp++;
        }
    }
    while (tmp);

    /* allocate the table */
    if ((lcl_tbl = cache_tbl = typecallocn(REDIR_KEY, cache_tbl_nelem)) == NULL)
    {
        no_memory("build_keylist()");
        return (FALSE);
    }

    cache_tbl_nelem = 0;
    while(isspace(*redirlist))
        redirlist++;
    while (*redirlist)
    {
        int  done, hit, indx;
        char **str, *delim;

        if ((delim = strchr(redirlist, ':')) == NULL)
            return (invalid_keylist());
        *delim = '\0';
        str    = keymap;
        for (hit = indx = 0; indx < TABLESIZE(keymap) && !hit; str++)
        {
            if (**str && stricmp(*str, redirlist) == 0)
                hit = 1;
            else
                indx++;
        }
        if (! hit)
        {
            (void) unsupported_key(redirlist);
            *delim = ':';
            return (FALSE);
        }
        lcl_tbl->vk = indx;   /* Found key desc, update virtual key code. */
        *delim++  = ':';
        redirlist = delim;
        while(isspace(*redirlist))
            redirlist++;
        for (done = 0; !done; redirlist++)
        {
            switch (*redirlist)
            {
                case ':':
                    done = 1;
                    break;
                case 'S':
                case 's':
                    lcl_tbl->modifier |= SHIFT_PRESSED;
                    break;
                case 'C':
                case 'c':
                    lcl_tbl->modifier |= (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED);
                    break;
                case 'A':
                case 'a':
                    lcl_tbl->modifier |= (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED);
                    break;
                default:
                    if (! isspace(*redirlist))
                        return (invalid_keylist());
                    break;
            }
        }
        while(isspace(*redirlist))
            redirlist++;
        for (done = 0; ! done; )
        {
            switch (*redirlist)
            {
                case ',':
                case '\0':
                    done = 1;
                    break;
                case 'F':
                case 'f':
                    lcl_tbl->action |= FLUSH_BUFFERS;
                    break;
                case 'S':
                case 's':
                    lcl_tbl->action |= SWITCH_FOCUS;
                    break;
                case 'Y':
                case 'y':
                    lcl_tbl->action |= SYNC_BUFFER;
                    break;
                default:
                    if (! isspace(*redirlist))
                        return (invalid_keylist());
                    break;
            }
            if (*redirlist)
                redirlist++;
        }
        cache_tbl_nelem++;
        lcl_tbl++;
        while(isspace(*redirlist))
            redirlist++;
    }
    rebuild_cache_tbl = FALSE;
    return (TRUE);
}



/*
 * FUNCTION
 *   oleauto_redirected_key(ULONG vk, ULONG modifier)
 *
 *   vk       - standard win32 virtual keycode.
 *
 *   modifier - win32 key state specifying which, if any, key modifiers
 *              were pressed (i.e., ALT, CTRL, SHIFT).
 *
 * DESCRIPTION
 *   Given a user keypress (and possible key modifier), see if it matches
 *   any of the keys selected for redirection to the winvile redirect
 *   HWND.  If a match, do the redirection after executing associated
 *   actions (if any).
 *
 * RETURNS
 *   Boolean, T -> key redirected.
 */

int
oleauto_redirected_key(ULONG vk, ULONG modifier)
{
    int       attached, hit;
    unsigned  i;
    REDIR_KEY *lcl_tbl;

    if (vk >= TABLESIZE(keymap))
        return (FALSE);
    if (*(keymap[vk]) == '\0')
        return (FALSE);              /* Can't redirect that key. */
    if (rebuild_cache_tbl && ! build_keylist())
    {
        /*
         * Bogus keylist -- error already reported and redirection
         * disabled.  Return TRUE to signal the winvile message pump that
         * key has been processed (in this case, discarded).
         */

        return (TRUE);
    }

    /* Is user's vk in the list of winvile redirected keys? */
    for (hit = i = 0, lcl_tbl = cache_tbl;
                        !hit && i < cache_tbl_nelem; i++)
    {
        if (lcl_tbl->vk == vk && modifier == lcl_tbl->modifier)
            hit = 1;
        else
            lcl_tbl++;
    }
    if (! hit)
        return (FALSE);              /* Key not selected for redirection. */

    /* If this key has an associated action, execute it now. */
    if (lcl_tbl->action && ! execute_action(lcl_tbl->action))
    {
        /*
         * An action failed and an error has been reported.  Return TRUE to
         * signal the winvile message pump that key has been processed (in
         * this case, discarded).
         */

        return (TRUE);
    }

    attached = FALSE;
    if (modifier)
    {
        /*
         * It's not possible to send a key modifier to another window's
         * message queue.  Instead, that window's key state must be directly
         * modified.
         */

        BYTE keystate[256];
        BOOL ok;

        if ((ok = AttachThreadInput(winvile_tid, redirect_tid, TRUE)) == TRUE)
        {
            attached = TRUE;
            if ((ok = GetKeyboardState(keystate)) == TRUE)
            {
                if (modifier & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
                    keystate[VK_CONTROL] = 1;
                if (modifier & SHIFT_PRESSED)
                    keystate[VK_SHIFT] = 1;
                if (modifier & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED))
                    keystate[VK_MENU] = 1;
                ok = SetKeyboardState(keystate);
            }
        }
        if (! ok)
        {
            char msg[128];

            ntwinio_redirect_hwnd(FALSE);
            if (attached)
                (void) AttachThreadInput(winvile_tid, redirect_tid, FALSE);
            sprintf(msg,
              "Input attachment to thread ID %#0x failed, redirection disabled",
                    redirect_tid);

            /* Killed keyboard redirection, so make sure user sees error */
            w32_message_box((HWND) winvile_hwnd(), msg, MB_OK|MB_ICONSTOP);

            /*
             * Return TRUE to signal the winvile message pump that key has
             * been processed (in this case, discarded).
             */
            return (TRUE);
        }
    }
    if (! PostMessage(redirect_hwnd, WM_KEYDOWN, vk, 0))
    {
        char msg[128];

        ntwinio_redirect_hwnd(FALSE);
        sprintf(msg,
        "Keyboard redirection to window hwnd %#0lx failed, redirection disabled",
                (unsigned long) redirect_hwnd);

        /* Killed keyboard redirection, so make sure user sees error */
        w32_message_box((HWND) winvile_hwnd(), msg, MB_OK|MB_ICONSTOP);
    }
    if (attached)
    {
        /*
         * Detach.  AttachThreadInput() resets the previously manipulated
         * keyboard state.
         */

        (void) AttachThreadInput(winvile_tid, redirect_tid, FALSE);
    }

    /*
     * Return TRUE to signal the winvile message pump that key has either
     * been processed or discarded.
     */
    return (TRUE);
}

} /* Extern "C" */