📜 ⬆️ ⬇️

Missing Index Case

The point, of course, is not as interesting as that of Russinovich, but, I hope, it will be useful for some developers. The main purpose of the presentation is to show the means by which we can analyze the behavior of the program at the lowest level.

So, there is an application written in C # that uses the .net framework 1.1 (yes). After some changes, the application started throwing out such a uninformative exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.DataTable. ResetIndexes ()
at System.Data.Merger.MergeTable(DataTable src, DataTable dst)
at System.Data.Merger.MergeTableData(DataTable src)
at System.Data.Merger.MergeTable(DataTable src)
at System.Data.DataSet.Merge(DataTable table, Boolean preserveChanges, MissingSchemaAction missingSchemaAction)
at Com.Product.App.RequestForm. InitEdit (InitRequestArgs args)

InitEdit is a form function in an application that initializes data. The source code line of the application on which it is thrown:
dsRequestList.Merge(dsLast.Tables[ "TABLE_REQUESTS" ], false , MissingSchemaAction.Ignore);

The code of the ResetIndexes function, which shows us the Reflector, is not very difficult to say:
  1. internal void ResetIndexes ()
  2. {
  3. this .RecomputeCompareInfo ();
  4. if ( this .indexes! = null )
  5. {
  6. this .SetShadowIndexes ();
  7. try
  8. {
  9. int count = this .shadowIndexes.Count;
  10. for ( int i = 0; i <count; i ++)
  11. {
  12. ((Index) this .shadowIndexes [i]). Reset ();
  13. }
  14. }
  15. finally
  16. {
  17. this .shadowIndexes = null ;
  18. }
  19. }
  20. }
* This source code was highlighted with Source Code Highlighter .

It seems like there are no problems. The function runs through the indices and causes each Reset . Where can there be an exception? SetShadowIndexes simply calls " this.shadowIndexes = this.LiveIndexes; " So, at the time of execution of line No. 9, shadowIndexes is non-zero. So, it seems reasonable, I think, to assume that somewhere inside this array there is a null element, and, accordingly, falls on line 12. I think the problem is this, I said to myself and went to work.

We start windbg, connect to the process, load Son of Strike debugger (sos.dll) into the debugger. I think everyone knows what it is, and there is no need to explain.
')
Put a stopping point on our function:

0:023> !bp app.exe Com.Product.App.RequestForm. InitEdit
Setting breakpoint at : 0xcef7988
bp 0xcef7988 " .echo [DEFAULT] [hasThis] Boolean Com.Product.App.RequestForm.InitEdit(Class Com.Product.Tickets.InitArgs) "
Setting breakpoint at : 0xcef7a38
bp 0xcef7a38 " .echo [DEFAULT] [hasThis] Boolean Com.Product.App.RequestForm.InitEdit(Class Com.Product.App.Requests.InitRequestArgs) "


(I need only the second function, so turn off the first point with the windbg command: bd 0).

You cannot put a stopping point directly on ResetIndexes , since the application uses datasets quite intensively and we will have to miss a lot of false calls.
Therefore, the points must be arranged sequentially, as the necessary functions are called.

After the debugger stops at InitEdit , put a dot on the DataSet.Merge :
0:000> !bp system.data.dll System.Data.DataSet.Merge
Setting breakpoint at : 0x8185418
bp 0x8185418 " .echo [DEFAULT] [hasThis] Void System.Data.DataSet.Merge(Class System.Data.DataSet) "

(again, the SoS will put a stop on all overloaded Merge functions, so we get rid of unnecessary ones)

After DataSet.Merge you need to go to System.Data.Merger.MergeTable , from where DataTable.ResetIndexes is called.

Check that the argument is really the one we expect:
0:000> !clrstack -a 1
Thread 0
ESP EIP
ESP/REG Object Name
eax 0x191cca10 Com.Product.App.Requests.dsRequests/TABLE_USERSDataTable
ecx 0x1562815c System.Data.Merger
edx 0x1952b684 System.Data.DataTable
esi 0x1562815c System.Data.Merger
edi 0x1952b684 System.Data.DataTable
0x0012e9b4 0x08186d00 [DEFAULT] [hasThis] Void System.Data.Merger.MergeTable(Class System.Data.DataTable,Class System.Data.DataTable)
EDI 0x1952b684 ESI 0x1562815c EBX 0x00000000 EDX 0x1952b684 ECX 0x1562815c
EAX 0x191cca10 EBP 0x0012e9dc ESP 0x0012e9b4 EIP 0x08186d00


