|
When you execute a lengthy operation (any computation exceeding say 50 msec, but also anything that might occasionally be slow, e.g. an internet access) in one of the GUI event handlers (e.g. a button's Click), it will block the main=GUI thread, as a result your GUI freezes.
There are a number of bad attempts to fix this. The good ones all involve another thread, but then, beware, other threads are not allowed to touch your GUI Controls. The recommended approach is by using one BackGroundWorker; it will perform the bulk of the job on a separate thread, yet it provides a progress reporting event which automatically does run on the main=GUI thread, so that is by far the number one way to keep your GUI alive and responsive.
When the BGW finishes up, it fires a Completion event which typically is used to report all mishaps that may have occurred (make sure to handle its Exception which unfortunately there is called Error), it could also be used to e.g. re-enable a Button; that event handler also runs on the main=GUI thread, again no worries.
If unfamiliar with BGW, look for (a) the documentation and (b) a good example, and study these first. It may seem complex, but it isn't; it is the easiest way into multi-threading, something most applications are bound to take advantage of.
Luc Pattyn [My Articles]
If you can't find it on YouTube try TikTok...
|
|
|
|
|
I have a C# console application that is launched from a WinForm application.
The output of the console application is well-formatted; however, user can run the console application directly. Is there a way to restrict/force the console application to be only run from the main application (WinForm)?
|
|
|
|
|
Pass a mystery parm to the console app that only the Windows Form knows about. No parm, no run.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
You can easily get the command line parameter from Task Manager, so the passed value should be changed every time it needs to be launched. I was thinking the parameter could be randomized or something else, then verified over a named pipe, or other IPC, with the WinForms app.
|
|
|
|
|
I was counting on the "user" being "typical" ... Task Manager? Wazzat?
This looked like an "inhouse" solution ... Anybody trying to bypass this simple solution is obviously asking for trouble.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
The thing about your "typcial" user is that some of them stumble across how to do things they shouldn't normally know how to do.
NEVER assume users stay uninformed. Security is only secure in an environment that never changes or is touched from the outside, and that's never the case.
|
|
|
|
|
There's a difference between "accidently" running a console app versus defeating a method intended to prevent "accidents".
Yes, there is a chance someone will type: ConsoleApp.exe supercalifragilisticexpialidocious.
There is also such a thing as overkill based on what the console app actually does.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
Oh I get it, I've been here before. I speak from experience I cannot talk about.
|
|
|
|
|
One size does not fit all.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
There's no direct way to stop the console app from being launched, no.
Applications always run as the user account that launches them. So if the user launches your WinForms app, it's running as the user, or it inherits the users security token. Any other processes your WinForms app launches inherits the environment and security token of the app that launched it.
You cannot prevent the user from running the console app. This would prevent the WinForms app from also launching it.
So, you have to get creative. You could have the console app look for a mutex the WinForms app creates, or use some interprocess communication channel between the WinForms app and the Console app, so that there is at least a mechanism for the WinForms app to say something like "yes, it's OK for you to run".
|
|
|
|
|
Just to add to what the others have said, If it's never to be run by the user, why create a console app at all?
Why not just run it as a separate thread within the existing WinForms process?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
May I suggest a possible answer?
The shell actually performs some quite useful tasks. If you make extensive use of shell facilities, it may take quite a few code lines to duplicate the functionality in your WinForms code.
|
|
|
|
|
|
As OriginalGriff suggested, you should probably just run it as a thread in the process. But if you insist launching a console app, have your WinForms app create shared memory that your console app will look for when launched. If not found, terminate the console app.
|
|
|
|
|
I have 5 column in a spreadsheet (DevExpress spreadsheet). The first four columns are string values. The fifth column is integer (numeric). I joined those first four columns and put it as KEYs in a list, then collected the fifth column entries as VALUEs in an another list. I used these two lists to create a dictionary.
Now, I want to sum values related to each duplicate keys. As you now, dictionary object throw an exception for duplicate keys. How can I sum those values related to each duplicate keys?
I tried this:
IWorkbook workbook = spreadsheetControl.Document;
Worksheet worksheet = workbook.Worksheets.ActiveWorksheet;
CellRange range = worksheet.GetDataRange();
int LastRow = range.BottomRowIndex;
var keys = new List<string>();
var values = new List<int>();
for (int i = 0; i < LastRow + 1; i++)
{
keys.Add(string.Join(",", worksheet.Cells[i, 0].DisplayText, worksheet.Cells[i, 1].DisplayText,
worksheet.Cells[i, 2].DisplayText, worksheet.Cells[i, 3].DisplayText));
values.Add((int)worksheet.Cells[i, 4].Value.NumericValue);
}
var mydic = new Dictionary<string, int>();
for (int i = 0; i < keys.Count; i++)
{
try
{
mydic.Add(keys[i], values[i]);
}
catch (Exception)
{
if (mydic.ContainsKey("f,a,r,d") == true)
{
MessageBox.Show("Duplicate");
}
}
}
I need to replace
if (mydic.ContainsKey("f,a,r,d") == true)
{
MessageBox.Show("Duplicate");
} with another code.
modified 2-Feb-21 12:48pm.
|
|
|
|
|
Ummm...why don't you check to see if the key already exists and if it does, just add the value you have to the value that's already in the dictionary?
if (keys.ContainsKey(key))
{
keys[key] += value;
}
else
{
keys.Add(key, value);
}
Easy. No exception handlers needed, nor goofy ass other code to handle a duplicate key.
|
|
|
|
|
"keys" object is an instance of list class. It doesn't support "ContainsKey" method.
|
|
|
|
|
OK, why are you making a list of keys?
Why are you making a list of values?
You mention you're using a Dictionary, so why the lists when the dictionary can do everything you're taking about so far.
|
|
|
|
|
I used the basics of your code and found a solution:
for (int i = 0; i < keys.Count; i++)
{
if (mydic.ContainsKey(keys[i]))
{
mydic[keys[i]] += values[i];
}
else
{
mydic.Add(keys[i], values[i]);
}
}
|
|
|
|
|
Hi I have a linq Query as below
Query A:
from a in violations
join b in programs on a.ViolationTypeId equals b.ViolationTypeId
join d in branches on b.BranchId equals d.BranchId
join e in subprograms on a.ViolationSubtypeId equals e.ViolationTypeId into es
from e in es.DefaultIfEmpty()
join f in nonUniquePenalties on a.ViolationSubtypeId equals f.ViolationTypeId && f.ParentViolationTypeId==a.ViolationTypeId
where a.ApplyUniquePenalty == true
And I have an another Collection with name: nonUniquePenalties, which has ViolationTypeId and ParentViolationTypeId.
the problem that I have is: violations has two fields, ViolationSubtypeId which can be null and ViolationTypeId can not null.
Now I want to left join nonUniquePenalties such a way that, if violations doesn't have ViolationSubtypeId , it has to check only for ViolationTypeId of violations and ParentViolationTypeId of nonUniquePenalties, if violations has ViolationSubtypeId and ViolationTypeId, linq has to check for
ViolationSubtypeId of violations with ViolationTypeId and ViolationTypeId of violations with ParentViolationTypeId of nonUniquePenalties.
Any help please? And it has to be left outer join with other collections, since if there is no values in nonUniquePenalties, still the lnq should yield records.
I tried a little bit, but any help would be very helpful, thanks in advance.
|
|
|
|
|
I have a number of devices with Com ports (UART:s running at MBaud speeds) that I need to monitor and make sense of. The devices send ASCII-strings with information about what's going on and I need to log this information on the screen row-by-row as the strings arrive (one row per port_DataReceived event). I would also like to get a timestamp when each string arrives and log the timestamp in the leftmost part of the row and the time elapsed since previous row just to the right of the timestamp. The following requirements apply:
a) The GUI must have a checkbox with the text "Auto-scroll". When unchecked, I should be able to scroll among the already written rows.
b) The GUI must have a "Clear Log Window" button, which clears the log the window when pressed, even if data logging is ongoing (of course, the very next microsecond after the button is pressed, new data has arrived and will be put in the first row again).
c) The bulk of the code must be written in C# (with no WPF). However, simple event handlers, such as button event and checkbox-changed handlers, with very little code inside them are ok in other langugages as well.
d) Must show the data in columns that can easily be read, either in a table or something supporting equal width characters (e.g. Courier New font).
e) If it's not Excel, then it should easy to copy-and-paste into Excel for further in-depth analysis.
f) The timestamp should be as accurate as possible, i.e. I want to exit the interrupt routines asap in order to not block interrupts from other Com port.
g) Must be able to show text in different colors.
h) Must be able to show more than 65535 characters.
My questions are:
A. What queuing technique should I use to ensure that I stay as little times as possible in the port_DataReceived(object sender, SerialDataReceivedEventArgs e) event handler? Can I use a simple backgroundWorker (Threading in C# - Part 3 - Using Threads[^], basically queuing addRowToLogWindow-tasks) or is there a faster/better approach?
B. What GUI component should I use to display the data in? I've considered the following:
1. Directly in the Visual Studio Output window: I don't know how to implement the "Auto-scroll" checkbox and "Clear Log Window" button. Also, there seems to come other unwanted data in this window as well (exceptions, threads are terminated, etc).
2. In a console window (cmd.exe): I don't know how to implement the "Auto-scroll" checkbox and "Clear Log Window" button.
3. A multiline TextBox: I don't know how to support equal character width and text in different color.
4. Self-containing Excel: I'm under heavy time constraints and don't have time to learn VBA.
5. Excel controlled from a C# application: I don't know how to make the VBA-event handlers for the "Auto-scroll" checkbox and "Clear Log Window" button to signal back those event to the C# application. Also, I foresee problems how to implement the addRowToLogWindow, how does the C# application know which row is the last row (I don't think I can just append a row in the end, like a multiline TextBox or a DataGridView support). If auto-scrolling is turned off, the user could possibly have deleted a few rows, so a lastAppendedRow-variable in the C# application wouldn't work.
6. DataGridView: My experience is that they are slow and I would need to implement my own copy-to-clipboard function (but it shouldn't be very difficult).
7. RichTextBox: This seems to be the winner, unless I've overlooked something.
Does anybody have any comments/suggestions?
|
|
|
|
|
1. Since your application will not be running in Visual Studio, that is not an option.
2. Console applications do not lend themselves well to this type of problem.
3. Does not satisfy your requirements.
4. Same as 3.
5. More difficult than .NET's built in controls.
6. The best choice, but it does need to be managed properly. DatagridViews become slow if you overload them with too much data; you need to implement efficient paging.
7. Not the best option, as it is used for pure text, rather than tabulated data that you are looking for.
|
|
|
|
|
Richard MacCutchan wrote: 7. Not the best option, as it is used for pure text, rather than tabulated data that you are looking for.
No, but it can be easily solved by setting the font to Courier New and inserting tabs.
|
|
|
|
|
Well I was just giving my opinion. What you go with is entirely your choice.
|
|
|
|
|
First off, don't assume that a DataReceived event will contain a full message: COM ports aren't packet based - they are character based, and a DataReceived event will fire after each character arrives if necessary.
What you need to do is read each COM port continuously in a separate thread and pass complete messages back to your UI thread for processing / logging / display. The simplest way to handle that would be to use a BackgroundWorker as they have two advantages in this scenario:
1) They provide an easy "status update" mechanism which allows you to ignore Invoke requirements and just pass a whole message back to your UI for display. In a Console app, that means that one task - teh original is responsible for queueing messages for the display, which is good - multiple tasks woudl mean that messages could easily get mixed together instead of separated by lines or similar.
2) They are automatically terminated when the app is: this is not the case with all thread types. This means your in-thread processing can be a lot cleaner as it doesn't have to check for termination at all.
Auto scroll in a console - that's slightly complicated because it's all manual stuff: you'd need to buffer stuff up for display until the "autoscroll hold" is released, and then output the buffer in one long splurt. For a textbox, it's a similar procedure - add items with TextBox.AppendText and it will autoscroll to the end.
Clearing the console is simple: Console.Clear Method (System) | Microsoft Docs[^]
A DGV can be set to use any font you like, just like any other control. I wouldn;t faff with embedding Excel unless you really, really have to!
DGV's aren't slow unless you mistreat them: any control with a lot of data will be sluggish but this may help: Walkthrough: Implement virtual mode in DataGridView control - Windows Forms .NET Framework | Microsoft Docs[^]
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|