summaryrefslogtreecommitdiff
path: root/src/debug/di/shimprocess.cpp
blob: 684173abca2ced0c4dd30c403ba8592d21e96f40 (plain)
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
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//*****************************************************************************
// File: ShimProcess.cpp
// 

//
// The V3 ICD debugging APIs have a lower abstraction level than V2.
// This provides V2 ICD debugging functionality on top of the V3 debugger object.
//*****************************************************************************

#include "stdafx.h"

#include "safewrap.h"
#include "check.h" 

#include <limits.h>
#include "shimpriv.h"

#if !defined(FEATURE_CORESYSTEM)
#include <tlhelp32.h>
#endif

//---------------------------------------------------------------------------------------
//
// Ctor for a ShimProcess
//
// Notes:
//    See InitializeDataTarget in header for details of how to instantiate a ShimProcess and hook it up.
//    Initial ref count is 0. This is the convention used int the RS, and it plays well with semantics
//    like immediately assigning to a smart pointer (which will bump the count up to 1).

ShimProcess::ShimProcess() :
    m_ref(0),
    m_fFirstManagedEvent(false),
    m_fInCreateProcess(false),
    m_fInLoadModule(false),
    m_fIsInteropDebugging(false),
    m_fIsDisposed(false),
    m_loaderBPReceived(false)
{   
    m_ShimLock.Init("ShimLock", RSLock::cLockReentrant, RSLock::LL_SHIM_LOCK);
    m_ShimProcessDisposeLock.Init(
        "ShimProcessDisposeLock", 
        RSLock::cLockReentrant | RSLock::cLockNonDbgApi,
        RSLock::LL_SHIM_PROCESS_DISPOSE_LOCK);
    m_eventQueue.Init(&m_ShimLock);
    m_pShimCallback.Assign(new ShimProxyCallback(this)); // Throws

    m_fNeedFakeAttachEvents = false;
    m_ContinueStatusChangedData.Clear();    

    m_pShimStackWalkHashTable = new ShimStackWalkHashTable();

    m_pDupeEventsHashTable = new DuplicateCreationEventsHashTable();

    m_machineInfo.Clear();

    m_markAttachPendingEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL);
    if (m_markAttachPendingEvent == NULL)
    {
        ThrowLastError();
    }

    m_terminatingEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL);
    if (m_terminatingEvent == NULL)
    {
        ThrowLastError();
    } 
}

//---------------------------------------------------------------------------------------
//
// ShimProcess dtor. Invoked when reference count goes to 0.
//
// Assumptions:
//    Dtors should not do any interesting work. If this object has been initialized, 
//    then call Dispose() first.    
//
//
ShimProcess::~ShimProcess()
{
    // Expected that this was either already disposed first, or not initialized.
    _ASSERTE(m_pWin32EventThread == NULL);

    _ASSERTE(m_ShimProcessDisposeLock.IsInit());
    m_ShimProcessDisposeLock.Destroy();

    if (m_markAttachPendingEvent != NULL)
    {
        CloseHandle(m_markAttachPendingEvent);
        m_markAttachPendingEvent = NULL;
    }

    if (m_terminatingEvent != NULL)
    {
        CloseHandle(m_terminatingEvent);
        m_terminatingEvent = NULL;
    }

    // Dtor will release m_pLiveDataTarget
}

//---------------------------------------------------------------------------------------
//
// Part of initialization to hook up to process.
//
// Arguments:
//      pProcess - debuggee object to connect to. Maybe null if part of shutdown.
//
// Notes:
//     This will take a strong reference to the process object. 
//     This is part of the initialization phase.
//     This should only be called once.
//    
//
void ShimProcess::SetProcess(ICorDebugProcess * pProcess)
{
    PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(NULL);

    // Data-target should already be setup before we try to connect to a process.
    _ASSERTE(m_pLiveDataTarget != NULL);

    // Reference is kept by m_pProcess;
    m_pIProcess.Assign(pProcess);

    // Get the private shim hooks. This just exists to access private functionality that has not
    // yet been promoted to the ICorDebug interfaces.
    m_pProcess = static_cast<CordbProcess *>(pProcess);
    
    if (pProcess != NULL)
    {
        // Verify that DataTarget + new process have the same pid?
        _ASSERTE(m_pProcess->GetPid() == m_pLiveDataTarget->GetPid());
    }
}

//---------------------------------------------------------------------------------------
//
// Create a Data-Target around the live process.
//
// Arguments:
//      processId - OS process ID to connect to. Must be a local, same platform, process.
//
// Return Value:
//    S_OK on success.
//
// Assumptions:
//    This is part of the initialization dance.
//
// Notes:
//    Only call this once, during the initialization dance. 
//
HRESULT ShimProcess::InitializeDataTarget(DWORD processId)
{
    _ASSERTE(m_pLiveDataTarget == NULL);

    
    HRESULT hr = BuildPlatformSpecificDataTarget(GetMachineInfo(), processId, &m_pLiveDataTarget);
    if (FAILED(hr))
    {
        _ASSERTE(m_pLiveDataTarget == NULL);
        return hr;
    }
    m_pLiveDataTarget->HookContinueStatusChanged(ShimProcess::ContinueStatusChanged, this);

    // Ref on pDataTarget is now 1.
    _ASSERTE(m_pLiveDataTarget != NULL);

    return S_OK;
}

//---------------------------------------------------------------------------------------
//
// Determines if current thread is the Win32 Event Thread
//
// Return Value:
//    True iff current thread is win32 event thread, else false.
//
// Notes:
//    The win32 event thread is created by code:ShimProcess::CreateAndStartWin32ET
//
bool ShimProcess::IsWin32EventThread()
{
    return (m_pWin32EventThread != NULL) && m_pWin32EventThread->IsWin32EventThread();
}

//---------------------------------------------------------------------------------------
//
// Add a reference
//
void ShimProcess::AddRef()
{
    InterlockedIncrement(&m_ref);
}

//---------------------------------------------------------------------------------------
//
// Release a reference.
//
// Notes:
//     When ref goes to 0, object is deleted. 
//
void ShimProcess::Release()
{
    LONG ref = InterlockedDecrement(&m_ref);
    if (ref == 0)
    {
        delete this;
    }
}

//---------------------------------------------------------------------------------------
//
// Dispose (Neuter) the object. 
//
//
// Assumptions:
//    This is called to gracefully shutdown the ShimProcess object.
//    This must be called before destruction if the object was initialized.
//
// Notes:
//    This will release all external resources, including getting the win32 event thread to exit.
//    This can safely be called multiple times.
//    
void ShimProcess::Dispose()
{   
    // Serialize Dispose with any other locked access to the shim.  This helps
    // protect against the debugger detaching while we're in the middle of
    // doing stuff on the ShimProcess
    RSLockHolder lockHolder(&m_ShimProcessDisposeLock);

    m_fIsDisposed = true;

    // Can't shut down the W32ET if we're on it.
    _ASSERTE(!IsWin32EventThread());

    m_eventQueue.DeleteAll();

    if (m_pWin32EventThread != NULL)
    {
        // This will block waiting for the thread to exit gracefully.
        m_pWin32EventThread->Stop();

        delete m_pWin32EventThread;
        m_pWin32EventThread = NULL;
    }

    if (m_pLiveDataTarget != NULL)
    {
        m_pLiveDataTarget->Dispose();
        m_pLiveDataTarget.Clear();
    }

    m_pIProcess.Clear();
    m_pProcess = NULL;

    _ASSERTE(m_ShimLock.IsInit());
    m_ShimLock.Destroy();

    if (m_pShimStackWalkHashTable != NULL)
    {
        // The hash table should be empty by now.  ClearAllShimStackWalk() should have been called.
        _ASSERTE(m_pShimStackWalkHashTable->GetCount() == 0);

        delete m_pShimStackWalkHashTable;
        m_pShimStackWalkHashTable = NULL;
    }

    if (m_pDupeEventsHashTable != NULL)
    {
        if (m_pDupeEventsHashTable->GetCount() > 0)
        {
            // loop through all the entries in the hash table, remove them, and delete them
            for (DuplicateCreationEventsHashTable::Iterator pCurElem = m_pDupeEventsHashTable->Begin(),
                pEndElem = m_pDupeEventsHashTable->End();
                pCurElem != pEndElem; 
            pCurElem++)
            {
                DuplicateCreationEventEntry * pEntry = *pCurElem;
                delete pEntry;
            }
            m_pDupeEventsHashTable->RemoveAll();
        }

        delete m_pDupeEventsHashTable;
        m_pDupeEventsHashTable = NULL;
    }
}



