How to fix this badly designed code - c#

I have this code:
private void timer1_Tick(object sender, EventArgs e)
{
#region BaseAddress
Process[] test = Process.GetProcessesByName(process); //Get process handle
if (test.Any())
{
int Base = test[0].MainModule.BaseAddress.ToInt32();
}
#endregion
//lots of other code blocks
}
I now want to take the region 'BaseAddress' out of the timer1_Tick control to make the code more efficient and have it run once at the beginning of the program. The other code in this control makes frequent use of the variable 'Base', what is the best way to make this globally accessible without having to go back through all the instances that use Base and do something like MyGlobals.Base, for example?

Lazy load the address into a static variable. It won't initialize until you use it for the first time, then will remain in memory for the life of the application.
public static MyGlobals
{
private static readonly Lazy<int> _processBase = new Lazy<int>(() => GetProcessBase("MyProcessName"));
// I don't recommend using the word Base, but OK...
public static int Base { get { return _processBase.Value; } }
private static int GetProcessBase(string processName)
{
int b = 0;
Process[] p = Process.GetProcessesByName(processName);
if(p != null && p.Length > 0)
{
b = p[0].MainModule.BaseAddress.ToInt32();
}
return b;
}
}
In some other part of the app...
private void timer1_Tick(object sender, EventArgs e)
{
if(MyGlobals.Base > 0)
{
// TODO: change "Base" to "MyGlobals.Base" in code below or it won't compile...
//lots of other code blocks
}
}

The method I would use in this case would be to create a singleton class ProcessFetcher (for example) with a Base property.
My class would have a fetch() function and isDataPresent property.
You can decide to call fetch manually or put it on the constructor.

Related

how to access array from one class to another class in C# winforms?

