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 | //***************************************************************************** |
5 | // File: RsAppDomain.cpp |
6 | // |
7 | |
8 | // |
9 | //***************************************************************************** |
10 | #include "stdafx.h" |
11 | #include "primitives.h" |
12 | #include "safewrap.h" |
13 | |
14 | #include "check.h" |
15 | |
16 | #include <tlhelp32.h> |
17 | #include "wtsapi32.h" |
18 | |
19 | #ifndef SM_REMOTESESSION |
20 | #define SM_REMOTESESSION 0x1000 |
21 | #endif |
22 | |
23 | #include "corpriv.h" |
24 | #include "../../dlls/mscorrc/resource.h" |
25 | #include <limits.h> |
26 | |
27 | |
28 | /* ------------------------------------------------------------------------- * |
29 | * AppDomain class methods |
30 | * ------------------------------------------------------------------------- */ |
31 | |
32 | // |
33 | // Create a CordbAppDomain object based on a pointer to the AppDomain instance |
34 | // in the CLR. Pre-populates some cached information about the AppDomain |
35 | // from the CLR using DAC. |
36 | // |
37 | // Arguments: |
38 | // pProcess - the CordbProcess object that this AppDomain is part of |
39 | // vmAppDomain - the address in the CLR of the AppDomain object this corresponds to. |
40 | // This will be used to read any additional information about the AppDomain. |
41 | // |
42 | // Assumptions: |
43 | // The IMetaSig object should have been allocated by |
44 | // IMDInternal on a valid metadata blob |
45 | // |
46 | // |
47 | CordbAppDomain::CordbAppDomain(CordbProcess * pProcess, VMPTR_AppDomain vmAppDomain) |
48 | : CordbBase(pProcess, LsPtrToCookie(vmAppDomain.ToLsPtr()), enumCordbAppDomain), |
49 | m_AppDomainId(0), |
50 | m_breakpoints(17), |
51 | m_sharedtypes(3), |
52 | m_modules(17), |
53 | m_assemblies(9), |
54 | m_vmAppDomain(vmAppDomain) |
55 | { |
56 | // This may throw out of the Ctor on error. |
57 | |
58 | // @dbgtodo reliability: we should probably tolerate failures here and keep track |
59 | // of whether our ADID is valid or not, and requery if necessary. |
60 | m_AppDomainId = m_pProcess->GetDAC()->GetAppDomainId(m_vmAppDomain); |
61 | |
62 | LOG((LF_CORDB,LL_INFO10000, "CAD::CAD: this:0x%x (void*)this:0x%x<%d>\n" , this, (void *)this, m_AppDomainId)); |
63 | |
64 | #ifdef _DEBUG |
65 | m_assemblies.DebugSetRSLock(pProcess->GetProcessLock()); |
66 | m_modules.DebugSetRSLock(pProcess->GetProcessLock()); |
67 | m_breakpoints.DebugSetRSLock(pProcess->GetProcessLock()); |
68 | m_sharedtypes.DebugSetRSLock(pProcess->GetProcessLock()); |
69 | #endif |
70 | |
71 | } |
72 | |
73 | /* |
74 | A list of which resources owened by this object are accounted for. |
75 | |
76 | RESOLVED: |
77 | // AddRef() in CordbHashTable::GetBase for a special InProc case |
78 | // AddRef() on the DB_IPCE_CREATE_APP_DOMAIN event from the LS |
79 | // Release()ed in Neuter |
80 | CordbProcess *m_pProcess; |
81 | |
82 | WCHAR *m_szAppDomainName; // Deleted in ~CordbAppDomain |
83 | |
84 | // Cleaned up in Neuter |
85 | CordbHashTable m_assemblies; |
86 | CordbHashTable m_sharedtypes; |
87 | CordbHashTable m_modules; |
88 | CordbHashTable m_breakpoints; // Disconnect()ed in ~CordbAppDomain |
89 | |
90 | private: |
91 | */ |
92 | |
93 | CordbAppDomain::~CordbAppDomain() |
94 | { |
95 | |
96 | // We expect to be Neutered before being released. Neutering will release our process ref |
97 | _ASSERTE(IsNeutered()); |
98 | } |
99 | |
100 | |
101 | // Neutered by process. Once we're neutered, we lose our backpointer to the CordbProcess object, and |
102 | // thus can't do things like call GetProcess() or Continue(). |
103 | void CordbAppDomain::Neuter() |
104 | { |
105 | // This check prevents us from calling this twice and underflowing the internal ref count! |
106 | if (IsNeutered()) |
107 | { |
108 | return; |
109 | } |
110 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
111 | |
112 | // |
113 | // Disconnect any active breakpoints |
114 | // |
115 | { |
116 | CordbBreakpoint* entry; |
117 | HASHFIND find; |
118 | |
119 | for (entry = m_breakpoints.FindFirst(&find); |
120 | entry != NULL; |
121 | entry = m_breakpoints.FindNext(&find)) |
122 | { |
123 | entry->Disconnect(); |
124 | } |
125 | } |
126 | |
127 | // Mark as neutered so that our children can tell the appdomain has now |
128 | // exited. |
129 | CordbBase::Neuter(); |
130 | |
131 | // |
132 | // Purge neuter lists. |
133 | // |
134 | m_TypeNeuterList.NeuterAndClear(GetProcess()); |
135 | m_SweepableNeuterList.NeuterAndClear(GetProcess()); |
136 | |
137 | |
138 | m_assemblies.NeuterAndClear(GetProcess()->GetProcessLock()); |
139 | m_modules.NeuterAndClear(GetProcess()->GetProcessLock()); |
140 | m_sharedtypes.NeuterAndClear(GetProcess()->GetProcessLock()); |
141 | m_breakpoints.NeuterAndClear(GetProcess()->GetProcessLock()); |
142 | |
143 | } |
144 | |
145 | |
146 | HRESULT CordbAppDomain::QueryInterface(REFIID id, void **ppInterface) |
147 | { |
148 | if (id == IID_ICorDebugAppDomain) |
149 | { |
150 | *ppInterface = (ICorDebugAppDomain*)this; |
151 | } |
152 | else if (id == IID_ICorDebugAppDomain2) |
153 | { |
154 | *ppInterface = (ICorDebugAppDomain2*)this; |
155 | } |
156 | else if (id == IID_ICorDebugAppDomain3) |
157 | { |
158 | *ppInterface = (ICorDebugAppDomain3*)this; |
159 | } |
160 | else if (id == IID_ICorDebugAppDomain4) |
161 | { |
162 | *ppInterface = (ICorDebugAppDomain4*)this; |
163 | } |
164 | else if (id == IID_ICorDebugController) |
165 | *ppInterface = (ICorDebugController*)(ICorDebugAppDomain*)this; |
166 | else if (id == IID_IUnknown) |
167 | *ppInterface = (IUnknown*)(ICorDebugAppDomain*)this; |
168 | else |
169 | { |
170 | *ppInterface = NULL; |
171 | return E_NOINTERFACE; |
172 | } |
173 | |
174 | ExternalAddRef(); |
175 | return S_OK; |
176 | } |
177 | |
178 | |
179 | //--------------------------------------------------------------------------------------- |
180 | // |
181 | // Ensure the AppDomain friendly name has been set. |
182 | // |
183 | // Return value: |
184 | // S_OK on success, or a failure code if we couldn't read the name for some reason. |
185 | // There shouldn't be any reason in practice for this to fail other than a corrupt |
186 | // process image. |
187 | // |
188 | // Assumptions: |
189 | // The AppDomain object has already been initialized to know about |
190 | // it's corresponding VM appdomain. |
191 | // InvalidateName is called whenever the name may have changed to prompt us to re-fetch. |
192 | // |
193 | //--------------------------------------------------------------------------------------- |
194 | HRESULT CordbAppDomain::RefreshName() |
195 | { |
196 | if (m_strAppDomainName.IsSet()) |
197 | { |
198 | // If we already have a valid name, we're done. |
199 | return S_OK; |
200 | } |
201 | |
202 | // Use DAC to get the name. |
203 | |
204 | _ASSERTE(!m_vmAppDomain.IsNull()); |
205 | IDacDbiInterface * pDac = NULL; |
206 | HRESULT hr = S_OK; |
207 | EX_TRY |
208 | { |
209 | pDac = m_pProcess->GetDAC(); |
210 | |
211 | #ifdef _DEBUG |
212 | // For debug, double-check the cached value against getting the AD via an AppDomainId. |
213 | VMPTR_AppDomain pAppDomain = pDac->GetAppDomainFromId(m_AppDomainId); |
214 | _ASSERTE(m_vmAppDomain == pAppDomain); |
215 | #endif |
216 | |
217 | // Get the actual string contents. |
218 | pDac->GetAppDomainFullName(m_vmAppDomain, &m_strAppDomainName); |
219 | |
220 | // Now that m_strAppDomainName is set, don't fail without clearing it. |
221 | } |
222 | EX_CATCH_HRESULT(hr); |
223 | |
224 | _ASSERTE(SUCCEEDED(hr) == m_strAppDomainName.IsSet()); |
225 | |
226 | return hr; |
227 | } |
228 | |
229 | |
230 | HRESULT CordbAppDomain::Stop(DWORD dwTimeout) |
231 | { |
232 | FAIL_IF_NEUTERED(this); |
233 | PUBLIC_API_ENTRY(this); |
234 | return (m_pProcess->StopInternal(dwTimeout, this->GetADToken())); |
235 | } |
236 | |
237 | HRESULT CordbAppDomain::Continue(BOOL fIsOutOfBand) |
238 | { |
239 | PUBLIC_API_ENTRY(this); |
240 | FAIL_IF_NEUTERED(this); |
241 | return m_pProcess->ContinueInternal(fIsOutOfBand); |
242 | } |
243 | |
244 | HRESULT CordbAppDomain::IsRunning(BOOL *pbRunning) |
245 | { |
246 | PUBLIC_API_ENTRY(this); |
247 | VALIDATE_POINTER_TO_OBJECT(pbRunning, BOOL *); |
248 | FAIL_IF_NEUTERED(this); |
249 | |
250 | *pbRunning = !m_pProcess->GetSynchronized(); |
251 | |
252 | return S_OK; |
253 | } |
254 | |
255 | HRESULT CordbAppDomain::HasQueuedCallbacks(ICorDebugThread *pThread, BOOL *pbQueued) |
256 | { |
257 | PUBLIC_API_ENTRY(this); |
258 | FAIL_IF_NEUTERED(this); |
259 | |
260 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pThread,ICorDebugThread *); |
261 | VALIDATE_POINTER_TO_OBJECT(pbQueued,BOOL *); |
262 | |
263 | return m_pProcess->HasQueuedCallbacks (pThread, pbQueued); |
264 | } |
265 | |
266 | HRESULT CordbAppDomain::EnumerateThreads(ICorDebugThreadEnum **ppThreads) |
267 | { |
268 | // @TODO E_NOIMPL this |
269 | // |
270 | // (use Process::EnumerateThreads and let users filter their own data) |
271 | HRESULT hr = S_OK; |
272 | PUBLIC_API_BEGIN(this); |
273 | { |
274 | ValidateOrThrow(ppThreads); |
275 | |
276 | RSInitHolder<CordbEnumFilter> pThreadEnum( |
277 | new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList())); |
278 | |
279 | GetProcess()->PrepopulateThreadsOrThrow(); |
280 | |
281 | RSInitHolder<CordbHashTableEnum> pEnum; |
282 | GetProcess()->BuildThreadEnum(this, NULL, pEnum.GetAddr()); |
283 | |
284 | // This builds up auxillary list. don't need pEnum after this. |
285 | hr = pThreadEnum->Init(pEnum, this); |
286 | IfFailThrow(hr); |
287 | |
288 | pThreadEnum.TransferOwnershipExternal(ppThreads); |
289 | } |
290 | PUBLIC_API_END(hr); |
291 | return hr; |
292 | } |
293 | |
294 | |
295 | HRESULT CordbAppDomain::SetAllThreadsDebugState(CorDebugThreadState state, |
296 | ICorDebugThread *pExceptThisThread) |
297 | { |
298 | PUBLIC_API_ENTRY(this); |
299 | FAIL_IF_NEUTERED(this); |
300 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
301 | |
302 | return m_pProcess->SetAllThreadsDebugState(state, pExceptThisThread); |
303 | } |
304 | |
305 | HRESULT CordbAppDomain::Detach() |
306 | { |
307 | PUBLIC_REENTRANT_API_ENTRY(this); // may be called from IMDA::Detach |
308 | FAIL_IF_NEUTERED(this); |
309 | |
310 | return E_NOTIMPL; |
311 | } |
312 | |
313 | HRESULT CordbAppDomain::Terminate(unsigned int exitCode) |
314 | { |
315 | PUBLIC_API_ENTRY(this); |
316 | FAIL_IF_NEUTERED(this); |
317 | return E_NOTIMPL; |
318 | } |
319 | |
320 | void CordbAppDomain::AddToTypeList(CordbBase *pObject) |
321 | { |
322 | INTERNAL_API_ENTRY(this); |
323 | _ASSERTE(pObject != NULL); |
324 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
325 | this->m_TypeNeuterList.Add(GetProcess(), pObject); |
326 | } |
327 | |
328 | |
329 | HRESULT CordbAppDomain::CanCommitChanges( |
330 | ULONG cSnapshots, |
331 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
332 | ICorDebugErrorInfoEnum **pError) |
333 | { |
334 | return E_NOTIMPL; |
335 | } |
336 | |
337 | HRESULT CordbAppDomain::CommitChanges( |
338 | ULONG cSnapshots, |
339 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
340 | ICorDebugErrorInfoEnum **pError) |
341 | { |
342 | return E_NOTIMPL; |
343 | } |
344 | |
345 | |
346 | /* |
347 | * GetProcess returns the process containing the app domain |
348 | */ |
349 | HRESULT CordbAppDomain::GetProcess(ICorDebugProcess **ppProcess) |
350 | { |
351 | PUBLIC_REENTRANT_API_ENTRY(this); |
352 | FAIL_IF_NEUTERED(this); |
353 | |
354 | VALIDATE_POINTER_TO_OBJECT(ppProcess,ICorDebugProcess **); |
355 | |
356 | _ASSERTE (m_pProcess != NULL); |
357 | |
358 | *ppProcess = static_cast<ICorDebugProcess *> (m_pProcess); |
359 | m_pProcess->ExternalAddRef(); |
360 | |
361 | return S_OK; |
362 | } |
363 | |
364 | //--------------------------------------------------------------------------------------- |
365 | // |
366 | // Callback for assembly enumeration. |
367 | // |
368 | // Arguments: |
369 | // vmDomainAssembly - new assembly to add |
370 | // pThis - user data for CordbAppDomain to add assembly too |
371 | // |
372 | // |
373 | // Assumptions: |
374 | // Invoked as callback from code:CordbAppDomain::PrepopulateAssemblies |
375 | // |
376 | // Notes: |
377 | // |
378 | |
379 | // static |
380 | void CordbAppDomain::AssemblyEnumerationCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pThis) |
381 | { |
382 | CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pThis); |
383 | INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess()); |
384 | |
385 | // This lookup will cause the cache to be populated if we haven't seen this assembly before. |
386 | pAppDomain->LookupOrCreateAssembly(vmDomainAssembly); |
387 | } |
388 | |
389 | |
390 | //--------------------------------------------------------------------------------------- |
391 | // |
392 | // Cache a new assembly |
393 | // |
394 | // Arguments: |
395 | // vmDomainAssembly - new assembly to add to cache |
396 | // |
397 | // Return Value: |
398 | // Pointer to Assembly in cache. |
399 | // NULL on failure, and sets unrecoverable error. |
400 | // |
401 | // Assumptions: |
402 | // Caller guarantees assembly is not already added. |
403 | // Called under the stop-go lock. |
404 | // |
405 | // Notes: |
406 | // |
407 | CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_DomainAssembly vmDomainAssembly) |
408 | { |
409 | INTERNAL_API_ENTRY(GetProcess()); |
410 | |
411 | VMPTR_Assembly vmAssembly; |
412 | GetProcess()->GetDAC()->GetAssemblyFromDomainAssembly(vmDomainAssembly, &vmAssembly); |
413 | |
414 | RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, vmDomainAssembly)); |
415 | |
416 | return pAssembly.TransferOwnershipToHash(&m_assemblies); |
417 | } |
418 | |
419 | CordbAssembly * CordbAppDomain::CacheAssembly(VMPTR_Assembly vmAssembly) |
420 | { |
421 | INTERNAL_API_ENTRY(GetProcess()); |
422 | |
423 | RSInitHolder<CordbAssembly> pAssembly(new CordbAssembly(this, vmAssembly, VMPTR_DomainAssembly())); |
424 | |
425 | return pAssembly.TransferOwnershipToHash(&m_assemblies); |
426 | } |
427 | |
428 | //--------------------------------------------------------------------------------------- |
429 | // |
430 | // Build up cache of assmeblies |
431 | // |
432 | // Arguments: |
433 | // |
434 | // Return Value: |
435 | // Throws on error. |
436 | // |
437 | // Assumptions: |
438 | // This is an non-invasive inspection operation called when the debuggee is stopped. |
439 | // |
440 | // Notes: |
441 | // This can safely be called multiple times. |
442 | // |
443 | |
444 | void CordbAppDomain::PrepopulateAssembliesOrThrow() |
445 | { |
446 | INTERNAL_API_ENTRY(GetProcess()); |
447 | |
448 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
449 | |
450 | if (!GetProcess()->IsDacInitialized()) |
451 | { |
452 | return; |
453 | } |
454 | |
455 | // DD-primitive that invokes a callback. |
456 | GetProcess()->GetDAC()->EnumerateAssembliesInAppDomain( |
457 | this->m_vmAppDomain, |
458 | CordbAppDomain::AssemblyEnumerationCallback, |
459 | this); // user data |
460 | } |
461 | |
462 | //--------------------------------------------------------------------------------------- |
463 | // |
464 | // Public API tp EnumerateAssemblies enumerates all assemblies in the app domain |
465 | // |
466 | // Arguments: |
467 | // ppAssemblies - OUT: get enumerator |
468 | // |
469 | // Return Value: |
470 | // S_OK on success. |
471 | // |
472 | // |
473 | // Notes: |
474 | // This will prepopulate the list of assemblies (useful for non-invasive case |
475 | // where we don't get debug event). |
476 | // |
477 | |
478 | HRESULT CordbAppDomain::EnumerateAssemblies(ICorDebugAssemblyEnum **ppAssemblies) |
479 | { |
480 | HRESULT hr = S_OK; |
481 | PUBLIC_API_BEGIN(this); |
482 | { |
483 | ValidateOrThrow(ppAssemblies); |
484 | *ppAssemblies = NULL; |
485 | |
486 | PrepopulateAssembliesOrThrow(); |
487 | |
488 | RSInitHolder<CordbHashTableEnum> pEnum; |
489 | CordbHashTableEnum::BuildOrThrow( |
490 | this, |
491 | GetProcess()->GetContinueNeuterList(), // ownership |
492 | &m_assemblies, |
493 | IID_ICorDebugAssemblyEnum, |
494 | pEnum.GetAddr()); |
495 | pEnum.TransferOwnershipExternal(ppAssemblies); |
496 | |
497 | } |
498 | PUBLIC_API_END(hr); |
499 | return hr; |
500 | } |
501 | |
502 | // Implement public interface |
503 | HRESULT CordbAppDomain::GetModuleFromMetaDataInterface( |
504 | IUnknown *pIMetaData, |
505 | ICorDebugModule **ppModule) |
506 | { |
507 | PUBLIC_API_ENTRY(this); |
508 | FAIL_IF_NEUTERED(this); |
509 | VALIDATE_POINTER_TO_OBJECT(pIMetaData, IUnknown *); |
510 | VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **); |
511 | |
512 | |
513 | |
514 | HRESULT hr = S_OK; |
515 | |
516 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
517 | |
518 | *ppModule = NULL; |
519 | |
520 | EX_TRY |
521 | { |
522 | CordbModule * pModule = GetModuleFromMetaDataInterface(pIMetaData); |
523 | _ASSERTE(pModule != NULL); // thrown on error |
524 | |
525 | *ppModule = static_cast<ICorDebugModule*> (pModule); |
526 | pModule->ExternalAddRef(); |
527 | } |
528 | EX_CATCH_HRESULT(hr); |
529 | |
530 | |
531 | return hr; |
532 | } |
533 | |
534 | // Gets a CordbModule that has the given metadata interface |
535 | // |
536 | // Arguments: |
537 | // pIMetaData - metadata interface |
538 | // |
539 | // Returns: |
540 | // CordbModule whose associated metadata matches the metadata interface provided here |
541 | // Throws on error. Returns non-null |
542 | // |
543 | CordbModule * CordbAppDomain::GetModuleFromMetaDataInterface(IUnknown *pIMetaData) |
544 | { |
545 | HRESULT hr = S_OK; |
546 | |
547 | RSExtSmartPtr<IMetaDataImport> pImport; |
548 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // need for module enumeration |
549 | |
550 | // Grab the interface we need... |
551 | hr = pIMetaData->QueryInterface(IID_IMetaDataImport, (void**)&pImport); |
552 | if (FAILED(hr)) |
553 | { |
554 | ThrowHR(E_INVALIDARG); |
555 | } |
556 | |
557 | // Get the mvid of the given module. |
558 | GUID matchMVID; |
559 | hr = pImport->GetScopeProps(NULL, 0, 0, &matchMVID); |
560 | IfFailThrow(hr); |
561 | |
562 | CordbModule* pModule; |
563 | HASHFIND findmodule; |
564 | |
565 | PrepopulateModules(); |
566 | |
567 | for (pModule = m_modules.FindFirst(&findmodule); |
568 | pModule != NULL; |
569 | pModule = m_modules.FindNext(&findmodule)) |
570 | { |
571 | IMetaDataImport * pImportCurrent = pModule->GetMetaDataImporter(); // throws |
572 | _ASSERTE(pImportCurrent != NULL); |
573 | |
574 | // Get the mvid of this module |
575 | GUID MVID; |
576 | hr = pImportCurrent->GetScopeProps(NULL, 0, 0, &MVID); |
577 | IfFailThrow(hr); |
578 | |
579 | if (MVID == matchMVID) |
580 | { |
581 | return pModule; |
582 | } |
583 | } |
584 | |
585 | ThrowHR(E_INVALIDARG); |
586 | } |
587 | |
588 | HRESULT CordbAppDomain::EnumerateBreakpoints(ICorDebugBreakpointEnum **ppBreakpoints) |
589 | { |
590 | PUBLIC_API_ENTRY(this); |
591 | FAIL_IF_NEUTERED(this); |
592 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
593 | VALIDATE_POINTER_TO_OBJECT(ppBreakpoints, ICorDebugBreakpointEnum **); |
594 | |
595 | HRESULT hr = S_OK; |
596 | EX_TRY |
597 | { |
598 | RSInitHolder<CordbHashTableEnum> pEnum; |
599 | CordbHashTableEnum::BuildOrThrow( |
600 | this, |
601 | GetProcess()->GetContinueNeuterList(), // ownership |
602 | &m_breakpoints, |
603 | IID_ICorDebugBreakpointEnum, |
604 | pEnum.GetAddr()); |
605 | |
606 | pEnum.TransferOwnershipExternal(ppBreakpoints); |
607 | } |
608 | EX_CATCH_HRESULT(hr); |
609 | return hr; |
610 | } |
611 | |
612 | HRESULT CordbAppDomain::EnumerateSteppers(ICorDebugStepperEnum **ppSteppers) |
613 | { |
614 | PUBLIC_API_ENTRY(this); |
615 | FAIL_IF_NEUTERED(this); |
616 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
617 | VALIDATE_POINTER_TO_OBJECT(ppSteppers,ICorDebugStepperEnum **); |
618 | |
619 | HRESULT hr = S_OK; |
620 | EX_TRY |
621 | { |
622 | // |
623 | // !!! m_steppers may be modified while user is enumerating, |
624 | // if steppers complete (if process is running) |
625 | // |
626 | |
627 | RSInitHolder<CordbHashTableEnum> pEnum; |
628 | CordbHashTableEnum::BuildOrThrow( |
629 | GetProcess(), |
630 | GetProcess()->GetContinueNeuterList(), // ownership |
631 | &(m_pProcess->m_steppers), |
632 | IID_ICorDebugStepperEnum, |
633 | pEnum.GetAddr()); |
634 | |
635 | pEnum.TransferOwnershipExternal(ppSteppers); |
636 | } |
637 | EX_CATCH_HRESULT(hr); |
638 | return hr; |
639 | } |
640 | |
641 | |
642 | //--------------------------------------------------------------------------------------- |
643 | // |
644 | // CordbAppDomain::IsAttached - always returns true |
645 | // |
646 | // Arguments: |
647 | // pfAttached - out parameter, will be set to TRUE |
648 | // |
649 | // Return Value: |
650 | // CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered |
651 | // E_INVALIDARG if pbAttached is null |
652 | // Otherwise always returns S_OK. |
653 | // |
654 | // Notes: |
655 | // Prior to V3, we used to keep track of a per-appdomain attached status. |
656 | // Debuggers were required to explicitly attach to every AppDomain, so this |
657 | // did not provide any actual functionality. In V3, there is no longer any |
658 | // concept of per-AppDomain attach/detach. This API is provided for compatibility. |
659 | // |
660 | |
661 | HRESULT CordbAppDomain::IsAttached(BOOL *pfAttached) |
662 | { |
663 | PUBLIC_API_ENTRY(this); |
664 | FAIL_IF_NEUTERED(this); |
665 | VALIDATE_POINTER_TO_OBJECT(pfAttached, BOOL *); |
666 | |
667 | *pfAttached = TRUE; |
668 | |
669 | return S_OK; |
670 | } |
671 | |
672 | |
673 | //--------------------------------------------------------------------------------------- |
674 | // |
675 | // CordbAppDomain::Attach - does nothing |
676 | // |
677 | // Arguments: |
678 | // |
679 | // Return Value: |
680 | // CORDB_E_OBJECT_NEUTERED if the AppDomain has been neutered |
681 | // Otherwise always returns S_OK. |
682 | // |
683 | // Notes: |
684 | // Prior to V3, we used to keep track of a per-appdomain attached status. |
685 | // Debuggers were required to explicitly attach to every AppDomain, so this |
686 | // did not provide any actual functionality. In V3, there is no longer any |
687 | // concept of per-AppDomain attach/detach. This API is provided for compatibility. |
688 | // |
689 | |
690 | HRESULT CordbAppDomain::Attach() |
691 | { |
692 | PUBLIC_API_ENTRY(this); |
693 | FAIL_IF_NEUTERED(this); |
694 | ATT_REQUIRE_STOPPED_MAY_FAIL(m_pProcess); |
695 | |
696 | return S_OK; |
697 | } |
698 | |
699 | /* |
700 | * GetName returns the name of the app domain. |
701 | */ |
702 | HRESULT CordbAppDomain::GetName(ULONG32 cchName, |
703 | ULONG32 *pcchName, |
704 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]) |
705 | { |
706 | HRESULT hr = S_OK; |
707 | PUBLIC_API_BEGIN(this) |
708 | { |
709 | // Some reasonable defaults |
710 | if (szName) |
711 | *szName = 0; |
712 | |
713 | if (pcchName) |
714 | *pcchName = 0; |
715 | |
716 | |
717 | // Lazily refresh. |
718 | IfFailThrow(RefreshName()); |
719 | |
720 | const WCHAR * pName = m_strAppDomainName; |
721 | _ASSERTE(pName != NULL); |
722 | |
723 | hr = CopyOutString(pName, cchName, pcchName, szName); |
724 | } |
725 | PUBLIC_API_END(hr); |
726 | return hr; |
727 | } |
728 | |
729 | /* |
730 | * GetObject returns the runtime app domain object. |
731 | * Note: this is lazily initialized and may be NULL |
732 | */ |
733 | HRESULT CordbAppDomain::GetObject(ICorDebugValue **ppObject) |
734 | { |
735 | PUBLIC_API_ENTRY(this); |
736 | FAIL_IF_NEUTERED(this); |
737 | VALIDATE_POINTER_TO_OBJECT(ppObject,ICorDebugObjectValue **); |
738 | |
739 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
740 | |
741 | _ASSERTE(!m_vmAppDomain.IsNull()); |
742 | IDacDbiInterface * pDac = NULL; |
743 | HRESULT hr = S_OK; |
744 | EX_TRY |
745 | { |
746 | pDac = m_pProcess->GetDAC(); |
747 | VMPTR_OBJECTHANDLE vmObjHandle = pDac->GetAppDomainObject(m_vmAppDomain); |
748 | if (!vmObjHandle.IsNull()) |
749 | { |
750 | ICorDebugReferenceValue * pRefValue = NULL; |
751 | hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue); |
752 | *ppObject = pRefValue; |
753 | } |
754 | else |
755 | { |
756 | *ppObject = NULL; |
757 | hr = S_FALSE; |
758 | } |
759 | } |
760 | EX_CATCH_HRESULT(hr); |
761 | |
762 | return hr; |
763 | } |
764 | |
765 | /* |
766 | * Get the ID of the app domain. |
767 | */ |
768 | HRESULT CordbAppDomain::GetID (ULONG32 *pId) |
769 | { |
770 | PUBLIC_REENTRANT_API_ENTRY(this); |
771 | OK_IF_NEUTERED(this); |
772 | VALIDATE_POINTER_TO_OBJECT(pId, ULONG32 *); |
773 | |
774 | *pId = m_AppDomainId; |
775 | |
776 | return S_OK; |
777 | } |
778 | |
779 | //--------------------------------------------------------------------------------------- |
780 | // Remove an assembly from the ICorDebug cache. |
781 | // |
782 | // Arguments: |
783 | // vmDomainAssembly - token to remove. |
784 | // |
785 | // Notes: |
786 | // This is the opposite of code:CordbAppDomain::LookupOrCreateAssembly. |
787 | // This only need to be called at assembly unload events. |
788 | void CordbAppDomain::RemoveAssemblyFromCache(VMPTR_DomainAssembly vmDomainAssembly) |
789 | { |
790 | // This will handle if the assembly is not in the hash. |
791 | // This could happen if we attach right before an assembly-unload event. |
792 | m_assemblies.RemoveBase(VmPtrToCookie(vmDomainAssembly)); |
793 | } |
794 | |
795 | //--------------------------------------------------------------------------------------- |
796 | // Lookup (or create) the CordbAssembly for the given VMPTR_DomainAssembly |
797 | // |
798 | // Arguments: |
799 | // vmDomainAssembly - CLR token for the Assembly. |
800 | // |
801 | // Returns: |
802 | // a CordbAssembly object for the given CLR assembly. This may be from the cache, |
803 | // or newly created if not yet in the cache. |
804 | // Never returns NULL. Throws on error (eg, oom). |
805 | // |
806 | CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_DomainAssembly vmDomainAssembly) |
807 | { |
808 | CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmDomainAssembly)); |
809 | if (pAssembly != NULL) |
810 | { |
811 | return pAssembly; |
812 | } |
813 | return CacheAssembly(vmDomainAssembly); |
814 | } |
815 | |
816 | |
817 | // |
818 | CordbAssembly * CordbAppDomain::LookupOrCreateAssembly(VMPTR_Assembly vmAssembly) |
819 | { |
820 | CordbAssembly * pAssembly = m_assemblies.GetBase(VmPtrToCookie(vmAssembly)); |
821 | if (pAssembly != NULL) |
822 | { |
823 | return pAssembly; |
824 | } |
825 | return CacheAssembly(vmAssembly); |
826 | } |
827 | |
828 | |
829 | //--------------------------------------------------------------------------------------- |
830 | // Lookup or create a module within the appdomain |
831 | // |
832 | // Arguments: |
833 | // vmDomainFile - non-null module to lookup |
834 | // |
835 | // Returns: |
836 | // a CordbModule object for the given cookie. Object may be from the cache, or created |
837 | // lazily. |
838 | // Never returns null. Throws on error. |
839 | // |
840 | // Notes: |
841 | // If you don't know which appdomain the module is in, use code:CordbProcess::LookupOrCreateModule. |
842 | // |
843 | CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_Module vmModule, VMPTR_DomainFile vmDomainFile) |
844 | { |
845 | INTERNAL_API_ENTRY(this); |
846 | CordbModule * pModule; |
847 | |
848 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); // @dbgtodo locking: push this up. |
849 | |
850 | _ASSERTE(!vmDomainFile.IsNull() || !vmModule.IsNull()); |
851 | |
852 | // check to see if the module is present in this app domain |
853 | pModule = m_modules.GetBase(vmDomainFile.IsNull() ? VmPtrToCookie(vmModule) : VmPtrToCookie(vmDomainFile)); |
854 | if (pModule != NULL) |
855 | { |
856 | return pModule; |
857 | } |
858 | |
859 | if (vmModule.IsNull()) |
860 | GetProcess()->GetDAC()->GetModuleForDomainFile(vmDomainFile, &vmModule); |
861 | |
862 | RSInitHolder<CordbModule> pModuleInit(new CordbModule(GetProcess(), vmModule, vmDomainFile)); |
863 | pModule = pModuleInit.TransferOwnershipToHash(&m_modules); |
864 | |
865 | // The appdomains should match. |
866 | GetProcess()->TargetConsistencyCheck(pModule->GetAppDomain() == this); |
867 | |
868 | return pModule; |
869 | } |
870 | |
871 | |
872 | CordbModule* CordbAppDomain::LookupOrCreateModule(VMPTR_DomainFile vmDomainFile) |
873 | { |
874 | INTERNAL_API_ENTRY(this); |
875 | |
876 | _ASSERTE(!vmDomainFile.IsNull()); |
877 | return LookupOrCreateModule(VMPTR_Module::NullPtr(), vmDomainFile); |
878 | } |
879 | |
880 | |
881 | |
882 | //--------------------------------------------------------------------------------------- |
883 | // Callback invoked by DAC for each module in an assembly. Used to populate RS module cache. |
884 | // |
885 | // Arguments: |
886 | // vmModule - module from enumeration |
887 | // pUserData - user data, a 'this' pointer to the CordbAssembly to add to. |
888 | // |
889 | // Notes: |
890 | // This is called from code:CordbAppDomain::PrepopulateModules invoking DAC, which |
891 | // invokes this callback. |
892 | |
893 | // static |
894 | void CordbAppDomain::ModuleEnumerationCallback(VMPTR_DomainFile vmModule, void * pUserData) |
895 | { |
896 | CONTRACTL |
897 | { |
898 | THROWS; |
899 | } |
900 | CONTRACTL_END; |
901 | |
902 | CordbAppDomain * pAppDomain = static_cast<CordbAppDomain *> (pUserData); |
903 | INTERNAL_DAC_CALLBACK(pAppDomain->GetProcess()); |
904 | |
905 | pAppDomain->LookupOrCreateModule(vmModule); |
906 | } |
907 | |
908 | |
909 | // |
910 | // Use DAC to preopulate the list of modules for this assembly |
911 | // |
912 | // Notes: |
913 | // This may pick up modules for which a load notification has not yet been dispatched. |
914 | void CordbAppDomain::PrepopulateModules() |
915 | { |
916 | INTERNAL_API_ENTRY(GetProcess()); |
917 | |
918 | if (!GetProcess()->IsDacInitialized()) |
919 | { |
920 | return; |
921 | } |
922 | |
923 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
924 | |
925 | // Want to make sure we don't double-add modules. |
926 | // Modules for all assemblies are stored in 1 giant hash in the AppDomain, so |
927 | // we don't have a good way of querying if this specific assembly needs to be prepopulated. |
928 | // We'll check before adding each module that it's unique. |
929 | |
930 | PrepopulateAssembliesOrThrow(); |
931 | |
932 | HASHFIND hashfind; |
933 | |
934 | for (CordbAssembly * pAssembly = m_assemblies.FindFirst(&hashfind); |
935 | pAssembly != NULL; |
936 | pAssembly = m_assemblies.FindNext(&hashfind)) |
937 | { |
938 | |
939 | // DD-primitive that invokes a callback. |
940 | GetProcess()->GetDAC()->EnumerateModulesInAssembly( |
941 | pAssembly->GetDomainAssemblyPtr(), |
942 | CordbAppDomain::ModuleEnumerationCallback, |
943 | this); // user data |
944 | |
945 | } |
946 | } |
947 | |
948 | //----------------------------------------------------------------------------- |
949 | // |
950 | // Get a type that represents an array or pointer of the given type. |
951 | // |
952 | // Arguments: |
953 | // elementType - determines if this will be an array or a pointer |
954 | // nRank - Rank of the array to make |
955 | // pTypeArg - The type of array element or pointer. |
956 | // ppResultType - OUT: out parameter to hold outgoing CordbType. |
957 | // |
958 | // Returns: |
959 | // S_OK on success. Else failure. |
960 | // |
961 | HRESULT CordbAppDomain::GetArrayOrPointerType(CorElementType elementType, |
962 | ULONG32 nRank, |
963 | ICorDebugType * pTypeArg, |
964 | ICorDebugType ** ppResultType) |
965 | { |
966 | PUBLIC_API_ENTRY(this); |
967 | FAIL_IF_NEUTERED(this); |
968 | VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **); |
969 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
970 | |
971 | CordbType * pResultType = NULL; |
972 | |
973 | if (!(elementType == ELEMENT_TYPE_PTR && nRank == 0) && |
974 | !(elementType == ELEMENT_TYPE_BYREF && nRank == 0) && |
975 | !(elementType == ELEMENT_TYPE_SZARRAY && nRank == 1) && |
976 | !(elementType == ELEMENT_TYPE_ARRAY)) |
977 | { |
978 | return E_INVALIDARG; |
979 | } |
980 | |
981 | HRESULT hr = CordbType::MkType( |
982 | this, |
983 | elementType, |
984 | (ULONG) nRank, |
985 | static_cast<CordbType *>(pTypeArg), |
986 | &pResultType); |
987 | |
988 | if (FAILED(hr)) |
989 | { |
990 | return hr; |
991 | } |
992 | |
993 | _ASSERTE(pResultType != NULL); |
994 | |
995 | pResultType->ExternalAddRef(); |
996 | |
997 | *ppResultType = pResultType; |
998 | return hr; |
999 | |
1000 | } |
1001 | |
1002 | |
1003 | //----------------------------------------------------------------------------- |
1004 | // |
1005 | // Get a type that represents a function pointer with signature of the types given. |
1006 | // |
1007 | // Arguments: |
1008 | // cTypeArgs - count of the number of entries in rgpTypeArgs |
1009 | // rgpTypeArgs - Array of types |
1010 | // ppResultType - OUT: out parameter to hold outgoing CordbType. |
1011 | // |
1012 | // Returns: |
1013 | // S_OK on success. Else failure. |
1014 | // |
1015 | HRESULT CordbAppDomain::GetFunctionPointerType(ULONG32 cTypeArgs, |
1016 | ICorDebugType * rgpTypeArgs[], |
1017 | ICorDebugType ** ppResultType) |
1018 | { |
1019 | PUBLIC_API_ENTRY(this); |
1020 | FAIL_IF_NEUTERED(this); |
1021 | VALIDATE_POINTER_TO_OBJECT(ppResultType, ICorDebugType **); |
1022 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1023 | |
1024 | // Prefast overflow check: |
1025 | S_UINT32 allocSize = S_UINT32(cTypeArgs) * S_UINT32(sizeof(CordbType *)); |
1026 | |
1027 | if (allocSize.IsOverflow()) |
1028 | { |
1029 | return E_INVALIDARG; |
1030 | } |
1031 | |
1032 | CordbType ** ppTypeInstantiations = reinterpret_cast<CordbType **>(_alloca(allocSize.Value())); |
1033 | |
1034 | for (unsigned int i = 0; i < cTypeArgs; i++) |
1035 | { |
1036 | ppTypeInstantiations[i] = (CordbType *) rgpTypeArgs[i]; |
1037 | } |
1038 | |
1039 | |
1040 | Instantiation typeInstantiation(cTypeArgs, ppTypeInstantiations); |
1041 | |
1042 | CordbType * pType; |
1043 | |
1044 | HRESULT hr = CordbType::MkType(this, ELEMENT_TYPE_FNPTR, &typeInstantiation, &pType); |
1045 | |
1046 | if (FAILED(hr)) |
1047 | { |
1048 | return hr; |
1049 | } |
1050 | |
1051 | _ASSERTE(pType != NULL); |
1052 | |
1053 | pType->ExternalAddRef(); |
1054 | |
1055 | *ppResultType = static_cast<ICorDebugType *>(pType); |
1056 | |
1057 | return hr; |
1058 | |
1059 | } |
1060 | |
1061 | // |
1062 | // ICorDebugAppDomain3 |
1063 | // |
1064 | |
1065 | HRESULT CordbAppDomain::GetCachedWinRTTypesForIIDs( |
1066 | ULONG32 cGuids, |
1067 | GUID * iids, |
1068 | ICorDebugTypeEnum * * ppTypesEnum) |
1069 | { |
1070 | #if !defined(FEATURE_COMINTEROP) |
1071 | |
1072 | return E_NOTIMPL; |
1073 | |
1074 | #else |
1075 | |
1076 | HRESULT hr = S_OK; |
1077 | PUBLIC_API_ENTRY(this); |
1078 | FAIL_IF_NEUTERED(this); |
1079 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1080 | |
1081 | _ASSERTE(!m_vmAppDomain.IsNull()); |
1082 | |
1083 | |
1084 | EX_TRY |
1085 | { |
1086 | *ppTypesEnum = NULL; |
1087 | |
1088 | DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> dacTypes; |
1089 | DacDbiArrayList<GUID> dacGuids; |
1090 | |
1091 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
1092 | |
1093 | dacGuids.Init(iids, cGuids); |
1094 | |
1095 | // retrieve type info from LS |
1096 | pDAC->GetCachedWinRTTypesForIIDs(m_vmAppDomain, dacGuids, &dacTypes); |
1097 | |
1098 | // synthesize CordbType instances |
1099 | int cItfs = dacTypes.Count(); |
1100 | NewArrayHolder<CordbType*> pTypes(NULL); |
1101 | |
1102 | if (cItfs > 0) |
1103 | { |
1104 | pTypes = new CordbType*[cItfs]; |
1105 | for (int n = 0; n < cItfs; ++n) |
1106 | { |
1107 | hr = CordbType::TypeDataToType(this, |
1108 | &(dacTypes[n]), |
1109 | &pTypes[n]); |
1110 | } |
1111 | } |
1112 | |
1113 | // build a type enumerator |
1114 | CordbTypeEnum* pTypeEnum = CordbTypeEnum::Build(this, GetProcess()->GetContinueNeuterList(), cItfs, pTypes); |
1115 | if ( pTypeEnum == NULL ) |
1116 | { |
1117 | IfFailThrow(E_OUTOFMEMORY); |
1118 | } |
1119 | |
1120 | (*ppTypesEnum) = static_cast<ICorDebugTypeEnum*> (pTypeEnum); |
1121 | pTypeEnum->ExternalAddRef(); |
1122 | |
1123 | } |
1124 | EX_CATCH_HRESULT(hr); |
1125 | |
1126 | return hr; |
1127 | |
1128 | #endif // !defined(FEATURE_COMINTEROP) |
1129 | } |
1130 | |
1131 | HRESULT CordbAppDomain::GetCachedWinRTTypes( |
1132 | ICorDebugGuidToTypeEnum * * ppTypesEnum) |
1133 | { |
1134 | #if !defined(FEATURE_COMINTEROP) |
1135 | |
1136 | return E_NOTIMPL; |
1137 | |
1138 | #else |
1139 | |
1140 | HRESULT hr = S_OK; |
1141 | PUBLIC_API_ENTRY(this); |
1142 | FAIL_IF_NEUTERED(this); |
1143 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1144 | |
1145 | EX_TRY |
1146 | { |
1147 | *ppTypesEnum = NULL; |
1148 | |
1149 | DacDbiArrayList<GUID> guids; |
1150 | DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> types; |
1151 | |
1152 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
1153 | |
1154 | // retrieve type info from LS |
1155 | pDAC->GetCachedWinRTTypes(m_vmAppDomain, &guids, &types); |
1156 | |
1157 | _ASSERTE(guids.Count() == types.Count()); |
1158 | if (guids.Count() != types.Count()) |
1159 | IfFailThrow(E_FAIL); |
1160 | |
1161 | int cnt = types.Count(); |
1162 | |
1163 | RsGuidToTypeMapping* pMap = new RsGuidToTypeMapping[cnt]; |
1164 | |
1165 | for (int i = 0; i < cnt; ++i) |
1166 | { |
1167 | pMap[i].iid = guids[i]; |
1168 | CordbType* pType; |
1169 | hr = CordbType::TypeDataToType(this, &(types[i]), &pType); |
1170 | if (SUCCEEDED(hr)) |
1171 | { |
1172 | pMap[i].spType.Assign(pType); |
1173 | } |
1174 | else |
1175 | { |
1176 | // spType stays NULL |
1177 | } |
1178 | } |
1179 | |
1180 | CordbGuidToTypeEnumerator * enumerator = new CordbGuidToTypeEnumerator(GetProcess(), &pMap, cnt); |
1181 | _ASSERTE(pMap == NULL); |
1182 | GetProcess()->GetContinueNeuterList()->Add(GetProcess(), enumerator); |
1183 | |
1184 | hr = enumerator->QueryInterface(IID_ICorDebugGuidToTypeEnum, reinterpret_cast<void**>(ppTypesEnum)); |
1185 | _ASSERTE(SUCCEEDED(hr)); |
1186 | IfFailThrow(hr); |
1187 | |
1188 | } |
1189 | EX_CATCH_HRESULT(hr); |
1190 | |
1191 | return hr; |
1192 | |
1193 | #endif // !defined(FEATURE_COMINTEROP) |
1194 | } |
1195 | |
1196 | //----------------------------------------------------------- |
1197 | // ICorDebugAppDomain4 |
1198 | //----------------------------------------------------------- |
1199 | |
1200 | HRESULT CordbAppDomain::GetObjectForCCW(CORDB_ADDRESS ccwPointer, ICorDebugValue **ppManagedObject) |
1201 | { |
1202 | #if defined(FEATURE_COMINTEROP) |
1203 | |
1204 | PUBLIC_API_ENTRY(this); |
1205 | FAIL_IF_NEUTERED(this); |
1206 | |
1207 | VALIDATE_POINTER_TO_OBJECT(ppManagedObject, ICorDebugValue **); |
1208 | HRESULT hr = S_OK; |
1209 | |
1210 | *ppManagedObject = NULL; |
1211 | |
1212 | EX_TRY |
1213 | { |
1214 | VMPTR_OBJECTHANDLE vmObjHandle = GetProcess()->GetDAC()->GetObjectForCCW(ccwPointer); |
1215 | if (vmObjHandle.IsNull()) |
1216 | { |
1217 | hr = E_INVALIDARG; |
1218 | } |
1219 | else |
1220 | { |
1221 | ICorDebugReferenceValue *pRefValue = NULL; |
1222 | hr = CordbReferenceValue::BuildFromGCHandle(this, vmObjHandle, &pRefValue); |
1223 | *ppManagedObject = pRefValue; |
1224 | } |
1225 | } |
1226 | EX_CATCH_HRESULT(hr); |
1227 | |
1228 | return hr; |
1229 | |
1230 | #else |
1231 | |
1232 | return E_NOTIMPL; |
1233 | |
1234 | #endif // defined(FEATURE_COMINTEROP) |
1235 | } |
1236 | |