which event for readping pane fully instantiated.

B

BatKing

Hi,

I am working on a outlook shared add-in.

what I am trying to do is trying to add a button on the reading pane and
mail inspector window's header place (at the right side of the from, to
......). This is kind like Linkedin outlook tool bars. while Outlook object
model doesn't support this, I had to learn win32 API to add the button.

after some learning and research, I found what I need to do were

1. I need to find the reading pane window and it's child windows by using
FindWindow win32 API
2. resize the child window to make space for my button
3. use CreateWindowEX to create the button
4. set the reading pane window as the parnet window of my created button.

everything working fine except I don't know which event is the best event
for me to do the above step #1 for finding the reading pane window when a
mail reading pane wasn't there before. for inspector window the active event
is the right event. However for reading pane, all the event on the explorer
windows doesn't seems care about the reading pane window's initialization. I
have try both Selection change event and Active event of explorer, but both
doesn't seems works during outlook start up. this means the reading pane
window is not fully instantiated yet during the 2 events. so the reading pane
window is not there yet ( or I should say I could only get a weak reading
pane window object which is the fake one. but this is not 100% always the
case. there are some case if the reading pane window instantiated fast
enough, then everything works fine. but this is very rare and usually durng
debug).

everything works fine once outlook is fully started AND if I un-focus
outlook window and focus outlook again (active event), or select another
email (selection change event), at this time since the reading pane is fully
instantiated, then I can find the findow correctly. but during start up and
when outlook shows the 1st email in the reading pane, even selection change
event is fired but the reading pane is not fully instantiated yet. and my
findwindows method won't find the reading pane windows.
 
K

Ken Slovak - [MVP - Outlook]

Very cool, and very advanced Outlook programming.

About the only thing I can think of for that startup case would be to handle
the Activate() event and start up a timer. When the timer fires get the
reading/preview pane window. You'd have to experiment with the timer
interval.
 
B

BatKing

Hi Ken,

Thanks for helping. I am playing with the timer. but without success. Do you
have any example of how to use the timer within the active or
onSelectionChange event?

I have tried System.Threading.Timer and System.timers and both doesn't seems
to work. and with the timer my addin becomes very unstable (ie crashs outlook
or the UI freezes).
 
K

Ken Slovak - [MVP - Outlook]

One thing you have to make sure of is that OnStartupComplete() has fired
before you start up any timers or asynch processes. That event from
IDTExtensibility is a blocking event in Outlook 2007. I have ended up
hanging things when I don't make sure that the blocking event is finished
before I do anything like that. I have however, set up timers at the end of
OnStartupComplete() that fire after the event finishes and returns to
Outlook.

I usually use something like this:

// declared at class level
private System.Timers.Timer _timer = null;

// in the init handler for the class
if (_timer != null)
{
_timer.Enabled = false;
}
else
{
_timer = new System.Timers.Timer();
_timer.Enabled = false;

_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
}
_timer.Interval = 500; // 1/2 second

// at the very end of the Activate() event handler

if (_startup)
{
// any other code that must run on first Activate() event

_startup = false;

_timer.Enabled = true;
}

private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Enabled = false;

// other code
}

// release code for the class
if (_timer != null)
{
try
{
_timer.Enabled = false;
_timer.Dispose();
_timer = null;
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
}
}
 
B

BatKing

Hi Ken,

I have tried what you suggested. However it seems during the _timer_Elapsed
event, I cannot touch any UI stuff. (ie, finding the reading pane window and
create my button). if I put my method, findReadingPaneAndAddButton(), which
finding the reading pane window and creates the button, then the whole
outlook will freeze (sometime ramdonly).

in the following example, the author mention the UI thread. and it seems I
am facing the exactly problem. just imaging what happen if the explorer
window (the sibling window) doesn't initialize fast enough (not in this case
but just a example) and I need a timer event to find the explorer window.

http://www.codeproject.com/KB/office/additional_panel_Outlook.aspx

"In any process/application, there should always be just a single thread
which executes the message loop of all child windows - the UI thread. Thanks
for us, the call to OnStartupComplete (like all calls of the
IDTExtensibility2 interface) is made from the UI thread, which is very
important because we are going to create the PanelContainer window and we
will assign it as a child of the Outlook main window. If this call was made
from another thread, we would have bigger problems because the PanelContainer
instance would be created out of the UI thread, which would cause big
stability issues."

I think I am facing the "bigger problems" and "big stability issues".

Thanks
Leon
 
