Laden...

embedded single winword instance

Erstellt von mgr vor 17 Jahren Letzter Beitrag vor 16 Jahren 17.522 Views
M
mgr Themenstarter:in
6 Beiträge seit 2006
vor 17 Jahren
embedded single winword instance

Ich benütze eine Instanz von Winword innerhalb meiner C# Applikation als Text Editor. Das geht gut, nur wenn ich ein Winword Dokument nach meiner Applikation öffne wird dies mit der laufenden Instanz von Winword geöffnet. Weil ich in meiner Applikation z.b. die Menubars alle ausschalte, sind diese dann auch ausgeschaltet wenn ich Winword normal starte !!

Wie kann ich die eingebundene Instanz des Winword OLE Servers isolieren, damit keine Konflikte mit dem normalen gebrauch von Winword entstehen ?

3.728 Beiträge seit 2005
vor 17 Jahren
Text Editor

Du könntest eine extra Vorlage machen und speziell in dieser Vorlage die Menüs ausschalten. Ich würde allerdings Word überhaupt nicht als Texteditor verwenden. Das ist mit Elefanten auf Spatzen geschossen. Außerdem ist es Fehleranfällig, umständlich und lahm. Für formatierte Texteingabe gibt es die RichTextBox.

M
mgr Themenstarter:in
6 Beiträge seit 2006
vor 17 Jahren

Es sind leider nicht nur die Menus welche ich verstellen muss, auch einige Optionen welche meines Wissen nicht mit einer Vorlage geändert werden können.

Ich benötige in meinem Text Editor Tabellen, Bilder, Listen, Links, ... weshalb die RichTextBox nicht mehr genügt. Und ich habe keinen anderen Componenten für C# gefunden welcher diesen Ansprüchen genügt.

3.728 Beiträge seit 2005
vor 17 Jahren
Vsto

Dann würde ich meine Anwendung aber eher IN Word laufen lassen, als umgekehrt. Du könntest z.B. mittels VSTO Deine .NET Oberfläche in die Sidebar von Word verfrachten.

M
mgr Themenstarter:in
6 Beiträge seit 2006
vor 17 Jahren

hmmm ist nicht wirklich eine gute lösung ... Die Applikation sollte schon mit einem eigenen Gui aufwarten. Und auch mit einer In Word Lösung wäre immer noch das Problem das nachfolgend geöffnete dokumente mit der selben Instanz geöffnet werden.

Für word gäbe es einen startparameter /x welcher word dazu bringt nur auf eine DDE-Anfrage zu reagieren dies währe die Lösung. Jedoch habe ich es nicht hingekriegt word in diesem Modus über die COM (Microsoft.Office.Interop.Word) zu starten.

3.728 Beiträge seit 2005
vor 17 Jahren
Dde

Zum Stichwort DDE: DDE - Einstieg gesucht

Ganz im Ernst: Das wird niemals stabil funktionieren.

M
mgr Themenstarter:in
6 Beiträge seit 2006
vor 17 Jahren

hmmmm
was gibt es für alternativen ?

Ich brauche ein Wysiwyg Text editor welcher Text,Bilder,Tabellen und Listen unterstützt ... ich habe lange nach einem c# Componenten gesucht welcher dass kann aber nichts gefunden. es gibt 1-2 Produkte welche jedoch nicht gerade billig sind !

Hatte mal ein ähnliche Aufgabe in Java da konnte ich den HTMLTextPane benützen und dank des offenen Sources ein wenig zurecht stutzen.

A
154 Beiträge seit 2005
vor 17 Jahren

Hallo,

ich habe den selben Fall/Problem wie du.
Wenn du dein Word anpasst Button usw. dann werden diese Einstellungen für weitere normal gestarteten Wordinstanzen automatisch übernommen.

Mir ist aber aufgefallen, wenn ich eine Wordinstanz öffne und anschließend eine Wordinstanz mit Anpassungen starte, dann werden bei alle weiteren normal gestarteten Wordinstanzen, die Einstellungen der aller ersten Instanz übernommen.