I want to access array from one class to another class because my end-user enter the name list on one class. That list store into array in the same class. Then that name list access from another class. I'm not getting any errors in compile time. only I'm getting a run time error. I'm literally sorry to all coz I'm absolutely noob :(
public partial class custom : Form //class one which is end user enter the name list
{
public string PresentValue;
public string NormalValue;
public string[] PValue = new string[50];//public array
public string[] NValue = new string[50];//public array
}
public static int PresentArray = 0;// this line is used to increment the array index
private void cstmsvbtn_Click(object sender, EventArgs e)//this line enter the user namelist
{
PresentValue = cstmtst1.Text + "_PV";//concatinate '_PV'
NormalValue = cstmtst1.Text + "_NV";//concatinate '_NV'
PValue[PresentArray] = PresentValue;
NValue[PresentArray] = NormalValue;
PresentArray++;
}
public partial class print : Form // class to which is end user want to access that name list
{
custom customarray = new custom();// I instantiate the custom cass object
private void button1_Click(object sender, EventArgs e)//when i press this button message box show an empty white box only
{
MessageBox.Show(CustomArray.PValue[0],CustomArray.NValue[0]);
}
}
This is a common requirement and there are many ways to achieve this outcome (some of which might be considered "hacky"). Things I don't recommend:
Changing visibility to public for data fields that should be private
Creating tight dependencies of one form to the implementation details of another.
Creating "global" variables using the static keyword.
Since you claim to be a "noob" I'd like to suggest learning about the event keyword and using Events to communicate between forms. Yes, there is a small learning curve here, but chances are you'll use this a lot and it will be a good investment. I put a link in the Comments section so you can clone or browse this example and see if it does what you want it to (I recommend setting debugger break points so you can see why it does what it does).
What you have (according to your post) is a print form and a custom form. And though you don't really say, this example will have a MainForm that can show the other two:
PrintForm
The PrintForm requires the NValue and PValue arrays to do its printing. By declaring an event named ArrayRequest we give it the ability to request these arrays. Importantly, this class doesn't need to have any knowledge of where this information might be coming from.
public partial class PrintForm : Form
{
public PrintForm() => InitializeComponent();
This is how the class can initiate the request
public event ArrayRequestEventHandler ArrayRequest;
protected virtual void OnArrayRequest(ArrayRequestEventArgs e)
{
ArrayRequest?.Invoke(this, e);
}
When the button is clicked, try and get the information by callingOnArrayRequest
private void buttonShowArray_Click(object sender, EventArgs e)
{
ArrayRequestEventArgs req = new ArrayRequestEventArgs();
OnArrayRequest(req);
if(req.Count == 0)
{
MessageBox.Show("Invalid Request");
}
else
{
String[] allValues =
Enumerable.Range(0, req.Count)
.Select(index => $"{req.NValue[index]} | {req.PValue[index]}")
.ToArray();
MessageBox.Show(
text: string.Join(Environment.NewLine, allValues),
caption: "All Values"
);
}
}
}
// Defined outside the PrintForm class
public delegate void ArrayRequestEventHandler(Object sender, ArrayRequestEventArgs e);
public class ArrayRequestEventArgs : EventArgs
{
public int Count { get; set; }
public string[] PValue { get; set; }
public string[] NValue { get; set; }
}
CustomForm
The CustomForm as shown in your post is the class that contains the arrays.
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
}
We give this class the ability to fulfill a request for the arrays.
internal void ArraysRequested(object sender, ArrayRequestEventArgs e)
{
e.Count = _presentArray;
e.NValue = _nValue;
e.PValue = _pValue;
}
The data held in this class should be private.
// These should all be private
// See naming conventions: https://stackoverflow.com/a/17937309/5438626
// Set up visual studio to do this automatically: https://ardalis.com/configure-visual-studio-to-name-private-fields-with-underscore/
private string _normalValue;
private string _presentValue;
private int _presentArray = 0;
private string[] _pValue = new string[50];//public array
private string[] _nValue = new string[50];//public array
private void cstmsvbtn_Click(object sender, EventArgs e)
{
_presentValue = $"{cstmtst1.Text}_PV"; //concatinate '_PV'
_normalValue = $"{cstmtst1.Text}_NV"; //concatinate '_NV'
// Make sure index doesn't exceed the size of the array
if ((_presentArray < _pValue.Length) && (_presentArray < _nValue.Length))
{
_pValue[_presentArray] = _presentValue;
_nValue[_presentArray] = _normalValue;
_presentArray++;
}
else MessageBox.Show("Array is Full");
Text = $"Custom: Count={_presentArray}";
cstmtst1.Text = $"Hello {_presentArray + 1}";
}
}
MainForm
It is the MainForm class that oversees the operations and "knows" how the forms should interact. The constuctor method is where the connection is made between the event fired by PrintForm and the fulfillment by the CustomForm.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// THIS IS THE "GLUE"
_printForm.ArrayRequest += _customForm.ArraysRequested;
}
private CustomForm _customForm = new CustomForm();
private PrintForm _printForm = new PrintForm();
// In MainForm.Designer.cs
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
_customForm.Dispose();
_printForm.Dispose();
}
base.Dispose(disposing);
}
private void buttonShowCustom_Click(object sender, EventArgs e)
{
_customForm.ShowDialog(owner: this);
}
private void buttonShowPrint_Click(object sender, EventArgs e)
{
_printForm.ShowDialog(owner: this);
}
}
You will need to adapt this to your specific requirements but hopefully this will give you some basics to go on.

C# delegate throwing an exception

I thought I had done my research and figured this out, but when I try to pass data from one form to another, the program throws an exception. I'm using a delegate to try to call a function in one form from another. Here's the code I have.
In the parent form:
private void viewListToolStripMenuItem_Click(object sender, EventArgs e)
{
frmDataView dataview = frmDataView.GetInstance();
if (dataview.Visible)
dataview.BringToFront();
else
{
dataview.GotoRecord += GotoRecord;
dataview.Show();
}
}
private void GotoRecord(int index)
{
Current.record = index;
loadRecord(index);
setNavButtons();
}
In the child form, I'm trying to call GotoRecord in the parent form with the following code:
public partial class frmDataView : Form
{
AdvancedList<ScoutingRecord> displayedData = new AdvancedList<ScoutingRecord>(Current.data);
// Set the form up so that only one instance will be available at a time.
private static frmDataView _instance;
public static frmDataView GetInstance()
{
if (_instance == null)
_instance = new frmDataView();
return _instance;
}
public delegate void GotoRecordHandler(int index);
public GotoRecordHandler GotoRecord;
private void dgvMain_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
int row = e.RowIndex;
int teamnumber = (int)dgvMain.Rows[row].Cells["TeamNumber"].Value;
int matchnumber = (int)dgvMain.Rows[row].Cells["MatchNumber"].Value;
ScoutingRecord sr = Current.data.FirstOrDefault(x => x.TeamNumber == teamnumber && x.MatchNumber == matchnumber);
//int index = Current.data.IndexOf(sr);
GotoRecord(Current.data.IndexOf(sr));
}
Whenever I run the code, it throws the following exception:
GotoRecord was null
I feel like I'm missing something simple. Any suggestions on how to get this working?
As Eugène suggested:
GotoRecord?.Invoke(Current.data.IndexOf(sr));
or if on an older version and not using other threads:
if (GotoRecord != null)
{
GotoRecord(Current.data.IndexOf(sr));
}
EDIT: Corrected mistake in call.

