Visual Studio 2003 and SQL Server 2000 (yes, I know).
I have a Windows Forms program where I want to update an information display from a SQL Server 2000 database in real-time based on what the user types into a text box.
What I've done is essentially put the dataset refresh code in the text_changed event of the text box. I clear the dataset, get the new string to match against from the textbox, , set the parameter value in the data adapter, and fill the dataset. The display fields are labels with their text bound to the dataset fields. So when the user starts typing in a name, for example, with each letter typed the dataset is refreshed, showing only matches. I use a "LIKE" clause in the SQL to match on the data.
The actual code is more complex than that implies, as it makes calls to other places to get and format the search strings, and results are further filtered on other conditions such as radio buttons or check boxes, but that's the basic concept.
This seems to work fine, except after a relatively short time in use the program will crash with an unhandled Out of Memory error (my code includes error handling, which tells me and logs where the error occurred, but this exception is not getting handled).
What am I doing wrong, or what is the preferred way to implement this?
Thanks,
Ron
Loading
Sam HobbsPosted Mar 19, 2012, 7:36 PM
If you use just the first 10 characters for the auto-complete then that would be only about 400KB to cache the possibilities. The logic would be a little complicated; I am not sure what to do when there are multiple matches for a possible beginning of a string.
You could pre-process the strings and retain just the portion required to uniquely determine each of them. So for example if you have the strings "they will", and "they did" then you only need to look at the first six characters to differentiate between the two strings. This would be a little complicated since the number of characters to uniquely determine each string would vary. This could work since the program would know when to stop watching the text entered and to proceed and retrieve the data.
Another possibility would be that when a the database is being accessed due to a key being received, a flag would be checked and if the flag is on then the key is ignored (temporarily). If the flag is off then it is turned on and the database is read. After the database is read, if a key has been recieved during the database access then another read is performed with all keys received so far. If there are no keys waiting to be processed after a daabase access, then the flag is turned back back off.
Ron AllenPosted Mar 20, 2012, 2:53 PM
Ron AllenPosted Mar 19, 2012, 5:04 PM
I removed the Application.Exit(); you're right, it's extraneous.
I think I've figured it out; seems obvious in retrospect (don't they all). OK, the process is, the form loads and the user begins typing into the filter text box. Every time the TextChanged event fires, it attempts to clear and load the dataset being filtered. That can't happen as fast as people can type. So the dataset is in the process of being filled when it is cleared and another attempt is made to fill it. It's behaving as if every time the TextChanged event fires, a new instance of the dataset is created, which will eventually use up a lot of memory. Maybe I'm just not clear on this... I don't specifically create a new instance, but that sure does seem to be a possible explanation.
Comments? Suggestions?
I did not know about using a Mutex to detect a previous instance. I'll look into that, too, and if necessary start a new thread on that subject.
Thanks,
Ron
Sam HobbsPosted Mar 17, 2012, 7:21 AM
I suggest removing the "Application.Exit();". I see no reason for it and it could be causing a problem. If this error occurs only when there is a previous instance, then that would essentially prove it is the Application.Exit(). If the error occurs without a previous instance, then of course Application.Exit() is not the cause. Application.Exit() would be a problem if a window has been created or something else that needs cleanup.
You need to show more about the error than just the Exception.Message. There could be an inner exception that would be very useful. I assume you do not want to show your users too much in the MessageBox so it would help to have another way to capture the additional information. You can simply use Exception.ToString() to get most of the exception data.
Did you know you can use a Mutex to determine if there is a previous instance?
Ron AllenPosted Mar 16, 2012, 11:27 AM
static void Main()
{
try
{
bool prevInstance = false;
bool timeOK = true;
TimeSpan startTime = new TimeSpan(0, 45, 0); //12:45 am
TimeSpan endTime = new TimeSpan(6, 0, 0); //6:00 am
TimeSpan curTime = DateTime.Now.TimeOfDay;
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
foreach (Process process in processes)
{
if(process.Id != current.Id)
{
if(Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
{
prevInstance = true;
break;
}
}
}
if((curTime > startTime) && (curTime < endTime))
{
timeOK = false;
}
if (prevInstance == false && timeOK == true)
{
Form frmMain = new formMain();
Application.Run(frmMain);
}
else
{
if(prevInstance == true)
{
MessageBox.Show("The program is already running!", "Circ Tools", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if(timeOK == false)
{
MessageBox.Show("You can not log on between 12:45am and 6:00am.", "Circ Tools", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Application.Exit();
}
}
catch (System.Exception ex) // this is line 4112
{
MessageBox.Show("An error has occurred in Main(). Please note the following:" + Environment.NewLine + Environment.NewLine + ex.Message, "Circ Tools", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Sam HobbsPosted Mar 14, 2012, 10:29 PM
I think it is best to filter out the lines that say "[something]: Loaded 'something.dll', No symbols loaded." Those are informational and seldom useful; I have seen messages like that from the debugger for more than a decade (originally for C++ programs) and they have never been useful to me. The assembler code is also not likely to help.
The top (most recent) item in the stack that is for a line of your code is the item you want the most. So we need to see the code in the area of line 4112; the corresponding try block of course. Your catch block should also show as much relevant information as possible.
Ron AllenPosted Mar 14, 2012, 5:57 PM
Thanks for the reply.
The TextBox control in Visual Studio 2003 does not have an AutoCompleteMode property. I use ComboBox when I have a reasonably short list of choices for the user to choose from, but in this case it's meant to be a free-form entry field to filter the data. If I loaded up a ComboBox, it would have somewhere in the neighborhood of 40K items.
Literally everything in the program is subject to exception handling; I'm a little obessive-compulsive about that :) All exceptions are logged to a database, with all of the information I need to track it down; no need for users to even tell me there's a problem, usually. I run a report daily to see if anyone is having consistent issues with something.
OK, so I just got it to crash in debug mode again. Here's what I got:
'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'CirculationTools': Loaded 'C:\VisualStudioProjects\CirculationTools\bin\Debug\CirculationTools.exe', Symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.windows.forms\1.0.5000.0__b77a5c561934e089\system.windows.forms.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.drawing\1.0.5000.0__b03f5f7f11d50a3a\system.drawing.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.xml\1.0.5000.0__b77a5c561934e089\system.xml.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\accessibility\1.0.5000.0__b03f5f7f11d50a3a\accessibility.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.enterpriseservices\1.0.5000.0__b03f5f7f11d50a3a\system.enterpriseservices.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.enterpriseservices\1.0.5000.0__b03f5f7f11d50a3a\system.enterpriseservices.thunk.dll', No symbols loaded.
The program '[5320] CirculationTools.exe' has exited with code 0 (0x0).
The first time, the Call Stack reported the error at line 4112. Line 4112 is the catch of the exception handler in Main().
The second time, the Visual Studio Environment error message (not my error handler) said "An unhandled exception of type 'System.OutOfMemoryException' occurred in system.data.dll.
The call stack looks like this:
system.data.dll!System.Data.SqlClient.ConnectionPool.CheckForDeadConnections()+0x151 bytes
system.data.dll!System.Data.SqlClient.ConnectionPool.PoolCreateRequest(System.Object state =
When I close the error message, I get another one that says "There is no source code for the current location"
When I close that one, here's what the debug output looks like this:
'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'CirculationTools': Loaded 'C:\VisualStudioProjects\CirculationTools\bin\Debug\CirculationTools.exe', Symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.windows.forms\1.0.5000.0__b77a5c561934e089\system.windows.forms.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.drawing\1.0.5000.0__b03f5f7f11d50a3a\system.drawing.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.xml\1.0.5000.0__b77a5c561934e089\system.xml.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\accessibility\1.0.5000.0__b03f5f7f11d50a3a\accessibility.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.enterpriseservices\1.0.5000.0__b03f5f7f11d50a3a\system.enterpriseservices.dll', No symbols loaded.
'CirculationTools.exe': Loaded 'c:\windows\assembly\gac\system.enterpriseservices\1.0.5000.0__b03f5f7f11d50a3a\system.enterpriseservices.thunk.dll', No symbols loaded.
An unhandled exception of type 'System.OutOfMemoryException' occurred in system.data.dll
The disassembly looks like this, I've marked the line that's highlighted in the viewer
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,30h
00000006 push edi
00000007 push esi
00000008 push ebx
00000009 xor eax,eax
0000000b mov dword ptr [ebp-20h],eax
0000000e mov dword ptr [ebp-2Ch],eax
00000011 mov dword ptr [ebp-30h],eax
00000014 mov dword ptr [ebp-8],0
0000001b mov dword ptr [ebp-18h],ecx
0000001e mov dword ptr [ebp-1Ch],0
00000025 mov dword ptr [ebp-20h],0
0000002c mov dword ptr [ebp-24h],0
00000033 mov dword ptr [ebp-28h],0
0000003a mov dword ptr [ebp-2Ch],0
00000041 mov dword ptr [ebp-30h],0
00000048 mov dword ptr [ebp-1Ch],0
0000004f mov eax,dword ptr [ebp-18h]
00000052 mov ecx,dword ptr [eax+2Ch]
00000055 mov eax,dword ptr [ecx]
00000057 call dword ptr [eax+000000D4h]
0000005d mov esi,eax
0000005f mov dword ptr [ebp-30h],esi
00000062 mov ecx,dword ptr [ebp-30h]
00000065 call 6C88AEE4
0000006a mov eax,dword ptr [ebp-18h]
0000006d mov ecx,dword ptr [eax+2Ch]
00000070 mov eax,dword ptr [ecx]
00000072 call dword ptr [eax+00000094h]
00000078 mov esi,eax
0000007a mov dword ptr [ebp-20h],esi
0000007d mov eax,dword ptr [ebp-20h]
00000080 mov eax,dword ptr [eax+4]
00000083 mov dword ptr [ebp-24h],eax
00000086 mov dword ptr [ebp-28h],0
0000008d nop
0000008e jmp 00000113
00000093 mov eax,dword ptr [ebp-28h]
00000096 mov edx,dword ptr [ebp-20h]
00000099 cmp eax,dword ptr [edx+4]
0000009c jb 000000A8
0000009e mov ecx,1
000000a3 call 6C91ADBB
000000a8 mov edx,dword ptr [edx+eax*4+0Ch]
000000ac mov ecx,0AF16CBCh
000000b1 call 6C8B9ABF
000000b6 mov dword ptr [ebp-2Ch],eax
000000b9 cmp dword ptr [ebp-2Ch],0
000000bd je 00000110
000000bf mov ecx,dword ptr [ebp-2Ch]
000000c2 cmp dword ptr [ecx],ecx
000000c4 call dword ptr ds:[0AF16D14h]
000000ca movzx esi,al
000000cd test esi,esi
000000cf jne 00000110
000000d1 mov ecx,dword ptr [ebp-2Ch]
000000d4 cmp dword ptr [ecx],ecx
000000d6 call dword ptr ds:[0AF16D00h]
000000dc mov esi,eax
000000de test esi,esi
000000e0 je 00000110
000000e2 mov ecx,dword ptr [ebp-2Ch]
000000e5 cmp dword ptr [ecx],ecx
000000e7 call dword ptr ds:[0AF16D00h]
000000ed mov esi,eax
000000ef mov ecx,esi
000000f1 mov eax,dword ptr [ecx]
000000f3 call dword ptr [eax+38h]
000000f6 movzx esi,al
000000f9 test esi,esi
000000fb jne 00000110
000000fd mov edx,dword ptr [ebp-2Ch]
00000100 mov ecx,dword ptr [ebp-18h]
00000103 call dword ptr ds:[0AF17318h]
00000109 mov dword ptr [ebp-1Ch],1
00000110 inc dword ptr [ebp-28h]
00000113 mov eax,dword ptr [ebp-28h]
00000116 cmp eax,dword ptr [ebp-24h]
00000119 jl 00000093
0000011f nop
00000120 mov dword ptr [ebp-0Ch],0
00000127 mov dword ptr [ebp-8],0FCh
0000012e push 0C926D38h
00000133 jmp 00000135
00000135 mov ecx,dword ptr [ebp-30h]
00000138 call 6C88B106
0000013d pop eax
0000013e jmp eax
00000140 mov dword ptr [ebp-8],0
00000147 jmp 00000149
00000149 nop
0000014a jmp 00000151
0000014c call 6C9CCCF7
00000151 mov eax,dword ptr [ebp-1Ch] <---- THIS IS THE HIGHLIGHTED LINE
00000154 pop ebx
00000155 pop esi
00000156 pop edi
00000157 mov esp,ebp
00000159 pop ebp
0000015a ret
So I'm not getting a whole lot out of that to be honest, but maybe you or someone else can :)
Thanks,
Ron
Sam HobbsPosted Mar 14, 2012, 7:14 AM
Are you using the TextBox.AutoCompleteMode Property? Note that it is more typical to use a ComboBox but you can use a TextBox.
Using the TextBox.AutoCompleteMode Property I think you can bind data from a database to the control; that combination might simplify things for you.
Chances are there are errors in your program that are being ignored that then result in catastrophic failure. The best way to solve the problem is to go through your code and ensure that errors are caught.
You also can use the debugger for that. When you debug the program, look at the source code and the data; it is likely you will have a clue to the problem. I assume you have not done that, since if you had, you would provide more information here. When you know what the line is that is getting the error, and if you have some clues to the problem with the data, you can put a breakpoint earlier in your program and then debug to see what is happening.