StackOverflowException catch C# on UWP (Windows10) - c#

I have some recursive code in my windows10 App (UWP platform)
I know that there are no direct way to catch StackOverflowException on C#, but there is a way by using Thread class on Windows Forms API. And I can't apply solution to UWP, because there are no Thread class.
So, I have some code and I need to try execute it and stop it's Thread if it's fails.
I also want to detect exception if it's possible.
User can be satisfied by half finished work.
My application is closing if recursion is too big.
How to fix someWork(byte[] data) method?
public sealed partial class MainPage : Page {
private byte[] dataArray; // value is creating by user
// data can be very big, so not-resursive code can use all App's memory and crush it too!!
private async void ButtonClick(object sender, RoutedEventArgs ev) { // here I run method
bool isFinished = someWork(dataArray);
// then I show dataArray (no matter finished or not) to user
}
private static bool someWork(byte[] data) { // I need to fix this method
int start_arg = 0;
try {
someWork_recursive(data, start_arg);
return true; // All done
} catch (Exception) { } // I trying to catch StackOverflowException here
return false; // Half-finished
}
private static void someWork_recursive(byte[] data, int arg) { // My question is NOT about body of this method
// code that run someWork_recursive() again or not
// also code that change data[] values
}
}

Related

Need to implement "Scan" method in dll (non blocking)

Sorry for the title, i didn't find it easy to resume.
My issue is that I need to implement a c# dll that implements a 'scan' method, but this scan, when invoked, must not block the main thread of the application using the dll. Moreover, it is a duty that after the scan resolves it rises an Event.
So my issue (in the deep) is that i'm not so experienced at c#, and after very hard investigation i've come up with some solutions but i'm not very sure if they are the "right" procedures.
In the dll i've come up with:
public class Reader
{
public delegate void ReaderEventHandler(Object sender, AlertEventArgs e);
public void Scan(String ReaderName)
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = null;
//Code with blocking scan function here
if (ScanFinnished)
{
alertEventArgs.uuiData = "Scan Finnished!";
}
alertEventArgs.cardStateData = readerState[0].eventState;
ReaderEvent(new object(), alertEventArgs);
}
public event ReaderEventHandler ReaderEvent;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
private uint cardState = 0;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
public uint cardStateData
{
get { return cardState; }
set { cardState = value; }
}
#endregion
}
While in the main app I do:
Reader reader;
Task polling;
String SelectedReader = "Some_Reader";
private void bButton_Click(object sender, EventArgs e)
{
reader = new Reader();
reader.ReaderEvent += new Reader.ReaderEventHandler(reader_EventChanged);
polling = Task.Factory.StartNew(() => reader.Scan(SelectedReader));
}
void reader_EventChanged(object sender, AlertEventArgs e)
{
MessageBox.Show(e.uuiData + " Estado: " + e.cardStateData.ToString("X"));
reader.Dispose();
}
So here, it works fine but i don't know if it's the proper way, in addition i'm not able to handle possible Exceptions generated in the dll.
Also tried to use async/await but found it difficult and as I understand it's just a simpler workaround Tasks.
What are the inconvinients of this solution? how can i capture Exceptions (are they in other threads and that's why i cant try/catch them)? Possible concept faults?
When your class sends events, the sender usually is that class, this. Having new object() as sender makes absolutely no sense. Even null would be better but... just use this.
You shouldn't directly raise events as it might result in race conditions. Might not happen easily in your case but it's just a good guideline to follow. So instead of calling ReaderEvent(new object(), alertEventArgs); call RaiseReaderEvent(alertEventArgs); and create method for it.
For example:
private void RaiseReaderEvent(AlertEventArgs args)
{
var myEvent = ReaderEvent; // This prevents race conditions
if (myEvent != null) // remember to check that someone actually subscribes your event
myEvent(this, args); // Sender should be *this*, not some "new object()".
}
Though I personally like a bit more generic approach:
private void Raise<T>(EventHandler<T> oEvent, T args) where T : EventArgs
{
var eventInstance = oEvent;
if (eventInstance != null)
eventInstance(this, args);
}
Which can then be used to raise all events in same class like this:
Raise(ReaderEvent, alertEventArgs);
Since your scan should be non-blocking, you could use tasks, async/await or threads for example. You have chosen Tasks which is perfectly fine.
In every case you must understand that when you are not blocking your application, your application's main thread continues going like a train. Once you jump out of that train, you can't return. You probably should declare a new event "ErrorEvent" that is raised if your scan-procedure catches an exception. Your main application can then subscribe to that event as well, but you still must realize that those events are not (necessarily) coming from the main thread. When not, you won't be able to interact with your GUI directly (I'm assuming you have one due to button click handler). If you are using WinForms, you'll have to invoke all GUI changes when required.
So your UI-thread safe event handler should be something like this:
void reader_EventChanged(object sender, AlertEventArgs e)
{
if (InvokeRequired) // This true for others than UI Thread.
{
Invoke((MethodInvoker)delegate
{
Text = "My new title!";
});
}
else
Text = "My new title!";
}
In WPF there's Dispather that handles similar invoking.