D.h. Menüs, Buttons usw. sind ganz normal.

Vielleicht hilft es weiter, vorher eine unsichtbare normale Wordinstanz zu starten, und anschließend deine angepasste.
Beim Beenden muss natürlich wieder alles sauber aufgeräumt werden.

3.728 Beiträge seit 2005
vor 17 Jahren
HTML Edit Controol

Hier gibts was für HTML:

http://www.itwriting.com/htmleditor/

M
mgr Themenstarter:in
6 Beiträge seit 2006
vor 17 Jahren

word ist leider der einzige weg... da der 0-8-15 benutzer das handling von word gewohnt ist und nur schwer ein anderes akzeptiert ... man kan machen was man will es kommt immer der vergleich mit word ! word kann dies und das, wiso geschieht beim shortcut "shift alt control e" nicht was in word geschieht ... und und und

ich habe momentan einen sehr hässlichen workaround welcher jedoch funktioniert:


        /// <summary>
        /// workaround to open word as single instance which 
        /// ignores all other DDE request and multi-instances
        /// </summary>
        private void OpenWordWorkaround()
        {
            String file = "dummy.doc";
            String path = Directory.GetCurrentDirectory();
            path = path.Replace("\\bin\\Debug","");
            path += "\\" + file;

            //start word
            //the startparameter /x starts word in as single instance which ignores all other DDE request
            try
            {
                process = new System.Diagnostics.Process();
                process.EnableRaisingEvents = false;
                process.StartInfo.FileName = "winword";
                process.StartInfo.Arguments = path + " /x";
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
                process.Start();
            }
            catch(Exception e)
            {
                CloseWord();
                throw new WordOperationException("unable to start word process", e);
            }

            //get instance to com-interface
            //because the word com-interface ids are not unique (jeaaa billy) we start word with a 
            //dummy file and search for the com-interface of this file
            //we try this a few times because we dont know when word is started succesfully
            Exception exception = null;
            for (int i = 0; i < 10 && wordApplic == null; i++)
            {
                try
                {
                    //sleep a bit to get word the time to start
                    Thread.Sleep(200);   
                    DocumentClass openDoc = (DocumentClass)Marshal.BindToMoniker(path);    
                    //ask the document for the word application it is started in
                    wordApplic = (ApplicationClass)openDoc .Application;
                    //release the com object to the dummy file
                    Marshal.FinalReleaseComObject(openDoc);
                    exception = null;
                }
                catch(Exception e)
                {
                    exception = e;
                }
            }

            //searching the com interface is faild
            if (exception != null)
            {
                CloseWord();
                throw new WordOperationException("unable to connect word interface", exception);
            }

            //embed the word window into the application
            int res = User32Api.SetParent(process.MainWindowHandle.ToInt32(), this.Handle.ToInt32());
            if (res == 0)
            {
                CloseWord();
                throw new WordOperationException("unable to embed the word window");
            }

            log.Debug("MSWORD - Created new instance");
        }

92 Beiträge seit 2006
vor 17 Jahren

lösung (workaround #2)

            
        private void OpenWordInterop()
        {
            //open 2 word instances
            Microsoft.Office.Interop.Word.ApplicationClass wordApplicFAKE = new Microsoft.Office.Interop.Word.ApplicationClass();
            Microsoft.Office.Interop.Word.ApplicationClass wordApplicREAL = new Microsoft.Office.Interop.Word.ApplicationClass();
            log.Debug("MSWORD - Created new instance");

            //find the word window
            wordWnd = User32Api.FindWindow("Opusapp", null);
            if (wordWnd != 0)
            {
                int res = User32Api.SetParent(wordWnd, this.Handle.ToInt32());
            }
            else
            {
                throw new WordOperationException("unable to embed word window");
            }
            //kill the faked word instance
            object saveChanges = false;
            object missing = System.Reflection.Missing.Value;
            wordApplicFAKE.Quit(ref saveChanges, ref missing, ref missing);
            Marshal.FinalReleaseComObject(wordApplicFake);
            wordApplicFAKE= null;
        }
}

