Events fantôme et interop office
Je développe actuellement une application ou j’ai besoin d’intégrer l’éditeur de dessin Visio et, entre autre, m’abonner a certains événements. Seul soucis, l’évènement survient de manière aléatoire. Après quelques heures de recherche et l’écriture d’un wrapper de COM event qui a donné le même résultat, j’ai trouvé ceci sur un forum concernant un problème d’event dans outlook.
The reason is: “GC collect the .NET object, whichc wrapps COM object from Outlook )”. The solution is hold reference to this .NET object.
La solution, simplement garder une référence sur l’objet. Dans mon cas, ma page Visio dans un attribut privé
Convertisseur Word vers PDF en ligne de commande
J’ai besoin de convertir de documents docx en pdf afin d’intégrer la génération de documentations dans mes build TFS2010. Malheureusement, les outils de « conversion » word->pdf sont la plupart du temp des drivers d’impression ou nécessitent une action manuel. Voici donc deux bout de code. Le premier avec uniquement le code de sauvegarde en pdf et le second pour une application avec deux arguments in: et out:, une vérification de la validité des arguments et l’émission de logs d’erreur dans l’event log windows. (L’application allant tourner en background lors des build, c’est le seul moyen propre de savoir ou ca à merdé)
Donc on commence par ouvrir une instance Word et un document de manière classique. (Classique a l’interop office). Ensuite, il suffit de définir quelques variables, mais le principal se joue sur le WdSaveFormat à définir en temp que wdFormatPDF. Voici le code :
object inputFilePath = @"C:\teste.docx";
object outputFilePath = @"C:\teste.pdf";
object typeMissing = Type.Missing;
// Création d'une instance
_Application application = new Microsoft.Office.Interop.Word.Application();
// La rendre invisible
application.Application.Visible = false;
// Ouvrir le fichier word
Documents documents = application.Documents;
_Document document = documents.Open
(ref inputFilePath, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing);
// Définition des arguments de sauvegarde
object FileFormat = WdSaveFormat.wdFormatPDF;
object LockComments = false;
object AddToRecentFiles = false;
object ReadOnlyRecommended = false;
object EmbedTrueTypeFonts = true;
object SaveNativePictureFormat = false;
object SaveFormsData = false;
object SaveAsAOCELetter = false;
object InsertLineBreaks = false;
object AllowSubstitutions = false;
object LineEnding = WdLineEndingType.wdCRLF;
object AddBiDiMarks = false;
// Saauvegarde
document.SaveAs2(ref outputFilePath,
ref FileFormat,
ref LockComments,
ref typeMissing,
ref AddToRecentFiles,
ref typeMissing,
ref ReadOnlyRecommended,
ref EmbedTrueTypeFonts,
ref SaveNativePictureFormat,
ref SaveFormsData,
ref SaveAsAOCELetter,
ref typeMissing,
ref InsertLineBreaks,
ref AllowSubstitutions,
ref LineEnding,
ref AddBiDiMarks);
// Fermerture
application.Quit();
}
catch (Exception ex)
{
WriteLog(String.Format("Application Excepetion for files : " +
"\nInput : {0}\nOutput : {1}\nError : {2}",
inputFilePath, outputFilePath, ex.Message));
}
Et la version « directement utilisable » :
static void Main(string[] args)
{
Program p = new Program();
if (p.CheckInput(args))
p.Convert();
}
private object inputFilePath = null;
private object outputFilePath = null;
// Quelques vérifications basique.
public bool CheckInput(string[] args)
{
// Check nombre d'arguments
if (args.Length != 2)
{ WriteLog("Nombre d'arguments invalide"); return false; }
// Récupèration
foreach (string a in args)
{
if (a.StartsWith("in:")) inputFilePath = a.Substring(3);
if (a.StartsWith("out:")) outputFilePath = a.Substring(4);
}
// Verification non null
if (inputFilePath == null)
{ WriteLog("Fichier d'entré non spécifié"); return false; }
if (outputFilePath == null)
{ WriteLog("Fichier de sortie non spécifié"); return false; }
// Verification existence input
if(!(File.Exists(inputFilePath.ToString())))
{ WriteLog("Fichier d'entré n'existe pas"); return false; }
return true;
}
public void Convert()
{
try
{
object typeMissing = Type.Missing;
// Création d'une instance
_Application application = new Microsoft.Office.Interop.Word.Application();
// La rendre invisible
application.Application.Visible = false;
// Ouvrir le fichier word
Documents documents = application.Documents;
_Document document = documents.Open
(ref inputFilePath, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing);
// Définition des arguments de sauvegarde
object FileFormat = WdSaveFormat.wdFormatPDF;
object LockComments = false;
object AddToRecentFiles = false;
object ReadOnlyRecommended = false;
object EmbedTrueTypeFonts = true;
object SaveNativePictureFormat = false;
object SaveFormsData = false;
object SaveAsAOCELetter = false;
object InsertLineBreaks = false;
object AllowSubstitutions = false;
object LineEnding = WdLineEndingType.wdCRLF;
object AddBiDiMarks = false;
// Saauvegarde
document.SaveAs2(ref outputFilePath,
ref FileFormat,
ref LockComments,
ref typeMissing,
ref AddToRecentFiles,
ref typeMissing,
ref ReadOnlyRecommended,
ref EmbedTrueTypeFonts,
ref SaveNativePictureFormat,
ref SaveFormsData,
ref SaveAsAOCELetter,
ref typeMissing,
ref InsertLineBreaks,
ref AllowSubstitutions,
ref LineEnding,
ref AddBiDiMarks);
// Fermerture
application.Quit();
}
catch (Exception ex)
{
WriteLog(String.Format("Application Excepetion for files : " +
"\nInput : {0}\nOutput : {1}\nError : {2}",
inputFilePath, outputFilePath, ex.Message));
}
}
void WriteLog(string message)
{
// Ecriture dans l'event log
string sSource = "Word To Pdf Tool";
string sLog = "Application";
string sEvent = message;
// Vérification de la source, != création
if (!EventLog.SourceExists(sSource)) EventLog.CreateEventSource(sSource, sLog);
// Enregitrement du log
EventLog.WriteEntry(sSource, sEvent, EventLogEntryType.Error, 234);
Console.WriteLine(sEvent);
}
Exécution de macro dans Word depuis du C#
Voici un petit bout de code qui vous permettra d’appeler des macro dans un document, par exemple pour remplir une template.
Tout d’abord il faut rajouter une référence vers la librairie Microsoft.Office.Interop.Word
Ensuite on ouvre le document, la méthode open prend beaucoup d’arguments mais il ne faut pas être effrayé par la quantité, en effet il s’agit entre autre des user/pwd, read-only et ce genre de chose. De plus une description dans l’IntelliSense est disponible, so don’t worry be happy.
object filePath = @"C:\Users\Manu\Documents\Visual Studio 2010\Projects\MacroCall\MacroCall\bin\Debug\mondoc.docm";
object typeMissing = Type.Missing;
_Application application = new Microsoft.Office.Interop.Word.Application();
Documents documents = application.Documents;
_Document document = documents.Open
(ref filePath, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing);
Le document ouvert on vas créer une copie en l’enregistrant sous, les opérations suivantes s’exécuteront sur la copie et non sur l’original. La encore, la methode SaveAs est equivalente a la méthode Open.
object newFilePath = @"C:\Users\Manu\Documents\Visual Studio 2010\Projects\MacroCall\MacroCall\bin\Debug\mondoc2.docm";
document.SaveAs2(ref newFilePath, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing,
ref typeMissing, ref typeMissing, ref typeMissing, ref typeMissing);
Notre documenté chargé et un copie faite, nous allons passer a l’appel de nos macros.
Voici leur code :
Sub Macro1()
Label1.Caption = "De la macro"
End Sub
Sub Macro2(p As String)
Label1.Caption = p
End Sub
Sub AddProducts(productName As String, quantity As String, unitPrice As String)
Dim oTable As Table
Dim oCell As Cell
Dim oNewRow As Row
Set oTable = ActiveDocument.Tables(1)
oTable.Rows.Add
Set oNewRow = oTable.Rows(oTable.Rows.Count)
oNewRow.Cells(0).Range = productName
oNewRow.Cells(1).Range = quantity
oNewRow.Cells(2).Range = unitPrice
End Sub
La première vas juste remplir un label avec « De la macro » et la seconde vas rajouter dans un autre label le texte passé en argument, la dernière rajoute une ligne a un tableau et y insère les éléments passé en argument.
L’appel de macro s’exécute via la méthode InvokeMember. Les arguments sont le nom de la méthode à appeler suivi des BindingFlags, le binder a utiliser, la cible de l’invoke et enfin les arguments sous forme d’un tableau d’objet.
Basiquement, seul le tableau d’objet différera d’un appel de macro a l’autre.
Il suffit de mettre en première position le nom de la macro et rajouter ensuite un élément pour chaque argument nécessaire a la macro.
Voici nos 3 tableaux pour les 3 macros
Object[] Macro1 = new Object[] { "Macro1" };
Object[] Macro2 = new Object[] { "Macro2", "Mon Texte A Afficher" };
Object[] AddProducts = new Object[] { "AddProducts", "C# 4.0 - Ref Book", "1", "45€"};
Maintenant on les appels
application.GetType().InvokeMember
(
"Run",
System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod,
null,
application,
Macro1
);
application.GetType().InvokeMember
(
"Run",
System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod,
null,
application,
Macro2
);
application.GetType().InvokeMember
(
"Run",
System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod,
null,
application,
AddProducts
);
Et c’est fini, quand nous allons voir notre document, les macros se sont bien executés.


Laisser un commentaire