C# log4net TextWriterAppender is not working with TextBox - c#

C# project 1: Library
public class TaskDummy : IPlannedTask {
private static readonly log4net.ILog log =
log4net.LogManager.GetLogger(typeof(TaskDummy));
// implements interface
public void run(System.IO.TextWriter x, object paramIn=null) {
initLog(x);
String msg = "Dummy task is running";
// direct & indirect call of TextWriter::WriteLine
x.WriteLine(msg);
log.Info(msg);
}
private void initLog(System.IO.TextWriter x) {
log4net.Appender = new log4net.Appender.TextWriterAppender();
textAppender.Writer = x;
// ...
}
}
results in TaskDummy.dll. log4net is configured with a TextWriterAppender at runtime.
C# project 2: Console app in same solution with reference on project 1.
class Program {
static void Main(string[] args) {
TaskDummy t = new TaskDummy();
t.run(Console.Out);
}
}
This will print
Dummy task is running
INFO - Dummy task is running
as exspected (direct & indirect call). The TextWriter is the one from console output.
C# project 3: Windows Forms
public class TextBoxWriter : TextWriter {
protected TextBox box;
public TextBoxWriter(TextBox box) {
this.box = box;
}
public override Encoding Encoding {
get { return Encoding.Default; }
}
delegate void WriteLineCallBack(String value);
public override void WriteLine(String value) {
if(box.InvokeRequired) {
WriteLineCallBack d = new WriteLineCallBack(WriteLine);
box.Invoke(d, new object[] { value });
} else {
base.Write(value);
box.AppendText(value.ToString() + Environment.NewLine);
}
}
}
Implementation of TextWriter to write into Windows Forms Textbox (cross-thread). This project loads implementations of IPlannedTask (see project 1) and calls their method run():
// txtLog is a Textbox control
TextBoxWriter twOutput = new TextBowWriter(txtLog);
var DLL = Assembly.LoadFile("TaskDummy.dll");
Type t = DLL.getExportedTypes()[0];
dynamic d = Activator.CreateInstance(t);
((IPlannedTask)d).run(twOutput);
This results in only one line in textbox from the direct call of WriteLine (see project 1). The log.Info call does not work (the method TextBoxWriter::WriteLine is not even called).
=> Why does TextWriterAppender of log4net work with Console.Out but not with TextBoxWriter

Related

How to write console output to a control within multi threading MVC modeled application?