K

Ken Slovak - [MVP - Outlook]

The thread business is definitely critical when calling anything in the
Outlook object model. Calling into the object model from a background thread
will definitely hang or crash Outlook. But the timer in the Explorer wrapper
class should be running on the main thread unless you set up a separate
thread for it. Easy enough to check, just log or use Debug.WriteLine() with
this:

string currentThread = Thread.CurrentThread.ManagedThreadId.ToString();

I've run into threading problems when calling to display a form with a Web
browser control on it from a WordMail item, since that's running in a
different process and thread, and the .NET Web browser control is really a
wrapped Active-X control. In cases like that I've had to use code something
like this:

// main Connect class:
private static System.Threading.SynchronizationContext _syncContext = null;

// in OnStartupComplete()
if ((_explorers.Count > 0) || (_inspectors.Count > 0))
{
if ((_syncContext == null) &&
(System.Threading.SynchronizationContext.Current != null))
{
_syncContext = System.Threading.SynchronizationContext.Current;
}
}

Then in my calls to the form with the Web browser control from WordMail I
use code like this:

try
{
object[] args = { previewHTML, _stationeryToUse };

System.Threading.SendOrPostCallback callback = new
System.Threading.SendOrPostCallback(PreviewThreadHelper.InitPreviewBox);

Connect.SyncContext.Post(callback, args);
}

Connect exposes a property called SyncContext that returns/sets the
_syncContext thread object.

PreviewThreadHelper is a separate class that has an InitPreviewBox() method
that takes an Object state argument passed from the synchronizer caller. The
code there does the actual initialization and displaying of the preview form
that has the browser control.

You can see if you can adapt that sort of code to what you need to do,
calling back to the main UI thread that you picked up in the
OnStartupComplete() handler.
 
B

BatKing

Ken Slovak - said:
The thread business is definitely critical when calling anything in the
Outlook object model. Calling into the object model from a background thread
will definitely hang or crash Outlook. But the timer in the Explorer wrapper
class should be running on the main thread unless you set up a separate
thread for it. Easy enough to check, just log or use Debug.WriteLine() with
this:

string currentThread = Thread.CurrentThread.ManagedThreadId.ToString();

I've run into threading problems when calling to display a form with a Web
browser control on it from a WordMail item, since that's running in a
different process and thread, and the .NET Web browser control is really a
wrapped Active-X control. In cases like that I've had to use code something
like this:

// main Connect class:
private static System.Threading.SynchronizationContext _syncContext = null;

// in OnStartupComplete()
if ((_explorers.Count > 0) || (_inspectors.Count > 0))
{
if ((_syncContext == null) &&
(System.Threading.SynchronizationContext.Current != null))
{
_syncContext = System.Threading.SynchronizationContext.Current;
}
}

Then in my calls to the form with the Web browser control from WordMail I
use code like this:

try
{
object[] args = { previewHTML, _stationeryToUse };

System.Threading.SendOrPostCallback callback = new
System.Threading.SendOrPostCallback(PreviewThreadHelper.InitPreviewBox);

Connect.SyncContext.Post(callback, args);
}

Connect exposes a property called SyncContext that returns/sets the
_syncContext thread object.

PreviewThreadHelper is a separate class that has an InitPreviewBox() method
that takes an Object state argument passed from the synchronizer caller. The
code there does the actual initialization and displaying of the preview form
that has the browser control.

You can see if you can adapt that sort of code to what you need to do,
calling back to the main UI thread that you picked up in the
OnStartupComplete() handler.




BatKing said:
Hi Ken,

I have tried what you suggested. However it seems during the
_timer_Elapsed
event, I cannot touch any UI stuff. (ie, finding the reading pane window
and
create my button). if I put my method, findReadingPaneAndAddButton(),
which
finding the reading pane window and creates the button, then the whole
outlook will freeze (sometime ramdonly).

in the following example, the author mention the UI thread. and it seems I
am facing the exactly problem. just imaging what happen if the explorer
window (the sibling window) doesn't initialize fast enough (not in this
case
but just a example) and I need a timer event to find the explorer window.

http://www.codeproject.com/KB/office/additional_panel_Outlook.aspx

"In any process/application, there should always be just a single thread
which executes the message loop of all child windows - the UI thread.
Thanks
for us, the call to OnStartupComplete (like all calls of the
IDTExtensibility2 interface) is made from the UI thread, which is very
important because we are going to create the PanelContainer window and we
will assign it as a child of the Outlook main window. If this call was
made
from another thread, we would have bigger problems because the
PanelContainer
instance would be created out of the UI thread, which would cause big
stability issues."

