C# - Windows Service EventMonitor function with parameters - c#

I'm using PCSC library for SmartCard Readers events detection and trying to use it in Windows service.
Readers search function:
private void CheckPresentReaders()
{
using (var context = new SCardContext())
{
context.Establish(SCardScope.System);
PresentCardReaders = context.GetReaders();
}
}
SmartCard removed function:
private void SCardRemoved(object sender, CardStatusEventArgs e)
{
WriteToLog("Locking machine. SmartCard was removed.");
// LockWorkStation();
}
Monitor creation:
CheckPresentReaders();
if (PresentCardReaders.Length != 0)
{
SCardMonitor monitor = new SCardMonitor(ContextFactory.Instance, SCardScope.System);
monitor.CardRemoved += new CardRemovedEvent(SCardRemoved);
foreach (string reader in PresentCardReaders)
monitor.Start(reader);
}
WriteToLog function is a simple Log Entry creation function.
When it is compiled - service starting and then stopping immediately.
I have two suspects - not delegated WriteToLog and/or SCardRemoved, which requires two parameters -
(object sender, CardStatusEventArgs e)
Those are required by library.
Can this be a problem? Any other suggestions?
Thanks.

I've implemented it properly into Topshelf Service and it does work.
https://github.com/35359595/SmartCardMonitorService

Related

How to use multiple consumers in different programming language for same group ID in Kafka

I wanted to create a load balancing in Kafka (multiple programming languages) for a topic. So I did the following.
Created a topic with 4 partitions.
Created a producer in C# (producing messages every second)
Created one consumer(consumer1) in C# (consumer group: testConsumerGrp)
Created one more consumer(consumer2) in NodeJs (consumer group: testConsumerGrp)
I used confluent.kafka in C# and kafkajs in NodeJs.
I Open the producer and keep it running.
If I run only C# consumer, it works fine.
If I run only NodeJs consumer, it works fine.
If I run multiple C# consumer (only c# and less than 4 instances), it works fine.
If I run multiple NodeJs consumer (only NodeJs and less than 4 instances), it works fine.
If I run one C# and one NodeJs consumer then I am getting Inconsistent group protocol error
Can't we use two programming languages for a same consumer group?
Producer in C# - windows form
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Confluent.Kafka;
namespace KafkaProducer
{
public partial class frmProducer : Form
{
const string TOPIC = "testTopic";
private IProducer<Null, string> pBuilder;
public frmProducer()
{
InitializeComponent();
}
private async void timer1_Tick(object sender, EventArgs e)
{
try
{
// instead of sending some value, we send current DateTime as value
var dr = await pBuilder.ProduceAsync(TOPIC, new Message<Null, string> { Value = DateTime.Now.ToLongTimeString() });
// once done, add the value into list box
listBox1.Items.Add($"{dr.Value} - Sent to Partition: {dr.Partition.Value}");
listBox1.TopIndex = listBox1.Items.Count - 1;
}
catch (ProduceException<Null, string> err)
{
MessageBox.Show($"Failed to deliver msg: {err.Error.Reason}");
}
}
private void frmProducer_Load(object sender, EventArgs e)
{
ProducerConfig config = new ProducerConfig { BootstrapServers = "localhost:9092" };
pBuilder = new ProducerBuilder<Null, string>(config).Build();
timer1.Enabled = true;
}
private void frmProducer_FormClosing(object sender, FormClosingEventArgs e)
{
timer1.Enabled = false;
pBuilder.Dispose();
}
}
}
Consumer in C# - windows form
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Confluent.Kafka;
namespace KafkaConsumer
{
public partial class frmConsumer : Form
{
CancellationTokenSource cts = new CancellationTokenSource();
public frmConsumer()
{
InitializeComponent();
}
private void StartListen()
{
var conf = new ConsumerConfig
{
GroupId = "test-consumer-group",
BootstrapServers = "localhost:9092",
AutoOffsetReset = AutoOffsetReset.Earliest
};
using (var c = new ConsumerBuilder<Ignore, string>(conf).Build())
{
c.Subscribe("testTopic");
//TopicPartitionTimestamp tpts = new TopicPartitionTimestamp("testTopic", new Partition(), Timestamp. )
//c.OffsetsForTimes()
try
{
while (true)
{
try
{
var cr = c.Consume(cts.Token);
// Adding the consumed values into the UI
listBox1.Invoke(new Action(() =>
{
listBox1.Items.Add($"{cr.Value} - from Partition: {cr.Partition.Value}" );
listBox1.TopIndex = listBox1.Items.Count - 1;
}));
}
catch (ConsumeException err)
{
MessageBox.Show($"Error occured: {err.Error.Reason}");
}
}
}
catch (OperationCanceledException)
{
// Ensure the consumer leaves the group cleanly and final offsets are committed.
c.Close();
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
cts.Cancel();
}
private async void frmConsumer_Load(object sender, EventArgs e)
{
await Task.Run(() => StartListen());
}
}
}
Consumer in NodeJs
const { Kafka } = require("kafkajs");
const kafka = new Kafka({
clientId: 'my-app',
brokers: ["localhost:9092"]
});
const consumer = kafka.consumer({ groupId: "test-consumer-group" });
const run = async () => {
// Consuming
await consumer.connect();
await consumer.subscribe({ topic: "testTopic", fromBeginning: false });
await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
console.log(message.value.toString() + " - from Partition " + partition);
}
});
};
run().catch(console.error);
If I run C# and NodeJs consumer at same time then getting Inconsistent group protocol error.
How to use multiple consumer from different programming languages in Kafka?
Short answer:
This may not have as much to do with the different languages as you might think. This is happening due to the differences in the protocols of the 2 consumer clients (and their libraries).
Try setting the following property in both the consumer clients:
partition.assignment.strategy = round-robin
Note: I've just supplied the general property so you'll need to look at the language specific versions for your clients. You could even set this to range but keep it consistent.
The explanation goes like this:
Reading through the protocol on Kafka's wiki to find out the root cause of Inconsistent group protocol - it turns out that this is returned when:
There is an active consumer group with active/running consumers
And a new consumer arrives to join this group with a protocol type (or a set of protocols) that is not compatible with that of the current group
Now, there could be various aspects in the ConsumerGroupProtocolMetadata but one of the aspects that does seem to differ in the libraries of the clients that you're using is the partition.assignment.strategy.
The dotnet client is a wrapper around librdkafka defaults the value of the above property to range. Here's the reference.
where as
kafkajs as per the documentation defaults it to round-robin - hence causing the inconsistency.
Hope this helps.
I know this comes one year too late but this happens because of the same group naming
When you start the C# client it creates a group for its consumers.
E.g. group-1 (group-1-consumer-1,group-1-consumer-2, etc) - These names are automatically allocated so don't bother. I think you can set these manually but is not recommended to avoid potential name collision.
Now, when you set this in motion you cannot add the same group from a different group runner (from another microservice).
See what Lalit quoted from Kafka wiki:
There is an active consumer group with active/running consumers
Now, when you will start the nodeJs one, you should use a different group name as most likely will carry out other tasks with that data.
Yes, you can subscribe both groups to the same topics as Kafka will keep an offset for each group and where they left of.