//---------------------------------------------------------------------------------------
// Track (and close) file handles from debug events.
//
// Arguments:
//    pEvent - debug event
// 
// Notes: 
//    Some debug events introduce file handles that the debugger needs to track and
//    close on other debug events. For example, the LoadDll,CreateProcess debug
//    events both give back a file handle that the debugger must close. This is generally
//    done on the corresponding UnloadDll/ExitProcess debug events. 
// 
//    Since we won't use the file handles, we'll just close them as soon as we get them.
//    That way, we don't need to remember any state.
void ShimProcess::TrackFileHandleForDebugEvent(const DEBUG_EVENT * pEvent)
{
    CONTRACTL
    {
        THROWS;
    }
    CONTRACTL_END;

    HANDLE        hFile = NULL;

    switch(pEvent->dwDebugEventCode)
    {
        //
        // Events that add a file handle
        //
        case CREATE_PROCESS_DEBUG_EVENT:
            hFile = pEvent->u.CreateProcessInfo.hFile;
            CloseHandle(hFile);
            break;

        case LOAD_DLL_DEBUG_EVENT:
            hFile = pEvent->u.LoadDll.hFile;
            CloseHandle(hFile);
            break;

    }
}

//---------------------------------------------------------------------------------------
// ThreadProc helper to drain event queue.
//
// Arguments:
//    parameter - thread proc parameter, an ICorDebugProcess*
//
// Returns
//     0. 
//
// Notes:
//    This is useful when the shim queued a fake managed event (such as Control+C)
//    and needs to get the debuggee to synchronize in order to start dispatching events.
//    @dbgtodo sync: this will likely change as we iron out the Synchronization feature crew.
//
//    We do this in a new thread proc to avoid thread restrictions:
//    Can't call this on win32 event thread because that can't send the IPC event to 
//    make the aysnc-break request.
//    Can't call this on the RCET because that can't send an async-break (see SendIPCEvent for details)
//    So we just spin up a new thread to do the work.
//---------------------------------------------------------------------------------------
DWORD WINAPI CallStopGoThreadProc(LPVOID parameter)
{
    ICorDebugProcess* pProc = reinterpret_cast<ICorDebugProcess *>(parameter);

    // We expect these operations to succeed; but if they do fail, there's nothing we can really do about it.
    // If it fails on process exit/neuter/detach, then it would be ignorable.
    HRESULT hr;


    // Calling Stop + Continue will synchronize the process and force any queued events to be called.
    // Stop is synchronous and will block until debuggee is synchronized.
    hr = pProc->Stop(INFINITE);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    // Continue will resume the debuggee. If there are queued events (which we expect in this case)
    // then continue will drain the event queue instead of actually resuming the process.
    hr = pProc->Continue(FALSE);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    // This thread just needs to trigger an event dispatch. Now that it's done that, it can exit.
    return 0;
}