I think I am facing the "bigger problems" and "big stability issues".

Thanks
Leon
 
B

BatKing

Thanks Ken.

according MSDN lib, System.timer will use thread pool's thread. so I guess
that's why. And Thread.CurrentThread.ManagedThreadId.ToString() shows
different thread ID in my Connect.OnStartUpComplete() method and in my
_timer_Elapsed method in ExplorerWrapper class.

I am trying with System.Threading.SynchronizationContext and
System.Threading.SendOrPostCallback. However do I still need a timer?
otherwise how can I control the callback function to be excuted another time
if the reading pane window is not initilized yet?

Or according to MSDN, there is a way to force System.timer to execute on the
UI thread with Timer.SynchronizingObject Property. But it seems so confusing
and no example on that. (when instantiating the timer in my explorerWrapper
class, I don't know how to set the SynchronizingObject property)


Sorry, I am really new to this UI threading issue.


Ken Slovak - said:
The thread business is definitely critical when calling anything in the
Outlook object model. Calling into the object model from a background thread
will definitely hang or crash Outlook. But the timer in the Explorer wrapper
class should be running on the main thread unless you set up a separate
thread for it. Easy enough to check, just log or use Debug.WriteLine() with
this:

string currentThread = Thread.CurrentThread.ManagedThreadId.ToString();

I've run into threading problems when calling to display a form with a Web
browser control on it from a WordMail item, since that's running in a
different process and thread, and the .NET Web browser control is really a
wrapped Active-X control. In cases like that I've had to use code something
like this:

// main Connect class:
private static System.Threading.SynchronizationContext _syncContext = null;

// in OnStartupComplete()
if ((_explorers.Count > 0) || (_inspectors.Count > 0))
{
if ((_syncContext == null) &&
(System.Threading.SynchronizationContext.Current != null))
{
_syncContext = System.Threading.SynchronizationContext.Current;
}
}

Then in my calls to the form with the Web browser control from WordMail I
use code like this:

try
{
object[] args = { previewHTML, _stationeryToUse };

System.Threading.SendOrPostCallback callback = new
System.Threading.SendOrPostCallback(PreviewThreadHelper.InitPreviewBox);

Connect.SyncContext.Post(callback, args);
}

Connect exposes a property called SyncContext that returns/sets the
_syncContext thread object.

PreviewThreadHelper is a separate class that has an InitPreviewBox() method
that takes an Object state argument passed from the synchronizer caller. The
code there does the actual initialization and displaying of the preview form
that has the browser control.

You can see if you can adapt that sort of code to what you need to do,
calling back to the main UI thread that you picked up in the
OnStartupComplete() handler.




BatKing said:
Hi Ken,

I have tried what you suggested. However it seems during the
_timer_Elapsed
event, I cannot touch any UI stuff. (ie, finding the reading pane window
and
create my button). if I put my method, findReadingPaneAndAddButton(),
which
finding the reading pane window and creates the button, then the whole
outlook will freeze (sometime ramdonly).

in the following example, the author mention the UI thread. and it seems I
am facing the exactly problem. just imaging what happen if the explorer
window (the sibling window) doesn't initialize fast enough (not in this
case
but just a example) and I need a timer event to find the explorer window.

http://www.codeproject.com/KB/office/additional_panel_Outlook.aspx

"In any process/application, there should always be just a single thread
which executes the message loop of all child windows - the UI thread.
Thanks
for us, the call to OnStartupComplete (like all calls of the
IDTExtensibility2 interface) is made from the UI thread, which is very
important because we are going to create the PanelContainer window and we
will assign it as a child of the Outlook main window. If this call was
made
from another thread, we would have bigger problems because the
PanelContainer
instance would be created out of the UI thread, which would cause big
stability issues."

I think I am facing the "bigger problems" and "big stability issues".

Thanks
Leon
 
K

Ken Slovak - [MVP - Outlook]

You'd still need the timer to fire when you were out of Activate(), don't
forget you set up the timer there so you allow time for the event handler to
finish and then the timer fires. If your code finds that the reading pane
isn't initialized yet you just grab that timer instance and enable it again.

I haven't used the SynchronizingObject property so I'm not familiar with it.
You might want to review the thread at
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic42363.aspx,
which seems to explain what's going on with SynchronizingObject.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top