I have created and installed C# FileSystemWatcher, this part works but nothing happens when I add a file to the source folder, the file I add to the source folder should be copied to a destination folder, but that does not happen.
This is my Filesyswatcher.designer.cs
using System;
using System.Configuration;
using System.IO;
namespace HotFolderWatch
{
partial class FileSysWatcher
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.FSWatcher = new System.IO.FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this.FSWatcher)).BeginInit();
//
// FSWatcher
//
this.FSWatcher.Changed += new FileSystemEventHandler(FSWatcher_Changed);
this.FSWatcher.Created += new FileSystemEventHandler(FSWatcher_Created);
this.FSWatcher.Deleted += new FileSystemEventHandler(FSWatcher_Deleted);
this.FSWatcher.EnableRaisingEvents = true;
this.FSWatcher.NotifyFilter = ((System.IO.NotifyFilters)((((((System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName)
| System.IO.NotifyFilters.Size)
| System.IO.NotifyFilters.LastWrite)
| System.IO.NotifyFilters.LastAccess)
| System.IO.NotifyFilters.CreationTime)));
//
// FileSysWatcher
//
this.ServiceName = "FileSysWatcher";
((System.ComponentModel.ISupportInitialize)(this.FSWatcher)).EndInit();
}
#endregion
private System.IO.FileSystemWatcher FSWatcher;
/* DEFINE WATCHER EVENTS... */
/// <summary>
/// Event occurs when the contents of a File or Directory are changed
/// </summary>
private void FSWatcher_Changed(object sender,
System.IO.FileSystemEventArgs e)
{
//code here for newly changed file or directory
}
/// <summary>
/// Event occurs when the a File or Directory is created
/// </summary>
private void FSWatcher_Created(object sender,
System.IO.FileSystemEventArgs e)
{
//code here for newly created file or directory
try
{
System.IO.File.Copy(e.FullPath, DestinationPath + e.Name, true);
}
catch (Exception ex)
{
//Util.WriteToErrorLogFile(ex);
}
}
/// <summary>
/// Event occurs when the a File or Directory is deleted
/// </summary>
private void FSWatcher_Deleted(object sender,
System.IO.FileSystemEventArgs e)
{
//code here for newly deleted file or directory
}
}
}
And this is my FileSysWatcher.cs file..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace HotFolderWatch
{
partial class FileSysWatcher : ServiceBase
{
private string _userName;
public FileSysWatcher()
{
InitializeComponent();
}
public static string DestinationPath;
public const string MyServiceName = "FileSysWatcher";
private FileSystemWatcher watcher = null;
protected override void OnStart(string[] args)
{
FSWatcher.Path = ConfigurationManager.AppSettings["WatchPath"];
DestinationPath = ConfigurationManager.AppSettings["DestinationPath"];
_userName = Environment.UserName;
// Begin watching.
FSWatcher.EnableRaisingEvents = true;
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}
}
}
I have also tried to attach to the process while debugging but the event does not seem to occur, anyone see any mistakes that could cause this?
I have tested your code on my system (Windows 7 64): it works. A few things you should check:
Is the WatchPath / DestinationPath a local path on the system where the service is running ? Be aware that accessing a network share by a service typically requires that the service runs under a (domain) user account. Also, a mapped network drive is not seen by the service, so you should use UNC paths if acessing (SMB) file shares.
At the moment where the created event triggers, the file could be in use, so your copy may fail.
and the logging is commented beacuse that part isnt executed either.
Ok, but nevertheless, you should add more logging.
My Windows services usually contain:
protected override void OnStart(string[] args)
{
// this is not just a simple message, this has to be called very early before any worker thread
// to prevent a race condition in the .NET code of registering the event source
EventLog.WriteEntry("XxxxService is starting", EventLogEntryType.Information, 1000);
I was recently faced this issue, found this code below in above answer,
FSWatcher.EnableRaisingEvents = true;
This line of code was throwing exception previously, but later on, after adding above line, my watcher was triggering events, which makes no sense.
Related
I have created an object which is used to control a piece of test equipment (oscilloscope) which communicates using the Visa library. This object (scope object) works fine, but there is one method I created to query the scope for a waveform average, which takes a while to execute (around a second or so), and blocks execution of the UI while it is acquiring the data.
To get around this problem, I initially tried creating a task object and using this task object to execute the function that is querying the scope for the data; but I found that something in the Visa driver object itself was apparently still executing on the main thread (and thus slowing down my UI).
I then did one more test and created a new thread, and had this thread call a function. Inside this function, I initialized the scope object, setup the operating parameters, and then called the long-running function. This time, my UI was as responive as normal with no slowdowns.
So now, It seems that I need to actually initialize the scope object inside a new thread in order to get it to truly run asynchronously. But now I have a new challenge. I need to access the objects properties and methods from the main thread to set things up, query status info, etc. Is there a clean way to effectively read and write to a class's properties and methods from another thread? Or are there any existing libraries available to make this simpler?
My current idea is to create a wrapper class for the scope object and then have this wrapper class initialize the scope object in a new thread. But I'm not sure of the best way to access the object's members efficiently. Or better yet, is there a better approach to this problem?
EDIT: Below is some more information and code for the test program I wrote. The UI is a just a simple form with Acquire and Connect buttons, and two labels (one for showing the measurements and the other shows a number that gets incremented as I click on the "Click" button:
Here's the code for the Scope Object I created:
using System;
using Ivi.Scope.Interop;
using Tektronix.Tkdpo2k3k4k.Interop;
using System.Diagnostics;
namespace Test_App
{
public class DPO4034
{
#region [NOTES] Installing TekVisa Drivers for DPO4034
/*
1. Download and install the TekVisa Connectivity Software from here:
https://www.tek.com/oscilloscope/tds7054-software/tekvisa-connectivity-software-v411
2. Check under Start -> All Programs -> TekVisa and see if the "Open Choice Installation Manager" shortcut works.If not, then update all shortcuts to point to the correct base folder for the TekVISA files, which is "C:\Program Files\IVI Foundation\VISA\".
3. Download the DPO4000 series IVI driver from here:
https://www.tek.com/oscilloscope/dpo4054-software/dpo2000-dpo3000-dpo4000-ivi-driver
4. After running the unzip utility, open the unzipped folder and goto x64 -> Shared Components, and run the IviCleanupUtility_2.0.0.exe utility to make sure no shared IVI components exist.
5. Run the IviSharedComponents64_2.1.1.exe file to install shared components.
6. Go up one folder and open the IVI Driver Folder and run the Tkdpo2k3k4k-x64.msi installer to install the scope IVI driver.
7. In the VS project, add references to the following COM components:
• IviDriverLib
• IviScopeLib
• Tkdpo2k3k4kLib
8. Right Click on each of the three references in the Solution Explorer and select Properties in the menu. When the properties window appears, set the "Embed Interop Types" property to False.
*/
#endregion
#region Class Variables
Tkdpo2k3k4kClass driver; // IVI Driver representing the DPO4034
IIviScope scope; // IVI Scope object representing the DPO4034
#endregion
#region Class Constructors
public DPO4034()
{
this.driver = new Tkdpo2k3k4kClass();
this.scope = (IIviScope)driver;
}
~DPO4034()
{
this.Disconnect();
}
#endregion
#region Public Properties
/// <summary>
/// Returns true if the scope is connected (initialized)
/// </summary>
public bool Connected
{
get
{
return this.driver.IIviDriver_Initialized;
}
}
#endregion
#region Public Methods
/// <summary>
/// Initializes the connection to the scope
/// <paramref name="reset"/>Resets the scope after connecting if set to true</param>
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Connect(bool reset = false)
{
try
{
if (!this.Connected)
{
this.Disconnect();
}
this.driver.Initialize("TCPIP::10.10.0.200::INSTR", true, reset, "Simulate=false, DriverSetup= Model=DPO4034");
return true;
}
catch (Exception ex)
{
PrintError(ex, "Connect");
return false;
}
}
/// <summary>
/// Closes the connection to the scope
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Disconnect()
{
try
{
if (this.Connected)
{
this.driver.Close();
}
return true;
}
catch (Exception ex)
{
PrintError(ex, "Disconnect");
return false;
}
}
/// <summary>
/// Reads the average value of the waveform on the selected channel
/// </summary>
/// <param name="channel">1-4 for channels 1 to 4</param>
/// <returns>The measured average value</returns>
public double ReadWaveformAverage(int channel)
{
if (this.Connected)
{
try
{
double value = 0;
this.scope.Measurements.Item["CH" + channel.ToString()].FetchWaveformMeasurement(IviScopeMeasurementEnum.IviScopeMeasurementVoltageAverage, ref value);
return value;
}
catch (Exception ex)
{
PrintError(ex, "ReadWaveformAverage");
return 0;
}
}
else
{
PrintError("Oscilloscope not connected", "ReadWaveformAverage");
return 0;
}
}
#endregion
#region Private Methods
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(Exception err, string source = "") //, bool showMessageBox = false)
{
Debug.Print($"Error: {err.Message}");
Debug.Print($"Source: {source}");
}
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(string error, string source = "")
{
Debug.Print($"Error: {error}");
Debug.Print($"Source: {source}");
}
#endregion
}
}
Here's the code for the version of the form that uses an async function and tasks to directly call the acquisition functions:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Test_App
{
public partial class Form1 : Form
{
byte number = 0;
public Form1()
{
InitializeComponent();
}
private void cmdAcquire_Click(object sender, EventArgs e)
{
takeMeasurements();
}
async void takeMeasurements()
{
try
{
// Create new instance of the scope object and connect to it
DPO4034 Scope = new DPO4034();
Scope.Connect();
// Update status
PrintStatus(Scope.Connected ? "Connected" : "Error");
// Loop continuously and print the samples to the status label
while (Scope.Connected)
{
double inputVoltage = await Task.Run(() => Scope.ReadWaveformAverage(1));
double inputCurrent = await Task.Run(() => Scope.ReadWaveformAverage(2));
double outputVoltage = await Task.Run(() => Scope.ReadWaveformAverage(3));
PrintStatus($"CH1: {inputVoltage}\n" +
$"CH2: {inputCurrent}\n" +
$"CH3: {outputVoltage}\n");
}
}
catch (Exception)
{
PrintStatus("Error");
}
}
private void cmdIncrement(object sender, EventArgs e)
{
// This is just something for me to make the interface do to see
// how responsive it is
lblNumber.Text = number.ToString();
number++;
}
// Prints status text to the label on the form
private void PrintStatus(string text)
{
Status.Text = text;
}
}
}
and here's the code for the version of the form that uses a separate thread to run the scope object:
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_App
{
public partial class Form1 : Form
{
Thread t;
byte number = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(takeMeasurements));
}
private void cmdAcquire_Click(object sender, EventArgs e)
{
t.Start();
}
// Function to create scope object and take acquisitions
void takeMeasurements()
{
try
{
// Create new instance of the scope object and connect to it
DPO4034 Scope = new DPO4034();
Scope.Connect();
// Update status
PrintStatus(Scope.Connected ? "Connected" : "Error");
// Loop continuously and print the samples to the status label
while (Scope.Connected)
{
double inputVoltage = Scope.ReadWaveformAverage(1);
double inputCurrent = Scope.ReadWaveformAverage(2);
double outputVoltage = Scope.ReadWaveformAverage(3);
PrintStatus($"CH1: {inputVoltage}\n" +
$"CH2: {inputCurrent}\n" +
$"CH3: {outputVoltage}\n");
}
}
catch (Exception)
{
PrintStatus("Error");
}
}
private void cmdIncrement(object sender, EventArgs e)
{
// This is just something for me to make the interface do to see
// how responsive it is
lblNumber.Text = number.ToString();
number++;
}
// Prints status text to the label on the form
private void PrintStatus(string text)
{
if (!this.IsDisposed)
{
this.BeginInvoke((MethodInvoker)delegate
{
Status.Text = text;
});
}
else
{
t.Abort();
}
}
}
}
I hope this gives some more insight into what I'm trying to accomplish. Thank you all for your comments and I look forward to your feedback.
EDIT2: Just to be more clear, the method I would prefer to use (if possible) is the one using tasks. In the current program, the Scope object is initialized at the top of the form on the main thread and accessed by multiple objects within the program.
For anyone interested, I finally found a solution to the problem I was having with the GUI freezing when executing the ReadWaveformData() function.
The answer was to create a new thread inside of the Scope class that would call an Initialization function to initialize the internal scope and driver objects. Then the thread would do nothing but sit and host the instances until the ReadWavveformData() function is called inside a task. Here's the modified DPO4034 class:
using System;
using Ivi.Scope.Interop;
using Tektronix.Tkdpo2k3k4k.Interop;
using System.Diagnostics;
using System.Threading;
namespace Test_App
{
public class DPO4034
{
#region [NOTES] Installing TekVisa Drivers for DPO4034
/*
1. Download and install the TekVisa Connectivity Software from here:
https://www.tek.com/oscilloscope/tds7054-software/tekvisa-connectivity-software-v411
2. Check under Start -> All Programs -> TekVisa and see if the "Open Choice Installation Manager" shortcut works.If not, then update all shortcuts to point to the correct base folder for the TekVISA files, which is "C:\Program Files\IVI Foundation\VISA\".
3. Download the DPO4000 series IVI driver from here:
https://www.tek.com/oscilloscope/dpo4054-software/dpo2000-dpo3000-dpo4000-ivi-driver
4. After running the unzip utility, open the unzipped folder and goto x64 -> Shared Components, and run the IviCleanupUtility_2.0.0.exe utility to make sure no shared IVI components exist.
5. Run the IviSharedComponents64_2.1.1.exe file to install shared components.
6. Go up one folder and open the IVI Driver Folder and run the Tkdpo2k3k4k-x64.msi installer to install the scope IVI driver.
7. In the VS project, add references to the following COM components:
• IviDriverLib
• IviScopeLib
• Tkdpo2k3k4kLib
8. Right Click on each of the three references in the Solution Explorer and select Properties in the menu. When the properties window appears, set the "Embed Interop Types" property to False.
*/
#endregion
#region Class Variables
Tkdpo2k3k4kClass driver; // IVI Driver representing the DPO4034
IIviScope scope; // IVI Scope object representing the DPO4034
Thread t; // Thread to initialize the scope objects in to ensure that they async method calls do not run on the main thread
#endregion
#region Class Constructors
public DPO4034()
{
t = new Thread(new ThreadStart(Initialize));
t.Start();
// Wait for scope object to be initialized in the thread
while (this.scope == null);
}
~DPO4034()
{
this.Disconnect();
t.Abort();
}
#endregion
#region Public Properties
/// <summary>
/// Returns true if the scope is connected (initialized)
/// </summary>
public bool Connected
{
get
{
return this.driver.IIviDriver_Initialized;
}
}
#endregion
#region Public Methods
/// <summary>
/// Initializes the connection to the scope
/// <paramref name="reset"/>Resets the scope after connecting if set to true</param>
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Connect(bool reset = false)
{
try
{
if (!this.Connected)
{
this.Disconnect();
}
this.driver.Initialize("TCPIP::10.10.0.200::INSTR", true, reset, "Simulate=false, DriverSetup= Model=DPO4034");
return true;
}
catch (Exception ex)
{
PrintError(ex, "Connect");
return false;
}
}
/// <summary>
/// Closes the connection to the scope
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Disconnect()
{
try
{
if (this.Connected)
{
this.driver.Close();
}
return true;
}
catch (Exception ex)
{
PrintError(ex, "Disconnect");
return false;
}
}
/// <summary>
/// Reads the average value of the waveform on the selected channel
/// </summary>
/// <param name="channel">1-4 for channels 1 to 4</param>
/// <returns>The measured average value</returns>
public double ReadWaveformAverage(int channel)
{
if (this.Connected)
{
try
{
double value = 0;
this.scope.Measurements.Item["CH" + channel.ToString()].FetchWaveformMeasurement(IviScopeMeasurementEnum.IviScopeMeasurementVoltageAverage, ref value);
return value;
}
catch (Exception ex)
{
PrintError(ex, "ReadWaveformAverage");
return 0;
}
}
else
{
PrintError("Oscilloscope not connected", "ReadWaveformAverage");
return 0;
}
}
#endregion
#region Private Methods
private void Initialize()
{
this.driver = new Tkdpo2k3k4kClass();
this.scope = (IIviScope)driver;
// Does nothing but allow the objects to exist on the separate thread
while (true)
{
Thread.Sleep(int.MaxValue);
}
}
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(Exception err, string source = "") //, bool showMessageBox = false)
{
Debug.Print($"Error: {err.Message}");
Debug.Print($"Source: {source}");
}
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(string error, string source = "")
{
Debug.Print($"Error: {error}");
Debug.Print($"Source: {source}");
}
#endregion
}
}
If this is paired up with the version of the TestApp that uses the async tasks to execute the ReadWaveformData() function, then things run smoothly and I don't need to completely rewrite the scope class to get it to work in my program. Hope this is helpful for anyone else who may run into a similar challenge.
I created a Windows Forms application that uses a ChromiumBrowser. The application is composed by the follow components:
Main application
Web browser library
Launcher application
When I launch my application normally, the web browser works correctly. If I launch my application from a launcher, the web browser doesn't work. It tolds me the follow error:
Unhandled exception of 'System.IO.FileNotFoundException' in Unknown module.
Cannot load file or assembly 'CefSharp, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138' or a relative dependency.
Unable to found the specified file.
I need to use the launcher not only for updates, but since the application is distributed on network, sometimes there are problems to access files on the server.
The problem is not relative only to my app. I created a test solution that I posted below and I have had the same issue.
Notes on the project
Cefsharp runtime is located in C:\Program Files (x86)\CEFRuntime\x64 and C:\Program Files (x86)\CEFRuntime\x86. (I created an installer that copy the runtime files in this position). Runtime is based on the NuGet package.
All executables are compiled in AnyCpu (AnyCpu support)
Cefsharp version 57 (Cef redist 3.2987.1601)
Runtime content
x64 folder
cef.pak
CefSharp.BrowserSubprocess.Core.dll
CefSharp.BrowserSubprocess.Core.pdb
CefSharp.BrowserSubprocess.exe
CefSharp.BrowserSubprocess.pdb
CefSharp.Core.dll
CefSharp.Core.pdb
CefSharp.Core.xml
CefSharp.dll
CefSharp.pdb
CefSharp.WinForms.dll
CefSharp.WinForms.pdb
CefSharp.WinForms.XML
CefSharp.XML
cef_100_percent.pak
cef_200_percent.pak
cef_extensions.pak
chrome_elf.dll
d3dcompiler_47.dll
devtools_resources.pak
icudtl.dat
libcef.dll
libEGL.dll
libGLESv2.dll
natives_blob.bin
snapshot_blob.bin
widevinecdmadapter.dll
locales folder (with all .paks)
x86 folder
cef.pak
CefSharp.BrowserSubprocess.Core.dll
CefSharp.BrowserSubprocess.Core.pdb
CefSharp.BrowserSubprocess.exe
CefSharp.BrowserSubprocess.pdb
CefSharp.Core.dll
CefSharp.Core.pdb
CefSharp.Core.xml
CefSharp.dll
CefSharp.pdb
CefSharp.WinForms.dll
CefSharp.WinForms.pdb
CefSharp.WinForms.XML
CefSharp.XML
cef_100_percent.pak
cef_200_percent.pak
cef_extensions.pak
chrome_elf.dll
d3dcompiler_47.dll
devtools_resources.pak
icudtl.dat
libcef.dll
libEGL.dll
libGLESv2.dll
natives_blob.bin
snapshot_blob.bin
widevinecdmadapter.dll
locales folder (with all .paks)
I post the test solution that gives me the same error.
Test Solution
The test solution is composed by three projects:
StackOverflowIssueLauncher
StackOverflowIssue (reference to WebBrowser)
WebBrowser (dll library that contains the webbrowser)
The code is shown below:
Project StackOverflowIssueLauncher
Program.cs
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace StackOverflowIssueLauncher {
/// <summary>
/// Launcher program
/// </summary>
internal static class Program {
/// <summary>
/// Launcher body
/// </summary>
[STAThread, LoaderOptimization(LoaderOptimization.MultiDomainHost)]
private static void Main() {
//Initialize path of application
string startupPath = Environment.CurrentDirectory;
string cachePath = Path.Combine(Path.GetTempPath(), "Program-" + Guid.NewGuid());
string assemblyPath = CanonicalizePathCombine(startupPath, #"..\..\..\StackOverflowIssue\bin\Debug\");
string executablePath = Path.Combine(assemblyPath, "StackOverflowIssue.exe");
string configFile = executablePath + ".config";
//Start App Domain
try {
var setup = new AppDomainSetup() {
ApplicationName = "StackOverflowIssue",
ShadowCopyFiles = "true",
ShadowCopyDirectories = assemblyPath,
CachePath = cachePath,
ConfigurationFile = configFile
};
var domain = AppDomain.CreateDomain("StackOverflowIssue", AppDomain.CurrentDomain.Evidence, setup);
domain.ExecuteAssembly(executablePath);
AppDomain.Unload(domain);
}
catch (Exception ex) {
MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
//Empty cache path
try {
Directory.Delete(cachePath, true);
}
catch (Exception) {
//DO NOTHING
}
}
private static string CanonicalizePathCombine(string sourcePath, string destPath) {
string resultPath = Path.Combine(sourcePath, destPath);
var sb = new StringBuilder(Math.Max(260, 2 * resultPath.Length));
PathCanonicalize(sb, resultPath);
return sb.ToString();
}
[DllImport("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool PathCanonicalize([Out] StringBuilder sb, string src);
}
}
Project StackOverflowIssue
WebControlForm.cs
using System.Windows.Forms;
using WebBrowser;
namespace StackOverflowIssue {
/// <summary>
/// Form that contains the webbrowser control
/// </summary>
public class WebControlForm : Form {
/// <summary>
/// Create a new web control form
/// </summary>
public WebControlForm() {
InitializeComponent();
Controls.Add(new CefControl { Dock = DockStyle.Fill });
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// WebControlForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(529, 261);
this.Name = "WebControlForm";
this.Text = "WebControlForm";
this.ResumeLayout(false);
}
#endregion
}
}
MainForm.cs
using System;
using System.Windows.Forms;
namespace StackOverflowIssue {
/// <summary>
/// Main application form
/// </summary>
public partial class MainForm : Form {
/// <summary>
/// Creates the main form
/// </summary>
public MainForm() {
InitializeComponent();
}
/// <summary>
/// Show a new Web Control form
/// </summary>
/// <param name="sender">Object that raised the event</param>
/// <param name="e">Event arguments</param>
private void ShowBtn_Click(object sender, EventArgs e) {
var wcf = new WebControlForm();
wcf.Show(this);
}
/// <summary>
/// Variabile di progettazione necessaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Pulire le risorse in uso.
/// </summary>
/// <param name="disposing">ha valore true se le risorse gestite devono essere eliminate, false in caso contrario.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Codice generato da Progettazione Windows Form
/// <summary>
/// Metodo necessario per il supporto della finestra di progettazione. Non modificare
/// il contenuto del metodo con l'editor di codice.
/// </summary>
private void InitializeComponent() {
this.ShowBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// ShowBtn
//
this.ShowBtn.Location = new System.Drawing.Point(12, 12);
this.ShowBtn.Name = "ShowBtn";
this.ShowBtn.Size = new System.Drawing.Size(134, 40);
this.ShowBtn.TabIndex = 0;
this.ShowBtn.Text = "Show web browser";
this.ShowBtn.UseVisualStyleBackColor = true;
this.ShowBtn.Click += new System.EventHandler(this.ShowBtn_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.ShowBtn);
this.Name = "MainForm";
this.Text = "Main form";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button ShowBtn;
}
}
Program.cs
using System;
using System.Diagnostics;
using System.Windows.Forms;
using WebBrowser;
namespace StackOverflowIssue {
/// <summary>
/// Main application program
/// </summary>
internal static class Program {
/// <summary>
/// Main application program.
/// </summary>
[STAThread] private static void Main() {
WebBrowserInitializer.Initialize();
Debug.Print("Application started");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="cef.redist.x64" version="3.2987.1601" targetFramework="net452" />
<package id="cef.redist.x86" version="3.2987.1601" targetFramework="net452" />
<package id="CefSharp.Common" version="57.0.0" targetFramework="net452" />
<package id="CefSharp.WinForms" version="57.0.0" targetFramework="net452" />
</packages>
Project WebBrowser
CefControl.cs
using System.Windows.Forms;
using CefSharp.WinForms;
namespace WebBrowser {
/// <summary>
/// WebBrowser control
/// </summary>
public class CefControl: UserControl {
public CefControl() {
CefInitializer.Initialize();
InitializeComponent();
var cr = new ChromiumWebBrowser("https://www.google.com");
cr.Dock = DockStyle.Fill;
Controls.Add(cr);
}
/// <summary>
/// Variabile di progettazione necessaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Pulire le risorse in uso.
/// </summary>
/// <param name="disposing">ha valore true se le risorse gestite devono essere eliminate, false in caso contrario.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Codice generato da Progettazione componenti
/// <summary>
/// Metodo necessario per il supporto della finestra di progettazione. Non modificare
/// il contenuto del metodo con l'editor di codice.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
}
#endregion
}
}
CefInitializer.cs
using CefSharp;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace WebBrowser {
/// <summary>
/// Class that contains the base methods for CEF initializations
/// </summary>
public static class CefInitializer {
/// <summary>
/// Initialize properties
/// </summary>
static CefInitializer() {
CachePath = Path.Combine(Path.GetTempPath(), "SOIssue", "Cache");
LogFile = Path.Combine(Path.GetTempPath(), "SOIssue", "Logs");
UserDataPath = Path.Combine(Path.GetTempPath(), "SOIssue", "Data");
if (!Directory.Exists(CachePath))
Directory.CreateDirectory(CachePath);
if (!Directory.Exists(LogFile))
Directory.CreateDirectory(LogFile);
if (!Directory.Exists(UserDataPath))
Directory.CreateDirectory(UserDataPath);
//Complete the files combine
LogFile = Path.Combine(LogFile, "WebBrowser.log");
AppDomain.CurrentDomain.DomainUnload += (sender, args) => Shutdown();
}
/// <summary>
/// Shutdown all CEF instances
/// </summary>
internal static void Shutdown() {
using (var syncObj = new WindowsFormsSynchronizationContext()) {
syncObj.Send(o => {
if (Cef.IsInitialized)
Cef.Shutdown();
}, new object());
}
}
/// <summary>
/// Initialize CEF libraries
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)] internal static void Initialize() {
if (Cef.IsInitialized)
return;
//Get proxy properties
WebProxy proxy = WebRequest.DefaultWebProxy as WebProxy;
string cefPath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Cef)).Location);
Debug.Print($"CEF Library Path: {cefPath}");
Debug.Assert(cefPath != null, nameof(cefPath) + " != null");
var settings = new CefSettings() {
BrowserSubprocessPath = Path.Combine(cefPath, "CefSharp.BrowserSubprocess.exe"),
LocalesDirPath = Path.Combine(cefPath, "locales"),
ResourcesDirPath = cefPath,
Locale = CultureInfo.CurrentCulture.Name,
CachePath = CachePath,
LogFile = LogFile,
UserDataPath = UserDataPath
};
if (proxy == null || proxy.Address.AbsoluteUri != string.Empty)
settings.CefCommandLineArgs.Add("no-proxy-server", string.Empty);
Cef.Initialize(settings);
}
internal static readonly string CachePath;
internal static readonly string LogFile;
internal static readonly string UserDataPath;
}
}
WebBrowserInitializer.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace WebBrowser {
/// <summary>
/// Class that contains the assembly resolve functions
/// </summary>
public static class WebBrowserInitializer {
private static readonly object _initializer = new object();
private static bool _initialized;
/// <summary>
/// Check if the WebBrowser is initialized
/// </summary>
public static bool IsInitialized {
get {
lock (_initializer)
return _initialized;
}
}
/// <summary>
/// Initialize the current assembly
/// </summary>
public static void Initialize() {
lock (_initializer) {
if (!_initialized) {
AppDomain.CurrentDomain.AssemblyResolve += CefSharp_AssemblyResolve;
_initialized = true;
}
}
}
/// <summary>
/// Try to resolve the assembly
/// </summary>
/// <param name="sender">Object that has raised the event</param>
/// <param name="args">Event raised</param>
/// <returns>Assembly loaded</returns>
private static Assembly CefSharp_AssemblyResolve(object sender, ResolveEventArgs args) {
Debug.Print($"Library: {args.Name}");
if (!args.Name.StartsWith("CefSharp", StringComparison.OrdinalIgnoreCase))
return null;
string assemblyName = args.Name.Split(new[] {','}, 2)[0] + ".dll";
foreach (var path in GetAssemblyPaths()) {
string checkPath = Path.Combine(path, assemblyName);
if (File.Exists(checkPath)) {
Debug.Print($"Relative path FOUND for {args.Name} in {checkPath}");
return Assembly.UnsafeLoadFrom(checkPath);
}
Debug.Write($"Relative path not found for {args.Name} in {checkPath}");
}
return null;
}
/// <summary>
/// Get all possible assembly paths
/// </summary>
/// <returns>List of possible assembly paths</returns>
private static IEnumerable<string> GetAssemblyPaths() {
string pathPrefix = Environment.Is64BitProcess ? "x64" : "x86";
if (Directory.Exists(#"C:\Program Files (x86)\CEFRuntime\" + pathPrefix))
yield return #"C:\Program Files (x86)\CEFRuntime\" + pathPrefix;
yield return Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, pathPrefix);
yield return Path.Combine(Environment.CurrentDirectory, pathPrefix);
Assembly currentAssembly = Assembly.GetAssembly(typeof(CefInitializer));
if (!string.IsNullOrEmpty(currentAssembly.Location))
yield return Path.Combine(currentAssembly.Location, pathPrefix);
}
}
}
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="cef.redist.x64" version="3.2987.1601" targetFramework="net452" />
<package id="cef.redist.x86" version="3.2987.1601" targetFramework="net452" />
<package id="CefSharp.Common" version="57.0.0" targetFramework="net452" />
<package id="CefSharp.WinForms" version="57.0.0" targetFramework="net452" />
</packages>
Looking at CefSharp General Usage (https://github.com/cefsharp/CefSharp/wiki/General-Usage#need-to-knowlimitation), I noticed a line that explains that CefSharp works only on default AppDomain. I have looked at the project https://github.com/stever/AppHostCefSharp and I found a solution.
I need to run the WebBrowser on a Default AppDomain (I forked and edited RedGate.AppHost repository. See below for why i did it.). To allow communications between controls I implements two NamedPipes services, one on the main form, another on the created object.
I posted the complete solution (https://github.com/rupertsciamenna89/cefsharp-remoting) so the source code will be simplier to see. And it can be improved or fixed (like my english :))
I renamed the original projects to better names.
The solution is composed of 4 projects:
MainApplication [old StackOverflowIssue] (base application that I have to launch with ShadowCopy)
MainApplication.Launcher [old StackOverflowIssue.Launcher] (application launcher)
MainApplication.WebBrowser [old WebBrowser] (winforms controls library that contains the WebBrowser)
MainApplication.Interfaces (interfaces that must be implemented for the operations)
MainApplication.Interfaces
This project contains the interfaces that must be implemented by the client and the server. It contains five files:
IFormService is the interface that allows to create the control throught the RedGate.AppHost. It contains two Guids that identifies the unique names for Control/Server named pipes.
IAppClient is the client interface that will be implemented into the Control library to perform remote calls on the Application.
IAppServer is the server interface that will be implemented into the Application to accept remote calls from the Control library.
IWebBrowserClient is the client interface that will be implemented into Application to perform remote calls on the Control library.
IWebBrowserServer is the server interface that will be implemented into the Control libray to accept remote calls from the Application.
MainApplication.WebBrowser
This project implements the OutOfProcessEntryPoint interface that initializes the Control WCF Service. It contains the implementation of the Server interface and allows the remote client show the folder and retrieve the returned result.
MainApplication
I edited the Program.Main accepting the binaries path. I save this argument into a static variable that I'll use to crate the child process handle. The function that creates the process handle is this:
public static IChildProcessHandle CreateChildProcessHandle() {
string assemblyPath = _sourcePath ?? Path.GetDirectoryName(Assembly.GetAssembly(typeof(WebBrowserInitializer)).Location);
Debug.Assert(assemblyPath != null, "assemblyPath != null");
var al = new ChildProcessFactory() { ClientExecutablePath = _sourcePath };
return al.Create(Path.Combine(assemblyPath, "MainApplication.WebBrowser.dll"), false, Environment.Is64BitProcess);
}
If the source path isn't passed (like if I execute the application directly), RedGate will use the default location (the executing assembly path).
Once the windows is opened, the user could press the Show (or ShowDialog) button. The application "simply" run these lines of code:
//Generates client id and server id
string appId = Guid.NewGuid().ToString("N");
string controlId = Guid.NewGuid().ToString("N");
_service = AppServer.Start(appId, controlId);
_service.FormCompleted += Service_FormCompleted;
_locator = new FormServiceLocator(appId, controlId);
_element = _handle.CreateElement(_locator);
_service.StartRemoteClient();
_service.ShowDialog((long)Handle);
When the user will close the window, the callback function will be called:
private void Service_FormCompleted(object sender, AppServerEventArgs e) {
//Check if invoke is required
if (InvokeRequired) {
Invoke(new Action<object, AppServerEventArgs>(Service_FormCompleted), sender, e);
return;
}
_element = null;
MessageBox.Show(this, $"Result: {e.Result} - Data: {e.AdditionalData}");
}
MainApplication.Launcher
This is the project that launch our application with ShadowCopy enabled. I pass as argument the path of the binaries.
var domain = AppDomain.CreateDomain("CefSharp-Remoting",
AppDomain.CurrentDomain.Evidence, setup);
domain.ExecuteAssembly(executablePath, new[] { $"\"/path:{assemblyPath}\"" });
Why I forked the RedGate.AppHost repository
RedGate.AppHost try to found the Clients application looking into the Assembly location. With ShadowCopy enabled, this is not possible, because the application is copied into a "random" folder, and the Client application is in the source path.
I added the ClientExecutablePath property into the ChildProcessFactory.cs and ProcessStarter.cs, so the ProcessStarter use this folder instead of default folder if this property is setted.
You can see that edits in the follow files:
https://github.com/rupertsciamenna89/RedGate.AppHost/blob/master/RedGate.AppHost.Server/ChildProcessFactory.cs
https://github.com/rupertsciamenna89/RedGate.AppHost/blob/master/RedGate.AppHost.Server/ProcessStarter.cs
I've attempt to follow MSDN simple make a service instructions
Walkthrough: Creating a Windows Service Application in the Component Designer
https://msdn.microsoft.com/en-us/library/zt39148a(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
private System.ComponentModel.IContainer components;
private System.Diagnostics.EventLog eventLog1;
public Service1()
{
InitializeComponent();
eventLog1 = new System.Diagnostics.EventLog();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource("MySource", "MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("In OnStart");
}
protected override void OnStop()
{
}
}
}
I seem to fail at the 2nd hurdle, "Adding Features to the Service"
When I compile I get Ambiguity between 'WindowsService1.Service1.eventLog1' and 'WindowsService1.Service1.Service1.eventLog1' and The type 'WindowsService1.Service1' already contains a defintion for 'components'
Thanks for the comments people got me looking in the right place, seems that you don't need to do step4 it is done automatically when you add the eventlog in the design view.
Service1.designer.cs looks like this
namespace WindowsService4
{
partial class Service1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
//
// Service1
//
this.ServiceName = "Service1";
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
}
#endregion
private System.Diagnostics.EventLog eventLog1;
}
}
I have custom CMSModuleLoader code in web application project for Kentico CMS 8.2
I added this code in Old_App_Code\CMS folder which added a function in Global.asax file with the following code
private static void EnsureDynamicModules(object sender, EventArgs e)
{
ModuleEntryManager.EnsureModule<CMSModuleLoader>();
var discovery = new ModuleDiscovery();
var assembly = typeof(CMSModuleLoader).Assembly;
foreach (var module in discovery.GetModules(assembly))
{
ModuleEntryManager.EnsureModule(module);
}
}
This code is throwing error message:
The type 'CMSModuleLoader' cannot be used as type parameter 'T' in the generic type or method 'CMS.Core.ModuleEntryManager.EnsureModule()'. There is no implicit reference conversion from 'CMSModuleLoader' to 'CMS.Core.ModuleEntry'
The code is listed below
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Web;
using Aon.Exchange.Core;
using Aon.Exchange.Core.Exceptions;
using Aon.Exchange.Facade.Interfaces;
using Aon.Exchange.Domain.Entities;
using Aon.Exchange.Web.DependencyResolution;
using Common.Logging;
using System.Diagnostics;
using Aon.Exchange.Core.Extensions;
using Aon.Exchange.Core.Common.Logging;
using System.Collections.Specialized;
using CMS.OutputFilter;
using CMS.Membership;
using CMS.Base;
using CMS.Helpers;
/// <summary>
/// Module loader class, ensures initialization of other modules through this partial class
/// </summary>
public partial class CMSModuleLoader : CMSModuleLoaderBase
{
/// <summary>
/// Constructor
/// </summary>
public CMSModuleLoader()
: base("CMSModuleLoader")
{
}
private class CustomSecurityEventsAttribute : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
SecurityEvents.Authenticate.Execute += new EventHandler<AuthenticationEventArgs>(Authenticate_Execute);
}
/// <summary>
/// called on every kentico authenticate attempt
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Authenticate_Execute(object sender, AuthenticationEventArgs args)
{
if (args.User != null) //the authenticate was successful
{
try
{
var accountFacade = WebContainer.Instance.Container.GetInstance<IAccountFacade>();
accountFacade.ReconcileOnLogin(args.UserName);
}
catch (Exception e)
{
var logger = LogManager.GetCurrentClassLogger();
var ex = new Exception("IAccountFacade.ReconcileOnLogin method throw an error communicating with dynamics, the issue is not resolvable from Kentico thus regardless of the permission level of the current user, the exception will be bubbled up and the user will be shown error details or the custom error page.", e);
logger.Fatal(x => x("The current exception is caused by dynamics/data problems and the user will not be allowed to login. A system admin with access to dynamics is required to resolve the problem.", e));
throw ex;
}
ResetPasswordAttempts(args.User);
}
}
/// <summary>
/// Resets the number of password attempts for a user. We have to pass the userinfo object thru the layers because
/// doing a get, update, change from to the same userinfo object that we have here DOES NOT WORK. Luckily we can avoid giving all the layers
/// reference to the kentico dlls by passing the userinfo as an object and unpacking it in the repository.
/// </summary>
private void ResetPasswordAttempts(UserInfo user)
{
try
{
var accountFacade = WebContainer.Instance.Container.GetInstance<IAccountFacade>();
accountFacade.ResetPasswordAttempts(user);
}
catch (KenticoConfigurationException e)
{
var logger = LogManager.GetCurrentClassLogger();
logger.Warn(x => x("An exception was thrown during the IAccountFacade.ResetPasswordAttempts method.", e));
if (user.IsGlobalAdministrator)
{
logger.Error(x => x("IAccountFacade.ResetPasswordAttempts method throw an error, however because the user is logged in as a Kentico Global Admin, the system will ignore the error because the cause might be Kentico configuration and the Gloabl Admin who is logging in could fix the issue.", e));
}
else
{
var ex = new Exception("IAccountFacade.ResetPasswordAttempts method throw an error and the current user does not have the permissions to fix any potential Kentico configuration problems. The current user will not be allowed into the system.", e);
logger.Fatal(x => x("The current user does not have the permissions to login with the current system configuration errors; requires a user with Global Admin rights in kentico", e));
throw ex;
}
}
}
}
private class CustomErrorEventsAttribute : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
SystemEvents.Exception.Execute += new EventHandler<SystemEventArgs>(UnhandledException_Execute);
}
private void UnhandledException_Execute(object sender, SystemEventArgs e)
{
try
{
//log exception
var logger = LogManager.GetCurrentClassLogger();
logger.Error(x => x("Unhandled Exception caught by Kentico Global Exception handler", e.Exception));
}
catch
{
//swallow exceptions created when handling an exception.
}
var routeToCustomErrorPage = bool.Parse(ConfigurationManager.AppSettings[AppSettingKeys.FriendlyErrorRedirect_Enabled]);
var request = HttpContext.Current.Request;
var response = HttpContext.Current.Response;
//if web.config setting says so, and this isn't an api request, route to friendly error page
if (routeToCustomErrorPage && !request.Url.AbsolutePath.Contains("/api/"))
{
response.Redirect("~/CMSTemplates/AonExchange/FriendlyErrorPage.htm");
}
}
}
private class CustomSessionEventsAttribute : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
//CMSSessionEvents.Start.Before += new EventHandler<EventArgs>(OnBeginSession); -- Kentico 7
SessionEvents.UpdateSession.Before += new EventHandler<CMSEventArgs>(OnBeginSession);
}
private void OnBeginSession(object sender, EventArgs e)
{
var userSessionFacade = WebContainer.Instance.Container.GetInstance<IUserSessionFacade>();
if (userSessionFacade.IsAuthenticated())
{
var log = LogManager.GetCurrentClassLogger();
try
{
log.Info(x => x("Http request was authenticated, but a new session was created. Logging the user out and responding with an http redirect response."));
var accountFacade = WebContainer.Instance.Container.GetInstance<IAccountFacade>();
// This call to redirect will function with both normal page requests and ajax because of the SuppressFormsAuthenticationRedirectModule
accountFacade.Logout();
}
catch (Exception ex)
{
log.Error(x => x("An exception occurred while logging the user out because of a new session", ex));
}
}
}
}
private class CustomRequestEventsAttribute : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
//CMSRequestEvents.Begin.Before += new EventHandler<EventArgs>(OnBeforeBeginRequest); -- Kentico 7
//CMSRequestEvents.Begin.After += new EventHandler<EventArgs>(OnBeginRequest); -- Kentico 7
//CMSRequestEvents.End.Before += new EventHandler<EventArgs>(OnEndRequest); -- Kentico 7
RequestEvents.Prepare.Execute += new EventHandler<EventArgs>(OnBeforeBeginRequest);
RequestEvents.Begin.Execute += new EventHandler<EventArgs>(OnBeginRequest);
RequestEvents.End.Execute += new EventHandler<EventArgs>(OnEndRequest);
}
private void OnBeforeBeginRequest(object sender, EventArgs e)
{
if ((HttpContext.Current != null) && (HttpContext.Current.Request != null))
{
// Loads the request headers as a collection.
NameValueCollection headers = HttpContext.Current.Request.Headers;
// Gets the value from the FRONT-END-HTTPS header.
string forwardedSSL = headers.Get("FRONT-END-HTTPS");
CMS.Helpers.RequestContext.IsSSL = false;
// Checks if the original request used HTTPS.
if (!string.IsNullOrEmpty(forwardedSSL) && forwardedSSL.Equals("on", StringComparison.InvariantCultureIgnoreCase))
{
var log = LogManager.GetCurrentClassLogger();
log.Info(x => x("Https offloading is enabled."));
CMS.Helpers.RequestContext.IsSSL = true;
}
}
}
private void OnBeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items.Add("RequestId", Guid.NewGuid().ToString());
}
if (bool.Parse(ConfigurationManager.AppSettings[AppSettingKeys.MethodTrace_Enabled]))
{
var stopwatch = WebContainer.Instance.Container.GetInstance<Stopwatch>();
var log = LogManager.GetCurrentClassLogger();
log.Trace(x => x("Starting request for {0}", HttpContext.Current.Request.UrlOriginal().ToString()));
stopwatch.Start();
}
}
private void OnEndRequest(object sender, EventArgs e)
{
if (bool.Parse(ConfigurationManager.AppSettings[AppSettingKeys.MethodTrace_Enabled]))
{
var stopwatch = WebContainer.Instance.Container.GetInstance<Stopwatch>();
var log = LogManager.GetCurrentClassLogger();
stopwatch.Stop();
if (log.IsTraceEnabled)
log.Trace(new MethodTimingMessage(stopwatch.ElapsedMilliseconds, 0, "CMSLoader.OnEndRequest", string.Format("{0} execution time in milliseconds for {1}. ::Total Page Request::", stopwatch.ElapsedMilliseconds, HttpContext.Current.Request.UrlOriginal().ToString())));
}
if (HttpContext.Current != null)
{
HttpContext.Current.Items.Remove("RequestId");
}
}
}
/// <summary>
/// Attribute class that ensures the loading of new relic JavaScript insertion
/// </summary>
private class CustomNewRelicSubstitutionLoader : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
//OutputFilter.OnResolveSubstitution += ResolveCustomSubstitutions;
}
/// <summary>
/// Called to substitute the ~newrelic~ token from the kentico head element into the new relic code to instrument browser data.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ResolveCustomSubstitutions(object sender, SubstitutionEventArgs e)
{
if (!e.Match)
{
switch (e.Expression.ToLowerInvariant())
{
case "newrelic":
e.Result = NewRelic.Api.Agent.NewRelic.GetBrowserTimingHeader();
e.Match = true;
break;
}
}
}
}
}
You need to mark CMSModuleLoader with your custom attribute CustomSecurityEventsAttribute, like this:
[CustomSecurityEvents]
public partial class CMSModuleLoader : CMSModuleLoaderBase
I have the following code for a windows service project. I have successfully built it and installed it. When I start it I get an event started in the event log. However, I never get the event for "In Onstart" any idea why this is going on?
namespace ADServiceCarlos
{
public partial class ADServiceCarlos : ServiceBase
{
public ADServiceCarlos()
{
InitializeComponent();
this.AutoLog = true;
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MySource","MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("In OnStart");
}
protected override void OnStop()
{
}
}
}
OK, so this may solve your problem. It's hard to tell exactly without being able to see all your code, but read this - more specifically the "caution" part.
Do not use the constructor to perform processing that should be in
OnStart. Use OnStart to handle all initialization of your service. The
constructor is called when the application's executable runs, not when
the service runs. The executable runs before OnStart. When you
continue, for example, the constructor is not called again because the
SCM already holds the object in memory. If OnStop releases resources
allocated in the constructor rather than in OnStart, the needed
resources would not be created again the second time the service is
called.
So everything you are doing to initialise the event log in your constructor should be moved to the OnStart event. This will ensure it is create properly each time the service start, which means you should be able to log your OnStart event correctly (providing you do it after initialisation)
Based on what #musefan posted, here is an example. I had to move everything out of the constructor completely.
public class ServiceMain : System.ServiceProcess.ServiceBase
{
public const string SERVICE_NAME = "ServiceName";
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public ServiceMain()
{
// This call is required by the Windows.Forms Component Designer.
InitializeComponent();
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = SERVICE_NAME;
this.CanPauseAndContinue = true;
}
static void Main()
{
//Log all unhandled exceptions
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new ServiceMain() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
//Do your stuff here
}
static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
{
Environment.Exit(1);
}
}