//---------------------------------------------------------------------------------------
// Does default event handling for native debug events.
//
// Arguments:
//  pEvent - IN event ot handle
//  pdwContinueStatus - IN /OUT - continuation status for event. 
//
// Assumptions:
//    Called when target is stopped. Caller still needs to Continue the debug event.
//    This is called on the win32 event thread.
//
// Notes:
//    Some native events require extra work before continuing. Eg, skip loader 
//    breakpoint, close certain handles, etc.
//    This is only called in the manage-only case. In the interop-case, the 
//    debugger will get and handle these native debug events.
void ShimProcess::DefaultEventHandler(
    const DEBUG_EVENT * pEvent,
    DWORD * pdwContinueStatus)
{
    CONTRACTL
    {
        THROWS;
    }
    CONTRACTL_END;


    //
    // Loader breakpoint
    //

    BOOL fFirstChance;
    const EXCEPTION_RECORD * pRecord = NULL;

    if (IsExceptionEvent(pEvent, &fFirstChance, &pRecord))
    {    
        DWORD dwThreadId = GetThreadId(pEvent);

        switch(pRecord->ExceptionCode)
        {
        case STATUS_BREAKPOINT:
            {
                if (!m_loaderBPReceived)
                {
                    m_loaderBPReceived = true;

                    // Clear the loader breakpoint
                    *pdwContinueStatus = DBG_CONTINUE;                 

                    // After loader-breakpoint, notify that managed attach can begin.
                    // This is done to trigger a synchronization. The shim
                    // can then send the fake attach events once the target
                    // is synced.
                    // @dbgtodo sync: not needed once shim can
                    // work on sync APIs.
                    m_pProcess->QueueManagedAttachIfNeeded(); // throws
                }
            }
            break;

        /*
        // If we handle the Ctlr-C event here and send the notification to the debugger, then we may break pre-V4 
        // behaviour because the debugger may handle the event and intercept the handlers registered in the debuggee 
        // process.  So don't handle the event here and let the debuggee process handle it instead.  See Dev10 issue
        // 846455 for more info.
        //
        // However, when the re-arch is completed, we will need to work with VS to define what the right behaviour
        // should be.  We don't want to rely on in-process code to handle the Ctrl-C event.
        case DBG_CONTROL_C:
        {
        // Queue a fake managed Ctrl+C event.
        m_pShimCallback->ControlCTrap(GetProcess());

        // Request an Async Break
        // This is on Win32 Event Thread, so we can't call Stop / Continue.
        // Instead, spawn a new threead, and have that call Stop/Continue, which
        // will get the RCET to drain the event queue and dispatch the ControlCTrap we just queued.
        {
        DWORD dwDummyId;
        CreateThread(NULL,
        0,
        CallStopGoThreadProc,
        (LPVOID) GetProcess(),
        0,
        &dwDummyId);
        }

        // We don't worry about suspending the Control-C thread right now. The event is
        // coming asynchronously, and so it's ok if the debuggee slips forward while
        // we try to do a managed async break.


        // Clear the control-C event.
        *pdwContinueStatus = DBG_CONTINUE;
        }
        break;
        
*/
        }

    
    }


    // Native debugging APIs have an undocumented expectation that you clear for OutputDebugString.
    if (pEvent->dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
    {
        *pdwContinueStatus = DBG_CONTINUE;
    }

    //
    // File handles. 
    //
    TrackFileHandleForDebugEvent(pEvent);
}

//---------------------------------------------------------------------------------------
// Determine if we need to change the continue status
//
// Returns:
//    True if the continue status was changed. Else false.
//
// Assumptions:
//    This is single-threaded, which is enforced by it only be called on the win32et.
//    The shim guarnatees only 1 outstanding debug-event at a time.
//
// Notes:
//    See code:ShimProcess::ContinueStatusChangedWorker for big picture.
//    Continue status is changed from a data-target callback which invokes 
//    code:ShimProcess::ContinueStatusChangedWorker.
//    Call code:ShimProcess::ContinueStatusChangedData::Clear to clear the 'IsSet' bit.
//
bool ShimProcess::ContinueStatusChangedData::IsSet()
{

    return m_dwThreadId != 0;
}

//---------------------------------------------------------------------------------------
// Clears the bit marking 
//
// Assumptions:
//    This is single-threaded, which is enforced by it only be called on the win32et.
//    The shim guarantees only 1 outstanding debug-event at a time.
//
// Notes:
//    See code:ShimProcess::ContinueStatusChangedWorker for big picture.
//    This makes code:ShimProcess::ContinueStatusChangedData::IsSet return false.
//    This can safely be called multiple times in a row.
//
void ShimProcess::ContinueStatusChangedData::Clear()
{
    m_dwThreadId = 0;
}

//---------------------------------------------------------------------------------------
// Callback invoked from data-target when continue status is changed.
//
// Arguments:
//    pUserData - data we supplied to the callback. a 'this' pointer.
//    dwThreadId - the tid whose continue status is changing
//    dwContinueStatus - the new continue status.
//
// Notes:
//    

// Static
HRESULT ShimProcess::ContinueStatusChanged(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus)
{
    ShimProcess * pThis = reinterpret_cast<ShimProcess *>(pUserData);
    return pThis->ContinueStatusChangedWorker(dwThreadId, dwContinueStatus);
}

//---------------------------------------------------------------------------------------
// Real worker callback invoked from data-target when continue status is changed.
//
// Arguments:
//    dwThreadId - the tid whose continue status is changing
//    dwContinueStatus - the new continue status.
//
// Notes:
//    ICorDebugProcess4::Filter returns an initial continue status (heavily biased to 'gn').
//    Some ICorDebug operations may need to change the continue status that filter returned.
//    For example, on windows, hijacking a thread at an unhandled exception would need to
//    change the status to 'gh' (since continuing 2nd chance exception 'gn' will tear down the
//    process and the hijack would never execute).
//    
//    Such operations will invoke into the data-target (code:ICorDebugMutableDataTarget::ContinueStatusChanged)
//    to notify the debugger that the continue status was changed. 
//
//    The shim only executes such operations on the win32-event thread in a small window between
//    WaitForDebugEvent and Continue. Therefore, we know:
//    * the callback must come on the Win32EventThread (which means our handling the callback is
//       single-threaded.
//    * We only have 1 outstanding debug event to worry about at a time. This simplifies our tracking.
//
//    The shim tracks the outstanding change request in m_ContinueStatusChangedData.

HRESULT ShimProcess::ContinueStatusChangedWorker(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus)
{
    // Should only be set once. This is only called on the win32 event thread, which protects against races.
    _ASSERTE(IsWin32EventThread());    
    _ASSERTE(!m_ContinueStatusChangedData.IsSet());

    m_ContinueStatusChangedData.m_dwThreadId = dwThreadId;
    m_ContinueStatusChangedData.m_status     = dwContinueStatus;

    // Setting dwThreadId to non-zero should now mark this as set.
    _ASSERTE(m_ContinueStatusChangedData.IsSet());
    return S_OK;
}


//---------------------------------------------------------------------------------------
//
// Add a duplicate creation event entry for the specified key.
//
// Arguments:
//    pKey - the key of the entry to be added; this is expected to be an 
//           ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDModule
//
// Assumptions:
//    pKey is really an interface pointer of one of the types mentioned above
//
// Notes:
//    We have to keep track of which creation events we have sent already because some runtime data structures
//    are discoverable through enumeration before they send their creation events.  As a result, we may have
//    faked up a creation event for a data structure during attach, and then later on get another creation 
//    event for the same data structure.  VS is not resilient in the face of multiple creation events for
//    the same data structure.
//    
//    Needless to say this is a problem in attach scenarios only.  However, keep in mind that for CoreCLR,
//    launch really is early attach.  For early attach, we get three creation events up front: a create
//    process, a create appdomain, and a create thread.
//

void ShimProcess::AddDuplicateCreationEvent(void * pKey)
{
    NewHolder<DuplicateCreationEventEntry> pEntry(new DuplicateCreationEventEntry(pKey));
    m_pDupeEventsHashTable->Add(pEntry);
    pEntry.SuppressRelease();
}


//---------------------------------------------------------------------------------------
//
// Check whether the specified key exists in the hash table.  If so, remove it.
//
// Arguments:
//    pKey - the key of the entry to check; this is expected to be an 
//           ICDProcess/ICDAppDomain/ICDThread/ICDAssembly/ICDModule
//
// Return Value:
//    Returns true if the entry exists.  The entry will have been removed because we can't have more than two 
//    duplicates for any given event.
//
// Assumptions:
//    pKey is really an interface pointer of one of the types mentioned above
//
// Notes:
//    See code:ShimProcess::AddDuplicateCreationEvent.
//

bool ShimProcess::RemoveDuplicateCreationEventIfPresent(void * pKey)
{
    // We only worry about duplicate events in attach scenarios.
    if (GetAttached())
    {
        // Only do the check if the hash table actually contains entries.
        if (m_pDupeEventsHashTable->GetCount() > 0)
        {
            // Check if this is a dupe.
            DuplicateCreationEventEntry * pResult = m_pDupeEventsHashTable->Lookup(pKey);
            if (pResult != NULL)
            {
                // This is a dupe.  We can't have a dupe twice, so remove it.
                // This will help as a bit of optimization, since we will no longer check the hash table if
                // its count reaches 0.
                m_pDupeEventsHashTable->Remove(pKey);
                delete pResult;
                return true;
            }
        }
    }
    return false;
}


//---------------------------------------------------------------------------------------
// Gets the exception record format of the host
//
// Returns:
//    The CorDebugRecordFormat for the host architecture.
//
// Notes: 
//   This corresponds to the definition EXCEPTION_RECORD on the host-architecture. 
//   It can be passed into ICorDebugProcess4::Filter.
CorDebugRecordFormat GetHostExceptionRecordFormat()
{
#if defined(_WIN64)
    return FORMAT_WINDOWS_EXCEPTIONRECORD64;
#else
    return FORMAT_WINDOWS_EXCEPTIONRECORD32;
#endif
}

//---------------------------------------------------------------------------------------
// Main event handler for native debug events. Must also ensure Continue is called.
// 
// Arguments:
//   pEvent - debug event to handle
//
// Assumptions:
//   Caller did a Flush() if needed.
//  
// Notes:
//   The main Handle native debug events.
//   This must call back into ICD to let ICD filter the debug event (in case it's a managed notification).
// 
//   If we're interop-debugging (V2), then the debugger is expecting the debug events. In that case, 
//   we go through the V2 interop-debugging logic to queue / dispatch the events. 
//   If we're managed-only debugging, then the shim provides a default handler for the native debug.
//   This includes some basic work (skipping the loader breakpoint, close certain handles, etc).
//---------------------------------------------------------------------------------------
HRESULT ShimProcess::HandleWin32DebugEvent(const DEBUG_EVENT * pEvent)
{
    _ASSERTE(IsWin32EventThread());  

    //
    // If this is an exception event, then we need to feed it into the CLR.
    //
    BOOL dwFirstChance = FALSE;
    const EXCEPTION_RECORD * pRecord = NULL;
    const DWORD dwThreadId = GetThreadId(pEvent);

    bool fContinueNow = true;

    // If true, we're continuing (unhandled) a 2nd-chance exception
    bool fExceptionGoingUnhandled = false;

    // 
    const DWORD kDONTCARE = 0;
    DWORD dwContinueStatus = kDONTCARE;

    if (IsExceptionEvent(pEvent, &dwFirstChance, &pRecord))
    {
        // As a diagnostic aid we can configure the debugger to assert when the debuggee does DebugBreak()
#ifdef DEBUG
        static ConfigDWORD config;
        DWORD fAssert = config.val(CLRConfig::INTERNAL_DbgAssertOnDebuggeeDebugBreak);
        if (fAssert)
        {
            // If we got a 2nd-chance breakpoint, then it's extremely likely that it's from an
            // _ASSERTE in the target and we really want to know about it now before we kill the
            // target. The debuggee will exit once we continue (unless we are mixed-mode debugging), so alert now.
            // This assert could be our only warning of various catastrophic failures in the left-side.
            if (!dwFirstChance && (pRecord->ExceptionCode == STATUS_BREAKPOINT) && !m_fIsInteropDebugging)
            {            
                DWORD pid = (m_pLiveDataTarget == NULL) ? 0 : m_pLiveDataTarget->GetPid();

                CONSISTENCY_CHECK_MSGF(false, 
                    ("Unhandled breakpoint exception in debuggee (pid=%d (0x%x)) on thread %d(0x%x)\n"
                    "This may mean there was an assert in the debuggee on that thread.\n"
                    "\n"
                    "You should attach to that process (non-invasively) and get a callstack of that thread.\n"
                    "(This assert only occurs when CLRConfig::INTERNAL_DebuggerAssertOnDebuggeeDebugBreak is set)\n",
                    pid, pid, dwThreadId,dwThreadId));
            }
        }
#endif

        // We pass the Shim's proxy callback object, which will just take the callbacks and queue them 
        // to an event-queue in the shim. When we get the sync-complete event, the shim
        // will then drain the event queue and dispatch the events to the user's callback object.        
        const DWORD dwFlags = dwFirstChance ? 1 : 0;

        m_ContinueStatusChangedData.Clear();

        // If ICorDebug doesn't care about this exception, it will leave dwContinueStatus unchanged.
        RSExtSmartPtr<ICorDebugProcess4> pProcess4;
        GetProcess()->QueryInterface(IID_ICorDebugProcess4, (void**) &pProcess4);

        HRESULT hrFilter =  pProcess4->Filter(
            (const BYTE*) pRecord, 
            sizeof(EXCEPTION_RECORD),
            GetHostExceptionRecordFormat(),
            dwFlags, 
            dwThreadId, 
            m_pShimCallback,
            &dwContinueStatus);
        if (FAILED(hrFilter))
        {
            // Filter failed (eg. DAC couldn't be loaded), return the
            // error so it can become an unrecoverable error.
            return hrFilter;
        }

        // For unhandled exceptions, hijacking if needed.
        if (!dwFirstChance)
        {
            // May invoke data-target callback (which may call code:ShimProcess::ContinueStatusChanged) to change continue status.
            if (!m_pProcess->HijackThreadForUnhandledExceptionIfNeeded(dwThreadId))
            {
                // We decided not to hijack, so this exception is going to go unhandled
                fExceptionGoingUnhandled = true;
            }

            if (m_ContinueStatusChangedData.IsSet())
            {
                _ASSERTE(m_ContinueStatusChangedData.m_dwThreadId == dwThreadId);

                // Claiming this now means we won't do any other processing on the exception event.
                // This means the interop-debugging logic will never see 2nd-chance managed exceptions.
                dwContinueStatus = m_ContinueStatusChangedData.m_status;
            }
        }
    }

    // Do standard event handling, including Handling loader-breakpoint, 
    // and callback into CordbProcess for Attach if needed.
    HRESULT hrIgnore = S_OK;
    EX_TRY
    {
        // For NonClr notifications, allow extra processing.
        // This includes both non-exception events, and exception events that aren't
        // specific CLR debugging services notifications.
        if (dwContinueStatus == kDONTCARE) 
        {
            if (m_fIsInteropDebugging)
            {   
                // Interop-debugging logic will handle the continue.
                fContinueNow = false;
#if defined(FEATURE_INTEROP_DEBUGGING)
                // @dbgtodo interop: All the interop-debugging logic is still in CordbProcess.
                // Call back into that. This will handle Continuing the debug event.
                m_pProcess->HandleDebugEventForInteropDebugging(pEvent); 
#else
                _ASSERTE(!"Interop debugging not supported");
#endif
            }
            else
            {                    
                dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;    

                // For managed-only debugging, there's no user handler for native debug events,
                // and so we still need to do some basic work on certain debug events.
                DefaultEventHandler(pEvent, &dwContinueStatus);

                // This is the managed-only case. No reason to keep the target win32 frozen, so continue it immediately.
                _ASSERTE(fContinueNow);
            }
        }
    }
    EX_CATCH_HRESULT(hrIgnore);
    // Dont' expect errors here (but could probably return it up to become an
    // unrecoverable error if necessary). We still want to call Continue thought.
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrIgnore);

    //
    // Continue the debuggee if needed.
    //
    if (fContinueNow)
    {
        BOOL fContinueOk = GetNativePipeline()->ContinueDebugEvent(
            GetProcessId(pEvent), 
            dwThreadId, 
            dwContinueStatus);
        (void)fContinueOk; //prevent "unused variable" error from GCC
        SIMPLIFYING_ASSUMPTION(fContinueOk);

        if (fExceptionGoingUnhandled)
        {
            _ASSERTE(dwContinueStatus == DBG_EXCEPTION_NOT_HANDLED);
            // We just passed a 2nd-chance exception back to the OS which may have now invoked 
            // Windows error-reporting logic which suspended all threads in the target.  Since we're
            // still debugging and may want to break, inspect state and even detach (eg. to attach
            // a different sort of debugger that can handle the exception) we need to let our threads run.
            // Note that when WER auto-invokes a debugger it doesn't suspend threads, so it doesn't really
            // make sense for them to be suspended now when a debugger is already attached.
            // A better solution may be to suspend this faulting thread before continuing the event, do an 
            // async-break and give the debugger a notification of an unhandled exception.  But this will require
            // an ICorDebug API change, and also makes it harder to reliably get the WER dialog box once we're
            // ready for it.
            // Unfortunately we have to wait for WerFault.exe to start and actually suspend the threads, and
            // there doesn't appear to be any better way than to just sleep for a little here.  In practice 200ms
            // seems like more than enough, but this is so uncommon of a scenario that a half-second delay 
            // (just to be safe) isn't a problem.
            // Provide an undocumented knob to turn this behavior off in the very rare case it's not what we want
            // (eg. we're trying to debug something that races with crashing / terminating the process on multiple
            // threads)
            static ConfigDWORD config;
            DWORD fSkipResume = config.val(CLRConfig::UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException);
            if (!fSkipResume)
            {
                ::Sleep(500);
                hrIgnore = GetNativePipeline()->EnsureThreadsRunning();
                SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrIgnore);
            }
        }
    }    

    return S_OK;
}