We look at the name of the table, which is passed as a parameter.
0:000> !do (edx)
Name: System.Data.DataTable
MethodTable 0x04a633f8
EEClass 0x04a37244
Size 232(0xe8) bytes
GC Generation: 2
mdToken: 0x0200003d (c:\winxp\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll)
FieldDesc*: 0x04a62544
MT Field Offset Type Attr Value Name
0x7b308b0c 0x4000583 0x4 CLASS instance 0x00000000 site
...
0x04a633f8 0x40003fa 0x34 CLASS instance 0x15628170 extendedProperties
0x04a633f8 0x40003fb 0x38 CLASS instance 0x1952bc2c tableName
0x04a633f8 0x40003fc 0x3c CLASS instance 0x00000000 tableNamespace
...


The table name is written in the field with offset 0x38 from the beginning of the object.

0:000> !do poi(edx+38)
String: TABLE_USERS


TABLE_USERS - not the table that we need, so go ahead until we meet

0:000> !do poi(edx+38)
String: TABLE_REQUESTS


Only then add a breakpoint in ResetIndexes , run the program further, and stop again.

0:000> g
[DEFAULT] [hasThis] Void System.Data.DataTable.ResetIndexes()
eax=191c76ec ebx=00000001 ecx=191c70b4 edx=0006471c esi=19508a4c edi=19221a28
eip=07eff838 esp=0012e988 ebp=191c70b4 iopl=0 nv up ei ng nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000283
07eff838 55 push ebp
0:000> !clrstack -a 1
Thread 0
ESP EIP
ESP/REG Object Name
eax 0x191c76ec System.Collections.ArrayList
ecx 0x191c70b4 Com.Product.App.Requests.dsRequests/TABLE_REQUESTSDataTable
esi 0x19508a4c System.Data.DataRow
edi 0x19221a28 System.Data.DataTable
ebp 0x191c70b4 Com.Product.App.Requests.dsRequests/TABLE_REQUESTSDataTable
0x0012e988 0x07eff838 [DEFAULT] [hasThis] Void System.Data.DataTable.ResetIndexes()
EDI 0x19221a28 ESI 0x19508a4c EBX 0x00000001 EDX 0x0006471c ECX 0x191c70b4
EAX 0x191c76ec EBP 0x191c70b4 ESP 0x0012e988 EIP 0x07eff838


This is passed to ecx, take it and see what is in the indexes field:

0:000> !do ecx
Name: Com.Product.App.Requests.dsRequests/TABLE_REQUESTSDataTable
MethodTable 0x10bb39f4
EEClass 0x10ac9a40
Size 580(0x244) bytes
GC Generation: 2
mdToken: 0x02000296
FieldDesc*: 0x10bb2eb4
MT Field Offset Type Attr Value Name
0x7b308b0c 0x4000583 0x4 CLASS instance 0x00000000 site
...
0x04a633f8 0x40003f8 0x2c CLASS instance 0x191c76ec indexes
0x04a633f8 0x40003f9 0x30 CLASS instance 0x00000000 shadowIndexes
0x04a633f8 0x40003fa 0x34 CLASS instance 0x15629d98 extendedProperties
...


So far, all right. At the time of entry into ResetIndexe s, the indexes field is zero, then set using SetShadowIndexes .

0:000> !dc 0x191c76ec
Going to dump the Collection passed.
Name: System.Collections.ArrayList
GC Generation: 2

Address MT Class Name
0x1557971c 0x08121b80 System.Data.Index
0x1559e420 0x08121b80 System.Data.Index
0x1559e508 0x08121b80 System.Data.Index
0x1559e5f0 0x08121b80 System.Data.Index
0x1559e6d8 0x08121b80 System.Data.Index
0x1559e7c0 0x08121b80 System.Data.Index
0x1559e8a8 0x08121b80 System.Data.Index
0x1559e990 0x08121b80 System.Data.Index
0x1559ea78 0x08121b80 System.Data.Index
0x1559eb60 0x08121b80 System.Data.Index
0x1559ec48 0x08121b80 System.Data.Index
0x1559ed30 0x08121b80 System.Data.Index
0x1559ee18 0x08121b80 System.Data.Index
0x1559ef00 0x08121b80 System.Data.Index
0x1559efe8 0x08121b80 System.Data.Index
0x1559f0d0 0x08121b80 System.Data.Index
0x1559f1b8 0x08121b80 System.Data.Index
0x156f1a9c 0x08121b80 System.Data.Index
0x156f4cd4 0x08121b80 System.Data.Index
0x157962b0 0x08121b80 System.Data.Index
0x157ba910 0x08121b80 System.Data.Index


