Am creating a outlook add-in to track mail processing from mailbox. Am wrapping the folders and the items(adding some events into it ) and storing them in a local list to avoid GC clearing all the events after first execution. However still the folder add event only fires once. Not sure what is the problem.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using OutlookNS = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Net;
using System.Windows.Forms;
namespace OutlookAuditor
{
public partial class ThisAddIn
{
#region private variables
OutlookNS._NameSpace outNS;
OutlookNS.Explorer explorer;
string profileName = string.Empty;
List<SuperMailFolder> wrappedFolders = new List<SuperMailFolder>();
Logger logger = new Logger();
SuperMailFolder folderToWrap;
#endregion
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
try
{
OutlookNS.Application application = this.Application;
//Get the MAPI namespace
outNS = application.GetNamespace("MAPI");
//Get UserName
string profileName = outNS.CurrentUser.Name;
//Create a new outlook application
//I had to do this because my systems default mail box was ost file other just the below commented line is enough
//OutlookNS.MAPIFolder inbox = outNS.GetDefaultFolder(OutlookNS.OlDefaultFolders.olFolderInbox) as OutlookNS.MAPIFolder;
OutlookNS.MAPIFolder inbox;
OutlookNS.Folders folders = outNS.Folders;
OutlookNS.MAPIFolder selectedFolder = null;
if (folders.Count > 1)
{
List<string> folderNames = new List<string>();
foreach (OutlookNS.Folder folder in folders)
{
folderNames.Add(folder.Name);
}
using (selectMailBox frmSelect = new selectMailBox(folderNames))
{
if (DialogResult.OK == frmSelect.ShowDialog())
{
selectedFolder = folders[frmSelect.SelectedFolder];
}
}
}
else
{
selectedFolder = folders[1];
}
logger.SaveLog("Folder Selected " + selectedFolder.Name);
inbox = selectedFolder.Folders["Inbox"];//as OutlookNS.MAPIFolder;
//Create a super mail folder
folderToWrap = new SuperMailFolder(inbox, profileName);
wrappedFolders.Add(folderToWrap);
wrappedFolders.AddRange(folderToWrap.wrappedSubFolders);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(inbox);
}
catch (Exception ex)
{
logger.WriteException(ex);
}
finally
{
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
#region SuperMailItem object
class SuperMailItem
{
//local variable for avoiding GC invocation
OutlookNS.MailItem item;
string _profileName;
OutlookAuditor.Common.AuditItem auditItem;
string parentMailID;
string _folderName = string.Empty;
OutlookNS.MailItem replyItem;
Logger logger = new Logger();
//constructor that wraps mail item with required events
internal SuperMailItem(OutlookNS.MailItem MailItemToWrap, string profileName,string folderName)
{
try
{
item = MailItemToWrap as OutlookNS.MailItem;
_folderName = folderName;
if (item is OutlookNS.MailItem)
{
logger.SaveLog(item.Subject);
item.PropertyChange += MailItemToWrap_PropertyChange;
//item.PropertyChange += new OutlookNS.ItemEvents_10_PropertyChangeEventHandler(MailItemToWrap_PropertyChange);
((OutlookNS.ItemEvents_10_Event)item).Reply += SuperMailItem_Reply;
}
}
catch(Exception ex)
{
logger.WriteException(ex,"SuperMailItem Constructor");
}
}
void SuperMailItem_Reply(object Response, ref bool Cancel)
{
try
{
parentMailID = string.Empty;
replyItem = Response as OutlookNS.MailItem;
((OutlookNS.ItemEvents_10_Event)replyItem).Send += SuperMailItem_Send;
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
//this event is not firing
void SuperMailItem_Send(ref bool Cancel)
{
try
{
if (!Cancel)
{
createAuditItem();
auditItem.ActionDescription = "REPLY_SENT";
SaveLog();
}
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
//property change event- fires when any property of a mail item changes
void MailItemToWrap_PropertyChange(string Name)
{
try
{
createAuditItem();
//We are interested in UnRead property, if this property changes audit.
if (Name == "UnRead")
{
if (!item.UnRead)
{
auditItem.ActionDescription = "MAIL_READ";
}
else
{
auditItem.ActionDescription = "MAIL_UNREAD";
}
}
SaveLog();
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
void createAuditItem()
{
auditItem = new Common.AuditItem();
auditItem.ActionTimestamp = DateTime.Now;
auditItem.EntryID = item.EntryID;
auditItem.ProfileName = _profileName;
auditItem.ReceivedTimestamp = item.ReceivedTime;
auditItem.SystemIP = Helper.SystemIP();
auditItem.UserName = Helper.UserID();
auditItem.OriginalMailSentBy = item.Sender.Name;
auditItem.FolderName = _folderName;
auditItem.Subject = item.Subject;
}
void SaveLog()
{
Logger logger = new Logger();
logger.Save(auditItem);
}
}
#endregion
#region SuperMailFolder object
class SuperMailFolder
{
#region private variables
OutlookNS.MAPIFolder _wrappedFolder;
string _profileName;
List<SuperMailItem> wrappedItems = new List<SuperMailItem>();
public List<SuperMailFolder> wrappedSubFolders = new List<SuperMailFolder>();
string folderName = string.Empty;
Logger logger = new Logger();
#endregion
#region constructor
internal SuperMailFolder(OutlookNS.MAPIFolder folder, string profileName)
{
try
{
//assign it to local private master
_wrappedFolder = folder;
folderName = folder.Name;
_profileName = profileName;
//assign event handlers for the folder
_wrappedFolder.Items.ItemAdd +=Items_ItemAdd;
_wrappedFolder.Items.ItemRemove += Items_ItemRemove;
refreshItemList();
//Go through all the subfolders and wrap them as well
foreach (OutlookNS.MAPIFolder tmpFolder in _wrappedFolder.Folders)
{
logger.SaveLog("Wrapping folder " + tmpFolder.Name);
SuperMailFolder tmpWrapFolder = new SuperMailFolder(tmpFolder, _profileName);
wrappedSubFolders.Add(tmpWrapFolder);
wrappedSubFolders.AddRange(tmpWrapFolder.wrappedSubFolders);
}
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
#endregion
void Items_ItemRemove()
{
refreshItemList();
}
#region Handler of addition item into a folder
void Items_ItemAdd(object Item)
{
try
{
if (Item is OutlookNS.MailItem)
{
OutlookNS.MailItem item = Item as OutlookNS.MailItem;
wrappedItems.Add(new SuperMailItem(item, _profileName, folderName));
logger.SaveLog("Adding new item. New collection count:" + wrappedItems.Count.ToString());
OutlookAuditor.Common.AuditItem auditItem = new Common.AuditItem();
auditItem.ActionTimestamp = DateTime.Now;
auditItem.EntryID = item.EntryID;
auditItem.ProfileName = _profileName;
auditItem.ReceivedTimestamp = item.ReceivedTime;
auditItem.SystemIP = Helper.SystemIP();
auditItem.UserName = Helper.UserID();
auditItem.ActionDescription = "FOLDER_ADD";
auditItem.FolderName = folderName;
auditItem.OriginalMailSentBy = item.Sender.Name;
auditItem.Subject = item.Subject;
logger.Save(auditItem);
}
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
void refreshItemList()
{
try
{
wrappedItems.Clear();
wrappedItems = new List<SuperMailItem>();
logger.SaveLog("Wrapping items in " + folderName);
//Go through all the items and wrap it.
foreach (OutlookNS.MailItem item in _wrappedFolder.Items)
{
try
{
if (item is OutlookNS.MailItem)
{
OutlookNS.MailItem mailItem = item as OutlookNS.MailItem;
SuperMailItem wrappedItem = new SuperMailItem(mailItem, _profileName, folderName);
wrappedItems.Add(wrappedItem);
}
}
catch (Exception ex)
{
logger.WriteException(ex);
}
}
logger.SaveLog("Wrapped items in " + folderName + ":" + wrappedItems.Count.ToString());
}
catch(Exception ex)
{
logger.WriteException(ex);
}
}
#endregion
}
#endregion
static class Helper
{
public static string SystemIP()
{
string hostName = Dns.GetHostName();
string hostAddress = Dns.GetHostByName(hostName).AddressList[0].ToString();
return hostAddress;
}
public static string UserID()
{
return System.Security.Principal.WindowsIdentity.GetCurrent().Name;
}
}
}
The following code is the problem:
_wrappedFolder.Items.ItemAdd +=Items_ItemAdd;
_wrappedFolder.Items.ItemRemove += Items_ItemRemove;
The object that fires the events must be alive - in your case you set up an event handler on an implicit variable returned from the _wrappedFolder.Items property - as soon as the GC releases that implicit variable, no events will fire. Declare the Items object on the class level to make sure it stays referenced and alive.
Related
I have Pinger Application that gets all ping informations of servers written in the Servers.txt file
Half of them working servers, half of them not. I want to make my application write working servers to the another .txt file called WorkingServers.txt located in C:\Program Files\Servers folder
Here is my code:
using System;
using System.IO;
using System.Threading;
namespace AnchovyMultiPing
{
class Program
{
static void Main(string[] args)
{
var servers = File.ReadAllLines(#"C:\Program Files\Servers\Servers.txt");
foreach (var line in servers)
{
string currentServer = line;
if (args == null) throw new ArgumentNullException("args");
var waiter = new ManualResetEventSlim(false);
var pingData = new MultiPing(new[] { line }, waiter, 300);
waiter.Wait();
Console.WriteLine(pingData.GetPingInformation());
}
Console.WriteLine();
Console.WriteLine("- * - * - The Job is Done - * - * -");
Console.ReadLine();
}
}
}
and MultiPing.cs:
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
namespace AnchovyMultiPing
{
internal sealed class MultiPing : AbstractMultiPing
{
private int Timeout { get; set; }
private string[] Hosts { get; set; }
private int _count;
private ManualResetEventSlim Waiter { get; set; }
private readonly byte[] _buffer = Encoding.ASCII.GetBytes("aaa");
private Dictionary<string, long> _table = new Dictionary<string, long>();
private class Parameters
{
public String Host { get; set; }
public ManualResetEventSlim Event { get; set; }
}
public MultiPing(string[] hosts, ManualResetEventSlim waiter, int timeout = 12000)
{
Hosts = hosts;
Waiter = waiter;
Timeout = timeout;
RequestTime();
}
public override IMultiPings RequestTime()
{
try
{
_count = 0;
_table = new Dictionary<string, long>();
foreach (string host in Hosts)
{
using (var pingSender = new Ping())
{
pingSender.PingCompleted += PingCompletedCallback;
var options = new PingOptions(64, true);
try
{
pingSender.SendAsync(host, Timeout, _buffer, options,
new Parameters {Host = host, Event = Waiter});
}
catch
{
_count += 1;
if (_count == Hosts.Length)
Waiter.Set();
}
}
}
}
catch (MultiPingException ex)
{
Console.Write("RequestTime Exception");
Console.ReadKey();
}
return this;
}
//[MethodImpl(MethodImplOptions.Synchronized)] leaving this in favour of the operating system scheduler for better performance.
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
try
{
_count += 1;
PingReply reply = e.Reply;
if (reply != null && reply.Address != null && reply.Address.ToString() != "0.0.0.0")
{
if (_count > 0)
{
try
{
if (!_table.ContainsKey(reply.Address.ToString()))
_table.Add(((Parameters) e.UserState).Host, reply.RoundtripTime);
}
catch (NullReferenceException ex)
{
// catch null exception
throw new MultiPingException("Ping round trip time is null");
}
}
}
if (_count == Hosts.Length)
{
((Parameters) e.UserState).Event.Set();
}
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
Console.WriteLine(e.Error.ToString());
((ManualResetEventSlim) e.UserState).Set();
}
}
catch (MultiPingException ex)
{
Console.Write("Exception");
Console.ReadKey();
}
}
public override String GetPingInformation()
{
var build = new StringBuilder();
foreach (string host in _table.Keys)
{
build.AppendLine(string.Format("{0} : {1}", host, _table[host]));
}
return build.ToString();
}
public override String GetIp()
{
string ip = "";
long time = -1L;
foreach (string host in _table.Keys)
{
long roundTime = _table[host];
if ((time == -1L) || (roundTime >= 0 && roundTime < time))
{
time = roundTime;
ip = host;
}
}
return ip;
}
}
}
Declare new string at the beginning var log = string.Empty; At the end of the iteration add the server name to it, if the ping is successful. I don't know how the MultiPing class looks like, so I added artificial IsSuccessful, but what I mean is something like this:
waiter.Wait();
if(pingData.IsSuccessful)
{
log += line;
}
Console.WriteLine(pingData.GetPingInformation());
Then as the last line of your program save everything to the other file.
File.AppendAllText("C:\Program Files\Servers\WorkingServers.txt", log);
I have a .NET Core 3.1 Windows application (WinForm app) running on Windows 10 Version 10.0.17134 Build 17134. I am trying to connect and pair with Bluetooth devices using BluetoothLEAdvertisementWatcher. In the main form of the app I am using CefSharp.WinForms to display an HTML page.
My problem is that when I initialize the browser using _browser = new ChromiumWebBrowser(Settings.Default.IndexPageUrl); I can no longer pair with the Bluetooth device. I get AccessDenied returned from pairRequest.Status in my custom pairing event.
Here is the code:
using CefSharp;
using CefSharp.WinForms;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Enumeration;
namespace myApp
{
public partial class MainForm : Form
{
private readonly ILogger<MainForm> _logger;
private ChromiumWebBrowser _browser;
private static BluetoothLEAdvertisementWatcher _bleAdvertisementWatcher = null;
private static volatile ConcurrentDictionary<ulong,BleDeviceInfo> _bleDevicesInfo;
private readonly SemaphoreSlim _semaphoreSlim;
/// <summary>
/// Used to create a thread-safe boolean; do not access directly use AddingDevices instead
/// </summary>
private int _addingDevicesBool = 0;
public event Action<BleDeviceInfo> OnBleDeviceDetected;
public MainForm(): this(Program.LogFactory.CreateLogger<MainForm>())
{ }
public MainForm(ILogger<MainForm> logger)
{
_logger = logger;
_semaphoreSlim = new SemaphoreSlim(1, 1);
_bleDevicesInfo = new ConcurrentDictionary<ulong,BleDeviceInfo>();
InitBluetoothUtils();
InitializeBrowser();
}
#region browser setup
private void InitializeBrowser()
{
try
{
Cef.EnableHighDPISupport();
//When I comment out the 2 lines below I can succesfully pair with a BLE device
_browser = new ChromiumWebBrowser(Settings.Default.IndexPageUrl);
toolStripContainer.ContentPanel.Controls.Add(_browser);
//TODO: Uncomment the lines below to see Chrome Dev Tools
//#if DEBUG
// _browser.IsBrowserInitializedChanged += OnIsBrowserInitializedChanged;
//#endif
}
catch (Exception ex)
{
_logger.LogError(ex, Settings.BrowserInitError);
throw;
}
}
private void OnIsBrowserInitializedChanged(object sender, EventArgs e)
{
_browser.ShowDevTools();
}
#endregion
#region BLE setup
#region properties
/// <summary>
/// Thread-safe boolean property
/// </summary>
public bool ListeningForBleDevices
{
get { return (Interlocked.CompareExchange(ref _addingDevicesBool, 1, 1) == 1); }
set
{
if (value)
{
Interlocked.CompareExchange(ref _addingDevicesBool, 1, 0);
}
else
{
Interlocked.CompareExchange(ref _addingDevicesBool, 0, 1);
}
}
}
#endregion
private void InitBluetoothUtils()
{
StartListeningForBleDevices();
}
public void StartListeningForBleDevices()
{
ListeningForBleDevices = true;
StartBleWatcher();
}
private void StartBleWatcher()
{
try
{
ListeningForBleDevices = false;
if(_bleAdvertisementWatcher == null)
{
_bleAdvertisementWatcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active
};
// Only activate the watcher when we're recieving values >= -80
_bleAdvertisementWatcher.SignalStrengthFilter.InRangeThresholdInDBm = -80;
// Stop watching if the value drops below -90 (user walked away)
_bleAdvertisementWatcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90;
// Register events
_bleAdvertisementWatcher.Received += OnBleAdvWatcherReceived;
_bleAdvertisementWatcher.Stopped += OnBleAdvWatcherStopped;
// Wait 5 seconds to make sure the device is really out of range
_bleAdvertisementWatcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
_bleAdvertisementWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);
}
//Start Watcher
ListeningForBleDevices = true;
_bleAdvertisementWatcher.Start();
}
catch (Exception ex)
{
_logger.LogError(ex, string.Empty);
throw;
}
}
public void StopBleWatcher()
{
_bleAdvertisementWatcher?.Stop();
ListeningForBleDevices = false;
}
private void OnBleAdvWatcherStopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
_logger.LogInformation("Ble Advertisement Watcher stopped");
}
/// <summary>
/// This event will fire multiple times for the same BLE device
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private async void OnBleAdvWatcherReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
if(!ListeningForBleDevices)
{
return;
}
BleDeviceInfo bleDeviceInfo = null;
try
{
var advLocalName = args.Advertisement.LocalName;
if (string.IsNullOrEmpty(advLocalName) || !advLocalName.Contains("my custom prefix", StringComparison.OrdinalIgnoreCase))
{
return;
}
_semaphoreSlim.Wait();
bleDeviceInfo = await SetBleDeviceInfo(args).ConfigureAwait(false);
_semaphoreSlim.Release();
}
catch (Exception ex)
{
_logger.LogError(ex, string.Empty);
_semaphoreSlim.Release();
throw;
}
if(bleDeviceInfo != null)
{
OnBleDeviceDetected?.Invoke(bleDeviceInfo);
}
}
private async Task<BleDeviceInfo> SetBleDeviceInfo(BluetoothLEAdvertisementReceivedEventArgs args)
{
BleDeviceInfo bleDeviceInfo = null;
try
{
var bluetoothAddress = args.BluetoothAddress;
var deviceExists = GetBleDevice(bluetoothAddress);
if(deviceExists == null)
{
BluetoothLEDevice bleDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress);
//TODO: Pairing will not work when ChromiumWebBrowser gets initialized
var isPaired = await PairDevice(bleDevice).ConfigureAwait(false);
bleDeviceInfo = new BleDeviceInfo(bleDevice, args);
_bleDevicesInfo.AddOrUpdate(bleDeviceInfo.BluetoothAddress, bleDeviceInfo, (key, bleDeviceInfo) => bleDeviceInfo);
}
}
catch (Exception ex)
{
_logger.LogError(ex, string.Empty);
throw;
}
return bleDeviceInfo;
}
public BleDeviceInfo GetBleDevice(ulong bluetoothAddress)
{
try
{
return _bleDevicesInfo.Values.FirstOrDefault(d => d.BluetoothAddress.Equals(bluetoothAddress));
}
catch (Exception ex)
{
_logger.LogError(ex, string.Empty);
throw;
}
}
public async Task<bool> PairDevice(BluetoothLEDevice bleDevice)
{
if (bleDevice == null)
{
return false;
}
var isPaired = false; // bleDevice.DeviceInformation.Pairing.IsPaired;
try
{
var canPair = bleDevice.DeviceInformation.Pairing.CanPair;
var deviceId = bleDevice.DeviceId;
if (canPair)
{
DeviceInformationCustomPairing customPairing = bleDevice.DeviceInformation.Pairing.Custom;
customPairing.PairingRequested += OnCustomPairingRequested;
var pairRequest = await customPairing.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None);
customPairing.PairingRequested -= OnCustomPairingRequested;
var resultStatus = pairRequest.Status;
isPaired = resultStatus == DevicePairingResultStatus.Paired;
if (isPaired)
{
//TODO: Verify this code block
bleDevice.Dispose();
Thread.Sleep(2000); //try 2 second delay.
//Reload device so that the GATT services are there. This is why we wait.
bleDevice = await BluetoothLEDevice.FromIdAsync(deviceId);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, string.Empty);
throw;
}
return isPaired;
}
public async Task<bool> PairDevice(ulong bluetoothAddress)
{
BluetoothLEDevice bleDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress, BluetoothAddressType.Public);
return await PairDevice(bleDevice).ConfigureAwait(false);
}
private void OnCustomPairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
try
{
//This method does not get called when ChromiumWebBrowser is initialized
//However, when _browser = new ChromiumWebBrowser is not called this method works as expected
var deviceName = args.DeviceInformation.Name;
var devicePin = GetBlePin(deviceName);
var pinDeferral = args.GetDeferral();
args.Accept(devicePin.ToString(CultureInfo.InvariantCulture));
pinDeferral.Complete();
}
catch (Exception)
{
throw;
}
}
private int GetBlePin(string serialNumber)
{
return 12345;
}
#endregion
}
}
Things I have tried:
Calling InitializeBrowser on a separate thread using Task.Run(() => { InitializeBrowser(); }).ConfigureAwait(false);
Calling InitBluetoothUtils on a separate thread using Task.Run(() => { InitBluetoothUtils(); }).ConfigureAwait(false);
Calling both InitializeBrowser and InitBluetoothUtils on separate threads
Tried using SynchronizationContext by getting the current context in the MainForm constructor, and then calling PairDevice(bleDevice) using that context
Each time I get AccessDenied. The only thing that works successfully is when I comment out _browser = new ChromiumWebBrowser(BASettings.Default.IndexPageUrl); and toolStripContainer.ContentPanel.Controls.Add(_browser);
My word blocks add-ins for some reason. Following line gives no result.
ActiveDocument.Application.Selection.TypeText("Some String");
It appears as executed when debugging, however takes no effect on ActiveDocument. Do you know any possible reason? Macros/Add-ins are allowed in Word settings.
Michael
Basic word plug which store file into database before closing file.
public partial class ThisAddIn
{
private string friendlyErrorMessage = "An error has occurred inside the Case Manager Word Addin";
//static string filePath = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.DocumentBeforeClose += Application_DocumentBeforeClose;
}
//public static string dialogFilePath;
public static string dialogFileName;
public static Word.Document doc;
void Application_DocumentBeforeClose(Word.Document document, ref bool Cancel)
{
try
{
string filePath = this.Application.ActiveDocument.FullName.ToString();
string fileName = this.Application.ActiveDocument.Name;
//dialogFilePath = filePath;
dialogFileName = fileName;
doc = document;
string tempFile;
string tempPath;
//var form = new SaveFileDialog();
//form.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
//form.ShowDialog();
//System.Windows.Forms.MessageBox.Show(form.ToString());
//_T is for editing template and save file after saving.
//+ is for saving
//document.Save();
var iPersistFile = (IPersistFile)document;
iPersistFile.Save(tempPath, false);
if (filePath.Contains("_T"))
{
//Store file into DB
}
else
{
//Store file into DB
}
//object doNotSaveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
//document.Close(ref doNotSaveChanges, ref missing, ref missing);
Word._Document wDocument = Application.Documents[fileName] as Word._Document;
//wDocument.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
ThisAddIn.doc.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
}
catch (Exception exception)
{
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
Create a Ribbon designer :
public partial class TestRibbon
{
private string friendlyErrorMessage = "An error has occurred inside the Case Manager Outlook Addin";
private void TestRibbon_Load(object sender, RibbonUIEventArgs e)
{
}
public TestRibbon()
: base(Globals.Factory.GetRibbonFactory())
{
InitializeComponent();
try
{
System.Data.DataTable dt = new DBCall().GetData();//Get all menu in word tmenu tab and bind them using following code
if (dt.Rows.Count > 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
RibbonButton Field = this.Factory.CreateRibbonButton();
Field.Label = dt.Rows[i][1].ToString();
Field.Tag = i;
Field.ControlSize =
Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge;
Field.Click += Field_Click;
menu1.Items.Add(Field);
}
}
else
{
System.Windows.Forms.MessageBox.Show("No Fields are available in database");
}
}
catch (Exception exception)
{
}
}
/// <summary>
/// Ribbon Button Click Event
/// </summary>
void Field_Click(object sender, RibbonControlEventArgs e)
{
try
{
Microsoft.Office.Interop.Word.Range currentRange = Globals.ThisAddIn.Application.Selection.Range;
currentRange.Text = (sender as RibbonButton).Label;
}
catch (Exception exception)
{
}
}
}
I've been trying to implement a windows service that would keep vpn connection alive. I've found that it is possible to achieve using DotRas library by subscribing to RasConnectionWatcher.Disconnected event:
public class SampleService {
public SampleService() {
this.shutdownEvent = new ManualResetEvent(false);
this.connectionWatcher = new RasConnectionWatcher();
this.connectionWatcher.Disconnected += onVpnDisconnected;
}
// redial
void onVpnDisconnected(Object sender, RasConnectionEventArgs e) {
this.DialUp();
}
void DialUp() {
// connection setup is omitted
// keep the handle of the connection
this.connectionWatcher.Handle = dialer.Dial();
}
public void Start() {
this.thread = new Thread(WorkerThreadFunc);
this.thread.IsBackground = true;
this.thread.Start();
}
public void Stop() {
this.shutdownEvent.Set();
if(!this.thread.Join(3000)) this.thread.Abort();
}
private void WorkerThreadFunc() {
this.DialUp();
while(!this.shutdownEvent.WaitOne(0)) Thread.Sleep(1000);
}
}
When I start the service vpn connection opens without any problem, but when I manually interrupt the connection it seems that Disconnected event doesn't fire up.
solution 1
Found similar question/answer here:
http://social.msdn.microsoft.com/Forums/en-US/56ab2d0d-2425-4d76-81fc-04a1e1136141/ras-connection-application-and-service?forum=netfxnetcom.
solution 2
Got an answer from Jeff Winn yesterday:
https://dotras.codeplex.com/discussions/547038
public class VpnKeeperService : IService {
private ManualResetEvent shutdownEvent;
private RasConnectionWatcher connWatcher;
private Thread thread;
public VpnKeeperService() {
this.shutdownEvent = new ManualResetEvent(false);
this.connWatcher = new RasConnectionWatcher();
this.connWatcher.EnableRaisingEvents = true;
this.connWatcher.Disconnected += (s, args) => { this.DialUp(); };
}
Boolean DialUp() {
try {
using(var phoneBook = new RasPhoneBook()) {
var name = VpnConfig.GetConfig().ConnectionName;
var user = VpnConfig.GetConfig().Username;
var pass = VpnConfig.GetConfig().Password;
var pbPath = VpnConfig.GetConfig().PhoneBookPath;
phoneBook.Open(pbPath);
var entry = phoneBook.Entries.FirstOrDefault(e => e.Name.Equals(name));
if(entry != null) {
using(var dialer = new RasDialer()) {
dialer.EntryName = name;
dialer.Credentials = new NetworkCredential(user, pass);
dialer.PhoneBookPath = pbPath;
dialer.Dial();
}
}
else throw new ArgumentException(
message: "entry wasn't found: " + name,
paramName: "entry"
);
}
return true;
}
catch {
// log the exception
return false;
}
}
public void Start() {
this.thread = new Thread(WorkerThreadFunc);
this.thread.Name = "vpn keeper";
this.thread.IsBackground = true;
this.thread.Start();
}
public void Stop() {
this.shutdownEvent.Set();
if(!this.thread.Join(3000)) {
this.thread.Abort();
}
}
private void WorkerThreadFunc() {
if(this.DialUp()) {
while(!this.shutdownEvent.WaitOne(0)) {
Thread.Sleep(1000);
}
}
}
}
Hope it helps someone.
I've created a user control (windows form application) to get an instance of Word (either active or new), and provide a button to open documents into that instance using a file dialog picker.
The form contains 2 buttons, 1 for getting the word instance and another for opening a document. It also contains a list box for displaying the open documents, and an openfiledialog control to provide the means for selecting documents to open.
I am handling the Application.DocumentOpen event in order to populate the listbox...
m_wordApp.DocumentOpen += new msoWord.ApplicationEvents4_DocumentOpenEventHandler(m_wordApp_DocumentOpen);
I am determining when i need to reinvoke my method that populates the listbox to ensure that access to the control is on the same thread that created it....
private void AddDocument(string name)
{
try
{
if (m_documentsListBox.InvokeRequired && m_documentsListBox.IsHandleCreated&&!m_documentsListBox.IsDisposed)
{
this.Invoke(m_AddDocument, new object[] { name });
return;
}
if (!m_documentsListBox.Items.Contains(name))
m_documentsListBox.Items.Add(name);
}
catch (Exception ex)
{
}
}
Im not using 2 dots, and i believe i am releasing any COM objects correctly.
Why does the application hang on either the line of code that opens the document ...
WordDoc = m_wordDocs.Open(ref fileName);
or the line that reinvokes the AddDocument() method...
this.Invoke(m_AddDocument, new object[] { name });
somewhere along the line i think i must be having a thread issue, because the hang only happens if i choose to open a document using the button, rather than from within the Word application directly.
full code below...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
// use these for the core office & word references
using msoWord = Microsoft.Office.Interop.Word;
namespace MCDevNET.Office.Word.TestUIControls
{
public partial class OpenWordDocument : Form
{
public OpenWordDocument()
{
InitializeComponent();
m_openWordButton.Click += new EventHandler(buttonOpenWordApp_Click);
m_openDocumentButton.Click += new EventHandler(buttonOpenDocument_Click);
m_AddDocument = new UpdateListControl(AddDocument);
}
#region Form: control eventHandlers
void buttonOpenWordApp_Click(object sender, EventArgs e)
{
try
{
if (!IsValid(m_wordApp))
WordApp = GetInstance();
AddAllDocuments();
}
catch (Exception ex)
{
}
}
void buttonOpenDocument_Click(object sender, EventArgs e)
{
OpenWordDoc();
}
public delegate void UpdateListControl(string name);
private UpdateListControl m_AddDocument;
private void AddDocument(string name)
{
try
{
if (m_documentsListBox.InvokeRequired && m_documentsListBox.IsHandleCreated&&!m_documentsListBox.IsDisposed)
{
this.Invoke(m_AddDocument, new object[] { name });
return;
}
if (!m_documentsListBox.Items.Contains(name))
m_documentsListBox.Items.Add(name);
}
catch (Exception ex)
{
}
}
private void AddAllDocuments()
{
try
{
m_documentsListBox.Items.Clear();
if (m_wordDocs != null)
{
for (int i = 1; i <= m_wordDocs.Count; i++)
AddDocument(m_wordDocs[i].Name);
}
}
catch (Exception ex)
{
}
}
#endregion
#region Word: properties & eventhandlers
private msoWord.Document m_wordDoc;
public msoWord.Document WordDoc
{
get { return m_wordDoc; }
private set
{
try
{
if (m_wordDoc != value)
{
ReleaseCOMObject(m_wordDoc);
m_wordDoc = value;
}
}
catch (Exception ex)
{
}
}
}
private msoWord.Documents m_wordDocs;
public msoWord.Documents WordDocs
{
get { return m_wordDocs; }
private set
{
try
{
if (m_wordDocs != value)
{
ReleaseCOMObject(m_wordDocs);
m_wordDocs = value;
}
}
catch (Exception ex)
{
}
}
}
private msoWord.Application m_wordApp;
public msoWord.Application WordApp
{
get { return m_wordApp; }
set
{
try
{
if (m_wordApp != value)
{
if (m_wordApp != null)
{
m_wordApp.DocumentOpen -= new msoWord.ApplicationEvents4_DocumentOpenEventHandler(m_wordApp_DocumentOpen);
ReleaseCOMObject(m_wordApp);
}
m_wordApp = value;
if (IsValid(m_wordApp))
{
m_wordApp.DocumentOpen += new msoWord.ApplicationEvents4_DocumentOpenEventHandler(m_wordApp_DocumentOpen);
WordDocs = m_wordApp.Documents;
}
}
}
catch (Exception ex)
{
}
}
}
void m_wordApp_DocumentOpen(msoWord.Document doc)
{
try
{
string name = doc.Name;
AddDocument(name);
}
catch (Exception ex)
{
}
finally
{
ReleaseCOMObject(doc);
doc = null;
}
}
private msoWord.Application GetInstance()
{
msoWord.Application app = null;
try
{
app = (msoWord.Application)Marshal.GetActiveObject("Word.Application");
}
catch (Exception ex)
{
if (app == null)
app = new msoWord.Application();
}
finally
{
if (IsValid(app))
{
app.Visible = true;
app.Activate();
}
}
return app;
}
private void OpenWordDoc()
{
try
{
m_openFileDialog.AddExtension = true;
m_openFileDialog.Filter = "All Word (*.docx; *.docm; *.doc; *.dotx; *.dotm; *.dot)|*.docx;*.docm;*.doc;*.dotx;*.dotm;*.dot|Word Documents (*.docx)|*.docx|Word Macro-Enabled Documents (*.docm)|*.docm|Word 97-2003 Documents (*.doc)|*.doc|All Word Templates (*.dotx; *.dotm; *.dot)|*.dotx;*.dotm;*.dot|Word Templates (*.dotx)|*.dotx|Word Macro-Enabled Templates (*.dotm)|*.dotm)";
m_openFileDialog.FilterIndex = 1;
m_openFileDialog.Multiselect = false;
m_openFileDialog.Title = "Open Word Document";
if (m_openFileDialog.ShowDialog() == DialogResult.OK)
{
object fileName = m_openFileDialog.FileName;
WordDoc = m_wordDocs.Open(ref fileName);
}
}
catch (Exception ex)
{
}
}
private bool IsValid(msoWord.Application app)
{
try
{
if (app != null)
{
string name = app.Caption;
return true;
}
}
catch (Exception ex)
{
}
return false;
}
#endregion
private void ReleaseCOMObject(object comObject)
{
try
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
if (comObject != null && Marshal.IsComObject(comObject))
Marshal.ReleaseComObject(comObject);
}
catch (Exception ex)
{
}
}
}
}
namespace MCDevNET.Office.Word.TestUIControls
{
partial class OpenWordDocument
{
/// <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.m_documentsListBox = new System.Windows.Forms.ListBox();
this.m_openDocumentButton = new System.Windows.Forms.Button();
this.m_openWordButton = new System.Windows.Forms.Button();
this.m_openFileDialog = new System.Windows.Forms.OpenFileDialog();
this.SuspendLayout();
//
// lb_Documents
//
this.m_documentsListBox.FormattingEnabled = true;
this.m_documentsListBox.Location = new System.Drawing.Point(12, 41);
this.m_documentsListBox.Name = "lb_Documents";
this.m_documentsListBox.Size = new System.Drawing.Size(156, 134);
this.m_documentsListBox.TabIndex = 8;
//
// m_openDocumentButton
//
this.m_openDocumentButton.Location = new System.Drawing.Point(93, 12);
this.m_openDocumentButton.Name = "m_openDocumentButton";
this.m_openDocumentButton.Size = new System.Drawing.Size(75, 23);
this.m_openDocumentButton.TabIndex = 7;
this.m_openDocumentButton.Text = "Doc";
this.m_openDocumentButton.UseVisualStyleBackColor = true;
//
// m_openWordButton
//
this.m_openWordButton.Location = new System.Drawing.Point(12, 12);
this.m_openWordButton.Name = "m_openWordButton";
this.m_openWordButton.Size = new System.Drawing.Size(75, 23);
this.m_openWordButton.TabIndex = 6;
this.m_openWordButton.Text = "Word";
this.m_openWordButton.UseVisualStyleBackColor = true;
//
// m_openFileDialog
//
this.m_openFileDialog.FileName = "openFileDialog1";
//
// OpenWordDocument
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(182, 184);
this.Controls.Add(this.m_documentsListBox);
this.Controls.Add(this.m_openDocumentButton);
this.Controls.Add(this.m_openWordButton);
this.Name = "OpenWordDocument";
this.Text = "OpenWordDocument";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListBox m_documentsListBox;
private System.Windows.Forms.Button m_openDocumentButton;
private System.Windows.Forms.Button m_openWordButton;
private System.Windows.Forms.OpenFileDialog m_openFileDialog;
}
}
The hang happens on the call to Documents.Open(fileName)
You have an event handler wired up for the Application.DocumentOpen event. On removing this event handler the hang no longer occurs.
I presume the reason for the problem is that you are getting deadlocked as Word tries to fire that event before the Documents.Open call returns. Meaning the thread that handles the event is still busy.
Replacing
WordDoc = m_wordDocs.Open(ref fileName)
with
new System.Threading.Tasks.Task(() => WordDoc = m_wordDocs.Open(ref fileName))
.Start();
To open the document on a different thread seems to resolve the issue.