// Trivial accessor to get the event queue.
ManagedEventQueue * ShimProcess::GetManagedEventQueue() 
{
    return &m_eventQueue; 
}

// Combines GetManagedEventQueue() and Dequeue() into a single function
// that holds m_ShimProcessDisposeLock for the duration
ManagedEvent * ShimProcess::DequeueManagedEvent()
{
    // Serialize this function with Dispoe()
    RSLockHolder lockHolder(&m_ShimProcessDisposeLock);
    if (m_fIsDisposed)
        return NULL;

    return m_eventQueue.Dequeue();
}

// Trivial accessor to get Shim's proxy callback object.
ShimProxyCallback * ShimProcess::GetShimCallback() 
{
    return m_pShimCallback; 
}

// Trivial accessor to get the ICDProcess for the debuggee.
// A ShimProcess object can then provide V2 functionality by building it on V3 functionality
// exposed by the ICDProcess object.
ICorDebugProcess * ShimProcess::GetProcess()
{
    return m_pIProcess;
}

// Trivial accessor to get the data-target for the debuggee.
// The data-target lets us access the debuggee, especially reading debuggee memory.
ICorDebugMutableDataTarget * ShimProcess::GetDataTarget()
{
    return m_pLiveDataTarget;
};


// Trivial accessor to get the raw native event pipeline.
// In V3, ICorDebug no longer owns the event thread and it does not own the event pipeline either.
INativeEventPipeline * ShimProcess::GetNativePipeline()
{
    return m_pWin32EventThread->GetNativePipeline();
}

// Trivial accessor to expose the W32ET thread to the CordbProcess so that it can emulate V2 behavior.
// In V3, ICorDebug no longer owns the event thread and it does not own the event pipeline either.
// The Win32 Event Thread is the only thread that can use the native pipeline
// see code:ShimProcess::GetNativePipeline. 
CordbWin32EventThread * ShimProcess::GetWin32EventThread()
{
    return m_pWin32EventThread;
}


// Trivial accessor to mark whether we're interop-debugging.
// Retreived via code:ShimProcess::IsInteropDebugging
void ShimProcess::SetIsInteropDebugging(bool fIsInteropDebugging)
{
    m_fIsInteropDebugging = fIsInteropDebugging;
}

// Trivial accessor to check if we're interop-debugging. 
// This affects how we handle native debug events.
// The significant usage of this is in code:ShimProcess::HandleWin32DebugEvent
bool ShimProcess::IsInteropDebugging()
{
    return m_fIsInteropDebugging;
}


//---------------------------------------------------------------------------------------
// Begin queueing the fake attach events. 
//
// Notes:
//    See code:ShimProcess::QueueFakeAttachEvents for more about "fake attach events".
//
//    This marks that we need to send fake attach events, and queus a CreateProcess.
//    Caller calls code:ShimProcess::QueueFakeAttachEventsIfNeeded to finish queuing
//    the rest of the fake attach events.
void ShimProcess::BeginQueueFakeAttachEvents()
{
    m_fNeedFakeAttachEvents = true;

    // Put a fake CreateProcess event in the queue.
    // This will not be drained until we get a Sync-Complete from the Left-side.
    GetShimCallback()->QueueCreateProcess(GetProcess());
    AddDuplicateCreationEvent(GetProcess());
}

