1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 //*****************************************************************************
13 #ifdef FEATURE_DBG_PUBLISH
20 #ifndef SM_REMOTESESSION
21 #define SM_REMOTESESSION 0x1000
25 #include "../../dlls/mscorrc/resource.h"
28 // Publish shares header files with the rest of ICorDebug.
29 // ICorDebug should not call ReadProcessMemory & other APIs directly, it should instead go through
30 // the Data-target. ICD headers #define these APIs to help enforce this.
31 // Since Publish is separate and doesn't use data-targets, it can access the APIs directly.
32 // see code:RSDebuggingInfo#UseDataTarget
33 #undef ReadProcessMemory
35 //****************************************************************************
36 //************ App Domain Publishing Service API Implementation **************
37 //****************************************************************************
39 // This function enumerates all the process in the system and returns
41 BOOL GetAllProcessesInSystem(DWORD *ProcessId,
45 HandleHolder hSnapshotHolder;
47 #if !defined(FEATURE_CORESYSTEM)
48 // Load the dll "kernel32.dll".
49 HModuleHolder hDll = WszLoadLibrary(W("kernel32"));
50 _ASSERTE(hDll != NULL);
54 LOG((LF_CORDB, LL_INFO1000,
55 "Unable to load the dll for enumerating processes. "
56 "LoadLibrary (kernel32.dll) failed.\n"));
60 // Load the dll "api-ms-win-obsolete-kernel32-l1-1-0.dll".
61 HModuleHolder hDll = WszLoadLibrary(W("api-ms-win-obsolete-kernel32-l1-1-0.dll"));
62 _ASSERTE(hDll != NULL);
66 LOG((LF_CORDB, LL_INFO1000,
67 "Unable to load the dll for enumerating processes. "
68 "LoadLibrary (api-ms-win-obsolete-kernel32-l1-1-0.dll) failed.\n"));
74 // Create the Process' Snapshot
75 // Get the pointer to the requested function
76 FARPROC pProcAddr = GetProcAddress(hDll, "CreateToolhelp32Snapshot");
78 // If the proc address was not found, return error
79 if (pProcAddr == NULL)
81 LOG((LF_CORDB, LL_INFO1000,
82 "Unable to enumerate processes in the system. "
83 "GetProcAddr (CreateToolhelp32Snapshot) failed.\n"));
89 // Handle from CreateToolHelp32Snapshot must be freed via CloseHandle().
90 typedef HANDLE CREATETOOLHELP32SNAPSHOT(DWORD, DWORD);
93 ((CREATETOOLHELP32SNAPSHOT *)pProcAddr)(TH32CS_SNAPPROCESS, NULL);
95 if (hSnapshot == INVALID_HANDLE_VALUE)
97 LOG((LF_CORDB, LL_INFO1000,
98 "Unable to create snapshot of processes in the system. "
99 "CreateToolhelp32Snapshot() failed.\n"));
102 // HandleHolder doesn't deal with INVALID_HANDLE_VALUE, so we only assign if we have a legal value.
103 hSnapshotHolder.Assign(hSnapshot);
105 // Get the first process in the process list
106 // Get the pointer to the requested function
107 pProcAddr = GetProcAddress(hDll, "Process32First");
109 // If the proc address was not found, return error
110 if (pProcAddr == NULL)
112 LOG((LF_CORDB, LL_INFO1000,
113 "Unable to enumerate processes in the system. "
114 "GetProcAddr (Process32First) failed.\n"));
120 // need to initialize the dwSize field before calling Process32First
121 PE32.dwSize = sizeof (PROCESSENTRY32);
123 typedef BOOL PROCESS32FIRST(HANDLE, LPPROCESSENTRY32);
126 ((PROCESS32FIRST *)pProcAddr)(hSnapshot, &PE32);
130 LOG((LF_CORDB, LL_INFO1000,
131 "Unable to create snapshot of processes in the system. "
132 "Process32First() returned FALSE.\n"));
137 // Loop over and get all the remaining processes
138 // Get the pointer to the requested function
139 pProcAddr = GetProcAddress(hDll, "Process32Next");
141 // If the proc address was not found, return error
142 if (pProcAddr == NULL)
144 LOG((LF_CORDB, LL_INFO1000,
145 "Unable to enumerate processes in the system. "
146 "GetProcAddr (Process32Next) failed.\n"));
150 typedef BOOL PROCESS32NEXT(HANDLE, LPPROCESSENTRY32);
156 ProcessId [iIndex++] = PE32.th32ProcessID;
158 succ = ((PROCESS32NEXT *)pProcAddr)(hSnapshot, &PE32);
160 } while ((succ == TRUE) && (iIndex < (int)dwArraySize));
162 // I would like to know if we're running more than 512 processes on Win95!!
163 _ASSERTE (iIndex < (int)dwArraySize);
165 *pdwNumEntries = iIndex;
167 // If we made it this far, we succeeded
172 // We never want to wait infinite on an object that we can't verify.
173 // Wait with a timeout.
174 const DWORD SAFETY_TIMEOUT = 2000;
176 // ******************************************
178 // ******************************************
180 CorpubPublish::CorpubPublish()
181 : CordbCommonBase(0),
182 m_fpGetModuleFileNameEx(NULL)
184 // Try to get psapi!GetModuleFileNameExW once, and then every process object can use it.
185 // If we can't get it, then we'll fallback to getting information from the IPC block.
186 #if !defined(FEATURE_CORESYSTEM)
187 m_hPSAPIdll = WszLoadLibrary(W("psapi.dll"));
189 m_hPSAPIdll = WszLoadLibrary(W("api-ms-win-obsolete-psapi-l1-1-0.dll"));
192 if (m_hPSAPIdll != NULL)
194 m_fpGetModuleFileNameEx = (FPGetModuleFileNameEx*) GetProcAddress(m_hPSAPIdll, "GetModuleFileNameExW");
197 CordbCommonBase::InitializeCommon();
200 CorpubPublish::~CorpubPublish()
202 // m_hPSAPIdll is a module holder, so the dtor will free it automatically for us.
206 COM_METHOD CorpubPublish::QueryInterface(REFIID id, void **ppInterface)
208 if (id == IID_ICorPublish)
209 *ppInterface = (ICorPublish*)this;
210 else if (id == IID_IUnknown)
211 *ppInterface = (IUnknown*)(ICorPublish*)this;
215 return E_NOINTERFACE;
223 COM_METHOD CorpubPublish::EnumProcesses(COR_PUB_ENUMPROCESS Type,
224 ICorPublishProcessEnum **ppIEnum)
227 CorpubProcess* pProcessList = NULL ;
228 CorpubProcessEnum* pProcEnum = NULL;
231 if( Type != COR_PUB_MANAGEDONLY )
237 // call function to get PIDs for all processes in the system
238 #define MAX_PROCESSES 512
240 DWORD ProcessId[MAX_PROCESSES];
241 DWORD dwNumProcesses = 0;
242 if( !GetAllProcessesInSystem(ProcessId, MAX_PROCESSES, &dwNumProcesses) )
248 // iterate over all the processes to fetch all the managed processes
249 for (int i = 0; i < (int)dwNumProcesses; i++)
251 CorpubProcess *pProcess = NULL;
252 hr = GetProcessInternal( ProcessId[i], &pProcess );
255 _ASSERTE( pProcess == NULL );
256 goto exit; // a serious error has occurred, abort
261 // Success, Add the process to the list.
262 _ASSERTE( pProcess != NULL );
263 pProcess->SetNext( pProcessList );
264 pProcessList = pProcess;
268 // Ignore this process (isn't managed, or shut down, etc.)
269 _ASSERTE( pProcess == NULL );
273 // create and return the ICorPublishProcessEnum
274 pProcEnum = new (nothrow) CorpubProcessEnum(pProcessList);
275 if (pProcEnum == NULL)
282 hr = pProcEnum->QueryInterface(IID_ICorPublishProcessEnum, (void**)ppIEnum);
291 // release our handle on the process objects
292 while (pProcessList != NULL)
294 CorpubProcess *pTmp = pProcessList;
295 pProcessList = pProcessList->GetNextProcess();
298 if( pProcEnum != NULL )
300 pProcEnum->Release();
308 HRESULT CorpubPublish::GetProcess(unsigned pid,
309 ICorPublishProcess **ppProcess)
313 // Query for this specific process (even if we've already handed out a
314 // now-stale process object for this pid)
315 CorpubProcess * pProcess = NULL;
316 HRESULT hr = GetProcessInternal( pid, &pProcess );
319 // Couldn't get this process (doesn't exist, or isn't managed)
320 _ASSERTE( pProcess == NULL );
323 return hr; // there was a serious error trying to get this process info
325 return E_INVALIDARG; // this process doesn't exist, isn't managed or is shutting down
328 // QI to ICorPublishProcess and return it
329 _ASSERTE( pProcess != NULL );
330 hr = pProcess->QueryInterface(IID_ICorPublishProcess, (void**)ppProcess);
336 // Attempts to create a CorpubProcess object for a specific managed process
337 // On success returns S_OK and sets ppProcess to a new AddRef'd CorpubProcess
338 // object. Otherwise, returns S_FALSE if the process isn't managed or if it has
339 // terminated (i.e. it should be ignored), or and error code on a serious failure.
340 HRESULT CorpubPublish::GetProcessInternal(
342 CorpubProcess **ppProcess )
344 #if defined(FEATURE_DBGIPC_TRANSPORT_DI)
347 #else // !FEATURE_DBGIPC_TRANSPORT_DI
351 NewHolder<IPCReaderInterface> pIPCReader( new (nothrow) IPCReaderInterface() );
352 if (pIPCReader == NULL)
354 LOG((LF_CORDB, LL_INFO100, "CP::EP: Failed to allocate memory for IPCReaderInterface.\n"));
355 return E_OUTOFMEMORY;
358 // See if it is a managed process by trying to open the shared
360 hr = pIPCReader->OpenLegacyPrivateBlockTempV4OnPid(pid);
363 return S_FALSE; // Not a managed process
366 // Get the AppDomainIPCBlock
367 AppDomainEnumerationIPCBlock *pAppDomainCB = pIPCReader->GetAppDomainBlock();
368 if (pAppDomainCB == NULL)
370 LOG((LF_CORDB, LL_INFO1000, "CP::EP: Failed to obtain AppDomainIPCBlock.\n"));
374 // Get the process handle.
375 HANDLE hProcess = OpenProcess((PROCESS_VM_READ |
376 PROCESS_QUERY_INFORMATION |
380 if (hProcess == NULL)
382 LOG((LF_CORDB, LL_INFO1000, "CP::EP: OpenProcess() returned NULL handle.\n"));
386 // If the mutex isn't filled in, the CLR is either starting up or shutting down
387 if (pAppDomainCB->m_hMutex == NULL)
389 LOG((LF_CORDB, LL_INFO1000, "CP::EP: IPC block isn't properly filled in.\n"));
393 // Dup the valid mutex handle into this process.
395 if( !pAppDomainCB->m_hMutex.DuplicateToLocalProcess(hProcess, &hMutex) )
400 // Acquire the mutex, only waiting two seconds.
401 // We can't actually gaurantee that the target put a mutex object in here.
402 DWORD dwRetVal = WaitForSingleObject(hMutex, SAFETY_TIMEOUT);
404 if (dwRetVal == WAIT_OBJECT_0)
406 // Make sure the mutex handle is still valid. If
407 // its not, then we lost a shutdown race.
408 if (pAppDomainCB->m_hMutex == NULL)
410 LOG((LF_CORDB, LL_INFO1000, "CP::EP: lost shutdown race, skipping...\n"));
412 ReleaseMutex(hMutex);
419 // Again, landing here is most probably a shutdown race. Its okay, though...
420 LOG((LF_CORDB, LL_INFO1000, "CP::EP: failed to get IPC mutex.\n"));
422 if (dwRetVal == WAIT_ABANDONED)
424 ReleaseMutex(hMutex);
429 // Beware: if the target pid is not properly honoring the mutex, the data in the
430 // IPC block may still shift underneath us.
432 // If we get here, then hMutex is held by this process.
434 // Now create the CorpubProcess object for the ProcessID
435 CorpubProcess *pProc = new (nothrow) CorpubProcess(pid,
441 m_fpGetModuleFileNameEx);
443 // Release our lock on the IPC block.
444 ReleaseMutex(hMutex);
448 return E_OUTOFMEMORY;
450 pIPCReader.SuppressRelease();
452 // Success, return the Process object
457 #endif // FEATURE_DBGIPC_TRANSPORT_DI
462 // ******************************************
464 // ******************************************
467 CorpubProcess::CorpubProcess(DWORD dwProcessId,
471 AppDomainEnumerationIPCBlock *pAD,
472 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
473 IPCReaderInterface *pIPCReader,
474 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
475 FPGetModuleFileNameEx * fpGetModuleFileNameEx)
476 : CordbCommonBase(0, enumCorpubProcess),
477 m_dwProcessId(dwProcessId),
478 m_fIsManaged(fManaged),
479 m_hProcess(hProcess),
482 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
483 m_pIPCReader(pIPCReader),
484 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
488 // First try to get the process name from the OS. That can't be spoofed by badly formed IPC block.
489 // psapi!GetModuleFileNameExW can get that, but it's not available on all platforms so we
490 // need to load it dynamically.
491 if (fpGetModuleFileNameEx != NULL)
493 // MSDN is very confused about whether the lenght is in bytes (MSDN 2002) or chars (MSDN 2004).
494 // We err on the safe side by having buffer that's twice as large, and ignoring
495 // the units on the return value.
496 WCHAR szName[MAX_LONGPATH * sizeof(WCHAR)];
498 DWORD lenInCharsOrBytes = MAX_LONGPATH*sizeof(WCHAR);
500 // Pass NULL module handle to get "Main Module", which will give us the process name.
501 DWORD ret = (*fpGetModuleFileNameEx) (hProcess, NULL, szName, lenInCharsOrBytes);
504 // Recompute string length because we don't know if 'ret' is in bytes or char.
505 SIZE_T len = wcslen(szName) + 1;
506 m_szProcessName = new (nothrow) WCHAR[len];
507 if (m_szProcessName != NULL)
509 wcscpy_s(m_szProcessName, len, szName);
515 // This is a security feature on WinXp + above, so make sure it worked there.
516 CONSISTENCY_CHECK_MSGF(FALSE, ("On XP/2k03 OSes + above, we should have been able to get\n"
517 "the module name from psapi!GetModuleFileNameEx. fp=0x%p\n.", fpGetModuleFileNameEx));
519 // We couldn't get it from the OS, so fallthrough to getting it from the IPC block.
521 // Fetch the process name from the AppDomainIPCBlock
522 _ASSERTE (pAD->m_szProcessName != NULL);
524 if (pAD->m_szProcessName == NULL)
525 m_szProcessName = NULL;
530 _ASSERTE(pAD->m_iProcessNameLengthInBytes > 0);
532 // Note: this assumes we're reading the null terminator from
534 m_szProcessName = (WCHAR*) new (nothrow) char[pAD->m_iProcessNameLengthInBytes];
536 if (m_szProcessName == NULL)
538 LOG((LF_CORDB, LL_INFO1000,
539 "CP::CP: Failed to allocate memory for ProcessName.\n"));
544 BOOL bSucc = ReadProcessMemory(hProcess,
545 pAD->m_szProcessName,
547 pAD->m_iProcessNameLengthInBytes,
551 (nBytesRead != (SIZE_T)pAD->m_iProcessNameLengthInBytes))
553 // The EE may have done a rude exit
554 LOG((LF_CORDB, LL_INFO1000,
555 "CP::EAD: ReadProcessMemory (ProcessName) failed.\n"));
563 CorpubProcess::~CorpubProcess()
565 delete [] m_szProcessName;
566 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
568 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
569 CloseHandle(m_hProcess);
570 CloseHandle(m_hMutex);
574 HRESULT CorpubProcess::QueryInterface(REFIID id, void **ppInterface)
576 if (id == IID_ICorPublishProcess)
577 *ppInterface = (ICorPublishProcess*)this;
578 else if (id == IID_IUnknown)
579 *ppInterface = (IUnknown*)(ICorPublishProcess*)this;
583 return E_NOINTERFACE;
591 // Helper to tell if this process has exited.
592 bool CorpubProcess::IsExited()
594 DWORD res = WaitForSingleObject(this->m_hProcess, 0);
595 return (res == WAIT_OBJECT_0);
599 HRESULT CorpubProcess::IsManaged(BOOL *pbManaged)
601 *pbManaged = (m_fIsManaged == true) ? TRUE : FALSE;
607 // Allocates a local buffer (using 'new') and fills it by copying it from remote memory.
609 // - on success, S_OK, *ppNewLocalBuffer points to a newly allocated buffer containing
610 // the full copy from remote memoy. Caller must use 'delete []' to free this.
611 // - on failure, a failing HR. No memory is allocated.
612 HRESULT AllocateAndReadRemoteBuffer(
615 SIZE_T cbSize, // size of buffer to allocate + copy.
616 BYTE * * ppNewLocalBuffer
619 _ASSERTE(ppNewLocalBuffer != NULL);
620 *ppNewLocalBuffer = NULL;
623 if (pRemotePtr == NULL)
628 BYTE *pLocalBuffer = new (nothrow) BYTE[cbSize];
630 if (pLocalBuffer == NULL)
632 _ASSERTE(!"Failed to alloc memory. Likely size is bogusly large, perhaps from an attacker.");
633 return E_OUTOFMEMORY;
638 // Need to read in the remote process' memory
639 BOOL bSucc = ReadProcessMemory(hProcess,
641 pLocalBuffer, cbSize,
644 if ((bSucc == 0) || (nBytesRead != cbSize))
646 // The EE may have done a rude exit
647 delete [] pLocalBuffer;
651 *ppNewLocalBuffer = pLocalBuffer;
655 // Wrapper around AllocateAndReadRemoteBuffer,
656 // to ensure that we're reading an remote-null terminated string.
657 // Ensures that string is null-terminated.
658 HRESULT AllocateAndReadRemoteString(
661 SIZE_T cbSize, // size of buffer to allocate + copy.
662 __deref_out_bcount(cbSize) WCHAR * * ppNewLocalBuffer
665 // Make sure buffer has right geometry.
671 // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow.
672 SIZE_T ceSize = cbSize / sizeof(WCHAR);
673 if ((ceSize * sizeof(WCHAR)) != cbSize)
678 // It should at least have 1 char for the null terminator.
685 HRESULT hr = AllocateAndReadRemoteBuffer(hProcess, pRemotePtr, cbSize, (BYTE**) ppNewLocalBuffer);
688 // Ensure that the string we just read is actually null terminated.
689 // We can't call wcslen() on it yet, since that may AV on a non-null terminated string.
690 WCHAR * pString = *ppNewLocalBuffer;
692 if (pString[ceSize - 1] == W('\0'))
694 // String is null terminated.
697 pString[ceSize - 1] = W('\0');
699 SIZE_T ceTestLen = wcslen(pString);
700 if (ceTestLen == ceSize - 1)
702 // String was not previously null-terminated.
703 delete [] ppNewLocalBuffer;
711 // Enumerate the list of known application domains in the target process.
713 HRESULT CorpubProcess::EnumAppDomains(ICorPublishAppDomainEnum **ppIEnum)
715 VALIDATE_POINTER_TO_OBJECT(ppIEnum, ICorPublishAppDomainEnum **);
721 WCHAR *pAppDomainName = NULL;
722 CorpubAppDomain *pAppDomainHead = NULL;
724 // Lock the IPC block:
725 // We can't trust any of the data in the IPC block (including our own mutex handle),
726 // because we don't want bugs in the debuggee escalating into bugs in the debugger.
727 DWORD res = WaitForSingleObject(m_hMutex, SAFETY_TIMEOUT);
729 if (res == WAIT_TIMEOUT)
731 // This should only happen if the target process is illbehaved.
732 return CORDBG_E_TIMEOUT;
735 // If the process has gone away, or if it has cleared out its control block, then
736 // we've lost the race to access this process before it is terminated.
737 // Note that if the EE does a rude process exit, it won't have cleared the control block so there
738 // will be a small race window.
739 if (this->IsExited() || this->m_AppDomainCB->m_hMutex == NULL )
741 // This is the common case. A process holding the mutex shouldn't normally exit,
742 // but once it releases the mutex, it may exit asynchronously.
743 return CORDBG_E_PROCESS_TERMINATED;
746 if (res == WAIT_FAILED)
748 // This should be the next most common failure case
749 return HRESULT_FROM_GetLastError();
752 if (res != WAIT_OBJECT_0)
754 // Catch all other possible failures
758 int iAppDomainCount = 0;
759 AppDomainInfo *pADI = NULL;
761 // Make a copy of the IPC block so that we can gaurantee that it's not changing on us.
762 AppDomainEnumerationIPCBlock tempBlock;
763 memcpy(&tempBlock, m_AppDomainCB, sizeof(tempBlock));
765 // Allocate memory to read the remote process' memory into
766 const SIZE_T cbADI = tempBlock.m_iSizeInBytes;
768 // It's possible the process will not have any appdomains.
769 if ((tempBlock.m_rgListOfAppDomains == NULL) != (tempBlock.m_iSizeInBytes == 0))
771 _ASSERTE(!"Inconsistent IPC block in publish.");
776 // All the data in the IPC block is signed integers. They should never be negative,
777 // so check that now.
778 if ((tempBlock.m_iTotalSlots < 0) ||
779 (tempBlock.m_iNumOfUsedSlots < 0) ||
780 (tempBlock.m_iLastFreedSlot < 0) ||
781 (tempBlock.m_iSizeInBytes < 0) ||
782 (tempBlock.m_iProcessNameLengthInBytes < 0))
788 // Check other invariants.
789 if (cbADI != tempBlock.m_iTotalSlots * sizeof(AppDomainInfo))
791 _ASSERTE(!"Size mismatch");
796 hr = AllocateAndReadRemoteBuffer(m_hProcess, tempBlock.m_rgListOfAppDomains, cbADI, (BYTE**) &pADI);
801 _ASSERTE(pADI != NULL);
803 // Collect all the AppDomain info info a list of CorpubAppDomains
804 for (i = 0; i < tempBlock.m_iTotalSlots; i++)
806 if (!pADI[i].IsEmpty())
808 // Should be positive, and at least have a null-terminator character.
809 if (pADI[i].m_iNameLengthInBytes <= 1)
814 hr = AllocateAndReadRemoteString(m_hProcess,
815 (void*) pADI[i].m_szAppDomainName, pADI[i].m_iNameLengthInBytes, // remote string + size in bytes
822 // create a new AppDomainObject. This will take ownership of pAppDomainName.
823 // We know the string is a well-formed null-terminated string,
824 // but beyond that, we can't verify that the data is actually truthful.
825 CorpubAppDomain *pCurrentAppDomain = new (nothrow) CorpubAppDomain(pAppDomainName,
828 if (pCurrentAppDomain == NULL)
830 LOG((LF_CORDB, LL_INFO1000,
831 "CP::EAD: Failed to allocate memory for CorpubAppDomain.\n"));
837 // Since CorpubAppDomain now owns pAppDomain's memory, we don't worry about freeing it.
838 pAppDomainName = NULL;
840 // Add the appdomain to the list.
841 pCurrentAppDomain->SetNext(pAppDomainHead);
842 pAppDomainHead = pCurrentAppDomain;
844 // Shortcut to opt out of reading the rest of the array if it's empty.
845 if (++iAppDomainCount >= tempBlock.m_iNumOfUsedSlots)
851 _ASSERTE ((iAppDomainCount >= tempBlock.m_iNumOfUsedSlots)
852 && (i <= tempBlock.m_iTotalSlots));
854 // create and return the ICorPublishAppDomainEnum object, handing off the AppDomain list to it
855 CorpubAppDomainEnum *pTemp = new (nothrow) CorpubAppDomainEnum(pAppDomainHead);
863 pAppDomainHead = NULL; // handed off AppDomain list to enum, don't delete below
865 hr = pTemp->QueryInterface(IID_ICorPublishAppDomainEnum,
870 ReleaseMutex(m_hMutex);
872 // If we didn't hand off the AppDomain objects, delete them
873 while( pAppDomainHead != NULL )
875 CorpubAppDomain *pTemp = pAppDomainHead;
876 pAppDomainHead = pAppDomainHead->GetNextAppDomain();
883 if (pAppDomainName != NULL)
884 delete [] pAppDomainName;
886 // Either we succeeded && provided an enumerator; or we failed and didn't provide an enum.
887 _ASSERTE(SUCCEEDED(hr) == (*ppIEnum != NULL));
892 * Returns the OS ID for the process in question.
894 HRESULT CorpubProcess::GetProcessID(unsigned *pid)
896 *pid = m_dwProcessId;
902 * Get the display name for a process.
904 HRESULT CorpubProcess::GetDisplayName(ULONG32 cchName,
906 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
908 VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true);
909 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
911 // Reasonable defaults
918 const WCHAR *szTempName = m_szProcessName;
920 // In case we didn't get the name (most likely out of memory on ctor).
922 szTempName = W("<unknown>");
924 return CopyOutString(szTempName, cchName, pcchName, szName);
928 // ******************************************
930 // ******************************************
932 CorpubAppDomain::CorpubAppDomain (__in LPWSTR szAppDomainName, ULONG Id)
933 : CordbCommonBase (0, enumCorpubAppDomain),
935 m_szAppDomainName (szAppDomainName),
938 _ASSERTE(m_szAppDomainName != NULL);
941 CorpubAppDomain::~CorpubAppDomain()
943 delete [] m_szAppDomainName;
946 HRESULT CorpubAppDomain::QueryInterface (REFIID id, void **ppInterface)
948 if (id == IID_ICorPublishAppDomain)
949 *ppInterface = (ICorPublishAppDomain*)this;
950 else if (id == IID_IUnknown)
951 *ppInterface = (IUnknown*)(ICorPublishAppDomain*)this;
955 return E_NOINTERFACE;
964 * Get the name and ID for an application domain.
966 HRESULT CorpubAppDomain::GetID (ULONG32 *pId)
968 VALIDATE_POINTER_TO_OBJECT(pId, ULONG32 *);
976 * Get the name for an application domain.
978 HRESULT CorpubAppDomain::GetName(ULONG32 cchName,
980 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
982 VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true);
983 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
985 const WCHAR *szTempName = m_szAppDomainName;
987 // In case we didn't get the name (most likely out of memory on ctor).
989 szTempName = W("<unknown>");
991 return CopyOutString(szTempName, cchName, pcchName, szName);
996 // ******************************************
998 // ******************************************
1000 CorpubProcessEnum::CorpubProcessEnum (CorpubProcess *pFirst)
1001 : CordbCommonBase (0, enumCorpubProcessEnum),
1005 // Increment the ref count on each process, we own the list
1006 CorpubProcess * cur = pFirst;
1007 while( cur != NULL )
1010 cur = cur->GetNextProcess();
1014 CorpubProcessEnum::~CorpubProcessEnum()
1016 // Release each process in the list (our client may still have a reference
1018 while (m_pFirst != NULL)
1020 CorpubProcess *pTmp = m_pFirst;
1021 m_pFirst = m_pFirst->GetNextProcess();
1026 HRESULT CorpubProcessEnum::QueryInterface (REFIID id, void **ppInterface)
1028 if (id == IID_ICorPublishProcessEnum)
1029 *ppInterface = (ICorPublishProcessEnum*)this;
1030 else if (id == IID_IUnknown)
1031 *ppInterface = (IUnknown*)(ICorPublishProcessEnum*)this;
1034 *ppInterface = NULL;
1035 return E_NOINTERFACE;
1043 HRESULT CorpubProcessEnum::Skip(ULONG celt)
1045 while ((m_pCurrent != NULL) && (celt-- > 0))
1047 m_pCurrent = m_pCurrent->GetNextProcess();
1053 HRESULT CorpubProcessEnum::Reset()
1055 m_pCurrent = m_pFirst;
1060 HRESULT CorpubProcessEnum::Clone(ICorPublishEnum **ppEnum)
1062 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorPublishEnum **);
1066 HRESULT CorpubProcessEnum::GetCount(ULONG *pcelt)
1068 VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
1070 CorpubProcess *pTemp = m_pFirst;
1074 while (pTemp != NULL)
1077 pTemp = pTemp->GetNextProcess();
1083 HRESULT CorpubProcessEnum::Next(ULONG celt,
1084 ICorPublishProcess *objects[],
1085 ULONG *pceltFetched)
1087 VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorPublishProcess *,
1089 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *);
1091 if ((pceltFetched == NULL) && (celt != 1))
1093 return E_INVALIDARG;
1098 if (pceltFetched != NULL)
1109 while ((m_pCurrent != NULL) && (count < celt))
1111 hr = m_pCurrent->QueryInterface (IID_ICorPublishProcess,
1112 (void**)&objects[count]);
1120 m_pCurrent = m_pCurrent->GetNextProcess();
1123 if (pceltFetched != NULL)
1125 *pceltFetched = count;
1129 // If we reached the end of the enumeration, but not the end
1130 // of the number of requested items, we return S_FALSE.
1140 // ******************************************
1141 // CorpubAppDomainEnum
1142 // ******************************************
1143 CorpubAppDomainEnum::CorpubAppDomainEnum (CorpubAppDomain *pFirst)
1144 : CordbCommonBase (0, enumCorpubAppDomainEnum),
1148 CorpubAppDomain *pCur = pFirst;
1149 while( pCur != NULL )
1152 pCur = pCur->GetNextAppDomain();
1156 CorpubAppDomainEnum::~CorpubAppDomainEnum()
1158 // Delete all the app domains
1159 while (m_pFirst != NULL )
1161 CorpubAppDomain *pTemp = m_pFirst;
1162 m_pFirst = m_pFirst->GetNextAppDomain();
1167 HRESULT CorpubAppDomainEnum::QueryInterface (REFIID id, void **ppInterface)
1169 if (id == IID_ICorPublishAppDomainEnum)
1170 *ppInterface = (ICorPublishAppDomainEnum*)this;
1171 else if (id == IID_IUnknown)
1172 *ppInterface = (IUnknown*)(ICorPublishAppDomainEnum*)this;
1175 *ppInterface = NULL;
1176 return E_NOINTERFACE;
1184 HRESULT CorpubAppDomainEnum::Skip(ULONG celt)
1186 while ((m_pCurrent != NULL) && (celt-- > 0))
1188 m_pCurrent = m_pCurrent->GetNextAppDomain();
1194 HRESULT CorpubAppDomainEnum::Reset()
1196 m_pCurrent = m_pFirst;
1201 HRESULT CorpubAppDomainEnum::Clone(ICorPublishEnum **ppEnum)
1203 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorPublishEnum **);
1207 HRESULT CorpubAppDomainEnum::GetCount(ULONG *pcelt)
1209 VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
1211 CorpubAppDomain *pTemp = m_pFirst;
1215 while (pTemp != NULL)
1218 pTemp = pTemp->GetNextAppDomain();
1224 HRESULT CorpubAppDomainEnum::Next(ULONG celt,
1225 ICorPublishAppDomain *objects[],
1226 ULONG *pceltFetched)
1228 VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorPublishProcess *,
1230 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *);
1232 if ((pceltFetched == NULL) && (celt != 1))
1234 return E_INVALIDARG;
1239 if (pceltFetched != NULL)
1250 while ((m_pCurrent != NULL) && (count < celt))
1252 hr = m_pCurrent->QueryInterface (IID_ICorPublishAppDomain,
1253 (void **)&objects[count]);
1261 m_pCurrent = m_pCurrent->GetNextAppDomain();
1265 if (pceltFetched != NULL)
1267 *pceltFetched = count;
1271 // If we reached the end of the enumeration, but not the end
1272 // of the number of requested items, we return S_FALSE.
1282 #endif // defined(FEATURE_DBG_PUBLISH)