HttpModule in IIS 8.5 not being loaded - c#

I've written a simple managed HttpModule for IIS 8.5 in C# and have installed this into the global-assembly cache (CLR version 4.0.30319). This is detected as present by IIS and I've installed it as a module at the application host level.
Unfortunately it doesn't seem to be executed at all. We're only serving up static content, but since IIS in running in integrated mode I was under the impression that HttpModules were executed for all requests, not just those in the ASP.NET pipeline.
The HttpModule has been reduced to the simplest possible level - logging when the module is created, disposed of, a request begins and a request ends, as below:
using System;
using System.Web;
using System.Collections.Specialized;
using System.IO;
public class ServerLoggingModule : IHttpModule
{
public ServerLoggingModule()
{
using (StreamWriter file = new StreamWriter(#"C:\LocalIISAuth\logs\iis.log"))
{
file.WriteLine("Created module");
}
}
public string ModuleName
{
get { return "ServerMaskModule"; }
}
public void Dispose()
{
using (StreamWriter file = new StreamWriter(#"C:\LocalIISAuth\logs\iis.log"))
{
file.WriteLine("Disposed of module");
}
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(ServerLoggingModule.BeginRequestHandler);
context.EndRequest += new EventHandler(ServerLoggingModule.EndRequestHandler);
}
protected static void BeginRequestHandler(Object sender, EventArgs e)
{
using (StreamWriter file = new StreamWriter(#"C:\LocalIISAuth\logs\iis.log"))
{
file.WriteLine("BeginRequest");
}
}
protected static void EndRequestHandler(Object sender, EventArgs e)
{
using (StreamWriter file = new StreamWriter(#"C:\LocalIISAuth\logs\iis.log"))
{
file.WriteLine("EndRequest");
}
}
}
The request reaches the point in the pipeline the module resides at, but just seems to skip the module. There are no errors displayed and the page displays fine.
Thanks in advance

As it turns out we hadn't enabled the .NET extensibility Windows features which resulted in the module not being loaded. Unfortunately IIS does let you add the managed module to it, despite it surely knowing it would never be able to launch it!
To add these modules with PowerShell use:
Install-WindowsFeature Web-Net-Ext
Install-WindowsFeature Web-Net-Ext45

Sometimes pools get corrupted in some way, recreating pool fixes such problem. Hope it helps someone.

Related

Wireguard tunnel source to c#.Net: Service Run troubleshooting

I'm trying to make a simple console app client (starter.exe) on c# .NET Framework 4.6 to make a WireGuard protocol based connection using Wireguard source code.
What is done:
Downloaded wireguard source code from here: git://git.zx2c4.com/wireguard-windows
Successfuly built Tunnel.dll in ..\embeddable-dll-service\amd64\tunnel.dll via build.bat
Created a project in Visual Studio 2015.using the c# code from ..\embeddable-dll-service\csharp
Starting from here some strange thing are happenning:
if launching starter.exe \service <path to *.conf> I receive the
error
Service run error: The service process could not connect to the
service controller.
if launching starter.exe without parameters everything works fine until I remove the if{} block:
Unhandled Exception: System.ComponentModel.Win32Exception: The service
did not respond to the start or control request in a timely fashion
at WireGuardTunnel.Service.Add(String configFile) in
D:\Depository\BitBucket\WireGuard_Tunnel_Repository\WireGuardTunnel_proj\Launcher\Service.cs:line
69 at WireGuardTunnel.Program.Main(String[] args) in
D:\Depository\BitBucket\WireGuard_Tunnel_Repository\WireGuardTunnel_proj\Launcher\Program.cs:line
83
That means even if the code in if{} block is not executed it influencese somehow the application behaviour.
Next, as I want to make my app work with parameters I solved the
issue by removing return afer Service.Run and passing args[1] to Service.Add(args[1]). It works OK, but I have an extra log line (the first one due to Service.Run perpetual error described above) in the log:
Service run error: The service process could not connect to the
service controller. 235660: [TUN] [chicago4] Watching network
interfaces 245661: [TUN] [chicago4] Resolving DNS names
245661: [TUN] [chicago4] Creating Wintun interface 225660: [TUN]
[chicago4] Starting WireGuard/0.3.1 (Windows 6.1.7601; amd64)
So finally the questions:
Why Service.Run(confFile) does not work
Why Service.Run(confFile) influences the Service.Add(confFile)
Why if{} block is executed when I launch starte.exe with no parameters
The original Program.cs without modification:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
namespace Tunnel
{
class Program
{
[DllImport("kernel32.dll")]
private static extern bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handler, bool add);
private delegate bool SetConsoleCtrlEventHandler(UInt32 signal);
public static void Main(string[] args)
{
string baseDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
string configFile = Path.Combine(baseDirectory, "demobox.conf");
string logFile = Path.Combine(baseDirectory, "log.bin");
if (args.Length == 2 && args[0] == "/service")
{
configFile = args[1];
Service.Run(configFile);
return;
}
try { File.Delete(logFile); } catch { }
Ringlogger log = new Ringlogger(logFile, "GUI");
var logPrintingThread = new Thread(() =>
{
var cursor = Ringlogger.CursorAll;
while (Thread.CurrentThread.IsAlive)
{
var lines = log.FollowFromCursor(ref cursor);
foreach (var line in lines)
Console.WriteLine(line);
Thread.Sleep(300);
}
});
logPrintingThread.Start();
SetConsoleCtrlHandler(delegate
{
Service.Remove(configFile);
Environment.Exit(0);
return true;
}, true);
try
{
Service.Add(configFile);
logPrintingThread.Join();
}
finally
{
Service.Remove(configFile);
}
}
}
}
Bit late to the party but I was having the exact same issue as above and discovered that in order to get everything working correctly you have to have Tunnel.Service.Run("path to config") defined on application initialization either in your main loop or your constructor then you can run Tunnel.Service.Add("path to config", true) which will create the service and start the VPN connection. It's also good practice to destroy the service on close using Tunnel.Service.Remove("path to config", true) as the service will continue to run and you will still be connected to your VPN until it is stopped manually.

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.

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.

Web request monitoring for .net

Problem Background
I have few web applications which were developed using Microsoft .net framework 4.0 and running on the IIS 7.0 .so now i need to track traffic information(requested user,ip,application name ,address,request time etc..) for all these application and persist into a database or text file etc.
while i was searching on this i found HTTP handlers concept.
Is this handy to use my case ? or are there any alternatives which can fulfill my requirement?
I need to write this component as plug-abbe one.because i need connect this component easily to another web application also.
appreciate your ideas
Use an HttpModule to intercept all requests via the BeginRequest event, like this:
public class RequestLoggingModule : IHttpModule
{
private HttpApplication httpApp;
public void Init(HttpApplication httpApp)
{
this.httpApp = httpApp;
httpApp.BeginRequest += new EventHandler(OnBeginRequest);
}
void OnBeginRequest(object sender, EventArgs e)
{
// Get the user that made the request
HttpApplication application = (HttpApplication)sender;
HttpResponse response = application.Context.Response;
WindowsIdentity identity =
(WindowsIdentity)application.Context.User.Identity;
LogInformation(identity.Name);
// Do this for other information you want to log here
}
private void LogInformation(string data)
{
EventLog log = new EventLog();
log.Source = "Application XYZ Request Logging";
log.WriteEntry(data, EventLogEntryType.Information);
}
public void Dispose()
{
}
}

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