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();
}
}
}
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.
I want to get a list of installed MIDI Devices in Windows 10, using the Windows 10 UWP MIDI API.
This article shows example code to get a list of MIDI devices and their IDs, using C#:
using Windows.Devices.Midi;
using Windows.Devices.Enumeration;
...
private async void ListMidiDevices()
{
// Enumerate Input devices
var deviceList = DeviceInformation.FindAllAsync(
MidiInPort.GetDeviceSelector());
foreach (var deviceInfo in deviceList)
{
Console.WriteLine(deviceInfo.Id);
Console.WriteLine(deviceInfo.Name);
Console.WriteLine("----------");
}
// Output devices are enumerated the same way, but
// using MidiOutPort.GetDeviceSelector()
}
I tried inserting the code for ListMidiDevices in the Visual Studio Community 2015 "Hello World" example program. I put the code block in place of Console.WriteLine("Hello World!");
in the "hello world" console example. I added the "using" statements above in the proper place.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Midi;
using Windows.Devices.Enumeration;
namespace ConsoleApplicationHelloWorld
{
class Program
{
static void Main(string[] args)
{
// Enumerate Input devices
var deviceList = await DeviceInformation.FindAllAsync(
MidiInPort.GetDeviceSelector());
foreach (var deviceInfo in deviceList)
{
System.Diagnostics.Debug.WriteLine(deviceInfo.Id);
System.Diagnostics.Debug.WriteLine(deviceInfo.Name);
System.Diagnostics.Debug.WriteLine("----------");
}
// Output devices are enumerated the same way, but
// using MidiOutPort.GetDeviceSelector() }
}
}
Edit - VS wasn't building the UWP type. I upgraded to VS Community 2019, and installed ConsoleAppUniversal.vsix. Then I could create a new project - Console App C# Universal:
using System;
using Windows.Devices.Midi;
using Windows.Devices.Enumeration;
// This example code shows how you could implement the required main function for a
// Console UWP Application. You can replace all the code inside Main with your own custom code.
// You should also change the Alias value in the AppExecutionAlias Extension in the
// Package.appxmanifest to a value that you define. To edit this file manually, right-click
// it in Solution Explorer and select View Code, or open it with the XML Editor.
namespace ConsoleAppCsharpUniversal
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("starting - no args");
// Enumerate Input devices
var deviceList = DeviceInformation.FindAllAsync(
MidiInPort.GetDeviceSelector());
foreach (var deviceInfo in deviceList)
{
Console.WriteLine(deviceInfo.Id);
Console.WriteLine(deviceInfo.Name);
Console.WriteLine("----------");
}
Console.WriteLine("finish - no args");
}
else
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"arg[{i}] = {args[i]}");
}
}
Console.WriteLine("Press a key to continue: ");
Console.ReadLine();
}
}
}
Now the only remaining error is "foreach statement cannot operate on variables of type IAsyncOperation<DeviceInformationCollection> because IAsyncOperation<DeviceInformationCollection> does not contain a public instance definition for GetEnumerator"
Is there another way to access the device information without using an async method?
You have to make sure your project is targeting at least C# 7.1 (I think the template does have this out-of-the-box in VS 2019) and use the async Main method feature:
Your method signature will change to:
public static async Task Main(string[] args)
{
...
}
And then you need to await the FindAllAsync method:
var deviceList = await DeviceInformation.FindAllAsync(
MidiInPort.GetDeviceSelector());
Note: You can change the C# version by opening the csproj file in a text editor and adding the following into a <PropertyGroup>:
<LangVersion>7.1</LangVersion>
or even (if you want the latest features):
<LangVersion>latest</LangVersion>
I have been trying to diagnose a memory leak in a service which only appears on Windows 7/Server 2008 R2. I narrowed it down to where we are using Microsoft.Web.Administration.ServerManager to gather info about the apps in our site. I whittled it down to the console app below, which exhibits the same behavior. It might still be more complex than it needs to be, but I wanted to emulate the behavior of the service as much as possible.
I found a previous question here that was very similar and made the changes suggested in the answers. This appeared to reduce the rate of growth, but it still leaks significantly (under the comments "Original Test" I have commented out code that I changed based on those answers. the "Modified Test" comments indicate the changes I made. I didn't initially have the GC.Collect call in, and when I ran this on a Windows 10 system, it grew for quite some time before the garbage collection kicked in. With the GC.Collect call in place, it ran without growing on Win 10, but on Win 7 it made no difference.
I ran it under a profiler that indicated the memory being leaked was native, and that the leak was coming from nativerd.dll.
Has anyone encountered a problem like this? I'm new to C# and am still learning how Garbage Collection works, so I'm wondering if there is something I'm doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Web.Administration;
namespace ServerManagerLeakTest
{
class Program
{
static void Main(string[] args)
{
Console.Write("Working.");
var me = new MyClass();
me.Run();
}
}
internal class MyClass
{
ServerManagerWrapper _smw = new ServerManagerWrapper();
public void Run()
{
while (true)
{
var t = Task.Run(async delegate
{
DoWork();
await Task.Delay(1000);
});
try
{
t.Wait();
}
catch (Exception e)
{
Console.Write("Main Exception: " + e.Message);
}
Console.Write(".");
}
}
public void DoWork()
{
try
{
var data = _smw.GetWebApps().ToList();
data.Clear();
}
catch (Exception e)
{
Console.Write("DoWork Exception: " + e.Message);
}
}
}
internal class ServerManagerWrapper
{
public List<int> GetWebApps()
{
List<int> result = new List<int>() { };
// Original Test
//
// using (var serverManager = new ServerManager())
// {
// foreach (var site in serverManager.Sites)
// {
// result.AddRange(GetWebApps(site));
// }
//}
// Modified Test
var serverManager = new ServerManager();
foreach (var site in serverManager.Sites)
{
result.AddRange(GetWebApps(site));
}
serverManager.Dispose();
serverManager = null;
System.GC.Collect();
return result;
}
private IEnumerable<int> GetWebApps(Site site)
{
// Original Test
//
//for (var application in site.Applications)
//{
// yield return application.GetHashCode();
//}
// Modified Test
List<int> result = new List<int>() { };
for (int i = 0; i < site.Applications.Count; i++)
{
result.Add(site.Applications[i].GetHashCode());
}
return result;
}
}
}
Answer provided in comments from #Lex Li.
Move the check to a separate process. Calling IIS REST API, PowerShell, or even appcmd and parse the result. Let the leak be out of your own service.
I created 2 simple C# Console Projects (.net 4.5.2), added the v4.0.0.1 NetMQ Nuget package to each, loaded each program up into separate Visual Studio 2017 Community Editions, put a breakpoint on the 1 line contained within the OnReceiveReady callback method, started the subscriber program first, then started the publisher program. The ReceieveReady event is not being triggered in the subscriber. What am I doing wrong? Even if I chose subSocket.Subscribe("") then I still didn't get any messages received. Also, removing/modifying the Send/Receive HighWatermarks didn't change things either. Thanks for your help!
Here's the Publisher code:
using System;
using NetMQ;
using NetMQ.Sockets;
using System.Threading;
namespace SampleNQPub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
using (var pubSocket = new PublisherSocket())
{
Console.WriteLine("Publisher socket binding.");
pubSocket.Options.SendHighWatermark = 10;
pubSocket.Bind(addr);
for (int i=0; i < 30; i++)
{
pubSocket.SendMoreFrame("NQ").SendFrame(i.ToString());
Thread.Sleep(1000);
}
pubSocket.Disconnect(addr);
}
}
}
}
Here's the Subscriber code:
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
namespace SampleNQSub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
using (var subSocket = new SubscriberSocket())
{
subSocket.ReceiveReady += OnReceiveReady;
subSocket.Options.ReceiveHighWatermark = 10;
subSocket.Connect(addr);
subSocket.Subscribe("NQ");
for (int i=0; i < 20; i++)
{
Thread.Sleep(1000);
}
subSocket.Disconnect(addr);
}
}
static void OnReceiveReady(object sender, NetMQSocketEventArgs e)
{
var str = e.Socket.ReceiveFrameString();
}
}
}
Ok, this is a gotcha question in the NetMQ world and I just figured it out. You MUST setup a NetMQPoller that will wind up calling every ReceiveReady callback which you have added to it (NetMQPoller).
Here is the corrected code which will at least (i.e., ReceiveFrameString still only getting the "NQ" part but that's just another method call to fix) get the ReceiveReady event triggered:
using System.Threading;
using System.Threading.Tasks;
using NetMQ;
using NetMQ.Sockets;
namespace SampleNQSub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
NetMQPoller poller = new NetMQPoller();
using (var subSocket = new SubscriberSocket())
{
subSocket.ReceiveReady += OnReceiveReady;
subSocket.Options.ReceiveHighWatermark = 10;
subSocket.Connect(addr);
subSocket.Subscribe("NQ");
poller.Add(subSocket);
poller.RunAsync();
for (int i = 0; i < 20; i++)
{
Thread.Sleep(1000);
}
subSocket.Disconnect(addr);
}
}
static void OnReceiveReady(object sender, NetMQSocketEventArgs e)
{
var str = e.Socket.ReceiveFrameString();
e.Socket.ReceiveMultipartStrings();
}
}
}
I noticed that the authors of NetMQ decided in 4.x to take care of the Context object internally so the user wouldn't have to bare the burden of managing it. It would be nice also if they could hide this "polling pump" code from the user as well for the most simple use case.
As a comparison, take a look at the subscriber using NodeJS (with the zmq library) utilizing the Publisher console app I posted above (save this code to sub.js and, in a Windows console, type 'node sub.js'):
var zmq = require('zmq'), sock = zmq.socket('sub');
sock.connect('tcp://127.0.0.1:3004');
sock.subscribe('NQ');
console.log('Subscriber connected to port 3004');
sock.on('message', function() {
var msg = [];
Array.prototype.slice.call(arguments).forEach(function(arg) {
msg.push(arg.toString());
});
console.log(msg);
});
So where's the poller pump mechanism in this? (Answer: I don't care! I just want the messages supplied to me in a callback that I register. [Obviously, tongue-in-cheek. I get that a NetMQPoller is versatile and handles more complex issues, but for basic "give me a message in a callback when it arrives", it would be nice if it were handled internally by the library.])
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;
}