//---------------------------------------------------------------------------------------
// potentially Queue fake attach events like we did in V2.
//
// Arguments:
//   fRealCreateProcessEvent - true if the shim is about to dispatch a real create process event (as opposed
//                             to one faked up by the shim itself)
//   
// Notes:
//    See code:ShimProcess::QueueFakeAttachEvents for details.
void ShimProcess::QueueFakeAttachEventsIfNeeded(bool fRealCreateProcessEvent)
{
    // This was set high in code:ShimProcess::BeginQueueFakeAttachEvents
    if (!m_fNeedFakeAttachEvents)
    {
        return;
    }    
    m_fNeedFakeAttachEvents = false;

    // If the first event we get after attaching is a create process event, then this is an early attach
    // scenario and we don't need to queue any fake attach events.
    if (!fRealCreateProcessEvent)
    {
        HRESULT hr = S_OK;
        EX_TRY
        {
            QueueFakeAttachEvents();
        }
        EX_CATCH_HRESULT(hr);
    }
}

//---------------------------------------------------------------------------------------
// Send fake Thread-create events for attach, using an arbitrary order.
// 
// Returns:
//    S_OK on success, else error.
// 
// Notes:
//    This sends fake thread-create events, ala V2 attach.
//    See code:ShimProcess::QueueFakeAttachEvents for details
//    
//    The order of thread creates is random and at the mercy of ICorDebugProcess::EnumerateThreads.
//    Whidbey would send thread creates in the order of the OS's native thread
//    list. Since Arrowhead no longer sends fake attach events, the shim simulates
//    the fake attach events. But ICorDebug doesn't provide a way to get the
//    same order that V2 used. So without using platform-specific thread-enumeration,
//    we can't get the V2 ordering.
//
//    Compare to code:ShimProcess::QueueFakeThreadAttachEventsNativeOrder,
//    which sends threads in the OS native thread create order.
//    
HRESULT ShimProcess::QueueFakeThreadAttachEventsNoOrder()
{    
    ICorDebugProcess * pProcess = GetProcess();

    RSExtSmartPtr<ICorDebugThreadEnum> pThreadEnum;
    RSExtSmartPtr<ICorDebugThread> pThread;

    // V2 would only send create threads after a thread had run managed code. 
    // V3 has a discovery model where Enumeration can find threads before they've run managed code.
    // So the emulation here may send some additional create-thread events that v2 didn't send.
    HRESULT hr = pProcess->EnumerateThreads(&pThreadEnum);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
    if (FAILED(hr))
    {
        return hr;
    }

    ULONG cDummy;
    
    while(SUCCEEDED(pThreadEnum->Next(1, &pThread, &cDummy)) && (pThread != NULL))
    {
        RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
        hr = pThread->GetAppDomain(&pAppDomain);
        
        // Getting the appdomain shouldn't fail. If it does, we can't dispatch
        // this callback, but we can still dispatch the other thread creates.
        SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
        if (pAppDomain != NULL)
        {
            GetShimCallback()->CreateThread(pAppDomain, pThread);
            AddDuplicateCreationEvent(pThread);
        }
        pThread.Clear();        
    }

    return S_OK;
}

//---------------------------------------------------------------------------------------
// Send fake Thread-create events for attach, using the order of the OS native
// thread list.
// 
// Returns:
//    S_OK on success, else error.
// 
// Notes:
//    This sends fake thread-create events, ala V2 attach.
//    See code:ShimProcess::QueueFakeAttachEvents for details
//    The order of the thread creates matches the OS's native thread list.
//    This is important because the debugger can use the order of thread-create
//    callbacks to associate logical thread-ids (0,1,2...) with threads. Users
//    may rely on thread 0 always being the main thread.
//    In contrast, the order from ICorDebugProcess::EnumerateThreads is random.
//
//    Compare to code:ShimProcess::QueueFakeThreadAttachEventsNoOrder, which
//    sends the threads in an arbitrary order.
HRESULT ShimProcess::QueueFakeThreadAttachEventsNativeOrder() 
{ 
#ifdef FEATURE_CORESYSTEM
    _ASSERTE("NYI");
    return E_FAIL;
#else
    ICorDebugProcess * pProcess = GetProcess();

    DWORD dwProcessId;
    HRESULT hr = pProcess->GetID(&dwProcessId);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
    if (FAILED(hr))
    {
        return hr;
    }

    HANDLE hThreadSnap = INVALID_HANDLE_VALUE; 
    THREADENTRY32 te32; 

    // Take a snapshot of all running threads  
    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 
    if (hThreadSnap == INVALID_HANDLE_VALUE) 
    {
        hr = HRESULT_FROM_GetLastError();
        SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
        return hr; 
    }
    // HandleHolder doesn't deal with INVALID_HANDLE_VALUE, so we only assign if we have a legal value.
    HandleHolder hSnapshotHolder(hThreadSnap);

    // Fill in the size of the structure before using it. 
    te32.dwSize = sizeof(THREADENTRY32); 

    // Retrieve information about the first thread, and exit if unsuccessful
    if (!Thread32First(hThreadSnap, &te32)) 
    {
        hr = HRESULT_FROM_GetLastError();
        return hr;
    }

    // Now walk the thread list of the system,
    // and display information about each thread
    // associated with the specified process
    do 
    { 
        if (te32.th32OwnerProcessID == dwProcessId)
        {            
            RSExtSmartPtr<ICorDebugThread> pThread;
            pProcess->GetThread(te32.th32ThreadID, &pThread);
            if (pThread != NULL)
            {
                // If we fail to get the appdomain for some reason, then then
                // we can't dispatch this thread callback. But we can still
                // finish enumerating.
                RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
                HRESULT hrGetAppDomain = pThread->GetAppDomain(&pAppDomain);
                SIMPLIFYING_ASSUMPTION_SUCCEEDED(hrGetAppDomain);
                if (pAppDomain != NULL)
                {
                    GetShimCallback()->CreateThread(pAppDomain, pThread);
                    AddDuplicateCreationEvent(pThread);

                    //fix for issue DevDiv2\DevDiv 77523 - threads are switched out in SQL don't get thread create notifications
                    // mark that this thread has queued a create event
                    CordbThread* pThreadInternal = static_cast<CordbThread*>(pThread.GetValue());
                    pThreadInternal->SetCreateEventQueued();
                }
            }
        }
    } while(Thread32Next(hThreadSnap, &te32)); 


    //fix for issue DevDiv2\DevDiv 77523 - threads are switched out in SQL don't get thread create notifications
    //


    // Threads which were switched out won't be present in the native thread order enumeration above.
    // In order to not miss them we will enumerate all the managed thread objects and for any that we haven't
    // already queued a notification for, we will queue a notification now.
    RSExtSmartPtr<ICorDebugThreadEnum> pThreadEnum;
    RSExtSmartPtr<ICorDebugThread> pThread;
    hr = pProcess->EnumerateThreads(&pThreadEnum);
    if (FAILED(hr))
    {
        return hr;
    }

    ULONG cDummy;
    
    while(SUCCEEDED(pThreadEnum->Next(1, &pThread, &cDummy)) && (pThread != NULL))
    {
        RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;
        hr = pThread->GetAppDomain(&pAppDomain);
        CordbThread* pThreadInternal = static_cast<CordbThread*>(pThread.GetValue());

        // Getting the appdomain shouldn't fail. If it does, we can't dispatch
        // this callback, but we can still dispatch the other thread creates.
        SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
        if (pAppDomain != NULL && !pThreadInternal->CreateEventWasQueued())
        {
            GetShimCallback()->CreateThread(pAppDomain, pThread);
            AddDuplicateCreationEvent(pThread);
            pThreadInternal->SetCreateEventQueued();
        }
        pThread.Clear();        
    }


    return S_OK;
#endif
}