Hmm ... my main theory is falling apart. There are no zero elements there. Here is bad luck. Well, let's continue the execution of the code, and look at the exceptions from inside the windbg. Release the program. When an exception occurs, the debugger stops:
0:000> g
(1638.1738): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=191c70b4 ebx=00000015 ecx=00000000 edx=00000014 esi=195b0894 edi=00000014
eip=07eff899 esp=0012e960 ebp=0012e984 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010297
07eff899 8b01 mov eax,dword ptr [ecx] ds:0023:00000000=????????


We execute the ! Analyze -v command, the result of which is incredibly punctuated with various kinds of data about the exception that occurred. I give a compressed form:

0:000> !analyze -v

*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************

FAULTING_IP:
+7eff899
07eff899 8b01 mov eax,dword ptr [ecx]

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
.exr 0xffffffffffffffff
ExceptionAddress: 07eff899
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000000
Attempt to read from address 00000000

FAULTING_THREAD: 00001738

DEFAULT_BUCKET_ID: NULL_POINTER_READ

PROCESS_NAME: App.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

READ_ADDRESS: 00000000

FAILED_INSTRUCTION_ADDRESS:
+7eff899
07eff899 8b01 mov eax,dword ptr [ecx]

NTGLOBALFLAG: 0

APPLICATION_VERIFIER_FLAGS: 0

IP_ON_HEAP: 08186f34

MANAGED_STACK: !dumpstack -EE
!dumpstack -EE
Thread 0
Current frame: (MethodDesc 0x4a63200 +0x61 System.Data.DataTable.ResetIndexes)
ChildEBP RetAddr Caller,Callee
0012e984 08186f34 (MethodDesc 0x31fcff0 +0x234 System.Data.Merger.MergeTable)
0012e9b0 08185790 (MethodDesc 0x31fd020 +0x48 System.Data.Merger.MergeTableData)
0012e9dc 0818e31f (MethodDesc 0x31fcfe0 +0x2f System.Data.Merger.MergeTable)
0012e9ec 0818e2b1 (MethodDesc 0x7ec0ce8 +0x61 System.Data.DataSet.Merge)
0012ea00 0cef8056 (MethodDesc 0x7c8f8d0 +0x61e Com.Product.App.RequestForm.InitEdit)
0012ec3c 26a117e7 (MethodDesc 0x10eaee98 +0x57 Com.Product.Core.Entities.EntityContextCache..ctor)
0012ec50 0cef79df (MethodDesc 0x7c8f8c0 +0x57 Com.Product.App.RequestForm.InitEdit)
0012ec74 0818d593 (MethodDesc 0x7ec0448 +0x2b Com.CommonComponents.Tools.PerformanceTimer.sAddCheckPoint)
0012ec84 0cef7933 (MethodDesc 0x7c8da68 +0x8b Com.Product.Tickets.Ticket.OnInit)
0012ecb4 0cef7706 (MethodDesc 0x7d509e8 +0x6e Com.Product.App.RequestForm.OnInit)
0012ecf4 0cef766b (MethodDesc 0x7c8da58 +0x1b Com.Product.Tickets.Ticket.Init)
[ ]
---------


Call stack we have already seen, though in a somewhat abbreviated version. But the code now we will understand. Let's take a look at the code that caused the exception:
07eff899 8b01 mov eax,dword ptr [ecx] ds:0023:00000000=????????

The value is read from the memory at the address that is contained in ECX, and there (in ECX), as we see, zero ...

Let's now take a look at the ResetIndexes function, turned by a caring jit compiler into executable code for an x86 processor. I will repeat the function code in C #, and we will now compare it with the assembly code.

  1. internal void ResetIndexes ()
  2. {
  3. this .RecomputeCompareInfo ();
  4. if ( this .indexes! = null )
  5. {
  6. this .SetShadowIndexes ();
  7. try
  8. {
  9. int count = this .shadowIndexes.Count;
  10. for ( int i = 0; i <count; i ++)
  11. {
  12. ((Index) this .shadowIndexes [i]). Reset ();
  13. }
  14. }
  15. finally
  16. {
  17. this .shadowIndexes = null ;
  18. }
  19. }
  20. }
* This source code was highlighted with Source Code Highlighter .


