summaryrefslogtreecommitdiff
path: root/src/debug/di
diff options
context:
space:
mode:
authordanmosemsft <danmose@microsoft.com>2017-02-11 07:20:12 -0800
committerdanmosemsft <danmose@microsoft.com>2017-02-11 07:20:12 -0800
commit56d4ba8a9338c3ff7378d18378f38ad847f130f2 (patch)
tree40b9463880286b1bc0b4c3f858680f1ff210c933 /src/debug/di
parent8be2f9bb0039e2c49f59c4fb66cebf5467485ba2 (diff)
downloadcoreclr-56d4ba8a9338c3ff7378d18378f38ad847f130f2.tar.gz
coreclr-56d4ba8a9338c3ff7378d18378f38ad847f130f2.tar.bz2
coreclr-56d4ba8a9338c3ff7378d18378f38ad847f130f2.zip
Revert "Remove always defined FEATURE_CORESYSTEM"
This reverts commit 751771a8976f909af772e35c167bd7e3ffbe44c8.
Diffstat (limited to 'src/debug/di')
-rw-r--r--src/debug/di/cordb.cpp9
-rw-r--r--src/debug/di/module.cpp2
-rw-r--r--src/debug/di/process.cpp37
-rw-r--r--src/debug/di/publish.cpp18
-rw-r--r--src/debug/di/rsmain.cpp12
-rw-r--r--src/debug/di/rspriv.h4
-rw-r--r--src/debug/di/shimprocess.cpp149
-rw-r--r--src/debug/di/windowspipeline.cpp127
8 files changed, 356 insertions, 2 deletions
diff --git a/src/debug/di/cordb.cpp b/src/debug/di/cordb.cpp
index 3cb93756ec..c993fa095f 100644
--- a/src/debug/di/cordb.cpp
+++ b/src/debug/di/cordb.cpp
@@ -86,6 +86,15 @@ HINSTANCE g_hInst; // Instance handle to this piece of code
//*****************************************************************************
STDAPI CreateCordbObject(int iDebuggerVersion, IUnknown ** ppCordb)
{
+#if defined(FEATURE_CORECLR) && !defined(FEATURE_DBGIPC_TRANSPORT_DI) && !defined(FEATURE_CORESYSTEM)
+ // This API should not be called for Windows CoreCLR unless we are doing interop-debugging
+ // (which is only supported internally). Use code:CoreCLRCreateCordbObject instead.
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgEnableMixedModeDebugging) == 0)
+ {
+ _ASSERTE(!"Deprecated entry point CreateCordbObject() is called on Windows CoreCLR\n");
+ return E_NOTIMPL;
+ }
+#endif // FEATURE_CORECLR && !FEATURE_DBGIPC_TRANSPORT_DI
if (ppCordb == NULL)
{
diff --git a/src/debug/di/module.cpp b/src/debug/di/module.cpp
index 46487b92c9..36cc6f5f9e 100644
--- a/src/debug/di/module.cpp
+++ b/src/debug/di/module.cpp
@@ -5153,7 +5153,7 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInst
int skipBytes = 0;
-#if defined(DBG_TARGET_X86)
+#if defined(DBG_TARGET_X86) && defined(FEATURE_CORESYSTEM)
// Skip nop sleds on x86 coresystem. The JIT adds these instructions as a security measure,
// and incorrectly reports to us the wrong offset of the call instruction.
const BYTE nop_opcode = 0x90;
diff --git a/src/debug/di/process.cpp b/src/debug/di/process.cpp
index 2d73c3a273..927b438aff 100644
--- a/src/debug/di/process.cpp
+++ b/src/debug/di/process.cpp
@@ -381,6 +381,7 @@ IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger(
WCHAR *mutableFilePath = (WCHAR *)filePath;
+#if defined(FEATURE_CORESYSTEM)
size_t pathLen = wcslen(mutableFilePath);
const wchar_t *nidll = W(".ni.dll");
@@ -402,6 +403,7 @@ IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger(
{
wcscpy_s(mutableFilePath+pathLen-winmdLen, winmdLen, W(".winmd"));
}
+#endif//FEATURE_CORESYSTEM
ALLOW_DATATARGET_MISSING_MEMORY(
pMDII = LookupMetaDataFromDebuggerForSingleFile(pModule, mutableFilePath, dwImageTimeStamp, dwImageSize);
@@ -7565,7 +7567,27 @@ HRESULT CordbProcess::GetRuntimeOffsets()
{
-#if FEATURE_PAL
+#if !defined FEATURE_CORESYSTEM
+ // kernel32!OpenThread does not exist on all platforms (missing on Win98).
+ // So we need to delay load it.
+ typedef HANDLE (WINAPI *FPOPENTHREAD)(DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwThreadId);
+
+
+
+ HMODULE mod = WszGetModuleHandle(W("kernel32.dll"));
+
+ _ASSERTE(mod != NULL); // can't fail since Kernel32.dll is already loaded.
+
+ const FPOPENTHREAD pfnOpenThread = (FPOPENTHREAD)GetProcAddress(mod, "OpenThread");
+
+ if (pfnOpenThread != NULL)
+ {
+ m_hHelperThread = pfnOpenThread(SYNCHRONIZE, FALSE, dwHelperTid);
+ CONSISTENCY_CHECK_MSGF(m_hHelperThread != NULL, ("Failed to get helper-thread handle. tid=0x%x\n", dwHelperTid));
+ }
+#elif FEATURE_PAL
m_hHelperThread = NULL; //RS is supposed to be able to live without a helper thread handle.
#else
m_hHelperThread = OpenThread(SYNCHRONIZE, FALSE, dwHelperTid);
@@ -9738,11 +9760,13 @@ HRESULT CordbProcess::EnsureClrInstanceIdSet()
if (m_clrInstanceId == 0)
{
+#ifdef FEATURE_CORESYSTEM
if(m_cordb->GetTargetCLR() != 0)
{
m_clrInstanceId = PTR_TO_CORDB_ADDRESS(m_cordb->GetTargetCLR());
return S_OK;
}
+#endif
// The only case in which we're allowed to request the "default" CLR instance
// ID is when we're running in V2 mode. In V3, the client is required to pass
@@ -14885,6 +14909,17 @@ HRESULT CordbProcess::IsReadyForDetach()
// and hard detach failures (after which the process object is neutered).
if (m_pShim != NULL)
{
+#if !defined(FEATURE_CORESYSTEM) // CORESYSTEM TODO
+ HModuleHolder hKernel32;
+ hKernel32 = WszLoadLibrary(W("kernel32"));
+ if (hKernel32 == NULL)
+ return HRESULT_FROM_GetLastError();
+ typedef BOOL (*DebugActiveProcessStopSig) (DWORD);
+ DebugActiveProcessStopSig pDebugActiveProcessStop =
+ reinterpret_cast<DebugActiveProcessStopSig>(GetProcAddress(hKernel32, "DebugActiveProcessStop"));
+ if (pDebugActiveProcessStop == NULL)
+ return COR_E_PLATFORMNOTSUPPORTED;
+#endif
}
return S_OK;
diff --git a/src/debug/di/publish.cpp b/src/debug/di/publish.cpp
index a7e2ebb9c2..888988a10f 100644
--- a/src/debug/di/publish.cpp
+++ b/src/debug/di/publish.cpp
@@ -44,6 +44,19 @@ BOOL GetAllProcessesInSystem(DWORD *ProcessId,
{
HandleHolder hSnapshotHolder;
+#if !defined(FEATURE_CORESYSTEM)
+ // Load the dll "kernel32.dll".
+ HModuleHolder hDll = WszLoadLibrary(W("kernel32"));
+ _ASSERTE(hDll != NULL);
+
+ if (hDll == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO1000,
+ "Unable to load the dll for enumerating processes. "
+ "LoadLibrary (kernel32.dll) failed.\n"));
+ return FALSE;
+ }
+#else
// Load the dll "api-ms-win-obsolete-kernel32-l1-1-0.dll".
HModuleHolder hDll = WszLoadLibrary(W("api-ms-win-obsolete-kernel32-l1-1-0.dll"));
_ASSERTE(hDll != NULL);
@@ -55,6 +68,7 @@ BOOL GetAllProcessesInSystem(DWORD *ProcessId,
"LoadLibrary (api-ms-win-obsolete-kernel32-l1-1-0.dll) failed.\n"));
return FALSE;
}
+#endif
// Create the Process' Snapshot
@@ -169,7 +183,11 @@ CorpubPublish::CorpubPublish()
{
// Try to get psapi!GetModuleFileNameExW once, and then every process object can use it.
// If we can't get it, then we'll fallback to getting information from the IPC block.
+#if !defined(FEATURE_CORESYSTEM)
+ m_hPSAPIdll = WszLoadLibrary(W("psapi.dll"));
+#else
m_hPSAPIdll = WszLoadLibrary(W("api-ms-win-obsolete-psapi-l1-1-0.dll"));
+#endif
if (m_hPSAPIdll != NULL)
{
diff --git a/src/debug/di/rsmain.cpp b/src/debug/di/rsmain.cpp
index 542872577f..cd2063a5a0 100644
--- a/src/debug/di/rsmain.cpp
+++ b/src/debug/di/rsmain.cpp
@@ -864,8 +864,10 @@ Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion)
m_processes(11),
m_initialized(false),
m_debuggerSpecifiedVersion(iDebuggerVersion)
+#ifdef FEATURE_CORESYSTEM
,
m_targetCLR(0)
+#endif
{
g_pRSDebuggingInfo->m_Cordb = this;
@@ -1311,7 +1313,9 @@ HRESULT Cordb::SetTargetCLR(HMODULE hmodTargetCLR)
if (m_initialized)
return E_FAIL;
+#ifdef FEATURE_CORESYSTEM
m_targetCLR = hmodTargetCLR;
+#endif
// @REVIEW: are we happy with this workaround? It allows us to use the existing
// infrastructure for instance name decoration, but it really doesn't fit
@@ -1419,6 +1423,14 @@ bool Cordb::IsInteropDebuggingSupported()
// ICorDebug::SetUnmanagedHandler for details.
#ifdef FEATURE_INTEROP_DEBUGGING
+#if defined(FEATURE_CORECLR) && !defined(FEATURE_CORESYSTEM)
+ // Interop debugging is only supported internally on CoreCLR.
+ // Check if the special reg key is set. If not, then we don't allow interop debugging.
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgEnableMixedModeDebugging) == 0)
+ {
+ return false;
+ }
+#endif // FEATURE_CORECLR
return true;
#else
diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h
index 5b832c7731..d714517985 100644
--- a/src/debug/di/rspriv.h
+++ b/src/debug/di/rspriv.h
@@ -2315,7 +2315,9 @@ public:
CorDebugInterfaceVersion GetDebuggerVersion() const;
+#ifdef FEATURE_CORESYSTEM
HMODULE GetTargetCLR() { return m_targetCLR; }
+#endif
private:
bool IsCreateProcessSupported();
@@ -2335,7 +2337,9 @@ private:
//Note - this code could be useful outside coresystem, but keeping the change localized
// because we are late in the win8 release
+#ifdef FEATURE_CORESYSTEM
HMODULE m_targetCLR;
+#endif
};
diff --git a/src/debug/di/shimprocess.cpp b/src/debug/di/shimprocess.cpp
index 6572f2ac29..a6fc15407e 100644
--- a/src/debug/di/shimprocess.cpp
+++ b/src/debug/di/shimprocess.cpp
@@ -18,6 +18,9 @@
#include <limits.h>
#include "shimpriv.h"
+#if !defined(FEATURE_CORESYSTEM)
+#include <tlhelp32.h>
+#endif
//---------------------------------------------------------------------------------------
//
@@ -1079,8 +1082,114 @@ HRESULT ShimProcess::QueueFakeThreadAttachEventsNoOrder()
// 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
}
//---------------------------------------------------------------------------------------
@@ -1368,9 +1477,15 @@ void ShimProcess::QueueFakeAttachEvents()
//
// 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.
@@ -1627,8 +1742,42 @@ 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
diff --git a/src/debug/di/windowspipeline.cpp b/src/debug/di/windowspipeline.cpp
index 073a412608..c3050e3290 100644
--- a/src/debug/di/windowspipeline.cpp
+++ b/src/debug/di/windowspipeline.cpp
@@ -134,8 +134,36 @@ BOOL WindowsNativePipeline::DebugSetProcessKillOnExit(bool fKillOnExit)
// Enforces the bit set in code:WindowsNativePipeline::DebugSetProcessKillOnExit
void WindowsNativePipeline::UpdateDebugSetProcessKillOnExit()
{
+#if !defined(FEATURE_CORESYSTEM)
+ // Late bind to DebugSetProcessKillOnExit - WinXP and above only
+ HModuleHolder hKernel32;
+ hKernel32 = WszLoadLibrary(W("kernel32"));
+ SIMPLIFYING_ASSUMPTION(hKernel32 != NULL);
+ if (hKernel32 == NULL)
+ return;
+
+ typedef BOOL (*DebugSetProcessKillOnExitSig) (BOOL);
+ DebugSetProcessKillOnExitSig pDebugSetProcessKillOnExit =
+ reinterpret_cast<DebugSetProcessKillOnExitSig>(GetProcAddress(hKernel32, "DebugSetProcessKillOnExit"));
+
+ // If the API doesn't exist (eg. Win2k) - there isn't anything we can do, just
+ // silently ignore the request.
+ if (pDebugSetProcessKillOnExit == NULL)
+ return;
+
+ BOOL ret = pDebugSetProcessKillOnExit(m_fKillOnExit);
+
+ // Not a good failure path here.
+ // 1) This shouldn't fail.
+ // 2) Even if it does, this is likely called after the debuggee
+ // has already been created, and if this API fails, most scenarios will
+ // be unaffected, so we don't want to fail the overall debugging session.
+ SIMPLIFYING_ASSUMPTION(ret);
+
+#else
// The API doesn't exit on CoreSystem, just return
return;
+#endif
}
// Create an process under the debugger.
@@ -221,20 +249,69 @@ HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD
// Determine (if possible) whether a debugger is attached to the target process
HRESULT WindowsNativePipeline::IsRemoteDebuggerPresent(DWORD processId, BOOL* pfDebuggerPresent)
{
+#if !defined(FEATURE_CORESYSTEM)
+
+ // Get a process handle for the process ID.
+ HandleHolder hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId);
+ if (hProc == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ // Delay-bind to CheckRemoteDebuggerPresent - WinXP SP1 and above only
+ HModuleHolder hKernel32;
+ hKernel32 = WszLoadLibrary(W("kernel32"));
+ if (hKernel32 == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ typedef BOOL (*CheckRemoteDebuggerPresentSig) (HANDLE, PBOOL);
+ CheckRemoteDebuggerPresentSig pCheckRemoteDebuggerPresent =
+ reinterpret_cast<CheckRemoteDebuggerPresentSig>(GetProcAddress(hKernel32, "CheckRemoteDebuggerPresent"));
+ if (pCheckRemoteDebuggerPresent == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ // API exists - call it
+ if (!pCheckRemoteDebuggerPresent(hProc, pfDebuggerPresent))
+ return HRESULT_FROM_GetLastError();
+
+ return S_OK;
+#else
//CoreSystem doesn't have this API
return E_FAIL;
+#endif
}
// Detach
HRESULT WindowsNativePipeline::DebugActiveProcessStop(DWORD processId)
{
+#if !defined(FEATURE_CORESYSTEM)
+ // Late-bind to DebugActiveProcessStop since it's WinXP and above only
+ HModuleHolder hKernel32;
+ hKernel32 = WszLoadLibrary(W("kernel32"));
+ if (hKernel32 == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ typedef BOOL (*DebugActiveProcessStopSig) (DWORD);
+ DebugActiveProcessStopSig pDebugActiveProcessStop =
+ reinterpret_cast<DebugActiveProcessStopSig>(GetProcAddress(hKernel32, "DebugActiveProcessStop"));
+
+ // Win2K will fail here - can't find DebugActiveProcessStop
+ if (pDebugActiveProcessStop == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ // Ok, the API exists, call it
+ if (!pDebugActiveProcessStop(processId))
+ {
+ // Detach itself failed
+ return HRESULT_FROM_GetLastError();
+ }
+#else
// The API exists, call it
if (!::DebugActiveProcessStop(processId))
{
// Detach itself failed
return HRESULT_FROM_GetLastError();
}
+#endif
return S_OK;
}
@@ -287,6 +364,56 @@ BOOL WindowsNativePipeline::TerminateProcess(UINT32 exitCode)
// Resume any suspended threads (but just once)
HRESULT WindowsNativePipeline::EnsureThreadsRunning()
{
+#ifdef FEATURE_CORESYSTEM
_ASSERTE("NYI");
return E_FAIL;
+#else
+ _ASSERTE(m_dwProcessId != 0);
+
+ // Take a snapshot of all running threads (similar to ShimProcess::QueueFakeThreadAttachEventsNativeOrder)
+ // Alternately we could return thread creation/exit in WaitForDebugEvent. But we expect this to be used
+ // very rarely, so no need to complicate more common codepaths.
+ HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
+ THREADENTRY32 te32;
+
+ hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (hThreadSnap == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_GetLastError();
+
+ // 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))
+ return HRESULT_FROM_GetLastError();
+
+ // Now walk the thread list of the system and attempt to resume any that are part of this process
+ // Ignore errors - this is a best effort (but ASSERT in CHK builds since we don't expect errors
+ // in practice - we expect the process to be frozen at a debug event, so no races etc.)
+
+ HRESULT hr = S_FALSE; // no thread was resumed
+ do
+ {
+ if (te32.th32OwnerProcessID == m_dwProcessId)
+ {
+ HandleHolder hThread = ::OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
+ _ASSERTE(hThread != NULL);
+ if (hThread != NULL)
+ {
+ // Resume each thread exactly once (if they were suspended multiple times,
+ // then EnsureThreadsRunning would need to be called multiple times until it
+ // returned S_FALSE.
+ DWORD prevCount = ::ResumeThread(hThread);
+ _ASSERTE(prevCount >= 0);
+ if (prevCount >= 1)
+ hr = S_OK; // some thread was resumed
+ }
+ }
+ } while(Thread32Next(hThreadSnap, &te32));
+
+ return hr;
+#endif
}