I found a very simple and elegant way to direct console output to a textbox via this link, but now I don't find a way to implement it in my presenter class so that it works wherever I need it. This means in the main thread, but also in all the classes initiated in the main thread.
The problem is that the class I found online has a control (textbox) in it to which output is directed. In my presenter class, I cannot define controls. So when I start the presenter thread, which then starts the form in which I defined the TextWriter, output generated in the form is being written to the textbox as expected, but whenever I Console.Writeline in the presenter or in other instances created in the presenter, then it is still directed to the stadard debug output window...
I'm relatively new to MVC model, as you might already see. :)
So, TextBoxStreamWriter:
public class TextBoxStreamWriter : TextWriter
{
TextBox _output ;
public TextBoxStreamWriter(TextBox output)
{
_output = output;
}
public override void Write(char value)
{
MethodInvoker action =
delegate
{
_output.AppendText(value.ToString());
};
_output.BeginInvoke(action);
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
In my presenter class:
public MTMainPresenter(IMTMainForm view)
{
_view = view;
HandleShizzle();
Console.Writeline("Presenter initialized :( "); // << Doesn't work as I want it to...
}
program.cs:
private static MTMainPresenter _presenter;
static void Main()
{
var mainForm = new MTMainForm();
_presenter = new MTMainPresenter(mainForm);
Application.Run(mainForm);
}
In my form:
TextWriter _writer = null;
private void Form1_Load(object sender, EventArgs e)
{
_writer = new TextBoxStreamWriter(txtboxConsole);
Console.SetOut(_writer);
Console.Writeline("Yay !"); // << Works fine, the way I want it to
}
So basically, I created a test button in the form that generated a Console.WriteLine() and this works just fine. But when I do the same in my presenter, it writes to the standard debug window. I tried already initializing the TextWriter in the Procgram.cs class, but since it has a TextBox control, that needs to be assigned, I'm a bit lost on how to solve this...
I'm quite sure this is so easy to fix, but I lack the experience.
Thanks a lot in advance
P
I got it. After thinking it through for a while and trying some things, I found a way to do it. There will probably be a much better way, so please correct me if this is not according to best practices. :) But it works...
I can now call Console.WriteLine() anywhere (MTMainForm, Presenter, any instances of custom objects inside my presenter...).
Steps I'm making...
1. Create the form
2. In the form, create writer linked to a control
3. back in Program.cs, get the created writer from the form
4. deviate the output to the writer
5. run the form...
Program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = new MTMainForm();
TextWriter wrt = mainForm.GetWriter();
Console.SetOut(wrt);
_presenter = new MTMainPresenter(mainForm);
Application.Run(mainForm);
}
MTMainForm:
TextBoxStreamWriter _writer = null;
public MTMainForm()
{
InitializeComponent();
_writer = new TextBoxStreamWriter(textBoxConsole);
}
public TextBoxStreamWriter GetWriter()
{
return _writer;
}
TextBoxStreamWriter:
public class TextBoxStreamWriter : TextWriter
{
TextBox _output;
public TextBoxStreamWriter(TextBox output)
{
_output = output;
}
public override void Write(char value)
{
MethodInvoker action =
delegate
{
_output.AppendText(value.ToString());
if (_output.Text.Length > 4000)
_output.Text = _output.Text.Substring(2000, _output.Text.Length - 2000);
};
_output.BeginInvoke(action);
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}

C# EventHandler : How to handle EventHandler in client application?

I have written a class library(dll) which handle Telephony calls. This class library has delegates that handle phone call events such as OnCallReceived, OnHoldCall etc.
So now i want to add this class library to my Windows Forms App, and be able to handle Phone Call events(OnCall, OnHolde etc) in my Windows Forms application. How can achieve this?
For example
//My Class Library
Class Test
{
ThirdParyLibrary tpl
public Test()
{
tpl= new tpl();
tpl.OnReceiveCall += Handler(OnReceiveCall);
}
public void OnReceiveCall()
{
//i want this event to take place in client app
}
}
//My Windows Forms App
Client App
public main()
{
Test t =new Test()
//i want OnReceiveCall to be processed here
//t.OnReceiveCall
{
Message.Show('You received a call');
}
}
// i want this event to take place in client app
Since you want the Event Handling mechanism to take place in the Client App, which I suppose is another Class containing Main, I have created a small console that replicates the problem scenario
Uploaded to fiddle as well
using System;
namespace Test
{
public class ThirdPartyLibrary
{
public delegate void dEeventRaiser();
public event dEeventRaiser OnReceiveCall;
public string IncomingCall(int x)
{
if (x > 0 && OnReceiveCall != null)
{ OnReceiveCall(); return "Valid "+x.ToString(); }
return "Invalid"+x.ToString();
}
}
public class EventSubscription
{
public EventSubscription()
{
ThirdPartyLibrary a = new ThirdPartyLibrary();
a.OnReceiveCall += HandleTheCall;
var iAnswer = a.IncomingCall(24198724);
Console.WriteLine("Call received from "+iAnswer);
}
public virtual void HandleTheCall()
{
Console.WriteLine("Default way I handle the call");
}
}
public class Program : EventSubscription
{
public override void HandleTheCall()
{
Console.WriteLine("Override sucessful, new way to handle the call ");
}
static void Main(string [] args)
{
Program pb = new Program(); // Control goes EnventSubscription constructor as it is derived
Console.Read();
}
}
}
Output:

Can I bypass Console.ReadKey() issue when input is redirected?

I am a C# teacher and I wrote some automated HW checker for my students.
The students write C# Console Applications. My HW checker is based on input redirection so I can test their code on my own generated input.
The problem is that students sometimes end their program with a Console.ReadKey() instruction (They do so just to make the execution window not close when they ran the program under F5 - Debug). The Console.ReadKey() crashes when ran under input redirection with the following exception:
System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected from a file.
Do I have any way to "bypass" this problem (without altering the students code)? Maybe tell Console to ignore ReadKey instructions?
I see a clear case for a Dependency Injection pattern.
Let's build a simple example, with Read, ReadLine and WriteLine functionalities polymorphically: your students must write a homework in which a number given in the Console.ReadLine() must be parsed as int and returned to the Console Window.
Usually a student writes something like:
class Program
{
static void Main(string[] args)
{
var stringValue = Console.ReadLine();
int number;
if (int.TryParse(stringValue, out number))
Console.WriteLine($"The double of {number} is {number * 2}");
else
Console.WriteLine($"Wrong input! '{stringValue}' is not an integer!");
Console.Read();
}
}
Now, instead, create an interface for the Console functionalities:
public interface IOutput
{
void Read();
string ReadLine();
void WriteLine(string text);
}
A student must create a Homework class that wraps all the required homework code, using an IOutput instance in this way:
public class HomeWork
{
private IOutput _output;
public HomeWork(IOutput output)
{
_output = output;
}
public void Run()
{
_output.WriteLine("Give me an integer:");
var stringValue = _output.ReadLine();
int number;
if (int.TryParse(stringValue, out number))
_output.WriteLine($"The double of {number} is {number * 2}");
else
_output.WriteLine($"Wrong input! '{stringValue}' is not an integer!");
_output.Read();
}
}
The Main becomes:
static void Main(string[] args)
{
var h = new HomeWork(new ConsoleOutput());
h.Run();
}
You give them also the ConsoleOutput class:
public class ConsoleOutput : IOutput
{
public void Read()
{
Console.Read();
}
public string ReadLine()
{
return Console.ReadLine();
}
public void WriteLine(string text)
{
Console.WriteLine(text);
}
}
So the use it instead of call directly Console.Read() etc.
The student must pass to you not the entire Application, but only the Homework class.
You can create a test class that use the Homework class with some test implementations of IOutput like the followings:
public abstract class TestOutput : IOutput
{
public TestOutput()
{
Outputs = new List<string>();
}
public void Read()
{
//do nothing?
}
public abstract string ReadLine();
public void WriteLine(string text)
{
Outputs.Add(text);
}
public List<string> Outputs { get; set; }
}
public class TestOutputWithAValidNumber : TestOutput
{
public TestOutputWithAValidNumber(int value)
{
Value = value;
}
public override string ReadLine()
{
return Value.ToString();
}
public int Value { get; }
}
public class TestOutputWithNotValidNumber : TestOutput
{
public TestOutputWithNotValidNumber(string value)
{
Value = value;
}
public override string ReadLine()
{
return Value;
}
public string Value { get; }
}
The test class can be something like this:
[TestClass]
public class TestOutputClass
{
[TestMethod]
public void TestGoodNumber()
{
var testOutput = new TestOutputWithAValidNumber(1234);
var h = new HomeWork(testOutput);
h.Run();
Assert.AreEqual(1234, testOutput.Value);
Assert.AreEqual("Give me an integer:", testOutput.Outputs[0]);
Assert.AreEqual("The double of 1234 is 2468", testOutput.Outputs[1]);
}
[TestMethod]
public void TestWrongNumber()
{
var testOutput = new TestOutputWithNotValidNumber("foo");
var h = new HomeWork(testOutput);
h.Run();
Assert.AreEqual("foo", testOutput.Value);
Assert.AreEqual("Give me an integer:", testOutput.Outputs[0]);
Assert.AreEqual("Wrong input! 'foo' is not an integer!", testOutput.Outputs[1]);
}
}
If you need only to wrap the Console.Read() method, feel free to simplify all this code, but IMHO I thought that a wider view on this possible solution would have been useful anyway.
If the executables are in IL, you can create an easy application that uses ILDASM.
The key point is: disassemble the executable with ILDASM into a text file/stream, look for any call to Console.Read and remove it, than recompile it and run.

Access statusbar on form from cplex callback function c#

I'm working with an C# .Net application that uses Cplex DLL's for an optimization operation, and during that operation I want to write status progress to a statusbar on the that initiated the operation.
This is the general layout of the specific form;
namespace ActResMain
{
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//...
cplex.Use(new Cplex_ContinuousCallback());
cplex.Solve()
}
public void Update_OptimizeStatusbarPanel(String strText)
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
//From here I want to edit the statusbar at FormOptimize. I can write progress to console without any problems, but cannot reach function "Update_OptimizeStatusbarPanel".
//If I include "FormOptimize formOpt = new FormOptimize" here, i get Visual studio exception on illegal window reference.
}
}
}
}
I have also tried invoking the Update_OptimizeStatusbarPanel function like this:
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
FormCollection fc = Application.OpenForms;
var mpc = fc[1];
Type type = mpc.GetType();
MethodInfo dynMethod = type.GetMethod("Update_OptimizeStatusbarPanel");
dynMethod.Invoke(mpc, new object[] { String.Format("Running Optimization: {0} iterations ", Niterations)});
}
}
But then I get an exception from visual studio stating that an object created by one thread cannot be modified from another thread.
Maybe this is something stupid that I have missed, but help is greatly appriciated
EDIT: I edited the code as per Mohammad Dehghans suggestion,
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
cplex.Use(new Cplex_ContinuousCallback(this));
cplex.Solve()
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
if (Niterations % 10 == 0)
{
_formOptimize.Update_OptimizeStatusbarPanel(0, String.Format("Running Optimization: {0} iterations ", Niterations), 0);
}
}
}
public void Update_OptimizeStatusbarPanel(short panelIndex, String strText, short severity)
{
if (statusBar1.InvokeRequired)
statusBar1.Invoke(new Action<short, string, short>(Update_OptimizeStatusbarPanel), panelIndex, strText, severity);
else
{
if (panelIndex == 0)
{
//...
statusBarPanel_0.Text = strText;
}
else if (panelIndex == 1)
{
//...
statusBarPanel_1.Text = strText;
}
statusBar1.Refresh();
}
}
}
But by doing that I apparently broke something, as the application just ..stops after statusBar1.Invoke() is called the first time. If I pause the debugger it says that cplex.Solve() is executing, but then nothing more happens.
First of all, you need to pass the instance of your form to the implemented callback class, so when the Main method is called, you have access to the exact instance that is being shown on the screen.
Secondly, you need to use Invoke method to update the UI controls from anther thread (I've not worked with CPLEX so far, but I guess the callback is invoked from another thread. That's usual).
Read this for more information.
The complete code could be:
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//Misc code
cplex.Use(new Cplex_ContinuousCallback(this)); // <-- passing `this`
cplex.Solve()
//Misc code
}
public void Update_OptimizeStatusbarPanel(String strText)
{
if (statusBarPanel_1.InvokeRequired)
statusBarPanel_1.Invoke(Action<string>(Update_OptimizeStatusbarPanel), strText);
else
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
//...
_formOptimize.Update_OptimizeStatusbarPanel(String.Format("Running Optimization: {0} iterations ", Niterations));
}
}
}