How do i make the code wait until a bool signals it to continue

I created a WPF application and then converted it into a DLL by removing the app.xaml and setting the build to Class Library. I'm using a C# Windows Forms to test the DLL. The WPF DLL is preloaded so it can later on be called to show and display instantly without having to wait for a load. What I am trying to accomplish is to call the Show(ShowWPFApp) and have the code wait until a boolean is flipped by calling WPFAppResponse (this action is passed in via the initial load). The way I have it right now causes the UI to freeze up. Any idea on how i can get it to wait without the UI freezing up?
Windows Form calling WPF DLL
namespace WindowsFormsDLLTest
{
public partial class Form1 : Form
{
WPFDLL.LoadWPFApp wpfApp = null;
public Form1()
{
InitializeComponent();
}
private void btnLoadWPFApp_Click(object sender, EventArgs e)
{
wpfApp = new WPFDLL.LoadWPFApp();
try
{
wpfApp.Load();
}
catch (Exception ex)
{
}
}
private void btnShowWPFApp_Click(object sender, EventArgs e)
{
try
{
string result = null;
result = wpfApp.ShowWPFApp("John Doe");
}
catch (Exception ex)
{
}
}
}
}
WPF DLL Application
namespace WPFDLL
{
public class LoadWPFApp
{
private Application application = null;
private MainWindow mainWindow = null;
private bool waitOnShowWindow {get; set;}
private string returnResults = null;
public void Load()
{
StartLoadingWPFApp();
}
[STAThread]
private void StartLoadingWPFApp()
{
application = new Application();
SplashScreenWindow splashWindow = new SplashScreenWindow();
splashWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
splashWindow.Show();
try
{
mainWindow = new MainWindow(WPFAppResponse);
mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
splashWindow.Close();
application.Run();
}
catch (Exception ex)
{
splashWindow.Close();
MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "WPF App Error", MessageBoxButton.OK, MessageBoxImage.Error);
mainWindow = null;
}
}
public string ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
while(waitOnShowWindow)
{
//Code waits until bool is set to false
}
return returnResults;
}
public void WPFAppResponse(string person)
{
returnResults = person;
waitOnShowWindow = false;
mainWindow.Hide();
}
}
}
Launching a WPF app from Windows forms is messy. You have stumbled onto a rather complex threading problem. The general recommendation is to instead create that WPF application as a WPF control library. However, I see that this may not resolve the slow loading issue that you have, which is why you made the lightweight WinForms wrapper app.
The problem is your loop:
while(waitOnShowWindow)
{
//Code waits until bool is set to false
}
That loop is running on the UI thread, blocking it from processing windows messages. (If that concept is new to you, go look it up as it is important for Windows UI stuff.) For the UI to respond, it must be running a Windows message loop. I see two solutions:
Create your own message loop.
Return immediately, then get the result later.
Solution 1:
To create your own message loop, you need to use something like Dispatcher.Run() or Dispatcher.PushFrame(). Try this and see if it works:
public string ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
Dispatcher.CurrentDispatcher.Run();
// do whatever to get the results
return returnResults;
}
If that doesn't work, you might need to use PushFrame() instead. Here are some more in depth articles on that topic in case Dispatcher.Run() doesn't work.
http://www.codeproject.com/Articles/152137/DispatcherFrame-Look-in-Depth
http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
Solution 2:
public void ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
}
Now, the call will not block the UI thread and the WPF window will appear. The windows forms application is running a message loop, so the WPF can now run. But how do you get the result!? Since it is now running asynchronously, you will have to find some other way to get the return value back. I think an event on the MainWindow class would be the easiest way.
Also: That [STAThread] attribute isn't doing anything. [STAThread] only has meaning on the entry point of the app. Fortunately, your Windows forms app already puts [STAThread] on the Main() method, so your thread is an STA thread.
A work around could be using
await Task.Delay(1000);
inside your while loop. This might delay every run of your while loop and the UI will not freeze up. Am not sure if this would work for your case. Try and let me know. Hope this helps.
You will need to give execution back to the UI thread's event loop so that the UI doesn't freeze up. In a forms app, you can do this as follows:
while(waitOnShowWindow)
{
//Code waits until bool is set to false
System.Windows.Forms.Application.DoEvents();
}
Edit:
As Pointed out by Eric, there are potential problems with using DoEvents(), so don't go wild with it. See How to use DoEvents() without being "evil"?.
In a test app like this, it allows the code to work. However, a better solution would be to re-structure the application so that the call is unnecessary, using multi-threading if needed.

