I wrote an ETW EventSource using the Microsoft EventSource Libary 1.1.25 on Nuget. The purpose of the EventSource is to send events to a custom event log for a security application we maintain. The code works locally, but we can not get events to be written to the event log on the server.
The EventSource is named (similar too) Company-Security and sends events to the Admin Channel. Locally on my development machine, I can register the eventsource manifest with wevtutil, and see the Company-Security folder with the Admin log underneath in Windows Event Viewer. When I run the application, the events are recorded in the event log.
However, when I deploy the application to the test server (running Windows Server 2012), event logging is not working. The log is created and visible in Event Viewer after I register the manifest with wevtutil, though the name is slightly different. A folder named Company-Security/Admin is created with a log named Company-Security/Admin insider the folder. I can also run perfview on the server and see the events created. However, nothing is ever written to the event log. I have also put some debug statements in the EventSource code and can see that the EventSource IsEnabled() is returning true.
Below are code snippets of the base class and the implementation class of the eventsource I wrote.
I've researched and can't find any explanation as to why event logging does not work on the server, but works on the development machine. I assume I am missing something, but not sure what.
Abstract Base Class:
public abstract class SecurityEventsBase : EventSource {
protected unsafe void WriteEvent(int eventId, long arg1, string arg2, string arg3) {
if (IsEnabled()) {
if (arg2 == null) {
arg2 = "[not provided]";
}
if (arg3 == null) {
arg3 = "[not provided]"; ;
}
fixed (char* arg2Ptr = arg2) {
fixed (char* arg3Ptr = arg3) {
EventSource.EventData* dataDesc = stackalloc EventSource.EventData[3];
dataDesc[0].DataPointer = (IntPtr)(&arg1);
dataDesc[0].Size = 8;
dataDesc[1].DataPointer = (IntPtr)arg2Ptr;
dataDesc[1].Size = (arg2.Length + 1) * 2;
dataDesc[2].DataPointer = (IntPtr)arg3Ptr;
dataDesc[2].Size = (arg3.Length + 1) * 2;
WriteEventCore(eventId, 3, dataDesc);
}
}
}
}
EventSource Class:
[EventSource(Name="Company-Security",LocalizationResources="Events.Properties.Resources")]
public sealed class AuthorizationEvents : SecurityEventsBase {
public static AuthorizationEvents Log = new AuthorizationEvents();
[Event(2000,Level=EventLevel.Informational,Channel=EventChannel.Admin,Message="User '{1}' ({0}) logged in successfully from IP Address {2}")]
public void Login(long UserId, string UserName, string IPAddress) {
if (IsEnabled()) {
WriteEvent(2000, UserId, UserName, IPAddress);
}
}
** additional events would follow here**
}
I finally resolved this problem. It had to do with permissions on the folder the manifest and binary manifest resource files were stored in.
I found this StackOverflow answer which helped me resolve the problem: https://stackoverflow.com/a/13090615/5202678
I had to grant Read & Execute privileges to the folder to the local Users group to the folder the manifest files were stored in. Once I did this, events immediately started recording in the Event Log.
Related
System-Environment:
Windows 10 Pro - Version: 1909 - OS System Build: 18363.752
Microsoft Outlook 2019 MSO - Version 1808 - 32-Bit
Microsoft Exchange 2016 15.1 Build (Build 1979.3)
-- Microsoft Exchange is installed on Microsoft Server 2016
Outlook Redemption COM-Library - Version 5.22.0.5498
Issue Summary:
The application sends emails via Outlook using the Outlook-Redemption COM-Library. The class "RedemptionHandler" is our Singleton-Class which interacts with the Outlook-Redemption COM-Library. During the construction of the RedemptionHandler we create a RDOSession with a static class named RedemptionLoader and call Logon() on the RDOSession. The RDOSession is used afterwards in Initialize() to retrieve the Folders for Drafts and mails which are sent.
public static class RedemptionLoader
{
public static RDOSession new_RDOSession()
{
return (RDOSession)NewRedemptionObject(new Guid("29AB7A12-B531-450E-8F7A-EA94C2F3C05F"));
}
}
public class RedemptionHandler
{
private static RedemptionHandler instance = null;
private static readonly object padlock = new object();
private RDOSession _rdoSession;
private RDOFolder _rdoSentFolder;
private RDOFolder _rdoDraftsFolder;
private RDOItems _sentItems = null;
public EventHandler<MailGesendetEventArgs> MailSuccessfullySent;
private RedemptionHandler()
{
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon(null, null, false, null, null, null);
Initialize();
}
public static RedemptionHandler Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new RedemptionHandler();
}
return instance;
}
}
}
private void Initialize()
{
try
{
if (isInitialized) return;
_rdoSentFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderSentMail);
_sentItems = _rdoSentFolder.Items;
_sentItems.ItemAdd += MailSent;
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderDrafts);
isInitialized = true;
}
catch
{
//TODO
isInitialized = false;
}
}
}
At this point, we have a working instance from our RedemptionHandler. The COM-Object RDOSession is created and referenced within just as the RDOFolder for Drafts and Sent. We have also registrered an event-listener for the Sent-Folder to recognize new Mails in this folder.
In the next steps we want to send an email and recognize this email if its stored in the sent-folder. We use the RDOMail.Fields - Property to store custom data within the RDOMail-Object.
public RDOMail CreateMail(string recipient, string subject, string body, Guid gdSender, string storagePath)
{
RDOMail newMail = _rdoDraftsFolder.Items.Add(Redemption.rdoItemType.olMailItem);
newMail.Recipients.Add(recipient);
newMail.Recipients.ResolveAll();
newMail.Subject = subject;
newMail.HTMLBody = body;
newMail.BodyFormat = (int)rdoBodyFormat.olFormatHTML;
// Here we want to store an identifier in the RDOMail.Fields
int id = newMail.GetIDsFromNames(PropertyGuid, PropertyGdItemId);
newMail.Fields[id] = Guid.NewGuid().ToString();
return newMail;
}
After the mail creation we want to display the mail to the user because we dont want to send data without letting the user know about it.
public void DisplayMail(RDOMail mail, bool modal = false)
{
mail.Display(modal, null);
}
The Outlook window now comes to front and the user checks the mail and clicks on send.
The Mail is now stored in the Sent-Folder.
The MailSent Event gets invoked by the RDOFolder.Items.Add Listener.
private void MailSent(RDOMail mail)
{
var test = mail.Fields[SenderId];
Console.WriteLine(test);
// test value is correct!
}
Difference between Exchange in Online-Mode and Cache-Mode:
If we use the Exchange with Cache-Mode, everything works fine. Everytime we send an email, the MailSent is triggered and we can read data from the RDOMail.Fields-Property. If we switch to Exchange without Cache, the MailSent Event is triggered only once, when the first mail is sent. All emails afterwars are sent but dont trigger the MailSent-Event. If we delete this line of code, everything works also fine without Cache-Mode.
var test = mail.Fields[SenderId];
This is because we think that reading data from the RDOMail.Fields - Property does something special if the cache-mode from exchange is deactivated.
We need to store custom data within the mails to check if new mails in the sent-folder are created by our application or not.
We highly appreciate help and hints.
I tried to fix this issue without success. I've set-up a new Project without any other code:
public partial class RedemptionTest : Form
{
static RDOSession _rdoSession;
static RDOFolder _rdoSentFolder;
static RDOFolder _rdoDraftsFolder;
static RDOItems _draftItems;
static RDOItems _sentItems;
public RedemptionTest()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon();
_rdoSentFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderSentMail);
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts);
_sentItems = _rdoSentFolder.Items;
_draftItems = _rdoDraftsFolder.Items;
_draftItems.ItemAdd += DraftAdd;
_sentItems.ItemAdd += MailSent;
}
private void DraftAdd(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
private void MailSent(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
}
The Drafts-Folder Event is fired all the time, the MailSent Event is only fired the first time. I have stored all RDO-Objects in static variables to avoid them from being garbage collected.
The object raising the events (RDOItems) must be alive be able to fire the events. Your code is using multiple dot notation, which means the compiler creates an implicit variable to hold the RDOItems collection. As soon as that variable is released by the Garbage Collector, no events will be fired.
The line
_rdoSentFolder.Items.ItemAdd += MailSent;
must be changed to
RDOItems _sentItems; //global/class variable
..
_sentItems = _rdoSentFolder.Items;
_sentItems .ItemAdd += MailSent;
Have the same issue in Outlook VSTO add-in using Redemption. Happens for both Sent and Inbox folder. The same code works correctly in cached mode but fires events only once in Online mode.
Native Outlook object model Items.ItemAdd works correctly in Online mode for the same folder.
Currently, we were able to do a workaround for this by unsubscribing and resubscribing to event right in the event handler. Like this:
private void SentItems_ItemAdd(RDOMail rdoMail)
{
_sentItems.ItemAdd -= SentItems_ItemAdd;
_sentItems.ItemAdd += SentItems_ItemAdd;
Log.Debug("SentItems.ItemAdd");
SentMailItemAdd?.Invoke(rdoMail);
}
This is my first time dealing with Web Services. I have successfully Added a Web Service to one that I have created in VS 2010. What I'm trying to do is access the functions of the added web service in this .asmx file This is what I see now along with all the auto added code.
Service1.asmx.vb
Public Class Service1
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function HelloWorld() As String
Return "Hello World"
End Function
The added web service that I have added is called blahService. So I'm just curious as how do I access the functions that are in the added web service? Do I have to do something like this...?
Dim foo as new blahService()
Then when I go to access a function just do
foo.function()
1) From within your Solution Explorer of your project, right-click on “Service References”, then click on “Add Service Reference”
2) The dialog box which appears allows you to put in the URL of your web service. Enter it, then press the “Go” Button”
3) You can see that the name of the web service appears in the Services pane. Give your webservice a namespace (anything you like) that will be used to refer to it from within your project. Press the OK button. That namespace will now appear in the list of Service References
A web service is considered to have anonymous authentication if no specific permission is required to access it. The server is allowed to fulfill every request without regard to the entity who is requesting the information. This is the case for many web services on the internet.
For reference, this is the source code for the method which I will be calling from my application:
[WebMethod]
public List<string> GetStrings(int StartNumber, int EndNumber)
{
List<string> MyList = new List<string>();
for (int i = StartNumber; i <= EndNumber; i++)
{
MyList.Add("AuthASMXService String #" + i.ToString());
}
return MyList;
}
and here’s the code that will call the method in the web service instantiated above:
private void ASMXWebServiceInvoke_Click_1(object sender, RoutedEventArgs e)
{
ASMXWebServiceReference.WebService1SoapClient MyASMXWebServiceClient
= new ASMXWebServiceReference.WebService1SoapClient();
ASMXWebServiceReference.GetStringsResponse MyStringsResponse =
MyASMXWebServiceClient.GetStrings(10, 20);
ASMXWebServiceReference.ArrayOfString MyStrings =
MyStringsResponse.Body.GetStringsResult;
ASMXGridView.ItemsSource = MyStrings;
}
How do I connect to an ASMX web service?
I have a windows service that can create an executable in the users windows session, via calling the "CreateProcessAsUser" function. This works fine as long as there is a windows session already there. In the case that there isn't one already I'd like to be able to create one programmatically. Is this is possible? Can't seem to find a function to do it.
This isn't quite the solution for the question I asked, but it was the solution that helped achieve what I was trying to achieve by asking this question, if you see what I mean.
Rather than have having a windows services that creates a server session you can configure windows to automatically logon at boot time. This still means someone could accenditally log off, but cures the main reason for sessions disappearing: the server being rebooted. Use the following steps to activate auto-logon:
Press the Windows key + R on your keyboard to launch the “Run” dialog box.
Type regedit and hit enter to open the Registry Editor
Then browse to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\
Set AutoAdminLogon = 1 (create it if doesn't exist its a string variable)
Set DefaultUserName = your username (create it if doesn't exist its a string variable)
Set DefaultPassword = your password (create it if doesn't exist its a string variable)
Instructions were taken from this post:
http://channel9.msdn.com/Blogs/coolstuff/Tip-Auto-Login-Your-Windows-7-User-Account
You cannot create a new session from a service. Sessions are managed by the OS. New ones get created when users logon interactively.
#Robert, I know this is an old question and that you've already found something that works for you but in my case I was looking for something similar to your original question and I did finally figure it out so I thought I'd share. My solution uses only .NET and a COM reference not the Win32 API mentioned in your title, but I'm guessing that wasn't really a requirement for you.
I've written a simple utility to using the Remote Desktop ActiveX control (COM Reference). If you paste this code into a Class Library you can then call it by simply passing in the server, username, domain, and password and everything is done for you without any other interaction required. Once the method is complete you can then call your "CreateProcessAsUser" Code. I've written this utility in a way so that you could call it every time but initiating an RDP session takes several seconds so for performance sake I'd suggest you write another method to enumerate the sessions and see if your user is logged in and only call this utility when you determine that your user isn't logged in (That's what I did in my actual project). If you feel you need help with that post in the comments and I'll share how I did that but It's not really part of this question so for now I'm leaving it out.
Here's a link back to my question that has a few more requirements/details than this question.
Create Windows Session programmatically from Console or Windows Service
And here's my RDP utility. After you put this code in a class library you can then call it from a console app, winForms app, or from a windows service running on the same machine or from a remote machine.
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);
}
}
}
What about the LogonUser function?
http://winapi.freetechsecrets.com/win32/WIN32LogonUser.htm
How can i start a windows forms application before log on to windows? Is is possible to start a windows forms application before log on to windows? If it's not, do i have a chance to start a windows service before log on and invoke a windows forms application from the service that is already started before log on?
According to the comments to the question you want to run a standard desktop app, built with WinForms, not a service, that starts before the user has logged on.
This is not possible. What you need is a service.
Very basic, but should give you the gist. You also need to create a ServiceProcessInstaller for it (along with making a call to installutil).
public class WinFormHostService : System.ServiceProcess.ServiceBase
{
[STAThread]
public static void Main()
{
System.ServiceProcess.ServiceBase.Run(new WinFormHostService());
}
protected Process winFormsProcess;
public WinFormHostService()
{
this.ServiceName = "WinForm Host Service";
this.AutoLog = true;
}
protected override void OnStart(String[] args)
{
this.winFormsProcess = new Process();
try
{
this.winFormsProcess.UseShellExecute = false;
this.winFormsProcess.FileName = #"C:\Program Files\MyApp\MyApp.exe";
this.winFormsProcess.CreateNoWindow = true;
this.winFormsProcess.Start();
}
catch (Exception ex)
{
// unable to start process
}
}
}
This is basically like hosting a WCF service from a windows service, so if you need more details look up "WCF windows service host" (or alike) and see how that's done. Same premise, you're just using a Process instead.
I'm trying to create a server to client broadcast mechanism with SignalR and it doesnt seem to do anything.
I have a hub like this:
public class DataMessageService : Hub, IClientNotificationService
{
dynamic _clients;
public DataMessageService(IConnectionManager connectionManager)
{
_clients = connectionManager.GetClients<DataMessageService>();
}
public void SendDataChangeNotification(string entityName, string changeType, string id)
{
_clients.dataChangeNotification(new string[] {entityName, changeType, id});
}
}
My _Layouts.cshtml has this:
var _centralHub;
$(function() {
// startup the signalr hub and get the proxies we need
_centralHub = $.connection.dataMessageService;
$.connection.hub.start();
});
And I have some code in a partial which is loaded by a jquery tab using ajax:
_centralHub.dataChangeNotification = function (data) {
alert(data[0] + "::" + data[1] + "::" + data[2]);
if (data[0] == 'User') {
grid.refresh();
}
};
Now in the data layer, when some crud action occurs, I call DataMessageService.SendDataChangeNotification but nothing happens at the client end.
Am I missing something?
Update: I thought it might be something to do with the vs web server thingy but it also fails when using full IIS (on Win 7).
Another Update:
I had confused my service with my hub. I'v'e now split these so it looks like the following, but it still doesnt work.
public class DataMessageService : IClientNotificationService
{
public void SendDataChangeNotification(string entityName, string changeType, string id)
{
IConnectionManager icm = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = icm.GetClients<DataMessageHub>();
clients.dataChangeNotification(new string[] { entityName, changeType, id });
}
}
public class DataMessageHub : Hub
{
}
:(
Even more info:
This works with FireFox but not with IE or Chrome.
I also tried to create a simple sample app and this worked fine with Chrome and IE.
Given that we don't have web sockets available to us, long polling may not be a good idea for our users/infrastructure. Maybe one day...
A new instance of the hub is created every time it is resolved so you can't persist state like that.
You can get all clients in the hub from this.Clients.
To broadcast from outside of the hub class use this:
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<DataMessageService>();
clients.dataChangeNotification(new string[] {entityName, changeType, id});