VSTO addin crash Outlook - vsto

I need to loop all Outlook items and search for strings that full fill a regular expression pattern including all the attachments.
I know this is a big operation and require a lot of resources, but the project is demand to do this.
I managed to make a VSTO plugin that can do this, but the problem is that the addin cause Outlook to hang sometimes. This is not a good business solution, it is not working stable.
In my code I have make sure that I close COM object after using it.
outlookItem.Close(OlInspectorClose.olDiscard);
Marshal.ReleaseComObject(outlookItem);
The attachments are convert to steam and dispose efter using.
string AttachSchema = "http://schemas.microsoft.com/mapi/proptag/0x37010102";
byte[] filebyte = null;
PropertyAccessor pacc = attachment.PropertyAccessor;
filebyte = (byte[])pacc.GetProperty(AttachSchema);
Stream stream = new MemoryStream(filebyte);
Anyone can give some suggestions on how to make to addin stable?

Firstly, accessing PR_ATTACH_DATA_BIN (or any other large PT_BINARY properties) might not work using the PropertyAccessor object (especially in the online mode), so you might be better off using Attachment.SaveAsFile and then loading the file in memory (or a memory mapped file) if necessary.
Secondly, keep in mind that you can use OOM only on the primary Outlook thread. To run you code on the secondary thread, you need either Extended MAPI (C++ or Delphi only) or Redemption (I am its author - its RDO family of objects can be used on secondary threads).

Related

Does MS Word have an equivalent to ExecuteExcel4Macro

Chip Pearson RIP documented a feature in Excel Application.ExecuteExcel4Macro(HiddenName).
This feature provides a nicely hidden application level space for addins to store information... have a look: http://www.cpearson.com/Excel/hidden.htm
Is there something similar available for MS Word?
I tried intellisense but there is nothing that starts with Application.Ex
It would seem that the answer is No
Thank you cyboashu and Cindy Meister for your comments. The nice thing about ExecuteExcel4Macro is that it allows developers to place content in a very private space; apparently it is not possible (or extremely difficult) to read from this hidden space unless you know exactly what you're looking for, and it persists until all documents are closed.
There are various workarounds for MS Word:
ini file (system.PrivateProfileString) (not private, file persists when application closes)
customXML parts (tied to an instance of a document, dies with document)
document variables (tied to an instance of a document, dies with document)
Good as they are, these approaches are not quite what I was after (my question was more out of interest than actual need).
I had also thought of:
Creating an Excel object in Word and using the feature that way, but I think the same issue remains if things End suddenly. Similarly, I think a purpose built DLL would also fall victim to End.

Avoiding VSTO, Word Automation Timing RPC_E_CALL_REJECTED Errors

I am writing a VSTO VB.NET application that controls a Word document, using standard features such as copying, pasting, inserting text, and pages, for example, and no additional threads. I get the following error at different times: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED)).
This appears to be a timing issue with Word, since it fails almost randomly but only on Word calls. In the debugger, I can then click Continue and the code resumes.
I've seen a lot of comments about this, relevant ones seems to be about the COM IMessageFilter interface. But they all skip one important point: I just want to wait for Word to get free again and then continue. I don't want to prompt the user with options (such as with RetryRejectedCall). An ugly work around right now is intentional delays, like Threading.Thread.Sleep(500), but that's hardly the solution, and with many calls that's also impractical.
What is the proper way of handling this?
Most probably you are using the Word object model from secondary threads. Office application uses the single threaded apartment model. It is not recommended to use any object model from a secondary thread.
Also you may find the similar forum thread helpful.

Loading (and executing) a lisp-file in autocad using .NET