folgendes: Ein Programm mit embedded Word läuft ohne hässlichen shice wenn vorgängig ein normales Word geöffnet wurde - das embedded Word läuft dann nämlich nicht auf selbigem Prozess.

Um des nun zu provozieren wurde obige DoppelAktion ausgeführt, die zur Eingrenzung vieler Word Bugs führt. Aber wie erwähnt wäre der umgekehrte Weg wirklich der Einfachere (WordAddIn).

gruss
WorkArroundTschimmy 😉

A
64 Beiträge seit 2006
vor 17 Jahren
Frage zu: lösung (workaround #2)

Ich bin jetzt nicht so der C#-Guru, deshalb mal ne Zwischenfrage:

Wie komm ich an "User32Api" und "Marshal" ran???

Das ganze könnte mir mit meinem Problem auch helfen, ich brauche ein embedded Excel.

92 Beiträge seit 2006
vor 17 Jahren

sollte meiner Ansicht nach 1:1 auf excel ummünzbar sein... oder etwa nich? try it
aber (bin wohl nicht der erste) auch ich bin zur Einsicht gelangt, beim nächsten Projekt Applikation in Word (Add-In) statt Word in deine Applikation zu programmieren...

Zurück zum Topic

Marshal: using System.Runtime.InteropServices

User32Api: des is eine eigene class inder ich ein paar native calls auf n paar relevante dlls mache... (also einfach n Win32 API assistent)

für den fall oben: (empfehle da auch bei codeProject reinzuschauen, hats einen flotten Artikel/Code <- http://www.codeproject.com/dotnet/WordInDotnet.asp)

        [DllImport("user32.dll")]
        public static extern Int32 FindWindow(string strclassName, string strWindowName);
A
64 Beiträge seit 2006
vor 17 Jahren

Okay, jetzt brauch ich noch die SetParent-Funktion, dann kann ich's testen.
Der Name "Opusapp", ist der Zufall?

92 Beiträge seit 2006
vor 17 Jahren
        [DllImport("user32")]
        public static extern Int32 GetParent(int hwnd);

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
        public static extern bool SetWindowPos(
            Int32 hWnd,               // handle to window
            Int32 hWndInsertAfter,    // placement-order handle
            Int32 X,                  // horizontal position
            Int32 Y,                  // vertical position
            Int32 cx,                 // width
            Int32 cy,                 // height
            Int32 uFlags             // window-positioning options
        );

voilà

"Opusapp" <- name vom WordApplikationsFenster. Was das heissen soll? Office Pumpernickel Applikation Nr3 🙂 nein keine Ahnung

für Excel heisst die class glaub ich irgendwas "XLMAIN". Try it

A
64 Beiträge seit 2006
vor 17 Jahren

"XLMAIN" klappt.

Thanx.

Das "embedden" funktioniert nur mit MDIForms, oder?

A
64 Beiträge seit 2006
vor 17 Jahren

Ich muss mich selbst korrigieren.

Das "embedden" funktioniert auf allen Forms, die nicht MDIChild sind. Aber wieso???

Folgender Code steht in meinem MDIForm:


frmExcel fxlExcel = new frmExcel();
//    fxlExcel.MdiParent = this;
fxlExcel.Show();

Folgender Code folgt in meinem frmExcel beim Konstruktor:


private clsEmbedded Embedded = new clsEmbedded();
private Microsoft.Office.Interop.Excel.ApplicationClass appExcel;
public frmExcel()
{
    InitializeComponent();
    appExcel = Embedded.LoadEmbeddedExcel(this.Handle.ToInt32());
}

Und das macht meine Embedded-Klasse:


        public Excel.ApplicationClass LoadEmbeddedExcel(int WindowHandle)
        {
            Excel.ApplicationClass appExcelFAKE = new Excel.ApplicationClass();
            Excel.ApplicationClass appExcel = new Excel.ApplicationClass();

            int iExcelWindow = FindWindow("XLMAIN", null);
            if (iExcelWindow != 0)
            {
                SetParent(iExcelWindow, WindowHandle);
                appExcel.Visible = true;
                appExcel.WindowState = Excel.XlWindowState.xlMaximized;
            }
            else
            {
                appExcel.Quit();
                Marshal.FinalReleaseComObject(appExcel);
                appExcel = null;
            }

            appExcelFAKE.Quit();
            Marshal.FinalReleaseComObject(appExcelFAKE);
            appExcelFAKE = null;

            return appExcel;
        }

So funktioniert das ganze. Wenn ich aber den Kommentar rausnehme und somit das frmExcel zum MDIChild mache erhalte ich die allseits beliebte Meldung "Microsoft Excel hat ein Problem festgestellt und muss beendet werden".

Jemand ne Ahnung wieso???

3.728 Beiträge seit 2005
vor 17 Jahren
Office

Ich hab mal ähnliches mit Access versucht. Ist genauso abgeschmiert. Die MDI-Childs von Office Programmen greifen offenbar direkt auf ihren Parent zu. Vielleicht stellt das Hauptfenster bestimmte Informationen zur Verfügung. Keine Ahnung. Auf jeden Fall fehlen normalen .NET Forms irgendwelche Sachen, die die Office Childs brauchen. Es geht einfach nicht (Ich lasse mich natürlich gerne vom Gegenteil überzeugen). Nach zwei Tagen intensiven ausprobierens und suchens im Internet habe ich das Projekt entnvert abgebrochen.

A
64 Beiträge seit 2006
vor 17 Jahren

Die Lösung ist so poplig einfach, dass es schon fast wehtut!!!

Man setze ein Panel auf sein ChildForm und übergebe dem embedded Excel/Word/Access das Handle vom Panel und schon klappt's! 8)

Greetz, Alex.

A
64 Beiträge seit 2006
vor 17 Jahren
Weitere Probleme beim Embeden

Soweit funktioniert alles schon ganz gut.

Ich bin jetzt dabei meine eigenen, vereinfachten Word- und Excel-Klassen umzubauen und mit einer EmbedTo-Funktion zu versehen.

Dabei bin ich jetzt auf folgendes Problem gestoßen:
Bei einer aus dem Code heraus erzeugten Excel-Instanz kann ich jederzeit auf das benötigte Fenster-Handle über ExcelApplication.Hwnd zugreifen.

Word hat eine solche Eigenschaft aber nicht. Was muss ich da verwenden? Die .Creator-Eigenschaft? Oder muss ich immer die User32-Funktion FindWindow verwenden?

Falls es jemanden mal interessiert, könnte ich meinen gesamten Embedding-Code hier reinstellen. Quasi als Zusammenfassung.

Greetz, Alex.

3.728 Beiträge seit 2005
vor 17 Jahren
Word Handle

Im Word Objektmodell gibts keine Funktion, um den Handle zu bekommen. Ich würde es mit FindWindow versuchen.

P.S. Dein Office-Embedding Code würde mich schon interessieren.

A
64 Beiträge seit 2006
vor 17 Jahren

Ausschnitt aus meiner Excel-Klasse:


using System;
using System.Text;
using System.Security;
using Microsoft.Office.Interop;
using System.Collections.Generic;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Office
{
    public class clsExcel
    {
        // Interne Objekte -------------------------------------------------------------------
        private Microsoft.Office.Interop.Excel.Application appExcel;
        private Global.clsUser32 User32 = new Global.clsUser32();
        // --------------------------------------------------------------------------------------

        // Konstruktor/Destruktor -----------------------------------------------------------
        /// <summary>
        /// Erzeugt eine neue leere, nicht sichtbare Excel-Applikation.
        /// </summary>
        public clsExcel() 
        {
            appExcel = new Microsoft.Office.Interop.Excel.Application();
            appExcel.Visible = false;
        }

        ~clsExcel()
        {
            Marshal.FinalReleaseComObject(appExcel);
            appExcel = null;
        }
        // --------------------------------------------------------------------------------------

        // Methoden --------------------------------------------------------------------------
        /// <summary>
        /// Bettet die Excel-Applikation in ein als Parameter übergebenes Panel ein.
        /// </summary>
        /// <param name="pnlParent">Panel, in das die Excel-Applikation eingebettet werden soll.</param>
        public void EmbedTo(Panel pnlParent)
        {
            User32.EmbedToParent(appExcel.Hwnd, pnlParent.Handle.ToInt32());
            appExcel.WindowState = XlWindowState.xlNormal;
            User32.AdaptToParent(appExcel.Hwnd, pnlParent.Width, pnlParent.Height);
            appExcel.Visible = true;
        }
    }
}

Ausschnitt aus meiner Word-Klasse:


using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Word;

namespace Office
{
    public class clsWord
    {
        // Interne Objekte -------------------------------------------------------------------
        private Microsoft.Office.Interop.Word.Application appWord;
        private Global.clsGlobals Parameter = new Global.clsGlobals();
        private Global.clsUser32 User32 = new Global.clsUser32();
        // --------------------------------------------------------------------------------------

        // Eigenschaften ---------------------------------------------------------------------
        /// <summary>
        /// Boolean - Gibt an, ob die Word-Applikation angezeigt wird, oder legt dies fest.
        /// </summary>
        public bool Visible
        {
            get { return appWord.Visible; }
            set { appWord.Visible = value; }
        }

        /// <summary>
        /// Boolean - Gibt an, ob die Word-Applikation Fehler anzeigt, oder legt dies fest.
        /// </summary>
        public bool DisplayAlerts
        {
            get
            {
                if (appWord.DisplayAlerts == WdAlertLevel.wdAlertsAll) { return true; }
                else { return false; }
            }
            set
            {
                if (value) { appWord.DisplayAlerts = WdAlertLevel.wdAlertsAll; }
                else { appWord.DisplayAlerts = WdAlertLevel.wdAlertsNone; }
            }
        }
        // --------------------------------------------------------------------------------------

        // Konstruktor/Destruktor -----------------------------------------------------------
        /// <summary>
        /// Erzeugt eine neue leere, nicht sichtbare Word-Applikation und öffnet 
        /// ein neues Dokument (sFileName="") oder oder ein bestehendes Dokument.
        /// </summary>
        /// <param name="sFileName">String - Dateiname und -pfad des Dokuments, das geöffnet werden soll.</param>
        public clsWord(string sFileName)
        {
            appWord = new Microsoft.Office.Interop.Word.Application();
            Visible = false;
            DisplayAlerts = false;
            if (sFileName != "")
            {
                try
                {
                    object objFile = sFileName;
                    appWord.Documents.Open(ref objFile, ref Parameter.Missing, ref Parameter.False, ref Parameter.Missing,
                                            ref Parameter.Missing, ref Parameter.Missing, ref Parameter.Missing, ref Parameter.Missing,
                                            ref Parameter.Missing, ref Parameter.Missing, ref Parameter.Missing, ref Parameter.False,
                                            ref Parameter.Missing, ref Parameter.Missing, ref Parameter.Missing, ref Parameter.Missing);
                    try 
                    {
                        while (appWord.Documents.Count == 0)
                        { System.Windows.Forms.Application.DoEvents(); }
                    }
                    catch { }
                }
                catch
                { }
            }
        }

        ~clsWord()
        { }
        // --------------------------------------------------------------------------------------

        // Methoden --------------------------------------------------------------------------
        /// <summary>
        /// Bettet die Word-Applikation in ein als Parameter übergebenes Panel ein.
        /// </summary>
        /// <param name="pnlParent">Panel, in das die Word-Applikation eingebettet werden soll.</param>
        public void EmbedTo(Panel pnlParent)
        {
            int iWindowHandle = User32.FindWindowHandle("Word");
            User32.EmbedToParent(iWindowHandle, pnlParent.Handle.ToInt32());
            appWord.WindowState = WdWindowState.wdWindowStateNormal;
            User32.AdaptToParent(iWindowHandle, pnlParent.Width, pnlParent.Height);
            appWord.Visible = true;
        }
        // --------------------------------------------------------------------------------------
    }
}

Und das ist meine User32-Klasse:


using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Global
{
    class clsUser32
    {
        public clsUser32()
        { }

        [DllImport("user32.dll")]
        public static extern Int32 FindWindow(string strclassName, string strWindowName);
        [DllImport("user32.dll")]
        public static extern Int32 SetParent(int WhichWindow, int IntoWindow);
        [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
        public static extern bool SetWindowPos(
            Int32 hWnd,               // handle to window
            Int32 hWndInsertAfter,    // placement-order handle
            Int32 X,                  // horizontal position
            Int32 Y,                  // vertical position
            Int32 cx,                 // width
            Int32 cy,                 // height
            Int32 uFlags             // window-positioning options
        );

        public void EmbedToParent(int WindowHandle, int ParentHandle)
        { SetParent(WindowHandle, ParentHandle); }

        public void AdaptToParent(int WindowHandle, int Width, int Height)
        { SetWindowPos(WindowHandle, 0, 0, 0, Width, Height, 0); }

        public int FindWindowHandle(string ApplicationName)
        {
            switch (ApplicationName)
            {
                case "Word":
                    return FindWindow("opusapp", null);
                case "Excel":
                    return FindWindow("XLMAIN", null);
                default:
                    return 0;
            }
        }
    }
}

A
64 Beiträge seit 2006
vor 17 Jahren

Was mich jetzt noch stört ist, dass wenn ich ein embedded Excel auf diese Art erstell, immer wieder meine geöffneten Workbooks zugehen.

Ich kann mir da keinen Reim drauf machen. Das muss aber schleunigst abgestellt werden.

Jemand ne Idee?

A
64 Beiträge seit 2006
vor 17 Jahren

Das Problem mit den schließenden Workbooks hab ich jetzt im Griff.

Was mich jetzt noch stört ist, dass ich ständig beim Schließen des Fensters, in das ich das Excel embedded hab, von Excel die Fehlermeldung "Microsoft Excel hat einen Fehler verursacht und muss beendet werden" bekomm.

Meine Vermutung: Ich muss das Embedden lösen, bevor ich das Form schließ.
Wie kann ich denn das machen? Das Fenster an ein Fenster mit der Handle "0" embedden oder gibt's da noch eine Funktion in des User32.dll?

Gibt es irgendwo eine Dokumentation mit den Funktioinen der User32.dl?

3.728 Beiträge seit 2005
vor 17 Jahren
Desktop

Du setzt das Handle des Desktops als Parent.

A
64 Beiträge seit 2006
vor 17 Jahren

Wie bekomm ich das???

S
3 Beiträge seit 2006
vor 16 Jahren

Hallo,

ich habe auch auf diese Weise Excel in eine Form eingebettet. Jedoch habe ich leider das Problem, dass das eingebettete Excel nicht aktiv wird. Ex lässt sich erst aktivieren, wenn ich eine Tastatureingabe mache...
Hat evtl. jehmand eine Idee wie es besser klappen könnte ?

A
64 Beiträge seit 2006
vor 16 Jahren

An diesem Problem bin ich auch gescheitert. Deshalb bin ich nach langen hin und her doch dazu übergegangen Add-Ins zu programmieren.