Now disassemble from windbg. The function is small, but the assembly code is two pages: (

0:000> !u
No value passed in, defaulting to EIP
Will print '>>> ' at address: 0x07eff899
Normal JIT generated code
[DEFAULT] [hasThis] Void System.Data.DataTable.ResetIndexes()
Begin 0x07eff838, size 0xc4
07eff838 push ebp
07eff839 mov ebp,esp
07eff83b sub esp,18h
07eff83e push edi
07eff83f push esi
07eff840 push ebx ; .
07eff841 mov dword ptr [ebp-8],0 ;
07eff848 mov dword ptr [ebp-14h],ecx ; "ebp-14h"
; DataTable,
; , this.
07eff84b mov ecx,dword ptr [ebp-14h]
07eff84e call 07eff420 (System.Data.DataTable.RecomputeCompareInfo) ; (3)
07eff853 mov eax,dword ptr [ebp-14h]
07eff856 cmp dword ptr [eax+2Ch],0 ; (4) if (this.indexes != null)
07eff85a jne 07eff863
07eff85c pop ebx
07eff85d pop esi
07eff85e pop edi
07eff85f mov esp,ebp
07eff861 pop ebp
07eff862 ret ; , indexes == null
07eff863 mov ecx,dword ptr [ebp-14h]
07eff866 call dword ptr ds:[4A634CCh] ; (6) this.LiveIndexes
07eff86c mov ebx,dword ptr [ebp-14h]
07eff86f lea edx,[ebx+30h] ; (6) this.indexes
07eff872 call 01003048 ; (6) indexes = LiveIndexes, ,
; -
; SetShadowIndexes,
; , ,
; .
07eff877 mov eax,dword ptr [ebp-14h] ; (9)
07eff87a mov dword ptr [ebp-18h],eax ; (9)
07eff87d mov ecx,dword ptr [eax+30h] ; (9) ECX shadowIndexes.
07eff880 mov eax,dword ptr [ecx] ; (9) :
; (9) (, [ecx]) -
; (9) MethodTable ,
07eff882 call dword ptr [eax+0D0h] ; (9) ,
; (9) 0xD0 -
; (9) , ArrayList.Count,
07eff888 mov ebx,eax ; (9) eax.
; (9) , 07eff877
; (9) -
; (9) int count = this.shadowIndexes.Count;
07eff88a xor edi,edi ;(10) int i = 0;
07eff88c cmp ebx,0 ;(10) ,
;(10) shadowIndexes.Count == 0.
07eff88f jle 07eff8cc
07eff891 mov eax,dword ptr [ebp-18h] ;(12)
07eff894 mov ecx,dword ptr [eax+30h] ;(12) ECX shadowIndexes.
07eff897 mov edx,edi
>>> 07eff mov eax,dword ptr [ecx] ;(12) MethodTable
;(12) shadowIndexes ( ArrayList)
07eff89b call dword ptr [eax+0A0h] ;(12) shadowIndexes[i]
07eff8a1 mov edx,eax
07eff8a3 mov ecx,8121B80h
07eff8a8 call mscorwks!JIT_ChkCastClass (791e381f) ;(12)
;(12) : ((Index)this.shadowIndexes[i])
07eff8ad mov esi,eax
07eff8af cmp dword ptr [esi],eax
07eff8b1 mov ecx,esi
07eff8b3 call dword ptr ds:[8121C44h] ;(12) Index.InitRecords();
07eff8b9 mov edx,dword ptr ds:[2208EC4h]
07eff8bf mov ecx,esi
07eff8c1 call dword ptr ds:[8121C54h] ;(12) Index.OnListChanged();
;(12) , Index.Reset(),
;(12)
;(12) InitRecords OnListChanged
;(12) ,

07eff8c7 inc edi ;(10) i++ for.
07eff8c8 cmp edi,ebx ;(10)
07eff8ca jl 07eff891 ;(10) - "i < count". .
;(10)
;(10) :
;(10) .
07eff8cc mov dword ptr [ebp-0Ch],0
07eff8d3 mov dword ptr [ebp-8],0FCh
07eff8da push 7EFF8EEh
07eff8df jmp 07eff8e1
07eff8e1 mov eax,dword ptr [ebp-14h]
07eff8e4 mov dword ptr [eax+30h],0 ;(17) shadowIndexes
;(17) finally.
07eff8eb pop eax
07eff8ec jmp eax
07eff8ee mov dword ptr [ebp-8],0
07eff8f5 pop ebx
07eff8f6 pop esi
07eff8f7 pop edi
07eff8f8 mov esp,ebp
07eff8fa pop ebp
07eff8fb ret

The line on which the exception is generated is marked with ">>>", and it becomes clear that it’s not a buggy element in the shadowIndexes list that is to blame, but the array itself — somewhere in the looping process, the value of shadowIndexes becomes null.

Identify the culprit is now quite simple.

Let's look at the DataTable fields (0x1990a1d4 - this is the very this value from ECX):
0:000> dd 0x1990a1d4 + 2c
1990a200 1990a80c 1990a80c 15b03c88 0142ea34
1990a210 00000000 011e11f4 00000000 00000000
1990a220 01200c24 00000000 00000000 00000000
1990a230 0124ca7c 155796bc 15579758 00000000
1990a240 00000000 00000000 155a4c5c 00000000
1990a250 00000000 00000000 00000000 00000000
1990a260 15ae8eec 00000000 00000000 1990a824
1990a270 1990a418 1990a480 1990a4e8 00000002


At 1990a200, there are indexes (field with offset 2c), next to it is shadowIndexes. Put a breakpoint to write to this memory area.

0:000> ba w 4 1990a204

And run the program, which after a few unforgettable moments, falls back into the debugger. It means that a certain value was recorded at the address of interest.

0:000> g
Breakpoint 12 hit
eax=1990a80c ebx=00000000 ecx=1990a80c edx=1990a204 esi=1990a1d4 edi=1990a1d4
eip=0100304a esp=0012e288 ebp=0012e2b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
0100304a 81f81c825715 cmp eax,1557821Ch


We see that the current instruction is “cmp eax, 1557821Ch”. At first glance, this is strange. It is shown because such a breakpoint is triggered after a write event.

The! Clrstack command displays the call stack, full reflection (the last method in the System.Reflection.RuntimeMethodInfo.InternalInvoke stack, which is not very useful for us).

Use dumpstack.

0:000> !dumpstack -ee
Thread 0
Current frame:
ChildEBP RetAddr Caller,Callee
0012e284 0814f725 (MethodDesc 0x4a631c0 +0x2d System.Data.DataTable.RecordStateChanged)
0012e2b4 08144d6d (MethodDesc 0x4a63280 +0x1c5 System.Data.DataTable.SetNewRecord)
0012e314 08145b29 (MethodDesc 0x812c488 +0x29 System.Data.DataRow.EndEdit)
0012e328 081458d9 (MethodDesc 0x812c3c8 +0x131 System.Data.DataRow.set_Item)
0012e348 03206651 (MethodDesc 0x812c3a8 +0x49 System.Data.DataRow.set_Item)
0012e360 0320bac2 (MethodDesc 0x7ec3760 +0x5a Com.Product.Core.Entities.EntityAdapter.DBAssign)
0012e394 2ea362d7 (MethodDesc 0x7ec3d90 +0x3f Com.Product.Core.Entities.Request.set_Price)
0012e3b8 2ea359ea (MethodDesc 0x7c8faa0 +0x252 Com.Product.App.RequestForm.UpdatePrice
0012e494 0cefdf89 (MethodDesc 0x7d50218 +0x1a1 Com.Product.App.RequestForm.panel1_RadioButtonsValueRealyChanged)

0012e4b0 7b8808cd (MethodDesc 0x7b9e38c8 +0x45 System.Windows.Forms.Control.AccessibilityNotifyClients)
0012e4c4 0cefd04c (MethodDesc 0xb989dd8 +0x4c Com.Product.CommonControls.DBControls.NullableRadioButtonsPanel.OnRadioButtonsValueReallyChanged)
0012e4e4 7b92a038 (MethodDesc 0x7ba1ea48 +0x68 System.Windows.Forms.RadioButton.set_Checked)
0012e4f0 0cefcda1 (MethodDesc 0xb989d78 +0x111 Com.Product.CommonControls.DBControls.NullableRadioButtonsPanel.set_RadioButtonsValue)
0012e760 799dd8c1 (MethodDesc 0x79bb1468 +0x141 System.Reflection.RuntimeMethodInfo.InternalInvoke)
0012e7a4 799dd768 (MethodDesc 0x79bb1458 +0x18 System.Reflection.RuntimeMethodInfo.Invoke)
0012e7bc 7b1f5c81 (MethodDesc 0x7b328e20 +0x139 System.ComponentModel.ReflectPropertyDescriptor.SetValue)
0012e810 7b8a1973 (MethodDesc 0x7ba38350 +0xd3 System.Windows.Forms.Binding.SetPropValue)
0012e82c 7b8a16d8 (MethodDesc 0x7ba38330 +0x50 System.Windows.Forms.Binding.FormatObject)
0012e840 7b8a1888 (MethodDesc 0x7ba38340 +0x38 System.Windows.Forms.Binding.PushData)
0012e848 7b8a311c (MethodDesc 0x7ba38938 +0x8c System.Windows.Forms.BindingManagerBase.PushData)
0012e85c 7b8bcde7 (MethodDesc 0x7ba38cf8 +0x37 System.Windows.Forms.CurrencyManager.CurrencyManager_PushData)
0012e87c 7b8a1996 (MethodDesc 0x7ba38350 +0xf6 System.Windows.Forms.Binding.SetPropValue)
0012e88c 7b8bdc33 (MethodDesc 0x7ba38df8 +0x5b System.Windows.Forms.CurrencyManager.OnItemChanged)
0012e8ac 7b8a1878 (MethodDesc 0x7ba38340 +0x28 System.Windows.Forms.Binding.PushData)
0012e8b4 7b8a1ad9 (MethodDesc 0x7ba38390 +0x59 System.Windows.Forms.Binding.UpdateIsBinding)
0012e8bc 7b8bdff0 (MethodDesc 0x7ba38e88 +0x110 System.Windows.Forms.CurrencyManager.UpdateIsBinding)
0012e8d0 7b8bdec8 (MethodDesc 0x7ba38e78 +0x8 System.Windows.Forms.CurrencyManager.UpdateIsBinding)
0012e8d4 7b8bd78c (MethodDesc 0x7ba38db8 +0x1c4 System.Windows.Forms.CurrencyManager.List_ListChanged)
0012e900 08181927 (MethodDesc 0x812ce50 +0x37 System.Data.DataView.OnListChanged)
0012e928 10c456ac (MethodDesc 0x812ce40 +0x34 System.Data.DataView.IndexListChanged)
0012e930 10525133 (MethodDesc 0x812ce30 +0x33 System.Data.DataView.FireEvent)
0012e940 10c45663 (MethodDesc 0x31fada0 +0x2b System.Data.DataViewListener.IndexListChanged)
0012e950 0814c5bc (MethodDesc 0x81219d0 +0x1c System.Data.Index.OnListChanged)
0012e958 07eff8c7 (MethodDesc 0x4a63200 +0x8f System.Data.DataTable.ResetIndexes)
0012e984 08186f34 (MethodDesc 0x31fcff0 +0x234 System.Data.Merger.MergeTable)
0012e9b0 08185790 (MethodDesc 0x31fd020 +0x48 System.Data.Merger.MergeTableData)
0012e9dc 0818e31f (MethodDesc 0x31fcfe0 +0x2f System.Data.Merger.MergeTable)
0012e9ec 0818e2b1 (MethodDesc 0x7ec0ce8 +0x61 System.Data.DataSet.Merge)
0012ea00 0cef8056 (MethodDesc 0x7c8f8d0 +0x61e Com.Product.App.RequestForm.InitEdit)
0012ec3c 26a117e7 (MethodDesc 0x10eaee98 +0x57 Com.Product.Core.Entities.EntityContextCache..ctor)
0012ec50 0cef79df (MethodDesc 0x7c8f8c0 +0x57 Com.Product.App.RequestForm.InitEdit)
0012ec74 0818d593 (MethodDesc 0x7ec0448 +0x2b Com.CommonComponents.Tools.PerformanceTimer.sAddCheckPoint)
0012ec84 0cef7933 (MethodDesc 0x7c8da68 +0x8b Com.Product.Tickets.Ticket.OnInit)
0012ecb4 0cef7706 (MethodDesc 0x7d509e8 +0x6e Com.Product.App.RequestForm.OnInit)
0012ecf4 0cef766b (MethodDesc 0x7c8da58 +0x1b Com.Product.Tickets.Ticket.Init)
....


Well, actually, the culprit was found. The key calls are highlighted in the stack: ResetIndexes raises the OnListChanged event, which smoothly flows into the binding, which calls the UpdatePrice application function. This function, by coincidence, changes the data in dataset, which leads to the premature vanishing of shadowIndexes.

Several movements with tech nano-hammer sisharpa, the culprit is punished, the program works. Hooray!

Source: https://habr.com/ru/post/52441/


All Articles