//---------------------------------------------------------------------------------------
// Queues the fake Assembly and Module load events
// 
// Arguments:
//   pAssembly - non-null, the assembly to queue. 
//   
// Notes:
//   Helper for code:ShimProcess::QueueFakeAttachEvents
//   Queues create events for the assembly and for all modules within the
//   assembly. Most assemblies only have 1 module.
void ShimProcess::QueueFakeAssemblyAndModuleEvent(ICorDebugAssembly * pAssembly)
{
    RSExtSmartPtr<ICorDebugAppDomain> pAppDomain;

    HRESULT hr = pAssembly->GetAppDomain(&pAppDomain);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    //
    // Send the fake Load Assembly event.
    //
    GetShimCallback()->LoadAssembly(pAppDomain, pAssembly);
    AddDuplicateCreationEvent(pAssembly);

    //
    // Send Modules - must be in load order
    //
    RSExtSmartPtr<ICorDebugModuleEnum> pModuleEnum;
    hr = pAssembly->EnumerateModules(&pModuleEnum);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    ULONG countModules;
    hr = pModuleEnum->GetCount(&countModules);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    // ISSUE WORKAROUND 835869
    // The CordbEnumFilter used as the implementation of CordbAssembly::EnumerateModules has
    // a ref counting bug in it. It adds one ref to each item when it is constructed and never
    // removes that ref. Expected behavior would be that it adds a ref at construction, another on
    // every call to next, and releases the construction ref when the enumerator is destroyed. The
    // user is expected to release the reference they receive from Next. Thus enumerating exactly
    // one time and calling Release() does the correct thing regardless of whether this bug is present
    // or not. Note that with the bug the enumerator holds 0 references at the end of this loop,
    // however the assembly also holds references so the modules will not be prematurely released.
    for(ULONG i = 0; i < countModules; i++)
    {
        ICorDebugModule* pModule = NULL;
        ULONG countFetched = 0;
        pModuleEnum->Next(1, &pModule, &countFetched);
        _ASSERTE(pModule != NULL);
        if(pModule != NULL)
        {
            pModule->Release();
        }
    }

    RSExtSmartPtr<ICorDebugModule> * pModules = new RSExtSmartPtr<ICorDebugModule> [countModules];
    m_pProcess->GetModulesInLoadOrder(pAssembly, pModules, countModules);
    for(ULONG iModule = 0; iModule < countModules; iModule++)
    {
        ICorDebugModule * pModule = pModules[iModule];
        
        GetShimCallback()->FakeLoadModule(pAppDomain, pModule);
        AddDuplicateCreationEvent(pModule);

        // V2 may send UpdatePdbStreams for certain modules (like dynamic or in-memory modules).
        // We don't yet have this support for out-of-proc. 
        // When the LoadModule event that we just queued is actually dispatched, it will 
        // send an IPC event in-process that will collect the information and queue the event
        // at that time.
        // @dbgtodo : I don't think the above is true anymore - clean it up?
        
        RSExtSmartPtr<IStream> pSymbolStream;
        
        // ICorDebug has no public way to request raw symbols.  This is by-design because we
        // don't want people taking a dependency on a specific format (to give us the ability
        // to innovate for the RefEmit case).  So we must use a private hook here to get the
        // symbol data.
        CordbModule * pCordbModule = static_cast<CordbModule *>(pModule);
        IDacDbiInterface::SymbolFormat symFormat = IDacDbiInterface::kSymbolFormatNone;
        EX_TRY
        {
            symFormat = pCordbModule->GetInMemorySymbolStream(&pSymbolStream);
        }
        EX_CATCH_HRESULT(hr);
        SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);   // Shouldn't be any errors trying to read symbols

        // Only pass the raw symbols onto the debugger if they're in PDB format (all that was supported
        // in V2).  Note that we could have avoided creating a stream for the non-PDB case, but we'd have
        // to refactor GetInMemorySymbolStream and the perf impact should be negligable.
        if (symFormat == IDacDbiInterface::kSymbolFormatPDB)
        {
            _ASSERTE(pSymbolStream != NULL);    // symFormat should have been kSymbolFormatNone if null stream
            GetShimCallback()->UpdateModuleSymbols(pAppDomain, pModule, pSymbolStream);
        }

    }
    delete [] pModules;
}

//---------------------------------------------------------------------------------------
// Get an array of appdomains, sorted by increasing AppDomain ID
// 
// Arguments:
//    pProcess - process containing the appdomains
//    ppAppDomains - array that this function will allocate to hold appdomains
//    pCount - size of ppAppDomains array
//    
// Assumptions:
//    Caller must delete [] ppAppDomains
//    
// Notes
//   This is used as part of code:ShimProcess::QueueFakeAttachEvents.
//   The fake attach events want appdomains in creation order. ICorDebug doesn't provide
//   this ordering in the enumerators.  
//   
//   This returns the appdomains sorted in order of increasing AppDomain ID, since that's the best
//   approximation of creation order that we have.
//   @dbgtodo - determine if ICD will provide 
//   ordered enumerators
//   
HRESULT GetSortedAppDomains(ICorDebugProcess * pProcess, RSExtSmartPtr<ICorDebugAppDomain> **ppAppDomains, ULONG * pCount)
{
    _ASSERTE(ppAppDomains != NULL);

    HRESULT hr = S_OK;
    RSExtSmartPtr<ICorDebugAppDomainEnum> pAppEnum;

    //
    // Find the size of the array to hold all the appdomains
    // 
    hr = pProcess->EnumerateAppDomains(&pAppEnum);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
    ULONG countAppDomains = 0;

    hr =  pAppEnum->GetCount(&countAppDomains);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

    //
    // Allocate the array
    //
    RSExtSmartPtr<ICorDebugAppDomain> * pAppDomains = new RSExtSmartPtr<ICorDebugAppDomain>[countAppDomains];
    *ppAppDomains = pAppDomains;
    *pCount = countAppDomains;

    //
    // Load all the appdomains into the array
    // 
    ULONG countDummy;
    hr = pAppEnum->Next(countAppDomains, (ICorDebugAppDomain**) pAppDomains, &countDummy);
    SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
    SIMPLIFYING_ASSUMPTION(countDummy == countAppDomains);

    //
    // Now sort them based on appdomain ID.
    // We generally expect a very low number of appdomains (usually 1). So a n^2 sort shouldn't be a perf
    // problem here.
    // 
    for(ULONG i = 0; i < countAppDomains; i++)
    {
        ULONG32 id1;
        hr = pAppDomains[i]->GetID(&id1);
        SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

        for(ULONG j = i + 1; j < countAppDomains; j++)
        {
            ULONG32 id2;
            hr = pAppDomains[j]->GetID(&id2);
            SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);

            if (id1 > id2)
            {
                // swap values
                ICorDebugAppDomain * pTemp = pAppDomains[i];
                pAppDomains[i].Assign(pAppDomains[j]);
                pAppDomains[j].Assign(pTemp);

                // update id1 key since it's in the outer-loop.
                id1 = id2;
            }
        }
    }



    return S_OK;

}

