'System.OutOfMemoryException' : Error creating window handle - c#

I'm coding a medium winforms application... every now and then on creation of certain Forms i get this exception.
I resolved it in the past but without proper udnerstanding of what is going on...
The puzzling part is that everything worked fine yesterday when I finnished this form and tested it, now I get this:
An unhandled exception of type 'System.OutOfMemoryException' occurred
in System.Windows.Forms.dll
Additional information: Error creating window handle.
Exception is thrown in this block of code:
public partial class PonovniIspisRacunaForm : Form
{
DataTable dnevniPrometTable;
DataTable kontrolnaTrakaTable;
DataTable ukupniDnevniPrometTable;
DataTable stavkeRacunaTable;
ZisCode.Hibernate.Mdb.DataModel dnevniPrometDataModel;
ZisCode.Hibernate.Mdb.DataModel kontrolnaTrakaDataModel;
OsnovniPodaci.Porezi.Stope stope;
string brojRacuna;
string ZKI;
string JIR;
string Operator;
//decimal ukupno = 0.00m;
decimal tarifa;
decimal kolicina;
decimal iznos;
decimal porez;
decimal porez25;
decimal porez05;
decimal porez13;
decimal povratnaUkupno;
decimal osnov25;
decimal osnov05;
decimal osnov13;
//decimal nabavna;
PrintDocument printDocument;
public PonovniIspisRacunaForm()
{
InitializeComponent();
}
private void FinancijskiRekapitular_Load(object sender, EventArgs e)
{
stope = new OsnovniPodaci.Porezi.Stope();
// popunjava današnji datum
this.dtpDatum.Value = DateTime.Today;
// Get Data Table za određeni datum:
dnevniPrometDataModel = ZisCode.DataModels.Get("DnevniPromet");
kontrolnaTrakaDataModel = ZisCode.DataModels.Get("KontrolnaTraka");
PrintSetup();
ukupniDnevniPrometTable = MergeDnevniKontrolna();
if (ukupniDnevniPrometTable.Rows.Count != 0)
{
FillComboBox();
}
}
private void FillComboBox()
{
cbBrojRacuna.DataSource = ZisCode.Methods.DataTableOperations.SortDataTable(ukupniDnevniPrometTable, "Dokument", "DESC") // orderbydescending
.AsEnumerable().GroupBy(row => row.Field<string>("Dokument")).Select(group => group.First()).CopyToDataTable(); // groupby brojRacuna-Dokument
cbBrojRacuna.DisplayMember = "Dokument"; // Broj Računa
cbBrojRacuna.ValueMember = "Dokument";
cbBrojRacuna.SelectedIndex = 0;
}
private void PrintSetup()
{
// priprema za ispis
printDocument = new PrintDocument();
printDocument.DefaultPageSettings.PaperSize = new PaperSize("Racun size", 295, 500);
printDocument.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(CreateRekapitular);
// uklanja poruku dialog box (printing ..) kod ispisa
PrintController printController = new StandardPrintController();
printDocument.PrintController = printController;
// popravlja font
printPreview.UseAntiAlias = true;
}
}
to be more specific this line: this.dtpDatum.Value = DateTime.Today;
Why?? I have no idea...
I tried seting dateTimePicker in constructor and I got different exception unrelated to window handle.
I've read about this case on other questions but nothing really clicked for me.
Winforms issue - Error creating window handle
Error Creating Window Handle
These two answers don't help me since answers are to ambiguous and expect me to know how dispose is supposed to work in first place. Help, I'm a n00b!
UPDATE Posted Almost all code of the class as I was asked

A window handle is an unmanaged resource. There are only so many of them. If you don't free those handles, Windows will run out and you will get exceptions.
Classes that use native HANDLEs will need to free them when they are no longer needed. Those classes will implement IDisposable. You should look into this, it's a core part of .NET and crucial to working with unmanaged resources like handles.
Turn on your static code analysis (Right click on Project -> Properties -> Code Analysis and select Enable on build and pick Microsoft all rules) and fix all violations, especially those non-disposed IDisposables.

Since your exception is OutOfMemory it may be that you are just out of memory. Check task manager to see how much memory you have available and I would suggest to run your app through some memory profiler like RedGate or something similar to see where the leak is. In case of dispose take a look at this asnwer for more info C# Form.Close vs Form.Dispose