Correct way to handle BackGroundWorker inheritance with Action and a collection of parameters?

To clean up code that will use BackgroundWorker often and with the same settings, I created the following class as a helper:
public class MyBackGroundWorker : System.ComponentModel.BackgroundWorker
{
private Action _action;
private Action _stopAction;
public List<dynamic> Parameters { get; set; } = new List<dynamic>();
MyBackGroundWorker(Action method, Action doWhenFinished = null)
{
this._action = method;
this._stopAction = doWhenFinished;
Init();
}`
"Init()" contains all of the settings that will be applied each time:
private void Init()
{
this.WorkerReportsProgress = true;
this.WorkerSupportsCancellation = true;
this.DoWork += new DoWorkEventHandler(StartWork);
this.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkDone);
}
And the handlers and public methods are:
private void StartWork(object sender, DoWorkEventArgs e)
{
if (Parameters.Count() > 0)
{
ParameterInfo[] pi = _action.Method.GetParameters();
//now i'm stuck!!!!!
}
else
{
_action.Method.Invoke(null, this.Parameters);
//i know this is wrong, wrote it in to illustrate the goal
}
}
private void WorkDone(object sender, RunWorkerCompletedEventArgs e)
{
_stopAction.Method.Invoke(null, null); //>.<
}
public void Go()
{
RunWorkerAsync();
}
public void Stop()
{
this.CancelAsync();
}
Obviously there are some problems here. The end goal is to initialize the object with a method to start running, a method to do at the end (optional), and a parameter collection one adds to before running. A calling class might have a section like this:
using(var cmd = new MyBackGroundWorker(method1, StopLoadingSpinner))
{
cmd.Parameters.Add("Stringparam");
cmd.Parameters.Add(1);
StartLoadingSpinner();
cmd.Go();
}
I've looked some things up involving delegates to try to sort this out, but I know that's only one piece of the puzzle. From there, my searches online have been fruitless, as I'm not sure in what direction I need to research. Where do I go from here? The idea itself may be fundamentally flawed, and I'm willing to accept that, but what can I do to accomplish my goal of a BackgroundWorker so simply used by the calling method, while using variable types and counts of parameters based on the method I'm trying to run in the background?

returning value to main C#

I'll start by saying that i am no beginner in C# but not very much more and need help returning value to main. Or rather tell me what is the "correct" way.
I want to return a fail value (simply -1) from the application, in case of any exception and ending up in a catch. In that case passing info to main to return -1.
The way I solved it was by just adding a static global variable mainReturnValue (to be able to access it from main), and setting its value to -1 in the catches.
Is that a correct way of doing it, based on my current code?
If anyone is wondering the applications is executed without user interaction and that's why I need to catch the exit state. The form/GUI just displays info about the progress, in case it's started manually.
namespace ApplicationName
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{ ...
static int mainReturnValue = 0; //the return var
static int Main(string[] args)
{
Application.Run(new Form1(args));
return mainReturnValue; //returning 0 or -1 before exit
}
private void Form1_Load(object sender, System.EventArgs e)
{
the code..in turn also calling some sub functions such as DoExportData...and I want to be able to return the value to main from any function...
}
private int DoExportData(DataRow dr, string cmdText)
{
try { ... }
catch
{ mainReturnValue = -1; }
}
Thanks.
You could do this:
static int Main(string[] args)
{
Form1 form1 = new Form1(args);
Application.Run(form1);
return form1.Result;
}
and then define a property on your Form1 class, whose value you can set after the DoExportData method executes. For example:
public int Result { get; private set; }
private void Form1_Load(object sender, System.EventArgs e)
{
Result = DoExportData(...);
}
private int DoExportData(DataRow dr, string cmdText)
{
try
{
...
return 0;
}
catch
{
return -1;
}
}
How to: Get and Set the Application Exit Code from MSDN.
By the way, an exit code of 0 indicates success, while anything > 0 indicates an error.
I would add also something like this
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(CrashHandler);
static void CrashHandler(object sender, UnhandledExceptionEventArgs args) {
mainReturnValue = -1;
}
Just to be sure that even unhandled exceptions are "handled" by your application in a way you want, cause I presume your app is not only about one WindowsForm.

Should I go with reflection or delegates (C#)?

In one of my previous questions I explained about a form class that contain form field objects to save data in a user profile object (using profile provider).
The code is here bellow. Basically what I would like to accomplish is to pass as a parameter to my form field objects the field of the Profile object that they should interact in order to save the data later on.
You can see that in the following line:
//LastNameFormLine is an control that was added to my form page.
//The ProfileField parameter stores the field of the UserProfile object that is being manipulated by this control
LastNameFormLine.ProfileField = "UserProfile.LastName";
I was reading about reflection to be able to save this value in the UserProfileVisitor class, but I came across this concept of delegate in C# which I am not sure yet if I fully grasp.
Is it possible to delegate the ProfileField to a property on my UserProfile class? Or should I forget about it and go with reflection?
What would you suggest?
public partial class UserProfileForm : CustomIntranetWebappUserControl
{
protected override void OnInit(EventArgs e)
{
//AutoEventWireup is set to false
Load += Page_Load;
CancelLinkButton.Click += CancelButtonClickEvent;
SaveLinkButton.Click += SaveButtonClickEvent;
base.OnInit(e);
}
private void SaveButtonClickEvent(object sender, EventArgs e)
{
VisitFormFields();
}
private void VisitFormFields()
{
var userProfileVisitor = new UserProfileVisitor();
foreach (var control in Controls)
{
if (control is FormFieldUserControl)
{
var formField = (FormFieldUserControl) control;
formField.Visit(userProfileVisitor);
}
}
userProfileVisitor.Save();
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindText();
}
}
private void BindText()
{
LastNameFormLine.LabelText = string.Format("{0}:", HomePage.Localize("Last Name"));
LastNameFormLine.InputValue = UserProfile.LastName;
LastNameFormLine.IsMandatoryField = true;
LastNameFormLine.IsMultilineField = false;
LastNameFormLine.ProfileField = "UserProfile.LastName";
//... the rest of this method is exactly like the 4 lines above.
}
}
public abstract class FormFieldUserControl : CustomIntranetWebappUserControl
{
public string ProfileField { get; set; }
public abstract void Visit(UserProfileVisitor userProfileVisitor);
}
public partial class FormLineTextBox : FormFieldUserControl
{
//... irrelevant code removed...
public override void Visit(UserProfileVisitor userProfileVisitor)
{
if (userProfileVisitor == null)
{
Log.Error("UserProfileVisitor not defined for the field: " + ProfileField);
return;
}
userProfileVisitor.Visit(this);
}
}
public class UserProfileVisitor
{
public void Visit(FormLineTextBox formLine)
{
// The value of formLine.ProfileField is null!!!
Log.Debug(string.Format("Saving form field type {1} with profile field [{0}] and value {2}", formLine.ProfileField, formLine.GetType().Name, formLine.InputValue));
}
// ... removing irrelevant code...
public void Save()
{
Log.Debug("Triggering the save operation...");
}
}
Delegates are not for properties. However, Reflection is slow, may have issues with code security and it's not typesafe and may lead to runtime instead of compile-time problems on naming errors due to the late-bound nature.
That said, you may want to use getter and/or setter methods and use delegates on those.

Categories

Resources