I am in the process of writing a batch script to take a backup of my pst file whenever the outlook closes.
I am thinking of having a scheduled task based on windows event id.
I searched for various event id for Microsoft outlook but not able to get the desired.
I tried analyzing the eventvwr but not able to find the desired.
I am using Windows 7 Professional 64 bit, Outlook 2010. I am looking for the start and stop event id for outlook
So as I said before as far as I know there is no Event for Outlook Start/Close, if you have AddIns installed you could use the ID:45 to detect the Startup but still you don´t have a close Event!
The only way to get a Event on Outlook Start/Close is to make it on your own, either via a Outlook AddIn or an VBA-Makro executing on Outlook Startup and Quit Event.
Sample for Outlook AddIn:
public partial class ThisAddIn
{
private EventLog log = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
log = new EventLog();
log.Source = "OutlookAddIn";
log.Log = "Application";
log.WriteEntry("Outlook start", EventLogEntryType.Information, 1);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
if (log != null)
{
log.WriteEntry("Outlook stop", EventLogEntryType.Information, 0);
}
}
#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
}
UPDATE
I´ve tried the VBA Solution by myself and it worked ;-)
Create a Module with the following code (Source)
Option Explicit
Declare Function RegisterEventSource Lib "advapi32.dll" Alias _
"RegisterEventSourceA" (ByVal lpUNCServerName As String, _
ByVal lpSourceName As String) As Long
Declare Function DeregisterEventSource Lib "advapi32.dll" ( _
ByVal hEventLog As Long) As Long
Declare Function ReportEvent Lib "advapi32.dll" Alias _
"ReportEventA" ( _
ByVal hEventLog As Long, ByVal wType As Integer, _
ByVal wCategory As Integer, ByVal dwEventID As Long, _
ByVal lpUserSid As Any, ByVal wNumStrings As Integer, _
ByVal dwDataSize As Long, plpStrings As Long, _
lpRawData As Any) As Boolean
Declare Function GetLastError Lib "kernel32" () As Long
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
hpvDest As Any, hpvSource As Any, _
ByVal cbCopy As Long)
Declare Function GlobalAlloc Lib "kernel32" ( _
ByVal wFlags As Long, _
ByVal dwBytes As Long) As Long
Declare Function GlobalFree Lib "kernel32" ( _
ByVal hMem As Long) As Long
Public Const EVENTLOG_SUCCESS = 0
Public Const EVENTLOG_ERROR_TYPE = 1
Public Const EVENTLOG_WARNING_TYPE = 2
Public Const EVENTLOG_INFORMATION_TYPE = 4
Public Const EVENTLOG_AUDIT_SUCCESS = 8
Public Const EVENTLOG_AUDIT_FAILURE = 10
Public Sub LogNTEvent(sString As String, iLogType As Integer, _
iEventID As Long)
Dim bRC As Boolean
Dim iNumStrings As Integer
Dim hEventLog As Long
Dim hMsgs As Long
Dim cbStringSize As Long
hEventLog = RegisterEventSource("", Application.Name)
cbStringSize = Len(sString) + 1
hMsgs = GlobalAlloc(&H40, cbStringSize)
CopyMemory ByVal hMsgs, ByVal sString, cbStringSize
iNumStrings = 1
If ReportEvent(hEventLog, _
iLogType, 0, _
iEventID, 0&, _
iNumStrings, cbStringSize, _
hMsgs, hMsgs) = 0 Then
MsgBox GetLastError()
End If
Call GlobalFree(hMsgs)
DeregisterEventSource (hEventLog)
End Sub
And this is how your OutlookSessionApplication File should look like (how to get there) and don´t forget to allow makros or sign the one you´ve written ;-)
Private Sub Application_Quit()
Call LogNTEvent("OUTLOOK QUIT", _
EVENTLOG_INFORMATION_TYPE, 1000)
End Sub
Private Sub Application_Startup()
Call LogNTEvent("OUTLOOK START", _
EVENTLOG_INFORMATION_TYPE, 1001)
End Sub
use "outlookspy". it's a 3rd party app which is able to find dispID you need to capture new mail, new item etc...
Related
I'm writing an application in C# to remote control another application. I don't have access to that application yet, but it's most likely written in either Visual C++ or VB6. I'm currently remote controlling a simple C# winforms program I created instead. So far, I can get all of the controls in a manner similar to Spy++ using PInvoke. I can also do things like setting the text in a textbox. The problem I have is that I can't uniquely identify a specific text box between executions of the application I'm controlling. I can't use hWnd for example because it's different every time you run the application. GetWindowLong returns whatever the hWnd is so that's no help. The internet speaks of this message called WM_GETCONTROLNAME. The scary code below attempts to use that message to get the name the developer used to uniquely identify the controls. The SendMessage seems like it's returning the number of bytes in the name. But the buffer containing that name comes back all zeros.
So here's the question. How can I fix the code below so that it returns that name correctly? (Hopefully it's a boneheaded mistake that's easy to fix) Or, just as good, is there some other ID that will be guaranteed to be the same every time I run the program? Without something to uniquely identify the textbox controls, my code can't tell them apart.
I do have a hack in mind. It seems to me that using the position of the textboxes to tell them apart would work. But I'd much prefer to have the code below working.
public static string GetWindowText(IntPtr Handle)
{
int BufferSize = 256;
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlName = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr OtherMem = IntPtr.Zero;
try
{
OtherMem = VirtualAllocEx(Handle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
var Result = SendMessage(Handle, Message, new IntPtr(sb.Capacity), OtherMem);
//Result contains different numbers which seem like the correct lengths
var Result2 = ReadProcessMemory(Handle, OtherMem, ControlName, BufferSize, out BytesReadPointer);
//ControlName always comes back blank.
}
finally
{
var Result3 = VirtualFreeEx(Handle, OtherMem, BufferSize, FreeType.Release);
}
return ""; // Convert ControlName to a string
}
So after a good night's sleep and a little tinkering, I managed to fix the problem myself. There were two problems. First, I was passing in the handle to the control to VirtualAllocEx. But in the documentation for that function, it says that it takes a handle to the process. So started supplying that. Then VirtualAllocEx was telling me that I had an invalid handle. So it turns out the handle you pass to VirualAllocEx has to have PROCESS_VM_OPERATION set. So I made a call to OpenProcess to get that handle, then passed that to VirtualAllocEx. After that, everything worked. I just had to convert the byte array coming back into a string, and trim a bunch of nulls. This returns the Name property on the controls. I'll post the code here in case anyone else needs something like this.
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
public static string GetWindowText(IntPtr ControlHandle, IntPtr ProcessHandle)
{
int BufferSize = 256;
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlNameByteArray = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr RemoteProcessMemoryAddress = IntPtr.Zero;
IntPtr RemoteProcessHandle = IntPtr.Zero;
try
{
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
RemoteProcessHandle = OpenProcess(ProcessAccessFlags.CreateThread |
ProcessAccessFlags.QueryInformation |
ProcessAccessFlags.VirtualMemoryOperation |
ProcessAccessFlags.VirtualMemoryWrite |
ProcessAccessFlags.VirtualMemoryRead, false, Process.Id);
RemoteProcessMemoryAddress = VirtualAllocEx(RemoteProcessHandle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (RemoteProcessMemoryAddress == IntPtr.Zero)
{
throw new Exception("GetWindowText: " + GetLastWin32Error());
}
SendMessage(ControlHandle, Message, new IntPtr(sb.Capacity), RemoteProcessMemoryAddress);
ReadProcessMemory(RemoteProcessHandle, RemoteProcessMemoryAddress, ControlNameByteArray, BufferSize, out BytesReadPointer);
string ControlName = Encoding.Unicode.GetString(ControlNameByteArray).TrimEnd('\0');
return ControlName;
}
finally
{
VirtualFreeEx(RemoteProcessHandle, RemoteProcessMemoryAddress, BufferSize, FreeType.Release);
}
}
Basically, I'm almost finished making this note app which the users save notes etc. Basic note app function. The reason I'm not fully done is that i just need help with adding tiles to my app for the notes. Basically the user clicks the "Pin to start" from the menu item and for the selected note, pins that to the start. I've done this through:
Private Sub PinToStart_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
Dim Storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()
Dim data As SampleData = TryCast(TryCast(sender, MenuItem).DataContext, SampleData)
Dim selectedItem As ListBoxItem = TryCast(Me.SavedNotesList.ItemContainerGenerator.ContainerFromItem(data), ListBoxItem)
Dim directory As String = "./MyNote/SavedNotes/*.*"
Dim filenames As String() = Storage.GetFileNames(directory)
Dim dataSource As New List(Of SampleData)()
For Each filename As String In filenames
Dim ISF As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()
Dim FS As IsolatedStorageFileStream = ISF.OpenFile("MyNote/SavedNotes/" & filename, FileMode.Open, FileAccess.Read)
Dim FETime As String = Storage.GetCreationTime("MyNote/SavedNotes/" & data.FileNameX).ToString("dd/mm/yyyy h:mmtt")
Dim tileData As New StandardTileData() With { _
.Title = data.FileNameX, _
.BackgroundImage = New Uri("/Assets/202.png", UriKind.Relative), _
.BackTitle = data.FileNameX, _
.BackContent = data.Description}
ShellTile.Create(New Uri("/ViewPage.xaml?Title=" & data.FileNameX & "&Body=" & data.Description, UriKind.Relative), tileData)
Next
End Sub
Currently this is the code which creates the tile. Although there is one problem, once the tile is created it throws an exception and says "Tiles can only be created when the application is in the foreground" but it still proceeds and creates the tile with no problem. Second error i have is that I need a way to update the tile. I just don't know how.
Can anyone help me?
use HubTile control from Microsoft.Phone.Controls.Toolkit to created tile
you can try this code:
var shellTileData = new StandardTileData
{
BackgroundImage = new Uri("Path for image", UriKind.RelativeOrAbsolute),
BackContent = "xyz"
};
var tile = ShellTile.ActiveTiles.First();
tile.Update(shellTileData);
In a Windows form I need to change textbox BackColor on focus. I want to do this on every textbox or control focus.
When the focus on textbox1 BackColor of this textbox should be changed and now I press tab, focus goes to next textbox (textbox2) now the BackColor of textbox2 should be changes and textbox1 BackColor should be changed back as default color.
Behold the C# solution:
//Properties declaration
private System.Drawing.Color NormalColor = System.Drawing.Color.FromArgb(50, 82, 110);
private System.Drawing.Color FocusColor = System.Drawing.Color.FromArgb(42, 65, 84);
// Else where in the Constructor
textBox_Username.Enter += EnterEvent;
textBox_Password.Enter += EnterEvent;
textBox_Username.Leave += LeaveEvent;
textBox_Password.Leave += LeaveEvent;
// Outside the Constructor
private void EnterEvent(object sender, EventArgs e)
{
if (sender is TextBox)
((TextBox)sender).BackColor = FocusColor;
}
private void LeaveEvent(object sender, EventArgs e)
{
if (sender is TextBox)
((TextBox)sender).BackColor = NormalColor;
}
On textbox's event named GotFocus
Private Sub TextBox1_GotFocus(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles TextBox1.GotFocus
textbox1.backcolor = color.red
End Sub
On textbox's event named LostFocus
Private Sub TextBox1_LostFocus(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles TextBox1.LostFocus
textbox1.backcolor = color.white
End Sub
I am building a customer ajaxhelper extension in order to create an Ajax.ActionImage(...) method (see code below).
What I don't know how to do is "merge" the AjaxOptions into my anchor href attribute. I could use ajax.ActionLink(...) but then I don't know how t build my image element inside the created MvcHtmlString.
Thanks in advance!
<Extension()> _
Public Function ActionImage(ByVal ajax As AjaxHelper, ByVal controller As String, ByVal action As String, ByVal routeValues As Object, ByVal AjaxOptions As Object, ByVal imagePath As String, ByVal alt As String, ByVal width As Integer, ByVal height As Integer) As MvcHtmlString
Dim url = New UrlHelper(ajax.ViewContext.RequestContext)
Dim imgHtml As String
Dim anchorHtml As String
Dim imgbuilder = New TagBuilder("img")
imgbuilder.MergeAttribute("src", url.Content(imagePath))
imgbuilder.MergeAttribute("alt", alt)
imgbuilder.MergeAttribute("width", width)
imgbuilder.MergeAttribute("height", height)
imgHtml = imgbuilder.ToString(TagRenderMode.SelfClosing)
Dim anchorBuilder = New TagBuilder("a")
anchorBuilder.MergeAttribute("href", url.Action(action, controller, routeValues))
anchorBuilder.InnerHtml = imgHtml
anchorHtml = anchorBuilder.ToString(TagRenderMode.Normal)
Return MvcHtmlString.Create(anchorHtml)
End Function
First, you need to change the type of the variable ajaxOptions to AjaxOptions (in the System.Web.Ajax namespace). Once you have done this, you can add the following to merge your ajaxOptions into your anchor tag:
If ajaxHelper.ViewContext.UnobtrusiveJavaScriptEnabled Then
anchorBuilder.MergeAttributes(ajaxOptions.ToUnobtrusiveHtmlAttributes())
End If
You do not want the options inside your href. The options must be part of the anchor tag, in order to be parsed correctly by jquery.unobtrusive-ajax.js.
counsellorben
i'm using the Win32 progress dialog. The damnest thing is that when i call:
progressDialog.StopProgressDialog();
it doesn't disappear. It stays on screen until the user moves her mouse over it - then it suddenly disappers.
The call to StopProgressDialog returns right away (i.e. it's not a synchronous call). i can prove this by doing things after the call has returned:
private void button1_Click(object sender, EventArgs e)
{
//Force red background to prove we've started
this.BackColor = Color.Red;
this.Refresh();
//Start a progress dialog
IProgressDialog pd = (IProgressDialog)new ProgressDialog();
pd.StartProgressDialog(this.Handle, null, PROGDLG.Normal, IntPtr.Zero);
//The long running operation
System.Threading.Thread.Sleep(10000);
//Stop the progress dialog
pd.SetLine(1, "Stopping Progress Dialog", false, IntPtr.Zero);
pd.StopProgressDialog();
pd = null;
//Return form to normal color to prove we've stopped.
this.BackColor = SystemColors.Control;
this.Refresh();
}
The form:
starts gray
turns red to show we've stared
turns back to gray color to show we've called stop
So the call to StopProgressDialog has returned, except the progress dialog is still sitting there, mocking me, showing the message:
Stopping Progress Dialog
Doesn't Appear for 10 seconds
Additionally, the progress dialog does not appear on screen until the
System.Threading.Thread.Sleep(10000);
ten second sleep is over.
Not limited to .NET WinForms
The same code also fails in Delphi, which is also an object wrapper around Window's windows:
procedure TForm1.Button1Click(Sender: TObject);
var
pd: IProgressDialog;
begin
Self.Color := clRed;
Self.Repaint;
pd := CoProgressDialog.Create;
pd.StartProgressDialog(Self.Handle, nil, PROGDLG_NORMAL, nil);
Sleep(10000);
pd.SetLine(1, StringToOleStr('Stopping Progress Dialog'), False, nil);
pd.StopProgressDialog;
pd := nil;
Self.Color := clBtnFace;
Self.Repaint;
end;
PreserveSig
An exception would be thrown if StopProgressDialog was failing.
Most of the methods in IProgressDialog, when translated into C# (or into Delphi), use the compiler's automatic mechanism of converting failed COM HRESULTS into a native language exception.
In other words the following two signatures will throw an exception if the COM call returned an error HRESULT (i.e. a value less than zero):
//C#
void StopProgressDialog();
//Delphi
procedure StopProgressDialog; safecall;
Whereas the following lets you see the HRESULT's and react yourself:
//C#
[PreserveSig]
int StopProgressDialog();
//Delphi
function StopProgressDialog: HRESULT; stdcall;
HRESULT is a 32-bit value. If the high-bit is set (or the value is negative) it is an error.
i am using the former syntax. So if StopProgressDialog is returning an error it will be automatically converted to a language exception.
Note: Just for SaG i used the [PreserveSig] syntax, the returned HRESULT is zero;
MsgWait?
The symptom is similar to what Raymond Chen described once, which has to do with the incorrect use of PeekMessage followed by MsgWaitForMultipleObjects:
"Sometimes my program gets stuck and
reports one fewer record than it
should. I have to jiggle the mouse to
get the value to update. After a while
longer, it falls two behind, then
three..."
But that would mean that the failure is in IProgressDialog, since it fails equally well on CLR .NET WinForms and native Win32 code.
To really hide the dialog, I've added the following to my C++ wrapper class:
void CProgressDlg::Stop()
{
if ((m_isVisible)&&(m_bValid))
{
HWND hDlgWnd = NULL;
//Sometimes the progress dialog sticks around after stopping it,
//until the mouse pointer is moved over it or some other triggers.
//This process finds the hwnd of the progress dialog and hides it
//immediately.
IOleWindow *pOleWindow;
HRESULT hr=m_pIDlg->QueryInterface(IID_IOleWindow,(LPVOID *)&pOleWindow);
if(SUCCEEDED(hr))
{
hr=pOleWindow->GetWindow(&hDlgWnd);
if(FAILED(hr))
{
hDlgWnd = NULL;
}
pOleWindow->Release();
}
m_pIDlg->StopProgressDialog();
if (hDlgWnd)
ShowWindow(hDlgWnd, SW_HIDE);
m_isVisible = false;
m_pIDlg->Release();
m_bValid = false;
}
}
This is in C++, but you should be able to adapt this to C# without much problems.
Check the return value of the StopProgressDialog Method, maybe that will give you more information about what is going on:
HRESULT StopProgressDialog(VOID);
Returns S_OK if successful, or an error value otherwise.
The full P/Invoke signature is available, but here's the condensed version for easy reading:
[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IProgressDialog
{
void StartProgressDialog(IntPtr hwndParent,
[MarshalAs(UnmanagedType.IUnknown)] object punkEnableModless, //IUnknown
PROGDLG dwFlags, //DWORD
IntPtr pvResevered //LPCVOID
);
void StopProgressDialog();
void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
);
void SetAnimation(
IntPtr hInstAnimation, //HINSTANCE
ushort idAnimation //UINT
);
[PreserveSig]
[return: MarshalAs(UnmanagedType.Bool)]
bool HasUserCancelled();
void SetProgress(
uint dwCompleted, //DWORD
uint dwTotal //DWORD
);
void SetProgress64(
ulong ullCompleted, //ULONGLONG
ulong ullTotal //ULONGLONG
);
void SetLine(
uint dwLineNum, //DWORD
[MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
[MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
IntPtr pvResevered //LPCVOID
);
void SetCancelMsg(
[MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg,
object pvResevered
);
void Timer(PDTIMER dwTimerAction, object pvResevered);
}
Note: That almost all of the methods follow proper COM rules for their signatures. Except HasUserCancelled. It is does not follow the rules for the signature of a method in a COM class. All methods are supposed to return an HRESULT, and return values are supposed to be in an out retval paramater. HasUserCancelled actually returns a Boolean value.
Note: That almost all these worlds are yours. Except Europa. Attempt no landing there.
Note: That almost all your base are belong to us. Except WhatYouSay. Main light turn on.