Recently, we faced a very unique issue which kind of took me and one of my friend on long day roller-coaster for at least a week.
So, we have a single page .NET windows desktop application which basically creates an access database file and inserts data into it. Even though it's single page application it processes more than 5k rows of data in the access database and the process completion usually take at least an hour, sometimes even more depending on the data size.
The desktop app started crashing randomly in one of my desktop but the same application was working fine in the laptop. There was no exceptions thrown as such. Just crashed in a blink of eye.
For such cases, where app crashes without throwing any errors. We can check the event viewer log in windows application.
To open the event viewer log, follow the steps given below.
1. Search event viewer in windows search box.
2. Click on Event Viewer as shown in the image above.
3. Go to Windows logs > Application.
It will show all the log events for different applications. You can also filter out the logs using the Actions tab in left side of the screen.
When we checked the Event Viewer logs, it has the following errors.
Faulting application name: xxxxxxabase.exe, version: 5.2.0.0, time stamp: 0x678a637c Faulting module name: clr.dll, version: 4.8.9290.0, time stamp: 0x67214bca Exception code: 0xc0000005 Fault offset: 0x000000000006cb43 Faulting process id: 0x0x2FE4 Faulting application start time: 0x0x1DB68E946FAD420 Faulting application path: "Path to the executable\xxxxxabase.exe" Faulting module path: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Report Id: xxxxx - 7xxa5 - 4cxx - 8xx3 - 6a03xxxxxb9 Faulting package full name: Faulting package-relative application ID:
Most of the crash logs mentioned ucrtbase.dll as faulting module, and sometimes we also got faulting module as clr.dll as well.
Searching with the faulting module name over the internet provided many suggestions but crashes didn't go away. We went through the code, and unfortunately couldn't figure out the issue. Also, our mindset that "there shouldn't be any issue in the code since the app is running fine for most of the users", didn't help much rather prolonged our debug timing.
Exactly this is where we were wrong!
Anyway, we found this blog on Microsoft, which mentioned to remove and reinstall C++ redistributables for the ucrtbase.dll faulting module. We removed and reinstalled all C++ redistributables. After the reinstallation, app ran fine for couple of runs but again started crashing.
Link to official C++ redistributables: https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170
We went through this Stack Overflow post which lists out many possible solutions thanks to the users who posted the solutions which have worked for them. With the help of the post which mentioned to check for the crash dumps, we got the stack trace of the code and figured out that the issue is happening during the CRUD operation into the Access Database.
We were using the following code to insert the data in access database.
public int Insert(string tableName, object dbObject)
{
var query = string.Empty;
try
{
if (dbObject == null)
{
return 0;
}
var columns = ((BaseAccessModel)dbObject).GetColumns();
StringBuilder columnsQuery = new StringBuilder("");
StringBuilder parametersQuery = new StringBuilder("");
OleDbCommand cmd = new OleDbCommand();
object value = null;
for (int i = 0; i < columns.Count; i++)
{
value = GetColumnValue(columns[i], dbObject);
if (value != null)
{
columnsQuery.Append("[" + columns[i].ColumnName + "],");
parametersQuery.Append("[@" + columns[i].ColumnName + "],");
cmd.Parameters.Add(columns[i].ColumnName, columns[i].DBColumnType).Value = GetColumnValue(columns[i], dbObject);
}
}
query = string.Format("INSERT INTO [{0}] ({1}) VALUES ({2});", tableName, columnsQuery.ToString().Trim(','), parametersQuery.ToString().Trim(','));
cmd.CommandText = query;
cmd.Connection = _conn;
return cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw new Exception($"Failed to insert: {query} - Message: {ex.Message}");
}
}
The issue was that the oleDbCommand instance was not getting disposed properly. We were disposing oleDbConnection instances but not oleDbCommand.
Without this memory were not getting disposed properly and the app was crashing with random module errors. The fix was to move that portion of code into using block.
This is the update code which worked.
using (OleDbCommand cmd = new OleDbCommand())
{
object value = null;
for (int i = 0; i < columns.Count; i++)
{
value = GetColumnValue(columns[i], dbObject);
if (value != null)
{
columnsQuery.Append("[" + columns[i].ColumnName + "],");
parametersQuery.Append("[@" + columns[i].ColumnName + "],");
cmd.Parameters.Add(columns[i].ColumnName, columns[i].DBColumnType).Value = GetColumnValue(columns[i], dbObject);
}
}
query = string.Format("INSERT INTO [{0}] ({1}) VALUES ({2});", tableName, columnsQuery.ToString().Trim(','), parametersQuery.ToString().Trim(','));
cmd.CommandText = query;
cmd.Connection = _conn;
return cmd.ExecuteNonQuery();
}
Not disposing the database command connection was the actual issue but debugging it was hard because of the random faulting module with no stack trace in the first place.
At one point we thought that the issue could be with .NET 4.6 because that is what we were using in our .NET project.
But as it turned out, the actual issue was with our code only.
Hope it helps figure out your issue!!
Post a Comment