How to set focus on "Untitled - Notepad" using windows service

I want to set focus on notepad (Untitled - Notepad) and write some text into it. I have to create a Windows Service for this.
I can create windows service but don't know how to set focus on notepad.
Please provide me code samples in Windows Service
I have tried following code. But no luck with it.
namespace SampleService
{
public partial class Service1 : ServiceBase
{
string application = string.Empty;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
GetTaskWindows();
int iHandle = NativeWin32.FindWindow(null, application);
NativeWin32.SetForegroundWindow(iHandle);
}
protected override void OnStop()
{
GetTaskWindows();
int iHandle = NativeWin32.FindWindow(null, application);
NativeWin32.SetForegroundWindow(iHandle);
}
private void GetTaskWindows()
{
// Get the desktopwindow handle
int nDeshWndHandle = NativeWin32.GetDesktopWindow();
// Get the first child window
int nChildHandle = NativeWin32.GetWindow(nDeshWndHandle, NativeWin32.GW_CHILD);
while (nChildHandle != 0)
{
// Get only visible windows
if (NativeWin32.IsWindowVisible(nChildHandle) != 0)
{
StringBuilder sbTitle = new StringBuilder(1024);
// Read the Title bar text on the windows to put in combobox
NativeWin32.GetWindowText(nChildHandle, sbTitle, sbTitle.Capacity);
String sWinTitle = sbTitle.ToString();
{
if (sWinTitle.Length > 0)
{
if (sWinTitle.Contains("Notepad"))
{
application = sWinTitle;
}
}
}
}
// Look for the next child.
nChildHandle = NativeWin32.GetWindow(nChildHandle, NativeWin32.GW_HWNDNEXT);
}
}
}
}
Fixed myself. Created one small application which will focus on notepad and used sendKeys function to write text into notepad.

Categories

Resources