//---------------------------------------------------------------------------------------
// To emulate the V2 attach-handshake, give the shim a chance to inject fake attach events.
//
// Notes:
//   Do this before the queue is empty so that HasQueuedCallbacks() doesn't toggle from false to true.
//   This is called once the process is synchronized, which emulates V2 semantics on attach.
//   This may be called on the Win32Event Thread from inside of Filter, or on another thread.
void ShimProcess::QueueFakeAttachEvents()
{
    // Serialize this function with Dispose()
    RSLockHolder lockHolder(&m_ShimProcessDisposeLock);
    if (m_fIsDisposed)
        return;

    // The fake CreateProcess is already queued. Start queuing the rest of the events.
    // The target is stopped (synchronized) this whole time.
    // This will use the inspection API to look at the process and queue up the fake 
    // events that V2 would have sent in a similar situation. All of the callbacks to GetShimCallback()
    // just queue up the events. The event queue is then drained as the V2 debugger calls continue.

    HRESULT hr = S_OK;
    ICorDebugProcess * pProcess = GetProcess();

    //
    // First, Queue all the Fake AppDomains
    //
    RSExtSmartPtr<ICorDebugAppDomain> * pAppDomains = NULL; 
    ULONG countAppDomains = 0;

    hr =  GetSortedAppDomains(pProcess, &pAppDomains, &countAppDomains);
    if (FAILED(hr))
        return;

    for(ULONG i = 0; i < countAppDomains; i++)
    {
        // V2 expects that the debugger then attaches to each AppDomain during the Create-appdomain callback.
        // This was done to allow for potential per-appdomain debugging. However, only-process
        // wide debugging support was allowed in V2. The caller had to attach to all Appdomains.

        GetShimCallback()->CreateAppDomain(pProcess, pAppDomains[i]);
        AddDuplicateCreationEvent(pAppDomains[i]);
    }

    // V2 had a break in the callback queue at this point.

    //
    // Second, queue all Assembly and Modules events.
    //

    for(ULONG iAppDomain = 0; iAppDomain < countAppDomains; iAppDomain++)
    {
        ICorDebugAppDomain * pAppDomain = pAppDomains[iAppDomain];
        //
        // Send Assemblies. Must be in load order.
        //
        
        RSExtSmartPtr<ICorDebugAssemblyEnum> pAssemblyEnum;
        hr = pAppDomain->EnumerateAssemblies(&pAssemblyEnum);
        if (FAILED(hr))
            break;

        ULONG countAssemblies;
        hr = pAssemblyEnum->GetCount(&countAssemblies);
        if (FAILED(hr))
            break;

        RSExtSmartPtr<ICorDebugAssembly> * pAssemblies = new RSExtSmartPtr<ICorDebugAssembly> [countAssemblies];
        m_pProcess->GetAssembliesInLoadOrder(pAppDomain, pAssemblies, countAssemblies);
        for(ULONG iAssembly = 0; iAssembly < countAssemblies; iAssembly++)
        {
            QueueFakeAssemblyAndModuleEvent(pAssemblies[iAssembly]);
        }
        delete [] pAssemblies;

    }

    delete [] pAppDomains;

    
    // V2 would have a break in the callback queue at this point.
    
    // V2 would send all relevant ClassLoad events now. 
    //
    // That includes class loads for all modules that:
    //   - are dynamic
    //   - subscribed to class load events via ICorDebugModule::EnableClassLoadCallbacks.
    // We don't provide Class-loads in our emulation because:
    // 1. "ClassLoad" doesn't actually mean anything here. 
    // 2. We have no way of enumerating "loaded" classes in the CLR. We could use the metadata to enumerate
    //    all classes, but that's offers no value.
    // 3. ClassLoad is useful for dynamic modules to notify a debugger that the module changed and
    //    to update symbols; but the LoadModule/UpdateModule syms already do that.


    //
    // Third, Queue all Threads
    //
#if !defined(FEATURE_DBGIPC_TRANSPORT_DI) && !defined(FEATURE_CORESYSTEM)   
    // Use OS thread enumeration facilities to ensure that the managed thread
    // thread order is the same as the corresponding native thread order.
    QueueFakeThreadAttachEventsNativeOrder();
#else
    // Use ICorDebug to enumerate threads. The order of managed threads may
    // not match the order the threads were created in.
    QueueFakeThreadAttachEventsNoOrder();
#endif

    // Forth, Queue all Connections.
    // Enumerate connections is not exposed through ICorDebug, so we need to go use a private hook on CordbProcess.
    m_pProcess->QueueFakeConnectionEvents();

    // For V2 jit-attach, the callback queue would also include the jit-attach event (Exception, UserBreak, MDA, etc).
    // This was explicitly in the same callback queue so that a debugger would drain it as part of draining the attach
    // events. 

    // In V3, on normal attach, the VM just sends a Sync-complete event.
    // On jit-attach, the VM sends the jit-attach event and then the sync-complete. 
    // The shim just queues the fake attach events at the first event it gets from the left-side. 
    // In jit-attach, the shim will queue the fake events right before it queues the jit-attach event,
    // thus keeping them in the same callback queue as V2 did.
    
}

// Accessor for m_attached.
bool ShimProcess::GetAttached()
{
    return m_attached;
}
// We need to know whether we are in the CreateProcess callback to be able to 
// return the v2.0 hresults from code:CordbProcess::SetDesiredNGENCompilerFlags 
// when we are using the shim.
// 
// Expose m_fInCreateProcess
bool ShimProcess::GetInCreateProcess() 
{ 
    return m_fInCreateProcess; 
}

void ShimProcess::SetInCreateProcess(bool value) 
{ 
    m_fInCreateProcess = value; 
}

// We need to know whether we are in the FakeLoadModule callback to be able to 
// return the v2.0 hresults from code:CordbModule::SetJITCompilerFlags when
// we are using the shim.
// 
// Expose m_fInLoadModule
bool ShimProcess::GetInLoadModule() 
{ 
    return m_fInLoadModule; 

}

void ShimProcess::SetInLoadModule(bool value) 
{
    m_fInLoadModule = value; 
}

// When we get a continue, we need to clear the flags indicating we're still in a callback
void ShimProcess::NotifyOnContinue () 
{ 
    m_fInCreateProcess = false; 
    m_fInLoadModule = false; 
}

// The RS calls this function when the stack is about to be changed in any way, e.g. continue, SetIP, etc.
void ShimProcess::NotifyOnStackInvalidate()
{ 
    ClearAllShimStackWalk();
}

//---------------------------------------------------------------------------------------
//
// Filter HResults for ICorDebugProcess2::SetDesiredNGENCompilerFlags to emualte V2 error semantics.
// Arguments:
//    hr - V3 hresult 
//
// Returns:
//    hresult V2 would have returned in same situation.
HRESULT ShimProcess::FilterSetNgenHresult(HRESULT hr)
{
    if ((hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS) && !m_fInCreateProcess)
    {
        return hr;
    }
    if (m_attached)
    {
        return CORDBG_E_CANNOT_BE_ON_ATTACH;
    }
    return hr;
}

//---------------------------------------------------------------------------------------
// Filter HRs for ICorDebugModule::EnableJITDebugging, ICorDebugModule2::SetJITCompilerFlags 
// to emulate V2 error semantics
//
// Arguments:
//    hr - V3 hresult 
//
// Returns:
//    hresult V2 would have returned in same situation.
HRESULT ShimProcess::FilterSetJitFlagsHresult(HRESULT hr)
{
    if ((hr == CORDBG_E_MUST_BE_IN_LOAD_MODULE) && !m_fInLoadModule)
    {
        return hr;
    }
    if (m_attached && (hr == CORDBG_E_MUST_BE_IN_LOAD_MODULE))
    {
        return CORDBG_E_CANNOT_BE_ON_ATTACH;
    }
    return hr;
}

// ----------------------------------------------------------------------------
// ShimProcess::LookupOrCreateShimStackWalk
//
// Description: 
//    Find the ShimStackWalk associated with the specified ICDThread.  Create one if it's not found.
//
// Arguments:
//    * pThread - the specified thread
//
// Return Value:
//    Return the ShimStackWalk associated with the specified thread.
//    
// Notes:
//    The ShimStackWalks handed back by this function is only valid until the next time the stack is changed
//    in any way.  In other words, the ShimStackWalks are valid until the next time 
//    code:CordbThread::CleanupStack or code:CordbThread::MarkStackFramesDirty is called.
//    
//    ShimStackWalk and ICDThread have a 1:1 relationship.  Only one ShimStackWalk will be created for any
//    given ICDThread.  So if two threads in the debugger are walking the same thread in the debuggee, they
//    operate on the same ShimStackWalk.  This is ok because ShimStackWalks walk the stack at creation time,
//    cache all the frames, and become read-only after creation.
//    
//    Refer to code:ShimProcess::ClearAllShimStackWalk to see how ShimStackWalks are cleared.
//

ShimStackWalk * ShimProcess::LookupOrCreateShimStackWalk(ICorDebugThread * pThread)
{
    ShimStackWalk * pSW = NULL;

    {
        // do the lookup under the Shim lock
        RSLockHolder lockHolder(&m_ShimLock);
        pSW = m_pShimStackWalkHashTable->Lookup(pThread);
    }

    if (pSW == NULL)
    {
        // create one if it's not found and add it to the hash table
        NewHolder<ShimStackWalk> pNewSW(new ShimStackWalk(this, pThread));

        {
            // Do the lookup again under the Shim lock, and only add the new ShimStackWalk if no other thread
            // has beaten us to it.
            RSLockHolder lockHolder(&m_ShimLock);
            pSW = m_pShimStackWalkHashTable->Lookup(pThread);
            if (pSW == NULL)
            {
                m_pShimStackWalkHashTable->Add(pNewSW);
                pSW = pNewSW;
                
                // don't release the memory if all goes well
                pNewSW.SuppressRelease();
            }
            else
            {
                // The NewHolder will automatically delete the ShimStackWalk when it goes out of scope.
            }
        }
    }

    return pSW;
}