I'm currently in the process of rewriting some old AutoCAD plugins from VBA to VB.NET. As it turns out, a (rather large) part of said plugin is implemented in LISP, and I've been told to leave that be. So the problem became running LISP-code in AutoCAD from .NET. Now, there are a few resources online who explain the process necessary to do so (like this one), but all of them takes for granted that the lisp-files/functions are already loaded. The VBA-function I'm currently scratching my head trying to figure out how to convert does a "(LOAD ""<file>"")", and the script is built in such a way that it auto-executes on load (it's a simple script, doesn't register functions, just runs from start to end and does it's thing).
So my question is. How can I load (and thus execute) a lisp-file in autocad from a .NET plugin?
Ok, there are two ways to sendcommand via .NET.
The first thing you need to understand is that ThisDocument doesn't exist in .NET.
ThisDocument is the document where the VBA code is written, but since your addin is document undependant, it stands alone and you must take the documents from the Application object.
You access the application with:
Autodesk.AutoCAD.ApplicationServices.Application
If you want to transform it to the same Application object as in VBA, with same methods and functions
using Autodesk.Autocad.Interop;
using Autodesk.Autocad.Interop.Common;
AcadApplication App = (AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication;
The first application has MdiActiveDocument, from where you can call the Editor and send written commands, or call the SendStringToExecute as said in other answer.
The AcadApplication has ActiveDocument (an AcadDocument object that behaves exactly as in VBA).
This document will have the same SendCommand your VBA has, use it the same way it's done in VBA.
If you can explain better the autoexecute part, I can help with that too.

Saving a PDF document seems to be keeping a reference in memory (VB.NET)

I have a VB.NET user control that is saving a PDF document and then displaying that in a WebBrowser control. The code looks like this:
Using myPdfDoc As New FileStream(fileName, FileMode.Create)
Dim byt As Byte() = comLib.GetData();
If Not byt Is Nothing Then
myPdfDoc.Write(byt, 0, byt.Length)
myPdfDoc.Flush()
myPdfDoc.Close()
webBrowserCtl.Navigate(fileName)
End If
End Using
comLib is a COM interop library, written in VB6 that obtains the relevant data.
As far as I can tell, this code is keeping a reference to the PDF document (as VB.NET does not close when the program finishes). I found this article which seems to imply that adobe doesn't clean up after itself properly, but implementing its suggested changes doesn't seem to help.
Why might I be getting this behaviour? In VB6, a program not closing properly was always a result of stray object references that are not cleared up. Is this still true in VB.NET? If so, what can I do to identify which object, or why this might be happening?
I would separate this out: reading the data, writing the data, and viewing the data:
Dim byt As Byte() = Nothing
Try
byt = comLib.GetData()
Finally
If Not comLib Is Nothing Then
Marshal.ReleaseComObject(comLib)
End If
End Try
If Not byt Is Nothing Then
Using myPdfDoc As New FileStream(fileName, FileMode.Create)
myPdfDoc.Write(byt, 0, byt.Length)
End Using
Using webBrowserCtl As New WebBrowser()
webBrowserCtl.Navigate(fileName)
End Using
End If
The Marshal.ReleaseComObject call in the Finally ensures that the reference count is always decremented. The Flush and Close are not necessary, as Dispose will do this anyway. The WebBrowser control implements IDisposable, so I have used a Using block for that too.
You are doing something way more complicated than what I have done in the past. But I can tell you that PDF byte objects in .NET can chew up extremely large amounts of memory (even if disposed). I'd suggest using temp files on your file server (in an actual directory on the machine running your web server). Rather than keeping the objects in memory. I know the assembly PDFSharp has some good (and free) code you can use. But I do not know what would prevent your program from exiting. Good luck to you buddy.
PS: You may want to try calling the garbage collector on your own. And you should be able to see your threads in Visual Studio. When you attach to your w3wp.exe (IIS 7 process in Windows 7) process, you will get a context menu (Debug->Windows->Threads). Although I don't know if a COM thread would show in there.

Avoiding cross process calls when doing Word automation via VB.net

The short version
I've got a Word Addin in VB.net and VSTO that exposes a COM compatible object via Word.COMAddins.Object, so that the addin functionality can be called External to Word, without accesses to Word itself being cross-process.
The technique worked in VB6, but with VB.net, it still works, but it's much slower than the same code running directly from the addin via a task pane, as if the calls are all cross process when they shouldn't be.
x
The Long version
This addin essentially does tons of processing on Word Documents.
The addin can be run in two ways.
from within Word, using a taskpane
externally, via a set of classes
exposed to COM (because I have to
provide access to the functionality
to VB6 client apps.
BUT, here's the rub. Anyone who's ever done Word automation knows that code that runs perfectly acceptably INPROC with Word (in this case the instance of the ADDIN that Word itself loads), will generally run unacceptably slowly out of process (or cross process).
This app is no different.
Ages ago, I made use of a handy trick to circumvent this issue.
Create a Word Addin as usual
Expose an object via the
Word.COMAddin.Object property that
will let external code access your
addin.
In your external project, instead of
manipulating Word directly, Use the
Application.COMAddins collection,
find your addin, retrieve the
exposed COMAddin.Object property
from it and then call a method on
that object that does the work.
Of course, the call to your COMAddin.Object object will still be cross process, BUT, once execution is in the addin that is IN PROCESS with Word, your addin can now perform all the Word object manipulations it wants and it's fast because they're all in-process calls at that point.
That worked in the VB6 COM days.
But, I put together this VB.net vsto addin, and expose my addin object via the RequestComAddInAutomationService function of VSTO's Connect object
I can make calls into my addin externally and they all work exactly as I would expect them to, except they're all +slow+, very much like the calls into Word are still being performed cross process even though the code making those calls to Word is part of the addin dll that was loaded in-process by Word!
And slow as in a factor of about 10 to 1; what takes 3 seconds to run when run directly from the ADDIN via the task pane, takes ~30seconds to run when called from external code through the COMADDIN.object object.
I'm guessing that I'm running into some sort of issue with .net APPDOMAINS or something and what +really+ constitutes cross proc calls in .net, but I've found nothing so far that would even hint about this sort of thing.
My next step, barring some mystical insight, will be to code up a repro, which could get tricky because of the shear number of elements in play.
Any thoughts?
I've made the same observations with my VSTO Word add in. What I'd like to add here: When you add your procedure as a click handler to a button:
`this.testButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(YourProcedure);ยด
and implement your expensive procedure in "YourProcedure", you can call into Word's UI thread using
this.testButton.Execute();
This is not an elegant solution either, but maybe useful if you happen to have buttons ready in a CommandBar.
Unfortunately, the Event hook technique Thorben mentions wouldn't work for my particular situation.
So I'm closing this question out with the workaround that I mentioned in comments and I'll repeat here...
Well, not a perfect solution, but I have found +a+ solution. It involved a timer, so it's definitely suboptimal Essentially, when the addin is loaded by Word, (ie during the STARTUP event), initialize a timer (a WINFORMS timer, not a threading timer), and set it's interval to 500. When External code connects to the addin via the COMADDIN.OBject property, and makes a call into the addin, set a variable flag, which is being polled by the timer. When the timer sees it set, it resets the flag and performs the action.
It's not the clean solution I'd have preferred, but it's fairly easy to implement, moderately easy to understand after the fact, and it definitely avoids the slowdown of xprocess COM calls into Word.