I'm writing a small app that should display the number of characters in the current string from the clipboard. So for example, someone highlights a line of text and hits copy, then runs my app. I want it to display the number of characters in the string. Should be simple, but I keep getting Zero returned. There are related threads but none answer my question. Here is what I have so far (Its a console app btw.):
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace BuildandRun
{
class Program
{
static void Main(string[] args)
{
string data = Clipboard.GetText();
Console.WriteLine(data);
int dataLength = data.Length;
Console.WriteLine(dataLength + " Characters.");
Console.ReadLine();
}
}
}
From MSDN:
The Clipboard class can only be used in threads set to single thread
apartment (STA) mode. To use this class, ensure that your Main method
is marked with the STAThreadAttribute attribute.
Just change your code to:
[STAThreadAttribute]
static void Main( string[] args )
The Clipboard only works for Single Threaded Apartment threads.
Therefore the answer is to add the following to Main():
[STAThread]
static void Main(string[] args)
{
...
Or workaround like this:
public string GetClipboardText()
{
string result = "";
Thread thread = new Thread(() => result = Clipboard.GetText());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return result;
}
Related
The WebBrowser.Print method has the limitation of not allowing the caller to specify a printer other than the system's default one. As a workaround, it has been suggested[1], [2] to alter the system's default printer prior to calling Print(), however it's also reported[3] (and I experienced firsthand) that the WebBrowser instance will continue to print to the previously defined printer even after the system default is altered.
To work around that, registering a handler to the PrintTemplateTeardown event by accessing the underlying ActiveX object of the managed WebBrowser instance and waiting for the event to fire before printing further documents has been proposed[4], [5], and that is what I am trying to implement. I simplified what is a much more complex program to the MVCE presented below.
(The program is a .NET Core 3.1 Windows Forms application, with one form containing nothing more than a BackgroundWorker object named bw.)
Form1.cs
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Printing;
using System.Management;
namespace Demo_1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
BwPolling();
Thread.Sleep(20000);
}
}
private void BwPolling()
{
string[] htmlStrings = { "test1", "test2" };
foreach (string html in htmlStrings)
{
Invoke((MethodInvoker)delegate
{
PrinterSettings.StringCollection installedPrinters = PrinterSettings.InstalledPrinters;
foreach (string printer in installedPrinters)
{
string[] validPrinterNames =
{
"Microsoft Print to PDF",
"Microsoft XPS Document Writer"
};
if ( validPrinterNames.Contains(printer) )
{
SetDefaultPrinter(printer);
var wb = new WebBrowser();
wb.DocumentText = html;
// With inspiration from code by Andrew Nosenko <https://stackoverflow.com/users/1768303/noseratio>
// From: https://stackoverflow.com/a/19737374/3258851
// CC BY-SA 3.0
var wbax = (SHDocVw.WebBrowser)wb.ActiveXInstance;
TaskCompletionSource<bool> printedTcs = null;
SHDocVw.DWebBrowserEvents2_PrintTemplateTeardownEventHandler printTemplateTeardownHandler =
(p)
=> printedTcs.TrySetResult(true); // turn event into awaitable task
printedTcs = new TaskCompletionSource<bool>();
wbax.PrintTemplateTeardown += printTemplateTeardownHandler;
try
{
MessageBox.Show("Printing to " + printer);
wb.Print();
printedTcs.Task.Wait();
}
finally
{
wbax.PrintTemplateTeardown -= printTemplateTeardownHandler;
}
wb.Dispose();
}
}
});
}
}
private static bool SetDefaultPrinter(string name)
{
// With credits to Austin Salonen <https://stackoverflow.com/users/4068/austin-salonen>
// From: https://stackoverflow.com/a/714543/3258851
// CC BY-SA 3.0
using ( ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer") )
{
using ( ManagementObjectCollection objectCollection = objectSearcher.Get() )
{
foreach (ManagementObject mo in objectCollection)
{
if ( string.Compare(mo["Name"].ToString(), name, true) == 0 )
{
mo.InvokeMethod("SetDefaultPrinter", null);
return true;
}
}
}
}
return false;
}
}
}
The problem being faced is when I remove the message box from the BwPolling() method, right before calling Print(), i.e. when this line is removed:
MessageBox.Show("Printing to " + printer);
then the program freezes, nothing is printed, and the process must eventually be terminated.
I believe I can sort of understand the issue on its surface: WebBrowser requires an STA thread with an active message loop[6], [7]; by calling printedTcs.Task.Wait(); within a Invoke((MethodInvoker)delegate block (called on the Form1 instance; this. is ommited), I am blocking the STA thread and the application hangs waiting for an event that is never fired. This is in fact mentioned in a comment under the answer I credited in my code.
Just can't figure out what a proper solution would be. Got lost in attempts to run the printing routine in a secondary thread. Maybe something wrong in my execution, guess I require assistance in this. Any help?
Thanks.
In this page, the following code is an example for changing the affinity of the current process:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
Process myProcess = Process.GetCurrentProcess();
Console.WriteLine("ProcessorAffinity: {0}", myProcess.ProcessorAffinity);
Process.GetCurrentProcess().ProcessorAffinity = (System.IntPtr)3;
Console.WriteLine("ProcessorAffinity: {0} ", myProcess.ProcessorAffinity);
Console.ReadKey();
}
}
But the output for me is:
ProcessorAffinity: 255
ProcessorAffinity: 255
meaning that the affinity is not changed. What's the problem? And how can I change the affinity?
As #ChetanRanpariya mension in his comment, the issue is because you changeing ProcessorAffinity of one process object (returned from the second call of Process.GetCurrentProcess()) and checking it in another (returned from the first call of Process.GetCurrentProcess()). Here is corrected sample:
using (var currentProcess = Process.GetCurrentProcess())
{
Console.WriteLine($"ProcessorAffinity: {currentProcess.ProcessorAffinity}");
currentProcess.ProcessorAffinity = (System.IntPtr)3;
Console.WriteLine($"ProcessorAffinity: {currentProcess.ProcessorAffinity}");
}
I am writing some text to file using file.WriteAllLines. I want that when the WriteAllLines is writing to the file. Both other process are not allowed to read or write the file. Is it the default behavior of WriteAllLines?
Other processes are blocked from writing to the same file since internally the WriteAllLines method keeps a StreamWriter open on the file until all lines have been written.
See the implementation detail in the C# language reference
[UPDATE]
Reading is not possible as well, as proven with this test, which throws an IOException in the readerThread:
using System.IO;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestProject3
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
string[] lines = new string[5000000];
for (int i = 0; i < 5000000; i++)
{
lines[i] = $"Line_{i}";
}
Thread writerThread = new Thread(() =>
{
File.WriteAllLines("C:\\tmp\\wal_test.txt", lines);
});
Thread readerThread = new Thread(() =>
{
File.ReadLines("C:\\tmp\\wal_test.txt");
});
writerThread.Start();
Thread.Sleep(100);
readerThread.Start();
}
}
}
using System;
// Namespace
namespace ConsoleApp22
{
// Main Class
class Program
{
// Entry Point Method
static void Main(string[] args)
{
string name = "Florent Shomora";
// start here
Console.Write("Hello World"+ name);
}
}
}
This is my code and the problem is that the console pop up and die.
Do you want to see console window ? Try to use Console.Readline()
static void Main(string[] args)
{
string name = "Florent Shomora";
// start here
Console.Write("Hello World"+ name);
Console.Readline();
}
The problem is that your program works without error.
However, what does your program do? It prints a message to the screen, nothing more. This is a very fast operation. The program will complete this operation and terminate probably faster than you can read the message.
You can pause the program by adding another operation which will take more time, such as expecting user input:
string name = "Florent Shomora";
// start here
Console.Write("Hello World"+ name);
Console.ReadLine();
With that extra call to ReadLine() the application will now remain open and wait for you to press "return" before closing.
Try
Console.Readline()
it should show u the Console and u can exit by clicking for example enter
Is there any problem which i have to do carefully when starting new process in multiple thread application?
I tried this in a simple project:
static void Main(string[] args)
{
Process.Start(#"D:\System\Desktop\a.txt");
MessageBox.Show("Success");
}
And it runs perfectly. But when i do it in my big project which use multiple thread, it's thread is stopped working ("a.txt" is opened but "Success" is not shown) while my application (other thread) do well.
What is the problem in this situation?
If you have a Windows.Forms application and you try to show a message-box from a thread that is not the main user-interface thread, the behavior of the message-box is undefined. Meaning, it may or may not show, be inconsistent, or some other problem.
For instance, displaying a message-box from the BackgroundWorker's DoWork event may or may not work. In one case, the message-box-result was always cancel regardless of what button was clicked.
Therefore, if you are using a message-box just for debugging purposes, use a different technique. If you have to show a message-box, call it from the main user-interface thread.
A console-application should normally not have problems displaying message-boxes. Yet, I have had cases where I would have to sleep the thread for 100ms before the message-box call.
Note, as TomTom pointed out, the main user-interface thread is the application's Windows message loop. Which reminds me, I once had to create a Form in a Console application in order to create a Windows message loop, so my application could respond to Windows messages.
This isn't the answer - I can't put all this code in a comment...
This works for me. Tell me how your code differs from this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace Test
{
class Program
{
const string OutputFile = #"E:\Output.txt";
object _lock = new object();
static void Main(string[] args)
{
Program program = new Program();
Thread thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\Test.txt");
thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\DoesntExist.txt");
Console.ReadKey();
}
void ThreadMethod(object filename)
{
String result = RunNormal(filename as string);
lock (_lock)
{
FileInfo fi = new FileInfo(OutputFile);
if (!fi.Exists)
{
try
{
fi.Create().Close();
}
catch (System.Security.SecurityException secEx)
{
Console.WriteLine("An exception has occured: {0}", secEx.Message);
return;
}
}
StreamWriter sw = fi.AppendText();
sw.WriteLine(result);
sw.Close();
}
}
string RunNormal(string fullfilename)
{
try
{
Process.Start(fullfilename);
return fullfilename + "|Success";
}
catch (Exception e)
{
return fullfilename + "|" + e.ToString();
}
}
}
}
The output in Output.txt is:
E:\DoesntExist.txt|System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(String fileName)
at Test.Program.RunNormal(String fullfilename) in E:\Projekti\VS2010\Test\Test\Program.cs:line 59
E:\Test.txt|Success
How much different is your code? Do you call some other methods? How do you process the results?
Make sure Process.Start works. Passing a filename is not good enough in some cases. In you sample code, you would have to set the use-shell property; otherwise, you would have to use cmd start <filename> or equivalent.
Therefore, just start NotePad.exe to make sure Process.Start works. If it does then your problem is the process command and command line.