EventHandler vs Timeout

I had write a little application on c# to reading some plc data by using ethernet protocol. Ethernet socket, open and close are stored inside a .dll library.
Now, i using this public method:
public static string readdata()
{
try
{
...
return (plcdata());
}
catch
{}
}
My doubt: if the plcdata() (that is a method of a .dll) waiting a few second (for istance slow comunication ...) my application may be frozen.
So, i try to add a EventHandler on string returned like this:
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
auxDataTextBox.Text = plcdata();
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
In this case when the "plcdata()" changed, the auxDataIncoming will be raise.
It is correct? Or is better make a timeout control? Or make new thread?
Thanks a lot for yours opinion
Your change won't make a difference, it' still all running on the UI thread. To make plcdata() a non-blocking call you would need to fire it off on another thread e.g.
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
Task.Factory.StartNew(() => {
return plcData();
}).ContinueWith(task => {
auxDataTextBox.Text = task.Result;
}, null, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
This will not unfreeze your application. The effect will be exactly the same. This is because you are still running the plcdata on your UI thread.
The whole event structure you set up does not make sense at all.
You should look into multithreading. A very easy way to do this is using a BackgroundWorker.

Multithreading to speed up load times

I made a program that loads a bunch of computer information. In the Form_Load event I have it initialize 3 (that number will grow) panels of information. One that has a bunch of unit information seems to make the program load rather slowly. I've tried to speed it up a bunch by switching from WMI to using Native calls, which helped a bunch. Soon though I'm going to have network information posted as well. I used to load that panel but i disabled it for a little bit till I work out the bugs in my other panels. So while learning how I can use a seperate thread to update my battery information I figured that I might be able to create seperate threads in my unit information panel so that it might could load faster. I dont know that any of my information would cause concurrent issues, but i can work on that.
I want to start small so what if i change this
private void Form1_Load(object sender, EventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
to this
private void Form1_Load(object sender, EventArgs e)
{
Thread infoThread = new Thread(new ThreadStart(unitInformationPanel1.PopulateUnitInformation));
infoThread.Start();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
would the info thread be terminated when populate unit info is done? or would it be better to move that thread creation into PopulateUnitInformation? here is what it looks like.
public void PopulateUnitInformation()
{
unitModelLabel.Text = Properties.Settings.Default.UnitModelString;
serialNumberLabel.Text = Properties.Settings.Default.UnitSerialString;
biosVersionLabel.Text = UnitBios.GetBiosNumber();
osLabel.Text = OS.getOSString();
cpuLabel.Text = UnitCpu.GetCpuInfo();
var hdd = HddInfo.GetHddInfo();
diskNameLabel.Text = hdd.Name;
diskCapacityLabel.Text = hdd.Capacity;
diskFirmwareLabel.Text = hdd.Firmware;
memoryLabel.Text = MemoryInformation.GetTotalMemory();
NetworkPresenceInformation.GetAdapatersPresent();
biometricLabel.Text = BiometricInformation.IsPresent ? "Present" : "Not Present";
var networkAdaptersPresense = NetworkPresenceInformation.GetAdapatersPresent();
bluetoothLabel.Text = networkAdaptersPresense[0] ? "Present" : "Not Present";
wifiLabel.Text = networkAdaptersPresense[1] ? "Present" : "Not Present";
cellularLabel.Text = networkAdaptersPresense[2] ? "Present" : "Not Present";
}
--
wow i just ran it with the infothread and it still took some time to load (might be the 12 panels i created in the main thread. but it loaded the 12 frames and the unit information panel populated its information after everything loaded. That was cool, but is it safe? is it somewhat easy to make 12 threads for my panels? or is that dumb?
EDIT
this is what i did for stopwatch.
Stopwatch programTimer;
public Form1()
{
programTimer = Stopwatch.StartNew();
InitializeComponent();
SetupDebugWindow();
TerminateKeymon();
UnitModel.SetModel();
UnitSerialNumber.SetSerialNumber();
}
private void Form1_Shown(object sender, EventArgs e)
{
audioBrightnessPanel1.UpdateBrightnessTrackbar();
applicationLauncherPanel1.LoadApplications();
programTimer.Stop();
Console.WriteLine("Load Time: {0}",programTimer.ElapsedMilliseconds);
timer1.Start();
}
Will this be accurate?
EDIT 2 6/18/2012
Well I took the advice of using backgroundworker. Please let me know if i did this right.
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
You've asked a very broad question, but I'm going to give some general advice. If you want more specific information, you should consider deleting this question and posting more specific individual questions.
First and foremost, you should very strongly consider using something like the System.Threading.Task class for your multithreaded operations. There is a ton of information online about how to get started with it and how you can use Tasks to manage asynchronous operations. The short story is that if you're spinning up your own thread (as you're doing above), you almost certainly should be using something else to do that for you.
Adding multithreading to your code will not, in the strictest sense of the word, make it any "faster"; they will always take the same amount of total processor time. What it can and will do is two things: free up the UI thread to be responsive and allow you to split that "total processor time" across multiple cores or processors, should those be available to the system. So, if you have operation X that takes 10 seconds to complete, then just shifting operation X to another thread will not make it complete any faster than 10 seconds.
No, what you are doing above is not safe. I'm assuming that somewhere you've turned off checking for cross-thread communication errors in your app? Otherwise, that code should throw an exception, assuming this is a WinForms or WPF application. This is one reason to use Tasks, as you can easily separate the part of your process that actually takes a long time (or isn't UI related), then add a task continuation that uses the results and populates the UI elements within a properly synchronized context.
So my final approach this was as follows. I felt that my Main Form was doing more than it should. Sticking with the single responsibility principle I decided that MainForm should only be responsible for one thing, showing and displaying all 12 panels (now down to 11, i turned one into a menu item). So moved all the multithreading out of mainform and into program.cs. I found that this was even a little more difficult. What I did find though was a simple solution that allows me to not even worry about multithreading at all. It was the Idle event. Here is what i chose to do.
[STAThread]
static void Main()
{
DateTime current = DateTime.Now;
DateTime today = new DateTime(2012,7,19);
TimeSpan span = current.Subtract(today);
if (span.Days<0)
{
MessageBox.Show("Please adjust Time then restart Aspects","Adjust Time");
Process.Start("timedate.cpl").WaitForExit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Idle += new EventHandler(Application_Idle);
mainForm = new MainForm();
mainForm.Closing += new CancelEventHandler(mainForm_Closing);
#if !DEBUG
TerminateKeymon();
StartSerial();
SetupDefaultValues();
EmbeddedMessageBox(0);
#endif
Application.Run(mainForm);
}
}
static void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
mainForm.toolStripProgressBar1.Increment(1);
UnitInformation.SetupUnitInformation();
mainForm.toolStripProgressBar1.Increment(1);
Aspects.Unit.HddInfo.GetHddInfo();
mainForm.toolStripProgressBar1.Increment(1);
for (int i = 0; i < mainForm.Controls.Count; i++)
{
if (mainForm.Controls[i] is AbstractSuperPanel)
{
try
{
var startMe = mainForm.Controls[i] as AbstractSuperPanel;
startMe.StartWorking();
mainForm.toolStripProgressBar1.Increment(1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + mainForm.Controls[i].ToString());
}
}
}
mainForm.toolStripProgressBar1.Value = 0;
}
to sum up what that does is is I add a idle listener event. Once the thead goes idle (basically meaning that Mainform is finished drawing and making all 12 panels and is showing on my desktop) I then kill the idle event listener and tell all my panels and classes to start working one at a time, updating my progress bar as I go. It works great. The load time is still the same as it was before, but there is window visibile after only a few seconds. Maybe not the best use of resources, but i think the solution is simple and straight forward.
I had a question somewhat related to this for Mobile app development a few months back (see How to write a Trigger?), and Marc "the man" Gravell posted back with a simple class that I modified to return data to my main application whenever the thread was complete.
The actual class I put into use has loads of pointless data (for you), so I'm going to paste in a revised version of Mr. Gravell's code using techniques which I used to make them work:
First, I had to create my own EventArgs class:
public class SuperEventArgs : EventArgs {
private object data;
public SuperEventArgs(object data) : base() {
this.data = data;
}
public object Data { get { return data; } }
}
Using that, here is a class I created to pass my data back to the main thread:
public delegate event DataChangedHandler(object sender, SuperEventArgs e);
public class Simple1 {
private object parameter1, parameter2;
private Control parent;
#if PocketPC
public delegate void MethodInvoker(); // include this if it is not defined
#endif
public Simple1(Control frmControl, object param1, object param2) {
parent = frmControl;
parameter1 = param1;
parameter2 = param2;
}
public event DataChangedHandler DataChanged;
public void Start() {
object myData = new object(); // whatever this is. DataTable?
try {
// long routine code goes here
} finally {
if (DataChanged != null) {
SuperEventArgs e = new SuperEventArgs(myData);
MethodInvoker methInvoker = delegate {
DataChanged(this, e);
};
try {
parent.BeginInvoke(methInvoker);
} catch (Exception err) {
Log(err); // something you'd write
}
}
}
}
}
Back in the actual main thread of execution, you'd do something like this:
public partial class Form1 : Form {
private Simple1 simple;
public Form1() {
object query = new object(); // something you want to pass in
simple = new Simple1(this, query, DateTime.Now);
simple.DataChanged += new DataChangedHandler(simple1_DataChanged);
Thread thread = new Thread(simpleStart);
thread.Start();
}
private void simpleStart() {
if (simple != null) {
simple.Start();
}
}
private void simple1_DataChanged(object sender, SuperEventArgs e) {
MyFancyData fancy = e.Data as MyFancyData;
if (fancy != null) {
// populate your form with the data you received.
}
}
}
I know it looks long, but it works really well!
This is not anything I have actually tested, of course, because there isn't any data. If you get to working with it and you experience any issues, let me know and I'll happily help you work through them.
~JoeP

C#, Can I check on a lock without trying to acquire it?

I have a lock in my c# web app that prevents users from running the update script once it has started.
I was thinking I would put a notification in my master page to let the user know that the data isn't all there yet.
Currently I do my locking like so.
protected void butRefreshData_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(UpdateDatabase));
t.Start(this);
//sleep for a bit to ensure that javascript has a chance to get rendered
Thread.Sleep(100);
}
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
Updater.RepopulateDatabase();
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, AlreadyLockedJavaScript);
}
}
And I do not want to do
if(Monitor.TryEnter(myLock))
Monitor.Exit(myLock);
else
//show processing labal
As I imagine there is a slight possibility that it might display the notification when it isn't actually running.
Is there an alternative I can use?
Edit:
Hi Everyone, thanks a lot for your suggestions! Unfortunately I couldn't quite get them to work...
However I combined the ideas on 2 answers and came up with my own solution. It seems to be working so far but I have to wait for the process to complete...
Ok this seems to be working, I broke out the Repopule Method into it's own class.
public static class DataPopulation
{
public static bool IsUpdating = false;
private static string myLock = "My Lock";
private static string LockMessage = #"Sorry, the data repopulation process is already running and cannot be stopped. Please try again later. If the graphs are not slowly filling with data please contact your IT support specialist.";
private static string LockJavaScript = #"alert('" + LockMessage + #"');";
public static void Repopulate(object con)
{
if (Monitor.TryEnter(myLock))
{
IsUpdating = true;
MyProjectRepopulate.MyProjectRepopulate.RepopulateDatabase();
IsUpdating = false;
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, LockJavaScript);
}
}
}
In master I do
protected void Page_Load(object sender, EventArgs e)
{
if (DataPopulation.IsUpdating)
lblRefresh.Visible = true;
else
lblRefresh.Visible = false;
}
(Given that you are aware of the race condition for displaying this notification just after processing stopped.... )
You could switch to a CountdownEvent. This works similarly to a ManualResetEvent, but also provides CurrentCount and IsSet properies, which could be used to determine if something is being processed.
How about just setting a volaltile bool property somewhere that indicates an active lock, perhaps via callback method?
Explore Autoresetevents and ManualResetevents. You can have the spawned thread set the event and check the event in the main thread to display the message.
butRefreshData_Click()
{
lock(myLock)
{
if (isbusy) {/*tell user*/}
}
}
UpdateDatabase(object con)
{
lock(myLock)
{
if (isbusy) {/*tell user*/ return;}
else {isbusy = true;}
}
Updater.RepopulateDatabase();
lock(myLock)
{
isBusy = false;
}
}
Note: You should probably wrap UpdateDatabase in a try-finally to avoid isBusy from being stuck true if an exception is thrown.
As I imagine there is a slight
possibility that it might display the
notification when it isn't actually
running.
There will always be the possibility you send the "Working..." message and then immediately the job is finished. What you have should logically work.
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
System.Diagnostics.Debug.WriteLine("Doing the work");
Thread.Sleep(5000);
Monitor.Exit(myLock);
System.Diagnostics.Debug.WriteLine("Done doing the work");
}
else
{
System.Diagnostics.Debug.WriteLine("Entrance was blocked");
}
}

Categories