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: stack.cpp |
6 | // |
7 | |
8 | // |
9 | // CLRData stack walking. |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #include "stdafx.h" |
14 | |
15 | //---------------------------------------------------------------------------- |
16 | // |
17 | // ClrDataStackWalk. |
18 | // |
19 | //---------------------------------------------------------------------------- |
20 | |
21 | ClrDataStackWalk::ClrDataStackWalk(ClrDataAccess* dac, |
22 | Thread* thread, |
23 | ULONG32 flags) |
24 | { |
25 | m_dac = dac; |
26 | m_dac->AddRef(); |
27 | m_instanceAge = m_dac->m_instanceAge; |
28 | m_thread = thread; |
29 | m_walkFlags = flags; |
30 | m_refs = 1; |
31 | m_stackPrev = 0; |
32 | |
33 | INDEBUG( m_framesUnwound = 0; ) |
34 | } |
35 | |
36 | ClrDataStackWalk::~ClrDataStackWalk(void) |
37 | { |
38 | m_dac->Release(); |
39 | } |
40 | |
41 | STDMETHODIMP |
42 | ClrDataStackWalk::QueryInterface(THIS_ |
43 | IN REFIID interfaceId, |
44 | OUT PVOID* iface) |
45 | { |
46 | if (IsEqualIID(interfaceId, IID_IUnknown) || |
47 | IsEqualIID(interfaceId, __uuidof(IXCLRDataStackWalk))) |
48 | { |
49 | AddRef(); |
50 | *iface = static_cast<IUnknown*> |
51 | (static_cast<IXCLRDataStackWalk*>(this)); |
52 | return S_OK; |
53 | } |
54 | else |
55 | { |
56 | *iface = NULL; |
57 | return E_NOINTERFACE; |
58 | } |
59 | } |
60 | |
61 | STDMETHODIMP_(ULONG) |
62 | ClrDataStackWalk::AddRef(THIS) |
63 | { |
64 | return InterlockedIncrement(&m_refs); |
65 | } |
66 | |
67 | STDMETHODIMP_(ULONG) |
68 | ClrDataStackWalk::Release(THIS) |
69 | { |
70 | SUPPORTS_DAC_HOST_ONLY; |
71 | LONG newRefs = InterlockedDecrement(&m_refs); |
72 | if (newRefs == 0) |
73 | { |
74 | delete this; |
75 | } |
76 | return newRefs; |
77 | } |
78 | |
79 | HRESULT STDMETHODCALLTYPE |
80 | ClrDataStackWalk::GetContext( |
81 | /* [in] */ ULONG32 contextFlags, |
82 | /* [in] */ ULONG32 contextBufSize, |
83 | /* [out] */ ULONG32 *contextSize, |
84 | /* [size_is][out] */ BYTE contextBuf[ ]) |
85 | { |
86 | HRESULT status; |
87 | |
88 | if (contextSize) |
89 | { |
90 | *contextSize = ContextSizeForFlags(contextFlags); |
91 | } |
92 | |
93 | if (!CheckContextSizeForFlags(contextBufSize, contextFlags)) |
94 | { |
95 | return E_INVALIDARG; |
96 | } |
97 | |
98 | DAC_ENTER_SUB(m_dac); |
99 | |
100 | EX_TRY |
101 | { |
102 | if (!m_frameIter.IsValid()) |
103 | { |
104 | status = S_FALSE; |
105 | } |
106 | else |
107 | { |
108 | *(PT_CONTEXT)contextBuf = m_context; |
109 | UpdateContextFromRegDisp(&m_regDisp, (PT_CONTEXT)contextBuf); |
110 | status = S_OK; |
111 | } |
112 | } |
113 | EX_CATCH |
114 | { |
115 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
116 | { |
117 | EX_RETHROW; |
118 | } |
119 | } |
120 | EX_END_CATCH(SwallowAllExceptions) |
121 | |
122 | DAC_LEAVE(); |
123 | return status; |
124 | } |
125 | |
126 | HRESULT STDMETHODCALLTYPE |
127 | ClrDataStackWalk::SetContext( |
128 | /* [in] */ ULONG32 contextSize, |
129 | /* [size_is][in] */ BYTE context[ ]) |
130 | { |
131 | return SetContext2(m_frameIter.m_crawl.IsActiveFrame() ? |
132 | CLRDATA_STACK_SET_CURRENT_CONTEXT : |
133 | CLRDATA_STACK_SET_UNWIND_CONTEXT, |
134 | contextSize, context); |
135 | } |
136 | |
137 | HRESULT STDMETHODCALLTYPE |
138 | ClrDataStackWalk::SetContext2( |
139 | /* [in] */ ULONG32 flags, |
140 | /* [in] */ ULONG32 contextSize, |
141 | /* [size_is][in] */ BYTE context[ ]) |
142 | { |
143 | HRESULT status; |
144 | |
145 | if ((flags & ~(CLRDATA_STACK_SET_CURRENT_CONTEXT | |
146 | CLRDATA_STACK_SET_UNWIND_CONTEXT)) != 0 || |
147 | !CheckContextSizeForBuffer(contextSize, context)) |
148 | { |
149 | return E_INVALIDARG; |
150 | } |
151 | |
152 | DAC_ENTER_SUB(m_dac); |
153 | |
154 | EX_TRY |
155 | { |
156 | // Copy the context to local state so |
157 | // that its lifetime extends beyond this call. |
158 | m_context = *(PT_CONTEXT)context; |
159 | m_thread->FillRegDisplay(&m_regDisp, &m_context); |
160 | m_frameIter.ResetRegDisp(&m_regDisp, (flags & CLRDATA_STACK_SET_CURRENT_CONTEXT) != 0); |
161 | m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); |
162 | FilterFrames(); |
163 | status = S_OK; |
164 | } |
165 | EX_CATCH |
166 | { |
167 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
168 | { |
169 | EX_RETHROW; |
170 | } |
171 | } |
172 | EX_END_CATCH(SwallowAllExceptions) |
173 | |
174 | DAC_LEAVE(); |
175 | return status; |
176 | } |
177 | |
178 | HRESULT STDMETHODCALLTYPE |
179 | ClrDataStackWalk::Next(void) |
180 | { |
181 | HRESULT status = E_FAIL; |
182 | |
183 | INDEBUG( static const int kFrameToReturnForever = 56; ) |
184 | |
185 | DAC_ENTER_SUB(m_dac); |
186 | |
187 | EX_TRY |
188 | { |
189 | if (!m_frameIter.IsValid()) |
190 | { |
191 | status = S_FALSE; |
192 | } |
193 | else |
194 | #if defined(_DEBUG) |
195 | // m_framesUnwound is not incremented unless the special config value is set below in this function. |
196 | if (m_framesUnwound < kFrameToReturnForever) |
197 | #endif // defined(_DEBUG) |
198 | { |
199 | // Default the previous stack value. |
200 | m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); |
201 | StackWalkAction action = m_frameIter.Next(); |
202 | switch(action) |
203 | { |
204 | case SWA_CONTINUE: |
205 | // We sucessfully unwound a frame so update |
206 | // the previous stack pointer before going into |
207 | // filtering to get the amount of stack skipped |
208 | // by the filtering. |
209 | m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); |
210 | FilterFrames(); |
211 | status = m_frameIter.IsValid() ? S_OK : S_FALSE; |
212 | break; |
213 | case SWA_ABORT: |
214 | status = S_FALSE; |
215 | break; |
216 | default: |
217 | status = E_FAIL; |
218 | break; |
219 | } |
220 | } |
221 | |
222 | #if defined(_DEBUG) |
223 | // Test hook: when testing on debug builds, we want an easy way to test that the target |
224 | // stack behaves as if it's smashed in a particular way. It would be very difficult to create |
225 | // a test that carefully broke the stack in a way that would force the stackwalker to report |
226 | // success on the same frame forever, and have that corruption be reliable over time. However, it's |
227 | // pretty easy for us to control the number of frames on the stack for tests that use this specific |
228 | // internal flag. |
229 | if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget)) |
230 | { |
231 | if (m_framesUnwound >= kFrameToReturnForever) |
232 | { |
233 | status = S_OK; |
234 | } |
235 | else |
236 | { |
237 | m_framesUnwound++; |
238 | } |
239 | } |
240 | #endif // defined(_DEBUG) |
241 | } |
242 | EX_CATCH |
243 | { |
244 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
245 | { |
246 | EX_RETHROW; |
247 | } |
248 | } |
249 | EX_END_CATCH(SwallowAllExceptions) |
250 | |
251 | DAC_LEAVE(); |
252 | return status; |
253 | } |
254 | |
255 | HRESULT STDMETHODCALLTYPE |
256 | ClrDataStackWalk::GetStackSizeSkipped( |
257 | /* [out] */ ULONG64 *stackSizeSkipped) |
258 | { |
259 | HRESULT status; |
260 | |
261 | DAC_ENTER_SUB(m_dac); |
262 | |
263 | EX_TRY |
264 | { |
265 | if (m_stackPrev) |
266 | { |
267 | *stackSizeSkipped = |
268 | (TADDR)GetRegdisplaySP(&m_regDisp) - m_stackPrev; |
269 | status = S_OK; |
270 | } |
271 | else |
272 | { |
273 | status = S_FALSE; |
274 | } |
275 | } |
276 | EX_CATCH |
277 | { |
278 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
279 | { |
280 | EX_RETHROW; |
281 | } |
282 | } |
283 | EX_END_CATCH(SwallowAllExceptions) |
284 | |
285 | DAC_LEAVE(); |
286 | return status; |
287 | } |
288 | |
289 | HRESULT STDMETHODCALLTYPE |
290 | ClrDataStackWalk::GetFrameType( |
291 | /* [out] */ CLRDataSimpleFrameType *simpleType, |
292 | /* [out] */ CLRDataDetailedFrameType *detailedType) |
293 | { |
294 | HRESULT status; |
295 | |
296 | DAC_ENTER_SUB(m_dac); |
297 | |
298 | EX_TRY |
299 | { |
300 | if (m_frameIter.IsValid()) |
301 | { |
302 | RawGetFrameType(simpleType, detailedType); |
303 | status = S_OK; |
304 | } |
305 | else |
306 | { |
307 | status = S_FALSE; |
308 | } |
309 | } |
310 | EX_CATCH |
311 | { |
312 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
313 | { |
314 | EX_RETHROW; |
315 | } |
316 | } |
317 | EX_END_CATCH(SwallowAllExceptions) |
318 | |
319 | DAC_LEAVE(); |
320 | return status; |
321 | } |
322 | |
323 | HRESULT STDMETHODCALLTYPE |
324 | ClrDataStackWalk::GetFrame( |
325 | /* [out] */ IXCLRDataFrame **frame) |
326 | { |
327 | HRESULT status; |
328 | |
329 | DAC_ENTER_SUB(m_dac); |
330 | |
331 | EX_TRY |
332 | { |
333 | ClrDataFrame* dataFrame = NULL; |
334 | if (!m_frameIter.IsValid()) |
335 | { |
336 | status = E_INVALIDARG; |
337 | goto Exit; |
338 | } |
339 | |
340 | CLRDataSimpleFrameType simpleType; |
341 | CLRDataDetailedFrameType detailedType; |
342 | |
343 | RawGetFrameType(&simpleType, &detailedType); |
344 | dataFrame = |
345 | new (nothrow) ClrDataFrame(m_dac, simpleType, detailedType, |
346 | m_frameIter.m_crawl.GetAppDomain(), |
347 | m_frameIter.m_crawl.GetFunction()); |
348 | if (!dataFrame) |
349 | { |
350 | status = E_OUTOFMEMORY; |
351 | goto Exit; |
352 | } |
353 | |
354 | dataFrame->m_context = m_context; |
355 | UpdateContextFromRegDisp(&m_regDisp, &dataFrame->m_context); |
356 | m_thread->FillRegDisplay(&dataFrame->m_regDisp, |
357 | &dataFrame->m_context); |
358 | |
359 | *frame = static_cast<IXCLRDataFrame*>(dataFrame); |
360 | status = S_OK; |
361 | |
362 | Exit: ; |
363 | } |
364 | EX_CATCH |
365 | { |
366 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
367 | { |
368 | EX_RETHROW; |
369 | } |
370 | } |
371 | EX_END_CATCH(SwallowAllExceptions) |
372 | |
373 | DAC_LEAVE(); |
374 | return status; |
375 | } |
376 | |
377 | HRESULT STDMETHODCALLTYPE |
378 | ClrDataStackWalk::Request( |
379 | /* [in] */ ULONG32 reqCode, |
380 | /* [in] */ ULONG32 inBufferSize, |
381 | /* [size_is][in] */ BYTE *inBuffer, |
382 | /* [in] */ ULONG32 outBufferSize, |
383 | /* [size_is][out] */ BYTE *outBuffer) |
384 | { |
385 | HRESULT status; |
386 | |
387 | DAC_ENTER_SUB(m_dac); |
388 | |
389 | EX_TRY |
390 | { |
391 | switch(reqCode) |
392 | { |
393 | case CLRDATA_REQUEST_REVISION: |
394 | if (inBufferSize != 0 || |
395 | inBuffer || |
396 | outBufferSize != sizeof(ULONG32)) |
397 | { |
398 | status = E_INVALIDARG; |
399 | } |
400 | else |
401 | { |
402 | *(ULONG32*)outBuffer = 1; |
403 | status = S_OK; |
404 | } |
405 | break; |
406 | |
407 | case CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME: |
408 | // This code should be removed once the Windows debuggers stop using the old DAC API. |
409 | if ((inBufferSize != sizeof(ULONG32)) || |
410 | (outBufferSize != 0)) |
411 | { |
412 | status = E_INVALIDARG; |
413 | break; |
414 | } |
415 | |
416 | m_frameIter.SetIsFirstFrame(*(ULONG32 UNALIGNED *)inBuffer != 0); |
417 | status = S_OK; |
418 | break; |
419 | |
420 | case DACSTACKPRIV_REQUEST_FRAME_DATA: |
421 | if ((inBufferSize != 0) || |
422 | (inBuffer != NULL) || |
423 | (outBufferSize != sizeof(DacpFrameData))) |
424 | { |
425 | status = E_INVALIDARG; |
426 | break; |
427 | } |
428 | if (!m_frameIter.IsValid()) |
429 | { |
430 | status = E_INVALIDARG; |
431 | break; |
432 | } |
433 | |
434 | DacpFrameData* frameData; |
435 | |
436 | frameData = (DacpFrameData*)outBuffer; |
437 | frameData->frameAddr = |
438 | TO_CDADDR(PTR_HOST_TO_TADDR(m_frameIter.m_crawl.GetFrame())); |
439 | status = S_OK; |
440 | break; |
441 | |
442 | default: |
443 | status = E_INVALIDARG; |
444 | break; |
445 | } |
446 | } |
447 | EX_CATCH |
448 | { |
449 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
450 | { |
451 | EX_RETHROW; |
452 | } |
453 | } |
454 | EX_END_CATCH(SwallowAllExceptions) |
455 | |
456 | DAC_LEAVE(); |
457 | return status; |
458 | } |
459 | |
460 | HRESULT |
461 | ClrDataStackWalk::Init(void) |
462 | { |
463 | if (m_thread->IsUnstarted()) |
464 | { |
465 | return E_FAIL; |
466 | } |
467 | |
468 | if (m_thread->GetFilterContext()) |
469 | { |
470 | m_context = *m_thread->GetFilterContext(); |
471 | } |
472 | else |
473 | { |
474 | DacGetThreadContext(m_thread, &m_context); |
475 | } |
476 | m_thread->FillRegDisplay(&m_regDisp, &m_context); |
477 | |
478 | m_stackPrev = (TADDR)GetRegdisplaySP(&m_regDisp); |
479 | |
480 | ULONG32 iterFlags = NOTIFY_ON_NO_FRAME_TRANSITIONS; |
481 | |
482 | // If the filter is only allowing method frames |
483 | // turn on the appropriate iterator flag. |
484 | if ((m_walkFlags & SIMPFRAME_ALL) == |
485 | CLRDATA_SIMPFRAME_MANAGED_METHOD) |
486 | { |
487 | iterFlags |= FUNCTIONSONLY; |
488 | } |
489 | |
490 | m_frameIter.Init(m_thread, NULL, &m_regDisp, iterFlags); |
491 | if (m_frameIter.GetFrameState() == StackFrameIterator::SFITER_UNINITIALIZED) |
492 | { |
493 | return E_FAIL; |
494 | } |
495 | FilterFrames(); |
496 | |
497 | return S_OK; |
498 | } |
499 | |
500 | void |
501 | ClrDataStackWalk::FilterFrames(void) |
502 | { |
503 | // |
504 | // Advance to a state compatible with the |
505 | // current filtering flags. |
506 | // |
507 | |
508 | while (m_frameIter.IsValid()) |
509 | { |
510 | switch(m_frameIter.GetFrameState()) |
511 | { |
512 | case StackFrameIterator::SFITER_FRAMELESS_METHOD: |
513 | if (m_walkFlags & CLRDATA_SIMPFRAME_MANAGED_METHOD) |
514 | { |
515 | return; |
516 | } |
517 | break; |
518 | case StackFrameIterator::SFITER_FRAME_FUNCTION: |
519 | case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: |
520 | case StackFrameIterator::SFITER_NO_FRAME_TRANSITION: |
521 | if (m_walkFlags & CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE) |
522 | { |
523 | return; |
524 | } |
525 | break; |
526 | default: |
527 | break; |
528 | } |
529 | |
530 | m_frameIter.Next(); |
531 | } |
532 | } |
533 | |
534 | void |
535 | ClrDataStackWalk::RawGetFrameType( |
536 | /* [out] */ CLRDataSimpleFrameType* simpleType, |
537 | /* [out] */ CLRDataDetailedFrameType* detailedType) |
538 | { |
539 | if (simpleType) |
540 | { |
541 | switch(m_frameIter.GetFrameState()) |
542 | { |
543 | case StackFrameIterator::SFITER_FRAMELESS_METHOD: |
544 | *simpleType = CLRDATA_SIMPFRAME_MANAGED_METHOD; |
545 | break; |
546 | case StackFrameIterator::SFITER_FRAME_FUNCTION: |
547 | case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION: |
548 | *simpleType = CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE; |
549 | break; |
550 | default: |
551 | *simpleType = CLRDATA_SIMPFRAME_UNRECOGNIZED; |
552 | break; |
553 | } |
554 | } |
555 | |
556 | if (detailedType) |
557 | { |
558 | if (m_frameIter.m_crawl.GetFrame() && m_frameIter.m_crawl.GetFrame()->GetFrameAttribs() & Frame::FRAME_ATTR_EXCEPTION) |
559 | *detailedType = CLRDATA_DETFRAME_EXCEPTION_FILTER; |
560 | else |
561 | *detailedType = CLRDATA_DETFRAME_UNRECOGNIZED; |
562 | } |
563 | } |
564 | |
565 | //---------------------------------------------------------------------------- |
566 | // |
567 | // ClrDataFrame. |
568 | // |
569 | //---------------------------------------------------------------------------- |
570 | |
571 | ClrDataFrame::ClrDataFrame(ClrDataAccess* dac, |
572 | CLRDataSimpleFrameType simpleType, |
573 | CLRDataDetailedFrameType detailedType, |
574 | AppDomain* appDomain, |
575 | MethodDesc* methodDesc) |
576 | { |
577 | m_dac = dac; |
578 | m_dac->AddRef(); |
579 | m_instanceAge = m_dac->m_instanceAge; |
580 | m_simpleType = simpleType; |
581 | m_detailedType = detailedType; |
582 | m_appDomain = appDomain; |
583 | m_methodDesc = methodDesc; |
584 | m_refs = 1; |
585 | m_methodSig = NULL; |
586 | m_localSig = NULL; |
587 | } |
588 | |
589 | ClrDataFrame::~ClrDataFrame(void) |
590 | { |
591 | delete m_methodSig; |
592 | delete m_localSig; |
593 | m_dac->Release(); |
594 | } |
595 | |
596 | STDMETHODIMP |
597 | ClrDataFrame::QueryInterface(THIS_ |
598 | IN REFIID interfaceId, |
599 | OUT PVOID* iface) |
600 | { |
601 | if (IsEqualIID(interfaceId, IID_IUnknown) || |
602 | IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame))) |
603 | { |
604 | AddRef(); |
605 | *iface = static_cast<IUnknown*> |
606 | (static_cast<IXCLRDataFrame*>(this)); |
607 | return S_OK; |
608 | } |
609 | else if (IsEqualIID(interfaceId, __uuidof(IXCLRDataFrame2))) |
610 | { |
611 | AddRef(); |
612 | *iface = static_cast<IUnknown*> |
613 | (static_cast<IXCLRDataFrame2*>(this)); |
614 | return S_OK; |
615 | } |
616 | else |
617 | { |
618 | *iface = NULL; |
619 | return E_NOINTERFACE; |
620 | } |
621 | } |
622 | |
623 | STDMETHODIMP_(ULONG) |
624 | ClrDataFrame::AddRef(THIS) |
625 | { |
626 | return InterlockedIncrement(&m_refs); |
627 | } |
628 | |
629 | STDMETHODIMP_(ULONG) |
630 | ClrDataFrame::Release(THIS) |
631 | { |
632 | SUPPORTS_DAC_HOST_ONLY; |
633 | LONG newRefs = InterlockedDecrement(&m_refs); |
634 | if (newRefs == 0) |
635 | { |
636 | delete this; |
637 | } |
638 | return newRefs; |
639 | } |
640 | |
641 | HRESULT STDMETHODCALLTYPE |
642 | ClrDataFrame::GetContext( |
643 | /* [in] */ ULONG32 contextFlags, |
644 | /* [in] */ ULONG32 contextBufSize, |
645 | /* [out] */ ULONG32 *contextSize, |
646 | /* [size_is][out] */ BYTE contextBuf[ ]) |
647 | { |
648 | HRESULT status; |
649 | |
650 | if (contextSize) |
651 | { |
652 | *contextSize = ContextSizeForFlags(contextFlags); |
653 | } |
654 | |
655 | if (!CheckContextSizeForFlags(contextBufSize, contextFlags)) |
656 | { |
657 | return E_INVALIDARG; |
658 | } |
659 | |
660 | DAC_ENTER_SUB(m_dac); |
661 | |
662 | EX_TRY |
663 | { |
664 | *(PT_CONTEXT)contextBuf = m_context; |
665 | status = S_OK; |
666 | } |
667 | EX_CATCH |
668 | { |
669 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
670 | { |
671 | EX_RETHROW; |
672 | } |
673 | } |
674 | EX_END_CATCH(SwallowAllExceptions) |
675 | |
676 | DAC_LEAVE(); |
677 | return status; |
678 | } |
679 | |
680 | HRESULT STDMETHODCALLTYPE |
681 | ClrDataFrame::GetFrameType( |
682 | /* [out] */ CLRDataSimpleFrameType *simpleType, |
683 | /* [out] */ CLRDataDetailedFrameType *detailedType) |
684 | { |
685 | HRESULT status; |
686 | |
687 | DAC_ENTER_SUB(m_dac); |
688 | |
689 | EX_TRY |
690 | { |
691 | *simpleType = m_simpleType; |
692 | *detailedType = m_detailedType; |
693 | status = S_OK; |
694 | } |
695 | EX_CATCH |
696 | { |
697 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
698 | { |
699 | EX_RETHROW; |
700 | } |
701 | } |
702 | EX_END_CATCH(SwallowAllExceptions) |
703 | |
704 | DAC_LEAVE(); |
705 | return status; |
706 | } |
707 | |
708 | HRESULT STDMETHODCALLTYPE |
709 | ClrDataFrame::GetAppDomain( |
710 | /* [out] */ IXCLRDataAppDomain **appDomain) |
711 | { |
712 | HRESULT status; |
713 | |
714 | DAC_ENTER_SUB(m_dac); |
715 | |
716 | EX_TRY |
717 | { |
718 | if (m_appDomain) |
719 | { |
720 | ClrDataAppDomain* dataAppDomain = |
721 | new (nothrow) ClrDataAppDomain(m_dac, m_appDomain); |
722 | if (!dataAppDomain) |
723 | { |
724 | status = E_OUTOFMEMORY; |
725 | } |
726 | else |
727 | { |
728 | *appDomain = static_cast<IXCLRDataAppDomain*>(dataAppDomain); |
729 | status = S_OK; |
730 | } |
731 | } |
732 | else |
733 | { |
734 | *appDomain = NULL; |
735 | status = S_FALSE; |
736 | } |
737 | } |
738 | EX_CATCH |
739 | { |
740 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
741 | { |
742 | EX_RETHROW; |
743 | } |
744 | } |
745 | EX_END_CATCH(SwallowAllExceptions) |
746 | |
747 | DAC_LEAVE(); |
748 | return status; |
749 | } |
750 | |
751 | HRESULT STDMETHODCALLTYPE |
752 | ClrDataFrame::GetNumArguments( |
753 | /* [out] */ ULONG32 *numArgs) |
754 | { |
755 | HRESULT status; |
756 | |
757 | DAC_ENTER_SUB(m_dac); |
758 | |
759 | EX_TRY |
760 | { |
761 | if (!m_methodDesc) |
762 | { |
763 | status = E_NOINTERFACE; |
764 | } |
765 | else |
766 | { |
767 | MetaSig* sig; |
768 | |
769 | status = GetMethodSig(&sig, numArgs); |
770 | } |
771 | } |
772 | EX_CATCH |
773 | { |
774 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
775 | { |
776 | EX_RETHROW; |
777 | } |
778 | } |
779 | EX_END_CATCH(SwallowAllExceptions) |
780 | |
781 | DAC_LEAVE(); |
782 | return status; |
783 | } |
784 | |
785 | HRESULT STDMETHODCALLTYPE |
786 | ClrDataFrame::GetArgumentByIndex( |
787 | /* [in] */ ULONG32 index, |
788 | /* [out] */ IXCLRDataValue **arg, |
789 | /* [in] */ ULONG32 bufLen, |
790 | /* [out] */ ULONG32 *nameLen, |
791 | /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) |
792 | { |
793 | HRESULT status; |
794 | |
795 | DAC_ENTER_SUB(m_dac); |
796 | |
797 | EX_TRY |
798 | { |
799 | if (nameLen) |
800 | { |
801 | *nameLen = 0; |
802 | } |
803 | |
804 | if (!m_methodDesc) |
805 | { |
806 | status = E_NOINTERFACE; |
807 | goto Exit; |
808 | } |
809 | |
810 | MetaSig* sig; |
811 | ULONG32 numArgs; |
812 | |
813 | if (FAILED(status = GetMethodSig(&sig, &numArgs))) |
814 | { |
815 | goto Exit; |
816 | } |
817 | |
818 | if (index >= numArgs) |
819 | { |
820 | status = E_INVALIDARG; |
821 | goto Exit; |
822 | } |
823 | |
824 | if ((bufLen && name) || nameLen) |
825 | { |
826 | if (index == 0 && sig->HasThis()) |
827 | { |
828 | if (nameLen) |
829 | { |
830 | *nameLen = 5; |
831 | } |
832 | |
833 | StringCchCopy(name, bufLen, W("this" )); |
834 | } |
835 | else |
836 | { |
837 | if (!m_methodDesc->IsNoMetadata()) |
838 | { |
839 | IMDInternalImport* mdImport = m_methodDesc->GetMDImport(); |
840 | mdParamDef paramToken; |
841 | LPCSTR paramName; |
842 | USHORT seq; |
843 | DWORD attr; |
844 | |
845 | // Param indexing is 1-based. |
846 | ULONG32 mdIndex = index + 1; |
847 | |
848 | // 'this' doesn't show up in the signature but |
849 | // is present in the dac API indexing so adjust the |
850 | // index down for methods with 'this'. |
851 | if (sig->HasThis()) |
852 | { |
853 | mdIndex--; |
854 | } |
855 | |
856 | status = mdImport->FindParamOfMethod( |
857 | m_methodDesc->GetMemberDef(), |
858 | mdIndex, |
859 | ¶mToken); |
860 | if (status == S_OK) |
861 | { |
862 | status = mdImport->GetParamDefProps( |
863 | paramToken, |
864 | &seq, |
865 | &attr, |
866 | ¶mName); |
867 | if ((status == S_OK) && (paramName != NULL)) |
868 | { |
869 | if ((status = ConvertUtf8(paramName, |
870 | bufLen, nameLen, name)) != S_OK) |
871 | { |
872 | goto Exit; |
873 | } |
874 | } |
875 | } |
876 | } |
877 | else |
878 | { |
879 | if (nameLen) |
880 | { |
881 | *nameLen = 1; |
882 | } |
883 | |
884 | name[0] = 0; |
885 | } |
886 | } |
887 | } |
888 | |
889 | status = ValueFromDebugInfo(sig, true, index, index, arg); |
890 | |
891 | Exit: ; |
892 | } |
893 | EX_CATCH |
894 | { |
895 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
896 | { |
897 | EX_RETHROW; |
898 | } |
899 | } |
900 | EX_END_CATCH(SwallowAllExceptions) |
901 | |
902 | DAC_LEAVE(); |
903 | return status; |
904 | } |
905 | |
906 | HRESULT STDMETHODCALLTYPE |
907 | ClrDataFrame::GetNumLocalVariables( |
908 | /* [out] */ ULONG32 *numLocals) |
909 | { |
910 | HRESULT status; |
911 | |
912 | DAC_ENTER_SUB(m_dac); |
913 | |
914 | EX_TRY |
915 | { |
916 | if (!m_methodDesc) |
917 | { |
918 | status = E_NOINTERFACE; |
919 | } |
920 | else |
921 | { |
922 | MetaSig* sig; |
923 | |
924 | status = GetLocalSig(&sig, numLocals); |
925 | } |
926 | } |
927 | EX_CATCH |
928 | { |
929 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
930 | { |
931 | EX_RETHROW; |
932 | } |
933 | } |
934 | EX_END_CATCH(SwallowAllExceptions) |
935 | |
936 | DAC_LEAVE(); |
937 | return status; |
938 | } |
939 | |
940 | HRESULT STDMETHODCALLTYPE |
941 | ClrDataFrame::GetLocalVariableByIndex( |
942 | /* [in] */ ULONG32 index, |
943 | /* [out] */ IXCLRDataValue **localVariable, |
944 | /* [in] */ ULONG32 bufLen, |
945 | /* [out] */ ULONG32 *nameLen, |
946 | /* [size_is][out] */ __out_ecount_part(bufLen, *nameLen) WCHAR name[ ]) |
947 | { |
948 | HRESULT status; |
949 | |
950 | DAC_ENTER_SUB(m_dac); |
951 | |
952 | EX_TRY |
953 | { |
954 | if (!m_methodDesc) |
955 | { |
956 | status = E_NOINTERFACE; |
957 | goto Exit; |
958 | } |
959 | |
960 | MetaSig* sig; |
961 | ULONG32 numLocals; |
962 | |
963 | if (FAILED(status = GetLocalSig(&sig, &numLocals))) |
964 | { |
965 | goto Exit; |
966 | } |
967 | |
968 | if (index >= numLocals) |
969 | { |
970 | status = E_INVALIDARG; |
971 | goto Exit; |
972 | } |
973 | |
974 | MetaSig* argSig; |
975 | ULONG32 numArgs; |
976 | |
977 | if (FAILED(status = GetMethodSig(&argSig, &numArgs))) |
978 | { |
979 | goto Exit; |
980 | } |
981 | |
982 | // Can't get names for locals in the Whidbey runtime. |
983 | if (bufLen && name) |
984 | { |
985 | if (nameLen) |
986 | { |
987 | *nameLen = 1; |
988 | } |
989 | |
990 | name[0] = 0; |
991 | } |
992 | |
993 | // The locals are indexed immediately following the arguments |
994 | // in the NativeVarInfos. |
995 | status = ValueFromDebugInfo(sig, false, index, index + numArgs, |
996 | localVariable); |
997 | |
998 | Exit: ; |
999 | } |
1000 | EX_CATCH |
1001 | { |
1002 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1003 | { |
1004 | EX_RETHROW; |
1005 | } |
1006 | } |
1007 | EX_END_CATCH(SwallowAllExceptions) |
1008 | |
1009 | DAC_LEAVE(); |
1010 | return status; |
1011 | } |
1012 | |
1013 | HRESULT STDMETHODCALLTYPE |
1014 | ClrDataFrame::GetNumTypeArguments( |
1015 | /* [out] */ ULONG32 *numTypeArgs) |
1016 | { |
1017 | HRESULT status; |
1018 | |
1019 | DAC_ENTER_SUB(m_dac); |
1020 | |
1021 | EX_TRY |
1022 | { |
1023 | // XXX Microsoft. |
1024 | status = E_NOTIMPL; |
1025 | } |
1026 | EX_CATCH |
1027 | { |
1028 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1029 | { |
1030 | EX_RETHROW; |
1031 | } |
1032 | } |
1033 | EX_END_CATCH(SwallowAllExceptions) |
1034 | |
1035 | DAC_LEAVE(); |
1036 | return status; |
1037 | } |
1038 | |
1039 | HRESULT STDMETHODCALLTYPE |
1040 | ClrDataFrame::GetTypeArgumentByIndex( |
1041 | /* [in] */ ULONG32 index, |
1042 | /* [out] */ IXCLRDataTypeInstance **typeArg) |
1043 | { |
1044 | HRESULT status; |
1045 | |
1046 | DAC_ENTER_SUB(m_dac); |
1047 | |
1048 | EX_TRY |
1049 | { |
1050 | // XXX Microsoft. |
1051 | status = E_NOTIMPL; |
1052 | } |
1053 | EX_CATCH |
1054 | { |
1055 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1056 | { |
1057 | EX_RETHROW; |
1058 | } |
1059 | } |
1060 | EX_END_CATCH(SwallowAllExceptions) |
1061 | |
1062 | DAC_LEAVE(); |
1063 | return status; |
1064 | } |
1065 | |
1066 | |
1067 | HRESULT STDMETHODCALLTYPE |
1068 | ClrDataFrame::GetExactGenericArgsToken( |
1069 | /* [out] */ IXCLRDataValue ** genericToken) |
1070 | { |
1071 | HRESULT status; |
1072 | |
1073 | DAC_ENTER_SUB(m_dac); |
1074 | |
1075 | EX_TRY |
1076 | { |
1077 | if (!m_methodDesc) |
1078 | { |
1079 | status = E_NOINTERFACE; |
1080 | goto Exit; |
1081 | } |
1082 | |
1083 | MetaSig* sig; |
1084 | ULONG32 numLocals; |
1085 | |
1086 | if (FAILED(status = GetLocalSig(&sig, &numLocals))) |
1087 | { |
1088 | goto Exit; |
1089 | } |
1090 | |
1091 | // The locals are indexed immediately following the arguments |
1092 | // in the NativeVarInfos. |
1093 | status = ValueFromDebugInfo(sig, false, 1, (DWORD)ICorDebugInfo::TYPECTXT_ILNUM, |
1094 | genericToken); |
1095 | Exit: ; |
1096 | } |
1097 | EX_CATCH |
1098 | { |
1099 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1100 | { |
1101 | EX_RETHROW; |
1102 | } |
1103 | } |
1104 | EX_END_CATCH(SwallowAllExceptions) |
1105 | |
1106 | DAC_LEAVE(); |
1107 | return status; |
1108 | } |
1109 | |
1110 | HRESULT STDMETHODCALLTYPE |
1111 | ClrDataFrame::GetCodeName( |
1112 | /* [in] */ ULONG32 flags, |
1113 | /* [in] */ ULONG32 bufLen, |
1114 | /* [out] */ ULONG32 *symbolLen, |
1115 | /* [size_is][out] */ __out_ecount_opt(bufLen) WCHAR symbolBuf[ ]) |
1116 | { |
1117 | HRESULT status = E_FAIL; |
1118 | |
1119 | DAC_ENTER_SUB(m_dac); |
1120 | |
1121 | EX_TRY |
1122 | { |
1123 | TADDR pcAddr = PCODEToPINSTR(GetControlPC(&m_regDisp)); |
1124 | status = m_dac-> |
1125 | RawGetMethodName(TO_CDADDR(pcAddr), flags, |
1126 | bufLen, symbolLen, symbolBuf, |
1127 | NULL); |
1128 | } |
1129 | EX_CATCH |
1130 | { |
1131 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1132 | { |
1133 | EX_RETHROW; |
1134 | } |
1135 | } |
1136 | EX_END_CATCH(SwallowAllExceptions) |
1137 | |
1138 | DAC_LEAVE(); |
1139 | |
1140 | return status; |
1141 | } |
1142 | |
1143 | HRESULT STDMETHODCALLTYPE |
1144 | ClrDataFrame::GetMethodInstance( |
1145 | /* [out] */ IXCLRDataMethodInstance **method) |
1146 | { |
1147 | HRESULT status; |
1148 | |
1149 | DAC_ENTER_SUB(m_dac); |
1150 | |
1151 | EX_TRY |
1152 | { |
1153 | if (!m_methodDesc) |
1154 | { |
1155 | status = E_NOINTERFACE; |
1156 | } |
1157 | else |
1158 | { |
1159 | ClrDataMethodInstance* dataMethod = |
1160 | new (nothrow) ClrDataMethodInstance(m_dac, |
1161 | m_appDomain, |
1162 | m_methodDesc); |
1163 | *method = static_cast<IXCLRDataMethodInstance*>(dataMethod); |
1164 | status = dataMethod ? S_OK : E_OUTOFMEMORY; |
1165 | } |
1166 | } |
1167 | EX_CATCH |
1168 | { |
1169 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1170 | { |
1171 | EX_RETHROW; |
1172 | } |
1173 | } |
1174 | EX_END_CATCH(SwallowAllExceptions) |
1175 | |
1176 | DAC_LEAVE(); |
1177 | return status; |
1178 | } |
1179 | |
1180 | HRESULT STDMETHODCALLTYPE |
1181 | ClrDataFrame::Request( |
1182 | /* [in] */ ULONG32 reqCode, |
1183 | /* [in] */ ULONG32 inBufferSize, |
1184 | /* [size_is][in] */ BYTE *inBuffer, |
1185 | /* [in] */ ULONG32 outBufferSize, |
1186 | /* [size_is][out] */ BYTE *outBuffer) |
1187 | { |
1188 | HRESULT status; |
1189 | |
1190 | DAC_ENTER_SUB(m_dac); |
1191 | |
1192 | EX_TRY |
1193 | { |
1194 | switch(reqCode) |
1195 | { |
1196 | case CLRDATA_REQUEST_REVISION: |
1197 | if (inBufferSize != 0 || |
1198 | inBuffer || |
1199 | outBufferSize != sizeof(ULONG32)) |
1200 | { |
1201 | status = E_INVALIDARG; |
1202 | } |
1203 | else |
1204 | { |
1205 | *(ULONG32*)outBuffer = 1; |
1206 | status = S_OK; |
1207 | } |
1208 | break; |
1209 | |
1210 | default: |
1211 | status = E_INVALIDARG; |
1212 | break; |
1213 | } |
1214 | } |
1215 | EX_CATCH |
1216 | { |
1217 | if (!DacExceptionFilter(GET_EXCEPTION(), m_dac, &status)) |
1218 | { |
1219 | EX_RETHROW; |
1220 | } |
1221 | } |
1222 | EX_END_CATCH(SwallowAllExceptions) |
1223 | |
1224 | DAC_LEAVE(); |
1225 | return status; |
1226 | } |
1227 | |
1228 | HRESULT |
1229 | ClrDataFrame::GetMethodSig(MetaSig** sig, |
1230 | ULONG32* count) |
1231 | { |
1232 | if (!m_methodSig) |
1233 | { |
1234 | m_methodSig = new (nothrow) MetaSig(m_methodDesc); |
1235 | if (!m_methodSig) |
1236 | { |
1237 | return E_OUTOFMEMORY; |
1238 | } |
1239 | } |
1240 | |
1241 | *sig = m_methodSig; |
1242 | *count = m_methodSig->NumFixedArgs() + |
1243 | (m_methodSig->HasThis() ? 1 : 0); |
1244 | return *count ? S_OK : S_FALSE; |
1245 | } |
1246 | |
1247 | HRESULT |
1248 | ClrDataFrame::GetLocalSig(MetaSig** sig, |
1249 | ULONG32* count) |
1250 | { |
1251 | HRESULT hr; |
1252 | if (!m_localSig) |
1253 | { |
1254 | // It turns out we cannot really get rid of this check. Dynamic methods |
1255 | // (including IL stubs) do not have their local sig's available after JIT time. |
1256 | if (!m_methodDesc->IsIL()) |
1257 | { |
1258 | *sig = NULL; |
1259 | *count = 0; |
1260 | return E_FAIL; |
1261 | } |
1262 | |
1263 | COR_ILMETHOD_DECODER methodDecoder(m_methodDesc->GetILHeader()); |
1264 | mdSignature localSig = methodDecoder.GetLocalVarSigTok() ? |
1265 | methodDecoder.GetLocalVarSigTok() : mdSignatureNil; |
1266 | if (localSig == mdSignatureNil) |
1267 | { |
1268 | *sig = NULL; |
1269 | *count = 0; |
1270 | return E_FAIL; |
1271 | } |
1272 | |
1273 | ULONG tokenSigLen; |
1274 | PCCOR_SIGNATURE tokenSig; |
1275 | IfFailRet(m_methodDesc->GetModule()->GetMDImport()->GetSigFromToken( |
1276 | localSig, |
1277 | &tokenSigLen, |
1278 | &tokenSig)); |
1279 | |
1280 | SigTypeContext typeContext(m_methodDesc, TypeHandle()); |
1281 | m_localSig = new (nothrow) |
1282 | MetaSig(tokenSig, |
1283 | tokenSigLen, |
1284 | m_methodDesc->GetModule(), |
1285 | &typeContext, |
1286 | MetaSig::sigLocalVars); |
1287 | if (!m_localSig) |
1288 | { |
1289 | return E_OUTOFMEMORY; |
1290 | } |
1291 | } |
1292 | |
1293 | *sig = m_localSig; |
1294 | *count = m_localSig->NumFixedArgs(); |
1295 | return S_OK; |
1296 | } |
1297 | |
1298 | HRESULT |
1299 | ClrDataFrame::ValueFromDebugInfo(MetaSig* sig, |
1300 | bool isArg, |
1301 | DWORD sigIndex, |
1302 | DWORD varInfoSlot, |
1303 | IXCLRDataValue** _value) |
1304 | { |
1305 | HRESULT status; |
1306 | ULONG32 numVarInfo; |
1307 | NewHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL); |
1308 | ULONG32 codeOffset; |
1309 | ULONG32 valueFlags; |
1310 | ULONG32 i; |
1311 | |
1312 | TADDR ip = PCODEToPINSTR(GetControlPC(&m_regDisp)); |
1313 | if ((status = m_dac->GetMethodVarInfo(m_methodDesc, |
1314 | ip, |
1315 | &numVarInfo, |
1316 | &varInfo, |
1317 | &codeOffset)) != S_OK) |
1318 | { |
1319 | // We have signature info indicating that there |
1320 | // are values, but couldn't find any location info. |
1321 | // Optimized routines may have eliminated all |
1322 | // traditional variable locations, so just treat |
1323 | // this as a no-location case just like not being |
1324 | // able to find a matching lifetime. |
1325 | numVarInfo = 0; |
1326 | } |
1327 | |
1328 | for (i = 0; i < numVarInfo; i++) |
1329 | { |
1330 | if (varInfo[i].startOffset <= codeOffset && |
1331 | varInfo[i].endOffset >= codeOffset && |
1332 | varInfo[i].varNumber == varInfoSlot && |
1333 | varInfo[i].loc.vlType != ICorDebugInfo::VLT_INVALID) |
1334 | { |
1335 | break; |
1336 | } |
1337 | } |
1338 | |
1339 | ULONG64 baseAddr; |
1340 | NativeVarLocation locs[MAX_NATIVE_VAR_LOCS]; |
1341 | ULONG32 numLocs; |
1342 | |
1343 | if (i >= numVarInfo) |
1344 | { |
1345 | numLocs = 0; |
1346 | } |
1347 | else |
1348 | { |
1349 | numLocs = NativeVarLocations(varInfo[i].loc, &m_context, |
1350 | NumItems(locs), locs); |
1351 | } |
1352 | |
1353 | if (numLocs == 1 && !locs[0].contextReg) |
1354 | { |
1355 | baseAddr = TO_CDADDR(locs[0].addr); |
1356 | } |
1357 | else |
1358 | { |
1359 | baseAddr = 0; |
1360 | } |
1361 | |
1362 | TypeHandle argType; |
1363 | |
1364 | sig->Reset(); |
1365 | if (isArg && sigIndex == 0 && sig->HasThis()) |
1366 | { |
1367 | argType = TypeHandle(m_methodDesc->GetMethodTable()); |
1368 | valueFlags = CLRDATA_VALUE_IS_REFERENCE; |
1369 | } |
1370 | else |
1371 | { |
1372 | // 'this' doesn't show up in the signature but |
1373 | // is present in the indexing so adjust the |
1374 | // index down for methods with 'this'. |
1375 | if (isArg && sig->HasThis()) |
1376 | { |
1377 | |
1378 | sigIndex--; |
1379 | } |
1380 | |
1381 | do |
1382 | { |
1383 | sig->NextArg(); |
1384 | } |
1385 | while (sigIndex-- > 0); |
1386 | |
1387 | // == FailIfNotLoaded |
1388 | // Will also return null if type is not restored |
1389 | argType = sig->GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes); |
1390 | if (argType.IsNull()) |
1391 | { |
1392 | // XXX Microsoft - Sometimes types can't be looked |
1393 | // up and this at least allows the value to be used, |
1394 | // but is it the right behavior? |
1395 | argType = TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U8)); |
1396 | valueFlags = 0; |
1397 | } |
1398 | else |
1399 | { |
1400 | valueFlags = GetTypeFieldValueFlags(argType, NULL, 0, false); |
1401 | |
1402 | // If this is a primitive variable and the actual size is smaller than what we have been told, |
1403 | // then lower the size so that we won't read in trash memory (e.g. reading 4 bytes for a short). |
1404 | if ((valueFlags & CLRDATA_VALUE_IS_PRIMITIVE) != 0) |
1405 | { |
1406 | if (numLocs == 1) |
1407 | { |
1408 | UINT actualSize = argType.GetSize(); |
1409 | if (actualSize < locs[0].size) |
1410 | { |
1411 | locs[0].size = actualSize; |
1412 | } |
1413 | } |
1414 | } |
1415 | } |
1416 | } |
1417 | |
1418 | ClrDataValue* value = new (nothrow) |
1419 | ClrDataValue(m_dac, |
1420 | m_appDomain, |
1421 | NULL, |
1422 | valueFlags, |
1423 | argType, |
1424 | baseAddr, |
1425 | numLocs, |
1426 | locs); |
1427 | if (!value) |
1428 | { |
1429 | return E_OUTOFMEMORY; |
1430 | } |
1431 | |
1432 | *_value = value; |
1433 | return S_OK; |
1434 | } |
1435 | |