Create Windows Session programmatically from Console or Windows Service - c#

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.

Related

C# - Windows Service EventMonitor function with parameters

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

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 });

Skype bot (translation from VB) not working

I was following a tutorial on youtube on how to create a simple Skype bot. It was written in VB and with my limited knowledge I did my best to recreate it in C#
I stumbled upon "handles" which I can only assume is related to the eventhandler in C#
This is the code I've got so far but when I message myself from another skype account it doesn't respond. I've made sure to accept the little popup on skype that allows 3rd party software.
public partial class Form1 : Form
{
Skype oSkype = new Skype();
string trigger = "!";
public Form1()
{
InitializeComponent();
oSkype.Attach(7, false);
oSkype.MessageStatus += new _ISkypeEvents_MessageStatusEventHandler(oSkype_MessageStatus);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void oSkype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
{
if (Status == TChatMessageStatus.cmsReceived || Status == TChatMessageStatus.cmsSent)
{
string msg = pMessage.Body;
Chat c = pMessage.Chat;
if (msg.StartsWith(trigger))
{
listBox1.Items.Add(DateTime.Now.ToLongTimeString() + ": " + pMessage.Sender.Handle + " sent you a message");
msg = msg.Remove(0, 1).ToLower();
if (msg == "test")
{
c.SendMessage("Test");
}
else
{
c.SendMessage("Unrecognizable command.");
}
}
}
}
}
The code from the tutorial that I was following had this instead:
oSkype_MessageStatus(pMessage as ChatMessage, Status as TChatMessageStatus) Handles oSkype.MessageStatus
The closest to what I could come to implement this in c# was to add the void to the eventhandler in public Form1() which you can see in my code.
Thanks in advance!
Skype4Com's chat functions are not supported in the newer Skype versions. They were deprecated somewhere in-between 2013-2014.
From Skype's blog post Feature evolution and support for the Skype Desktop API:
I’m happy to share that we will be extending support for two of the most widely used features – call recording and compatibility with hardware devices – until we determine alternative options or retire the current solution. Although chat via third party applications, will cease to work as previously communicated.
It has been a while since I have worked with COM Skype bots, but your code seems to be fine.
Nevertheless, I would suggest you to move to a modern approach on bots. Please check out the new Microsoft Bot Framework

pass telephone to default windows softphone while its running

I developed a softphone for windows, I know how to register it as default tell application by reading this question, but I don`t know how get arguments sent from a web application or another win application while my softphone is running.
The standard code to call tell app from web app is something like this:
window.open("tel: 05525825");
If you have registered your application for the scheme tel: and the Command is "yourapp.exe %1", then you can read them from the commandline arguments as explained in How to access command line parameters outside of Main in C#:
string arguments = Environment.GetCommandLineArgs();
string phoneNumber = arguments[1];
Of course you need to do some sanity checking before bluntly accessing and using the array element.
If you setup the protocol URL keys correctly your application will be run with the data in the command line (E.g. args[] in main())
To pass data to an already running instance of your application the easiest way is to use the StartupNextInstance event provided by VisualBasic.ApplicationServices and re-process new incomming command lines:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace Foo
{
static class Program
{
[STAThread]
static void Main(string[] args)
{
var applicationBase = new ThisWindowsApplicationBase();
applicationBase.StartupNextInstance += (sender, e) => { applicationBase.HandleCommandLine(e.CommandLine); };
applicationBase.Run(args);
}
}
class ThisWindowsApplicationBase : WindowsFormsApplicationBase
{
internal ThisWindowsApplicationBase()
: base()
{
this.IsSingleInstance = true;
this.MainForm = new Form1();
this.HandleCommandLine(Environment.GetCommandLineArgs().Skip(1));
}
internal void HandleCommandLine(IEnumerable<string> commandLine)
{
this.MainForm.Text = "Processing: " + commandLine.FirstOrDefault();
}
}
}
Note this will not fire for the first run.

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