// ----------------------------------------------------------------------------
// ShimProcess::ClearAllShimStackWalk
//
// Description: 
//    Remove and delete all the entries in the hash table of ShimStackWalks.
// 
// Notes:
//    Refer to code:ShimProcess::LookupOrCreateShimStackWalk to see how ShimStackWalks are created.
//

void ShimProcess::ClearAllShimStackWalk()
{
    RSLockHolder lockHolder(&m_ShimLock);

    // loop through all the entries in the hash table, remove them, and delete them
    for (ShimStackWalkHashTable::Iterator pCurElem = m_pShimStackWalkHashTable->Begin(),
                                          pEndElem = m_pShimStackWalkHashTable->End();
         pCurElem != pEndElem; 
         pCurElem++)
    {
        ShimStackWalk * pSW = *pCurElem;
        m_pShimStackWalkHashTable->Remove(pSW->GetThread());
        delete pSW;
    }
}

//---------------------------------------------------------------------------------------
// Called before shim dispatches an event.
//
// Arguments:
//   fRealCreateProcessEvent - true if the shim is about to dispatch a real create process event (as opposed
//                             to one faked up by the shim itself)
// Notes:
//    This may be called from within Filter, which means we may be on the win32-event-thread.
//    This is called on all callbacks from the VM.
//    This gives us a chance to queue fake-attach events. So call it before the Jit-attach
//    event has been queued. 
void ShimProcess::PreDispatchEvent(bool fRealCreateProcessEvent /*= false*/)
{    
    CONTRACTL
    {
        THROWS;
    }
    CONTRACTL_END;

    // For emulating the V2 case, we need to do additional initialization before dispatching the callback to the user.    
    if (!m_fFirstManagedEvent)
    {
        // Remember that we're processing the first managed event so that we only call HandleFirstRCEvent() once
        m_fFirstManagedEvent = true;

        // This can fail with the incompatable version HR. The process has already been terminated if this
        // is the case. This will dispatch an Error callback 
        // If this fails, the process is in an undefined state.
        // @dbgtodo ipc-block: this will go away once we get rid
        // of the IPC block.
        m_pProcess->FinishInitializeIPCChannel(); // throws on error       
    }

    {
        // In jit-attach cases, the first event the shim gets is the event that triggered the jit-attach.
        // Queue up the fake events now, and then once we return, our caller will queue the jit-attach event.
        // In the jit-attach case, this is before a sync-complete has been sent (since the sync doesn't get sent
        // until after the jit-attach event is sent).
        QueueFakeAttachEventsIfNeeded(fRealCreateProcessEvent);
    }

    // Always request an sync (emulates V2 behavior). If LS is not sync-ready, it will ignore the request.
    m_pProcess->RequestSyncAtEvent();


}

// ----------------------------------------------------------------------------
// ShimProcess::GetCLRInstanceBaseAddress
// Finds the base address of [core]clr.dll 
// Arguments: none
// Return value: returns the base address of [core]clr.dll if possible or NULL otherwise
// 
CORDB_ADDRESS ShimProcess::GetCLRInstanceBaseAddress()
{
    CORDB_ADDRESS baseAddress = CORDB_ADDRESS(NULL);
    DWORD dwPid = m_pLiveDataTarget->GetPid();

#if defined(FEATURE_CORESYSTEM)
    // Debugger attaching to CoreCLR via CoreCLRCreateCordbObject should have already specified CLR module address.
    // Code that help to find it now lives in dbgshim.
#else
    // get a "snapshot" of all modules in the target
    HandleHolder hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
    MODULEENTRY32 moduleEntry = { 0 };

    if (hSnapshot == INVALID_HANDLE_VALUE)
    {
        // we haven't got a loaded CLR yet
        baseAddress = CORDB_ADDRESS(NULL);
    }
    else
    {
        // we need to loop through the modules until we find mscorwks.dll
        moduleEntry.dwSize = sizeof(MODULEENTRY32);

        if (!Module32First(hSnapshot, &moduleEntry))
        {
            baseAddress = CORDB_ADDRESS(NULL);
        }
        else
        {

            do
            {
                if (!_wcsicmp(moduleEntry.szModule, MAKEDLLNAME_W(MAIN_CLR_MODULE_NAME_W)))
                {
                    // we found it, so save the base address
                    baseAddress = PTR_TO_CORDB_ADDRESS(moduleEntry.modBaseAddr);
                }
            } while (Module32Next(hSnapshot, &moduleEntry));
        }
    }
#endif
    return baseAddress;
} // ShimProcess::GetCLRInstanceBaseAddress

// ----------------------------------------------------------------------------
// ShimProcess::FindLoadedCLR
//
// Description: 
//    Look for any CLR loaded into the process.  If found, return the instance ID for it.
//
// Arguments:
//    * pClrInstanceId - out parameter for the instance ID of the CLR
//
// Return Value:
//    Returns S_OK if a CLR was found, and stores its instance ID in pClrInstanceId.
//    Otherwise returns an error code.
//    
// Notes:
//    If there are multiple CLRs loaded in the process, the one chosen for the returned
//    instance ID is unspecified.
//    
HRESULT ShimProcess::FindLoadedCLR(CORDB_ADDRESS * pClrInstanceId)
{
    *pClrInstanceId = GetCLRInstanceBaseAddress();
    
    if (*pClrInstanceId == 0)
    {
        return E_UNEXPECTED;
    }

    return S_OK;
}


//---------------------------------------------------------------------------------------
//
// Locates DAC by finding mscordac{wks|core} next to DBI
//
// Return Value:
//    Returns the module handle for DAC
//    Throws on errors.
//

HMODULE ShimProcess::GetDacModule()
{
    HModuleHolder hDacDll;
    PathString wszAccessDllPath;

#ifdef FEATURE_PAL
    if (!PAL_GetPALDirectoryWrapper(wszAccessDllPath))
    {
        ThrowLastError();
    }
    PCWSTR eeFlavor = MAKEDLLNAME_W(W("mscordaccore"));
#else
    //
    // Load the access DLL from the same directory as the the current CLR Debugging Services DLL.
    //

    if (!WszGetModuleFileName(GetModuleInst(), wszAccessDllPath))
    {
        ThrowLastError();
    }

	if (!SUCCEEDED(CopySystemDirectory(wszAccessDllPath, wszAccessDllPath)))
    {
        ThrowHR(E_INVALIDARG);
    }

    // Dac Dll is named:
    //   mscordaccore.dll  <-- coreclr
    //   mscordacwks.dll   <-- desktop
    PCWSTR eeFlavor = 
        W("mscordaccore.dll");
    
#endif // FEATURE_PAL
    wszAccessDllPath.Append(eeFlavor);

    hDacDll.Assign(WszLoadLibrary(wszAccessDllPath));
    if (!hDacDll)
    {
        DWORD dwLastError = GetLastError();
        if (dwLastError == ERROR_MOD_NOT_FOUND)
        {
            // Give a more specific error in the case where we can't find the DAC dll.
            ThrowHR(CORDBG_E_DEBUG_COMPONENT_MISSING);
        }
        else
        {
            ThrowWin32(dwLastError);
        }
    }
    hDacDll.SuppressRelease();
    return (HMODULE) hDacDll;
}

MachineInfo ShimProcess::GetMachineInfo()
{
    return m_machineInfo;
}

void ShimProcess::SetMarkAttachPendingEvent()  
{
    SetEvent(m_markAttachPendingEvent);
}

void ShimProcess::SetTerminatingEvent()
{
    SetEvent(m_terminatingEvent);
}

RSLock * ShimProcess::GetShimLock()
{
    return &m_ShimLock;
}


bool ShimProcess::IsThreadSuspendedOrHijacked(ICorDebugThread * pThread)
{
    return m_pProcess->IsThreadSuspendedOrHijacked(pThread);
}