Create Windows Session programmatically from Console or Windows Service

How can I programmatically log in to windows to create a Windows Logon Session?
I need a way that works from a WinForms app, from a Console app, and (most important) from a Windows Service.
One other requirement is that I need it to work on a the local system that the program/service is running on and also for remote systems.
If there's a way to do this using pInvoke/Win32 API I am open to that too.
I found these similar questions/answers in my research:
Programmatically create and launch and RDP session (without gui)
The answer here says it's possible but and gives a link but the sample code from the link doesn't work
Create a Windows Session from a service via the Win32 API
No Solution to the question asked
Create Windows session programmatically
No Solution but the OP mentioned in a comment that http://freerdp.com worked for him.
I've created a simple utility that I believe meets all the requirements in the question. You'll need to add a COM reference to Microsoft Terminal Services Active Client 1.0 Type Library (ActiveX).
I thought it might not work for creating a session on the local machine but I tested in in 2012R2 running as a Service and it actually can. The same exact method can be called from a WinForms app or from a Console app. When launched from a WinForms or Console app, the a form is shown for a few seconds so I made sure to set the control to enabled = false so it can't be interacted with.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using AxMSTSCLib;
namespace Utility.RemoteDesktop
{
public class Client
{
private int LogonErrorCode { get; set; }
public void CreateRdpConnection(string server, string user, string domain, string password)
{
void ProcessTaskThread()
{
var form = new Form();
form.Load += (sender, args) =>
{
var rdpConnection = new AxMSTSCLib.AxMsRdpClient9NotSafeForScripting();
form.Controls.Add(rdpConnection);
rdpConnection.Server = server;
rdpConnection.Domain = domain;
rdpConnection.UserName = user;
rdpConnection.AdvancedSettings9.ClearTextPassword = password;
rdpConnection.AdvancedSettings9.EnableCredSspSupport = true;
if (true)
{
rdpConnection.OnDisconnected += RdpConnectionOnOnDisconnected;
rdpConnection.OnLoginComplete += RdpConnectionOnOnLoginComplete;
rdpConnection.OnLogonError += RdpConnectionOnOnLogonError;
}
rdpConnection.Connect();
rdpConnection.Enabled = false;
rdpConnection.Dock = DockStyle.Fill;
Application.Run(form);
};
form.Show();
}
var rdpClientThread = new Thread(ProcessTaskThread) { IsBackground = true };
rdpClientThread.SetApartmentState(ApartmentState.STA);
rdpClientThread.Start();
while (rdpClientThread.IsAlive)
{
Task.Delay(500).GetAwaiter().GetResult();
}
}
private void RdpConnectionOnOnLogonError(object sender, IMsTscAxEvents_OnLogonErrorEvent e)
{
LogonErrorCode = e.lError;
}
private void RdpConnectionOnOnLoginComplete(object sender, EventArgs e)
{
if (LogonErrorCode == -2)
{
Debug.WriteLine($" ## New Session Detected ##");
Task.Delay(10000).GetAwaiter().GetResult();
}
var rdpSession = (AxMsRdpClient9NotSafeForScripting)sender;
rdpSession.Disconnect();
}
private void RdpConnectionOnOnDisconnected(object sender, IMsTscAxEvents_OnDisconnectedEvent e)
{
Environment.Exit(0);
}
}
}
On a side note I found this question that says there may be a way to use the ActiveX control (for RDP) without using a windows form at all. I saw the example they gave and I was unsure hot to use their code for this situation.
ActiveX control without a form
If there's anyone out there who understands how to do this without hosting the ActiveX control on a Form please post an example.