The problem was private void
dtpDatum_TextChanged(object sender, EventArgs e)
{
ukupniDnevniPrometTable = MergeDnevniKontrolna();
FillComboBox();
}
I guess this event was fired up too many times and caused memory leak

Related

The application called an interface that was Marshalled for different thread error when using MangementObjectSearcher

I have used MouseKeyboardActivityMonitor to set some limitations for user activities for example disable mouse.
www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C
I have these code in my form
public partial class MyForm:Form
{
KeyboardHookListener kl;
MouseHookListener ml;
MyForm:Form()
{
ml = new MouseHookListener( new GlobalHooker());
ml.Enabled=true;
}
private void MyForm_Load
{
ml.MouseDownExt += ml_MouseDownExt;
// And same thing for Click or ...
}
private void ml_MouseDownExt( object sender,MouseEventExtArgs e)
{
e.Handled= true;
// I have got hard disk serial number here
string sn = HardDisk.Serial;
}
}
And code of HardDisk.Serial
ManagementObjectSearcher s= new ManagementObjectSearcher(" SELECT *...");
foreach( var wmi in s.Get())
{
}
I get error when I click on MyForm .
When I built my solution and run it manually
I get this error
The application called an interface that was Marshalled for different thread
Stack:
at system.management.MangementException.ThrowWithExtendedInfo( Exception e)
at system.management.MangementObjectSearcher.Get()
at HardDisk.Get_serial()
at ml_MouseDownExt( object sender,MouseEventExtArgs e)
at MouseKeyboardActivityMonitor.MouseHookListener.InvokeMouseEventHandlerExt(EventHandler'1 handler,MouseEventExtArgs e)
But when I run my solution with visual studio an exception will throw at HardDisk.serial
at s.Get line , I get this
Error:
Managed debugging assistant ' DisconnectedContext'
Has detected a problem in 'my app Name.exe'
Transition into com context 0xa4206 for this runtime callable wrapper failed with following error :
an outgoing call can't be made since the application is dispatching an input asynchronous call
It obvious that two error is from MangementObjectSearcher class.I get serial number in another place in MyForm .errors just occurres when get serial in ml_MouseDownExt method or other method that has been added to events of GlobalHooker .I have seen msdn. In inheritance hierarchy of MangementObjectSearcher I see System.MarshalByRefObject
https://msdn.microsoft.com/en-us/library/system.management.managementobjectsearcher(v=vs.110).aspx
I don't know that it is related to these errors or not
How should I avoid these errors?
Your hook callback isn't being raised on the right thread. And that's just the first problem. Wrap it in a BeginInvoke and all will be well:
private void ml_MouseDownExt( object sender,MouseEventExtArgs e)
{
e.Handled= true;
var wrongThread = new Action(()=>
{
// I have got hard disk serial number here
string sn = HardDisk.Serial;
//put anything else you were planning on doing with sn here
}
BeginInvoke(wrongThread, null);
}
The second problem is that you're trying to interact with a COM object in the handler for a global hook. BeginInvoke should get around that nicely by delaying it for a few microseconds.
Don't forget to make sure Dispose gets called on that global hook. Closing the app isn't enough unless you like rebooting often.

How can I prevent a DLL from causing problems when it is used more than once?

As Peter Duniho points out in a comment here, I was fixated on a red herring when I should have been focusing on something else altogether.
When I use Symbol.Barcode.Reader and Symbol.Barcode.ReaderData in one form, they work fine. I use them as I document here.
However, when I go from one form that uses the barcode scanning code to another one that also does, all Dallas breaks loose. I get the following exception in the second form, on startup:
Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
at Symbol.Barcode.Actions.Enable()
at HHS.frmFind.InitReader()
at HHS.frmFind.textBoxScan_GotFocus(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnGotFocus(EventArgs e)
at System.Windows.Forms.Control.WnProc(WM wm, Int32 wParam, Int32 lParam)
at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
at Microsoft.AGL.Forms.WL.SetVis(IntPtr hwnThis, BOOL fVis)
at System.Windows.Forms.Control.set_Visible(Boolean value)
at System.Windows.Forms.Form.ShowDialog()
The barcode scanning code between the two forms is identical, so it's not the code itself (it works fine the first time, in the first form).
The exception occurs immediately in the second form when the textbox that is set up for scanning is entered. It's GotFocus() event fires because the textbox gets focus when the form is displayed; OnGotFocus() calls InitReader(), which then fails. InitReader() is:
private bool InitReader()
{
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
// If reader is already present then retreat
if (this.barcodeReader != null)
{
return false;
}
// Create new reader, first available reader will be used.
this.barcodeReader = new Symbol.Barcode.Reader();
// Create reader data
this.barcodeReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Create event handler delegate
this.barcodeEventHandler = this.BarcodeReader_ReadNotify;
// Enable reader, with wait cursor
this.barcodeReader.Actions.Enable();
this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";
// Attach to activate and deactivate events
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
return true;
}
The objects being dealt with there are in Symbol.Barcode.dll, from Motorola. They are declared in the form like so:
private Symbol.Barcode.Reader barcodeReader;
private Symbol.Barcode.ReaderData barcodeReaderData;
If I bypass the first form, which has the same type of barcode scannig code, and go straight to this form, it doesn't crash.
So apparently it's that the closely related bits of code can't coexist. Why not and, more importantly, how can I prevent this revolting development?
UPDATE
With this logging added to InitReader:
private bool InitReader()
{
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
// If reader is already present then retreat
if (this.barcodeReader != null)
{
return false;
}
// Create new reader, first available reader will be used.
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #2");
this.barcodeReader = new Symbol.Barcode.Reader();
// Create reader data
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #3");
this.barcodeReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Create event handler delegate
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #4");
this.barcodeEventHandler = this.BarcodeReader_ReadNotify;
// Enable reader, with wait cursor
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #5");
this.barcodeReader.Actions.Enable();
this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";
// Attach to activate and deactivate events
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #6");
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #7");
return true;
}
...I see this in the log file after opening the Find form and crashing the app (it hangs for about 20 seconds before disappearing):
Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.InitReader
Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.I
...so it's crashing almost instantaneously after this block of code:
if (this.barcodeReader != null)
{
return false;
}
...as it only gets through half of the next logging line before rudely interrupting itself.
UPDATE 2
Fejesjoco may still be right (in his comment below), but I submit this logging code as exhibit "A":
public void WriteLog(string message)
{
if (!HHSConsts.Logging) return;
StringBuilder formattedMessage = new StringBuilder();
formattedMessage.AppendLine("Date: " + DateTime.Now.ToString());
formattedMessage.AppendLine("Message: " + message);
_streamWriter.WriteLine(formattedMessage.ToString());
_streamWriter.Flush();
}
The stream should get flushed after each line is written to the log.
UPDATE 3
The failure is always very consistent as to how long it "hangs" before it crashes; I can see the cursor blinking in the bar code text box of the find form, and it pulsates about 20 times (I actually counted it last time: 24).
This also is a bit odd; in Update 2, I showed the contents of the log file with all the log entries sprinkled into the InitReader method. With those commented out (except for the first one), the log file ends with:
Date: 3/20/2009 12:01:22 AM
Message: Reached frmFind.InitReader
Date: 3/20/2009 12:01:22 AM
Message: From application-wide exception handler: Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
at Symbol.Barcode.Actions.Enable()
at HHS.frmFind.InitReader()
...so the additional log file entries were preventing the exception msg from getting logged.
UPDATE 4
Actions.Enable?
I was unfamiliar with this, and when I added "Actions." I got a choice between Symbol.Barcode.Actions and Symbol.Generic.Actions.
I chose the first first, but it has no "Enable" method. Adding it scolded me with, "An object reference is required for the non-static field, method, or property 'Symbol.Generic.Actions.Enable()'"
I then commented out the using that was added, entered "Actions." again, and this time chose Symbol.Generic.Actions (and got the same err msg for my troubles).
How can I use Actions.Enable()?
UPDATE 5
C.Evenhuis: by "some events require re-attaching each time they occur" do you mean this:
private void StartRead()
{
// If we have both a reader and a reader data
if ((this.barcodeReader != null) && (this.barcodeReaderData != null))
{
// Submit a read
this.barcodeReader.ReadNotify += this.barcodeEventHandler;
this.barcodeReader.Actions.Read(this.barcodeReaderData);
}
}
private void StopRead()
{
// If we have a reader
if (this.barcodeReader != null)
{
// Flush (Cancel all pending reads)
this.barcodeReader.ReadNotify -= this.barcodeEventHandler;
this.barcodeReader.Actions.Flush();
}
}
...(barcodeReader.ReadNotify is attached in StartRead and detached in StopRead), or is more necessary?
I also have, in InitReader():
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
...which are implemented like so:
private void ReaderForm_Activated(object sender, EventArgs e)
{
// If there are no reads pending on barcodeReader start a new read
if (!this.barcodeReaderData.IsPending)
{
this.StartRead();
}
}
private void ReaderForm_Deactivate(object sender, EventArgs e)
{
this.StopRead();
}
InitReader() is called from textBoxScan_GotFocus(); textBoxScan has the focus when the form displays.
UPDATE 6
As to "Explicitly Close() classes before you Dispose() them", I call Dispose on two things, Symbol.Barcode.Reader and Symbol.Barcode.ReaderData, and neither one allows a Close() call.
UPDATE 7
This statement from C. Evenhuis: "you can't have two (foreground) readers enabled" led me to try the following:
private void FrmDelivery_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
this.barcodeReader.Actions.Disable();
}
...which seems to have pretty much done the trick - I can open the Find form without a hang resulting in an eventual crash. I can scan into the Find form after having scanned into the Delivery form. The only (unrelated?) problem that I still see is that my Find form is still partially obscured for some reason...
Symbol Motorola Zebra is building on a legacy C++ library with a legacy .NET wrapper. There are quite some pitfalls (some events require re-attaching each time they occur, some classes require explicit Close() before you Dispose() them, etc).
As you may have found out, you can invoke members of the Symbol classes from multiple threads / forms, but you can't have two (foreground) readers enabled, and some properties can only be set if no background readers are enabled, and there is no way to determine whether there are background readers enabled (ie DataWedge).
At our company we chose to initialize the scanner in the Form.Activated event and deinitialize it in the Deactivated event. Then when scanning was required, a call to Actions.Enable() would do the trick. Setting reader properties is done in try catch blocks :(
Quite odd that your application crashes at GetInterfaceParams, I would expect Setting them to cause trouble. Try your application with the bare minimum; don't set the BeepTime or WaveFile properties.
I'm not familiar with this type of scanner, but I can imagine initreader will do something at a hardware level. It will probably try to open some port. Because the port is already open it fails.
You need to implement a singleton service for your scanner instead of addressing it directly in your form. Then you can initialize the reader once for the complete application and use it wherever you'd like.
This will be a lot more 'SOLID' and thus more maintainable.

C# Continuously / uninterrupted reading data (from third party SDK)

Intro:
I am developing software that uses motion trackers to analyse human motor systems. Currently I am implementing hardware from xsens and using their SDK to receive data from their wireless sensors.
The SDK offers a COM interface with a "getData" method which you call to receive the currently available xyz axis data (simplified). If you do not call getData, you skip that "beat" so you will be missing data, there is no caching in their hardware/SDK.
Problem:
My problem is that I need to get data at a rate of at least 75Hz, preferably a bit more, but 75 would be acceptable, but I am currently quickly dropping to just 20 signals per second...
If I remove the processing bit (see the sample below) I get perfect sample rates, so I think either the dequeue is causing the enqueue to pause. Or the "heavy" CPU load is causing all threads to wait. I have no idea how to figure out what is actually causing it, the profiler (EQATEC) just shows my "GetData" method is taking longer after a while.
Question:
What is the best technique to use to accomplish this? Why would my "reading" thread be interrupted/blocked? There must be more cases where people need to read from something without being interrupted, but I have been Googleing for 2 weeks now and apparently I can't find the correct words.
Please advise.
Thanks
Simplified code sample, version 4, using a MultiMedia timer (http://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework) and a BackgroundWorker
public class Sample
{
private MultiMediaTimer _backgroundGetData;
private bool _backgroundGettingData;
private BackgroundWorker _backgroundProcessData;
private ConcurrentQueue<double> _acceleration = new ConcurrentQueue<double>();
private void StartProcess()
{
if (_backgroundGetData == null)
{
_backgroundGetData = new MultiMediaTimer {Period = 10, Resolution = 1, Mode = TimerMode.Periodic, SynchronizingObject = this};
_backgroundGetData.Tick += BackgroundGetDataOnTick;
}
_backgroundProcessData = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = true};
_backgroundProcessData.DoWork += BackgroundProcessDataOnDoWork;
_backgroundGetData.Start();
}
private void BackgroundProcessDataOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
double value;
if (!_acceleration.TryDequeue(out value)) value = 0;
//Do a lot of work with the values collected so far,
//this will take some time and I suspect it's the cause of the delays?
}
private void BackgroundGetDataOnTick(object sender, EventArgs eventArgs)
{
if (_backgroundGettingData) return;
_backgroundGettingData = true;
//123 represents a value I am reading from the sensors using the SDK
double value = 123;
if (value == -1)
{
Thread.Sleep(5);
continue;
}
_acceleration.Enqueue(value);
if (_acceleration.Count < 5) continue;
if (!_backgroundProcessData.IsBusy)
{
_backgroundProcessData.RunWorkerAsync();
}
_backgroundGettingData = false;
}
}
I am seeing the problem here
_backgroundProcessDataThread.Start();
while (!_backgroundProcessDataThread.IsAlive){}
_backgroundGetDataThread.Start();
while (!_backgroundGetDataThread.IsAlive) {}
Well, you can see here that you are having infinite loop here and the second thread starts only after first has finished its work. i.e. first thread is done. This is in no way an ideal model.
Sorry, I recognized the issue later.
The problem is, _backgroundGetDataThread will start only after _backgroundProcessDataThread has done its work.

Multithreading with textbox control [duplicate]

This question already has answers here:
Multithreading in C# with Win.Forms control
(2 answers)
Closed 9 years ago.
I'm beginner in C#. And I don't understand why next two examples are giving different results. I'm using microsoft example in msdn. In first example it displays one number in the textbox. In second example it displays all numbers from 0 to 1000 for each thread.
First example:
delegate void SetTextCallback(object text);
private void WriteString(object text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(WriteString);
this.BeginInvoke(d, new object[] { text });
}
else
{
for (int i = 0; i <= 1000; i++)
{
textBox1.Text = text.ToString();
}
}
}
Second example:
private void MyApp_Load(object sender, EventArgs e)
{
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
}
private void WriteString(object text)
{
for (int i = 0; i <= 1000; i++)
{
textBox1.Text = text.ToString();
}
}
And method which calls these examples
private void button1_Click(object sender, EventArgs e)
{
Thread th_1 = new Thread(WriteString);
Thread th_2 = new Thread(WriteString);
Thread th_3 = new Thread(WriteString);
Thread th_4 = new Thread(WriteString);
th_1.Priority = ThreadPriority.Highest;
th_2.Priority = ThreadPriority.BelowNormal;
th_3.Priority = ThreadPriority.Normal;
th_4.Priority = ThreadPriority.Lowest;
th_1.Start("1");
th_2.Start("2");
th_3.Start("3");
th_4.Start("4");
th_1.Join();
th_2.Join();
th_3.Join();
th_4.Join();
}
Well I analyzed in my VS. I am also new to C#. But what I could infer is the following:
Program 1:
Begin Invoke is aynschronous way of calling a method. Thus it shows only one result at the end. If you have slowly did F11 in VS and observe, not really everytime you get the result 4. But sometime you get 3 too when you do F11 and go step by step at certain places(I mean delaying), due to multi threading. You should remember that, multi threading always never behave in same manner all the time, which means suppose if an application or module in multithreading gives you one result at a time, two times, and 10 times, you cannot be sure that its the correct or optimized code. Because at client environment, due to its own behavior, it can lead to different results which may potentially be unnoticed while debugging or it wont even happen. I read it in a nice blog.
Program 2:
Since its multithreading, I could see the behavior of different threads getting invoked at different time, and as it does the job, I see the textboxes are getting updated quickly in fraction of seconds thats not possible for an human eye to notice, but final result, a single number is displayed. Also you do check for cross thread calls )When you do step into every line of code using F11, you will find this behavior and understand well ) and you order not to catch it. This makes the threads to work together in second case.
This is my inference, but I can say, pretty corny! I don't claim this with confidence, but just my observation :)
Let some great folks chime in with their views to help us :)
Cheers

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

Categories