diff options
author | danmosemsft <danmose@microsoft.com> | 2017-02-11 07:20:12 -0800 |
---|---|---|
committer | danmosemsft <danmose@microsoft.com> | 2017-02-11 07:20:12 -0800 |
commit | 56d4ba8a9338c3ff7378d18378f38ad847f130f2 (patch) | |
tree | 40b9463880286b1bc0b4c3f858680f1ff210c933 /src/debug/di | |
parent | 8be2f9bb0039e2c49f59c4fb66cebf5467485ba2 (diff) | |
download | coreclr-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.cpp | 9 | ||||
-rw-r--r-- | src/debug/di/module.cpp | 2 | ||||
-rw-r--r-- | src/debug/di/process.cpp | 37 | ||||
-rw-r--r-- | src/debug/di/publish.cpp | 18 | ||||
-rw-r--r-- | src/debug/di/rsmain.cpp | 12 | ||||
-rw-r--r-- | src/debug/di/rspriv.h | 4 | ||||
-rw-r--r-- | src/debug/di/shimprocess.cpp | 149 | ||||
-rw-r--r-- | src/debug/di/windowspipeline.cpp | 127 |
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 } |