BLE ValueChanged stops firing in C# app

I'm running Windows version 10.0.16299.0, and building on Visual Studio C# 2017. I can successfully connect to an unpaired BLE device from a Windows Forms app, and get ValueChanged events (1 per second), but not for long. I usually stop receiving those events in 40 seconds or less - usually less.
I realize this is likely a dispose/GC issue, but I don't see how. The device, service, characteristics, and descriptors are all stored as member variables in the main form and should not get collected:
public partial class Form1 : Form
{
private BluetoothLEDevice _device;
private List<GattDeviceService> _services;
private List<GattDescriptor> _descriptors = new List<GattDescriptor>();
private List<GattCharacteristic> _characteristics = new List<GattCharacteristic>();
private async void button1_Click(object sender, EventArgs e)
{
_device = await BluetoothLEDevice.FromIdAsync("BluetoothLE#BluetoothLE00:xx:xx:xx:xx:xx:xx:xx:xx:xx");
var services = await _device.GetGattServicesAsync();
foreach (var service in services.Services)
{
var chars = await service.GetCharacteristicsAsync();
foreach (var ch in chars.Characteristics)
{
var descriptors = await ch.GetDescriptorsAsync();
foreach (var desc in descriptors.Descriptors)
{
if (desc.AttributeHandle == 15 || desc.AttributeHandle == 26)
{
_services.Add(service);
_descriptors.Add(desc);
_characteristics.Add(ch);
var writer = new DataWriter();
writer.WriteBytes(new byte[] { 1, 0 });
var buf = writer.DetachBuffer();
await desc.WriteValueAsync(buf);
}
ch.ValueChanged += ChOnValueChanged;
}
}
}
}
In my sample, I click a button to establish a connection and subscribe to events. Before you say that writing to the descriptor is not how you would do it - I know. The device uses non-standard descriptor IDs which is why I must write to them directly.
Note that everything works, including the writes - I get no errors. It's just that the ValueChanged event is no longer fired after a short duration, and I can't figure out what else I must "cache" in order to prevent objects from being disposed, assuming that's what the problem is.
The problem is that because of the nested for each iterations you attach the characteristic_changed_event to multiple characteristics. That leads to unwanted behaviour.
The best way is to select the service that contains the wanted characteristic by UUID, then select the characteristic by UUID from that service.
If you insist on filtering by the wanted descriptor attribute handle,
finish all the "for each-es" before attaching the characteristic_changed_event.
The characteristic to attach to is probably first in _characteristics list.

How to programmatically pair a bluetooth device

I recently bought a Lilypad Simblee BLE Board and I'd like to pair it programmatically to my computer (using the 32feet.NET library in C#).
I'm aware the "How to programmatically pair a bluetooth device" has already been asked on StackOverflow (here for example), however for some reason, all my attempts to pair the device programmatically have failed. Indeed, I successfully paired the device with the "Manage Bluetooth devices" window in Windows 10 Settings panel (Settings > Devices > Bluetooth).
Firstly, I don't know the pairing method (either legacy or SSP) to use with my device. Windows never asked me for a PIN or something, so I guess it's SSP, but I'm unsure.
I searched on Google how to do a SSP pairing request with 32feet.NET: I found this.
However, once it discovered my device (the device discovery works properly), the pairing request instantly fails.
My code:
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System;
using System.Collections.Generic;
namespace HLK_Client
{
class HLKBoard
{
public event HLKBoardEventHandler HLKBoardConnectionComplete;
public delegate void HLKBoardEventHandler(object sender, HLKBoardEventArgs e);
private BluetoothClient _bluetoothClient;
private BluetoothComponent _bluetoothComponent;
private List<BluetoothDeviceInfo> _inRangeBluetoothDevices;
private BluetoothDeviceInfo _hlkBoardDevice;
private EventHandler<BluetoothWin32AuthenticationEventArgs> _bluetoothAuthenticatorHandler;
private BluetoothWin32Authentication _bluetoothAuthenticator;
public HLKBoard()
{
_bluetoothClient = new BluetoothClient();
_bluetoothComponent = new BluetoothComponent(_bluetoothClient);
_inRangeBluetoothDevices = new List<BluetoothDeviceInfo>();
_bluetoothAuthenticatorHandler = new EventHandler<BluetoothWin32AuthenticationEventArgs>(_bluetoothAutenticator_handlePairingRequest);
_bluetoothAuthenticator = new BluetoothWin32Authentication(_bluetoothAuthenticatorHandler);
_bluetoothComponent.DiscoverDevicesProgress += _bluetoothComponent_DiscoverDevicesProgress;
_bluetoothComponent.DiscoverDevicesComplete += _bluetoothComponent_DiscoverDevicesComplete;
}
public void ConnectAsync()
{
_inRangeBluetoothDevices.Clear();
_hlkBoardDevice = null;
_bluetoothComponent.DiscoverDevicesAsync(255, true, true, true, false, null);
}
private void PairWithBoard()
{
Console.WriteLine("Pairing...");
bool pairResult = BluetoothSecurity.PairRequest(_hlkBoardDevice.DeviceAddress, null);
if (pairResult)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Fail"); // Instantly fails
}
}
private void _bluetoothComponent_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
{
_inRangeBluetoothDevices.AddRange(e.Devices);
}
private void _bluetoothComponent_DiscoverDevicesComplete(object sender, DiscoverDevicesEventArgs e)
{
for (int i = 0; i < _inRangeBluetoothDevices.Count; ++i)
{
if (_inRangeBluetoothDevices[i].DeviceName == "HLK")
{
_hlkBoardDevice = _inRangeBluetoothDevices[i];
PairWithBoard();
return;
}
}
HLKBoardConnectionComplete(this, new HLKBoardEventArgs(false, "Didn't found any \"HLK\" discoverable device"));
}
private void _bluetoothAutenticator_handlePairingRequest(object sender, BluetoothWin32AuthenticationEventArgs e)
{
e.Confirm = true; // Never reach this line
}
}
}
Why does the pairing request fail?
The answer to the question you linked has a plausible suggestion... did you read it?
Also you should look at this question as well.
32feet library is built around legacy pairing, so that you either need to know the pin of the device you are connecting to, or you supply it with a null to get a popup window to enter a pin.
It also says that the windows function used by 32feet is deprecated in newer versions of windows. If that's true, the reason it's failing instantly is because you've passed a null pin in your pairing request and for it to proceed windows needs to show a dialog which no longer exists.
What happens if you try to connect with the pin "0000" or "1234" ?
I'm looking at the source code of WindowsBluetoothSecurity.cs in 32feet.net and I see if a pairing request fails, it logs the error code to Debug.WriteLine, any chance you could post that error code here?
One good work around to this problem might be to import BluetoothAuthenticateDeviceEx and use that manually to complete the pairing request. If you don't want to do this manually, it looks like in the latest version of the 32feet source, there is actually a SSP pairing method that utilises this method but it's not public and it's not used anywhere so you'll need to access it via reflection:
typeof(BluetoothSecurity)
.GetMethod("PairRequest", BindingFlags.Static | BindingFlags.NonPublic)
.Invoke(null, new object[] { _hlkBoardDevice.DeviceAddress, BluetoothAuthenticationRequirements.MITMProtectionNotRequired });

