Some legacy code I'm stuck maintaining is stuck in an infinite loop (and thus I myself seem to be in one); I can't figure out why/how, though.
Here's the app's entry point, where it instantiates the main form (frmCentral):
CODE EXHIBIT A
public static int Main(string [] args)
{
try
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(GlobalExceptionHandler);
string name = Assembly.GetExecutingAssembly().GetName().Name;
MessageBox.Show(string.Format("Executing assembly is {0}", name)); // TODO: Remove after testing <= this one is seen
IntPtr mutexHandle = CreateMutex(IntPtr.Zero, true, name);
long error = GetLastError();
MessageBox.Show(string.Format("Last error int was {0}", error.ToString())); // TODO: Remove after testing <= this one is also seen
if (error == ERROR_ALREADY_EXISTS)
{
ReleaseMutex(mutexHandle);
IntPtr hWnd = FindWindow("#NETCF_AGL_BASE_", null);
if ((int) hWnd > 0)
{
SetForegroundWindow(hWnd);
}
return 0;
}
MessageBox.Show("made it into Main method #4"); // TODO: Remove after testing <= this one is seen
ReleaseMutex(mutexHandle);
MessageBox.Show("made it into Main method #5"); // TODO: Remove after testing <= this one is seen
DeviceInfo devIn = DeviceInfo.GetInstance();
MessageBox.Show("made it into Main method #6"); // TODO: Remove after testing <= this one is seen
Wifi.DisableWifi();
MessageBox.Show("made it into Main method #7"); // TODO: Remove after testing <= this one is seen
// Instantiate a new instance of Form1.
frmCentral f1 = new frmCentral();
f1.Height = devIn.GetScreenHeight();
f1.Text = SSCS.GetFormTitle("SSCS HHS", "", "");
MessageBox.Show("made it before Application.Run() in Main method"); // TODO: Remove after testing <= this one is NOT seen
Application.Run(f1);
devIn.Close();
Application.Exit();
return 0;
}
catch(Exception ex)
{
SSCS.ExceptionHandler(ex, "Main");
return 0;
}
} // Main() method
frmCentral's constructor then calls a (what's the opposite of glorified?) singleton method named DBConnection.GetInstance():
CODE EXHIBIT B
public frmCentral()
{
try
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
MessageBox.Show("made it past InitializeComponent() in frmCentral constructor"); // <= this displays
devIn = DeviceInfo.GetInstance();
MessageBox.Show("made it past DeviceInfo.GetInstance() in frmCentral constructor"); // <= this displays
dbconn = DBConnection.GetInstance();
MessageBox.Show("made it past DBConnection.GetInstance() in frmCentral constructor");
WindowState = FormWindowState.Maximized;
UpdateMenuItemSelectable = false;
ResetConnectionFetchForm = false;
AllowNewItems = true;
listboxWork.Focus();
MessageBox.Show("made it through frmCentral constructor"); // <= this one does NOT display
}
catch (Exception ex)
{
SSCS.ExceptionHandler(ex, "frmCentral()");
}
} // frmCentral Constructor
Here's that "glorified" sort-of/kind-of singleton method:
CODE EXHIBIT C
// Singleton pattern, or at least a derivation thereof
public static DBConnection GetInstance()
{
MessageBox.Show("made it into DBConnection.GetInstance()");
try
{
if (instance == null)
{
MessageBox.Show("made it into DBConnection.GetInstance(); instance was null");
instance = new DBConnection();
}
}
catch(Exception ex)
{
SSCS.ExceptionHandler(ex, "DBConnection.GetInstance");
}
return instance;
}
That instantiates DBConnection and so its constructor is called:
CODE EXHIBIT D
private DBConnection()
{
try
{
// Connection String
string conStr = "Data Source = " + filename;
string cmpStr = conStr + ".tmp";
MessageBox.Show(string.Format("made it into DBConnection constructor. cmpStr == {0}", cmpStr)); // TODO: Comment out or remove
if (File.Exists(filename+".tmp"))
File.Delete(filename+".tmp");
engine = new SqlCeEngine(conStr);
MessageBox.Show(string.Format("SqlCeEngine created. conStr == {0}", conStr)); // TODO: Comment out or remove
if (File.Exists(filename))
{
MessageBox.Show(string.Format("file {0} exists", filename)); // TODO: Comment out or remove
}
else
{
// Create the SQL Server CE database
engine.CreateDatabase();
MessageBox.Show("Made it past call to engine.CreateDatabase()"); // TODO: Comment out or remove
}
engine.Dispose();
objCon = new SqlCeConnection(conStr);
MessageBox.Show("Made it past call to new SqlCeConnection(conStr)"); // TODO: Comment out or remove
objCon.Open();
}
catch(Exception ex)
{
SSCS.ExceptionHandler(ex, "DBConnection.DBConnection");
}
}
I see the *MessageBox.Show()*s from Code Exhibit A (unless otherwise noted), then the same for Code Exhibit B, then Code Exhibit C, then Code Exhibit D, then it goes back and forth between C and D "until the cows come home."
I don't see why the DBConnection constructor and the DBConnection GetInstance() recursively call each other, though...is there a needle in the haystack I'm missing, or an elephant hidden in plain sight, or...???
UPDATE
public static void ExceptionHandler(Exception ex, string location)
{
try
{
MessageBox.Show("Exception: " + ex.Message + "\n\nLocation: " + location, GetFormTitle("SSCS: " + ex.GetType().FullName,"",""));
}
catch(Exception exc)
{
MessageBox.Show("Exception Handler generated an exception!\n" + exc.Message + "\n\nCalling Location: " + location, GetFormTitle("SSCS: " + exc.GetType().FullName,"",""));
}
}
UPDATE 2
Here's more edifying obscurities:
public static string GetFormTitle(string formName, string serialNo, string siteNo)
{
string titleBar = formName == "" ? "SSCS HHS" : formName;
if((serialNo == ""))
{
User person = new User();
person.getUserFromTable();
serialNo = person.getSerialNo();
}
if (frmCentral.HashSiteMapping.ContainsKey(siteNo))
{
siteNo = (string) frmCentral.HashSiteMapping[siteNo];
}
if (serialNo != "")
titleBar += " - " + serialNo + (siteNo == "" ? "" : " Site #" + siteNo);
return titleBar;
}
UPDATE 3
The uncaught exception code I added:
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(GlobalExceptionHandler);
static void GlobalExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
MessageBox.Show(string.Format("GlobalExceptionHandler caught {0}; Compact Framework Version == {1}", e.Message, Environment.Version.ToString()));
}
I have not seen any evidence that this handler is ever reached, though (so far, anyway).
UPDATE 4
Very interesting - after adding a MessageBox.Show (or two) to GetFormTitle:
public static string GetFormTitle(string formName, string serialNo, string siteNo)
{
MessageBox.Show(string.Format("GetFormTitle() reached. formName == {0}; serialNo == {1}; siteNo == {2}", formName, serialNo, siteNo)); // TODO: Remove after testing
string titleBar = formName == "" ? "SSCS HHS" : formName;
if((serialNo == ""))
{
User person = new User();
person.getUserFromTable();
serialNo = person.getSerialNo();
}
if (frmCentral.HashSiteMapping.ContainsKey(siteNo))
{
siteNo = (string) frmCentral.HashSiteMapping[siteNo];
}
if (serialNo != "")
titleBar += " - " + serialNo + (siteNo == "" ? "" : " Site #" + siteNo);
MessageBox.Show(string.Format("titleBar val about to be returned. Val is {0}", titleBar)); // TODO: Remove after testing
return titleBar;
}
...these are the sequence of of MessageBox.Show()s that I see now:
0) Made it into DBConnection.GetInstance()
1) Made it into DBConnection.GetInstance() instance was null
2) Made it to DBConnection constructor cmpStr == ....
3) Sqlceengine created. conStr == ...
4) File \My Documents\HHSDB.SDF exists
5) Made it past call to new SqlCeConnection(conStr)
6) GetFormTitle() reached. fromName == SSCS:
System.Data.SqlserverCe.SqlCeException; serial No ==; siteNo ==
...followed by a round of more tail-chasing, recursive messages until I warm-boot (I hate to contradict the Fab 4, but contrary to popular opinion, Happiness is decidedly NOT a warm boot!*)
...so right in the midst of a MessageBox message display, an exception is inserted! Wholly Pennsylvania Hip-Hop (Key Rap)!
* Let's have no attempts at humor revolving around warm booty, now!
That was a superlative show of remote desk debugging, LB2!
Here is what's likely going on (at this point is a theory until OP can confirm):
The thing that starts the problem...
The issue that starts the infinite loop chain is likely objCon.Open();
in Exhibit D. There is probably some issue with connecting to the database, and the Open()
call, as it should, throws an exception. This of course get caught in the local catch
that is immediately following that line.
The local catch
calls SSCS.ExceptionHandler
which is shown in Update 1. It looks benign, but in it hides the culprit's accomplice by the name GetFormTitle
which is shown in Update 2.
Where things take a turn for the worse...
GetFormTitle
has a very interesting piece of code:
User person = new User();
person.getUserFromTable();
... which most likely is a model retrieving user info from the database (where else).
And so the infinite loop is created...
Well, to get User
from database, one needs a connection, which most likely causes DBConnection.GetInstance()
to be called, which gets to objCon.Open();
which starts the new cycle, and thus an infinite loop is created (ingeniously, without using any of language's built-in looping mechanisms, to be duly noted).
To confirm:
OP: Please put very plain (meaning no calls to GetFormTitle
, please) MessageBoxes in the GetFormTitle
, and if the above theory is right, you'll see it in the path of execution.
来源:https://stackoverflow.com/questions/23436775/how-is-this-causing-an-endless-loop