What is causing this DatabaseFileLockedException when trying to open a db4o database in an ASP.NET MVC app?

I'm building a small web application with ASP.NET MVC 2, using db4o as a datastore.
I have added an HttpModule—as per the example here—to give the application access to the db4o database, and everything is working perfectly on my development machine under the VS2008 ASP.NET Development Server.
However, when I deploy the app to my web host and try to access it, I get a DatabaseFileLockedException at the line where the HttpModule tries to open the database file. But there should be nothing else accessing the file; indeed on first run of the app it will only just have been created when this exception gets thrown.
The web host's servers are running IIS 7 on Windows Server 2008, and the application is running under Full Trust. It is a sub-application, in case that makes any difference.
I can't work out why this error is occurring on the live server, but not locally on my development server. Can anyone help me out or suggest what I should do next?
That's a mistake in the example-code. It assumes that the HttpModule.Init is only called once, which isn't necessarily true. Depending how your application is configured, it can be called multiple times. To fix this, check in the HttpModule-Handler if the instance is already there:
using System;
using System.Configuration;
using System.Web;
using Db4objects.Db4o;
namespace Db4oDoc.WebApp.Infrastructure
{
public class Db4oProvider : IHttpModule
{
private const string DataBaseInstance = "db4o-database-instance";
private const string SessionKey = "db4o-session";
// #example: open database when the application starts
public void Init(HttpApplication context)
{
if (null==HttpContext.Current.Application[DataBaseInstance])
{
HttpContext.Current.Application[DataBaseInstance] = OpenDatabase();
}
RegisterSessionCreation(context);
}
private IEmbeddedObjectContainer OpenDatabase()
{
string relativePath = ConfigurationSettings.AppSettings["DatabaseFileName"];
string filePath = HttpContext.Current.Server.MapPath(relativePath);
return Db4oEmbedded.OpenFile(filePath);
}
// #end example
// #example: close the database when the application shuts down
public void Dispose()
{
IDisposable toDispose = HttpContext.Current.Application[DataBaseInstance] as IDisposable;
if (null != toDispose)
{
toDispose.Dispose();
}
}
// #end example
// #example: provide access to the database
public static IObjectContainer Database
{
get { return (IObjectContainer)HttpContext.Current.Items[SessionKey]; }
}
// #end example
// #example: A object container per request
private void RegisterSessionCreation(HttpApplication httpApplication)
{
httpApplication.BeginRequest += OpenSession;
httpApplication.EndRequest += CloseSession;
}
private void OpenSession(object sender, EventArgs e)
{
IEmbeddedObjectContainer container =
(IEmbeddedObjectContainer)HttpContext.Current.Application[DataBaseInstance];
IObjectContainer session = container.OpenSession();
HttpContext.Current.Items[SessionKey] = session;
}
private void CloseSession(object sender, EventArgs e)
{
if (HttpContext.Current.Items[SessionKey] != null)
{
IObjectContainer session = (IObjectContainer)HttpContext.Current.Items[SessionKey];
session.Dispose();
}
}
// #end example
}
}
As alternative you could use the Application_Start from the Global.apsx, which is called only once for sure.
You have another problem here.
When AppPools restart there can be an overlap when the old AppPool is finishing request and the new AppPool is servicing new requests.
During this time you will have two processes trying to access the same db4o file
To get around this you can use something like the hack below.
Note the use of Db4oFactory.OpenServer instead of Db4oEmbedded.OpenFile. This allows the use of transactions on a more fine grained basis.
public IObjectServer OpenServer()
{
Logger.Debug("Waiting to open db4o server.");
var attempts = 0;
do
{
try
{
return Db4oFactory.OpenServer(fileName, 0);
}
catch (DatabaseFileLockedException ex)
{
attempts++;
if (attempts > 10)
{
throw new Exception("Couldn't open db4o server. Giving up!", ex);
}
Logger.Warn("Couldn't open db4o server. Trying again in 5sec.");
Thread.Sleep(5.Seconds());
}
} while (true);
}
Hope this helps
Sounds like permission issues if it works on dev. Stick a notepad file in the same directory and try to open that with some bare bones file code. I bet you'll have the same issue.

Categories