Formularfelder in PDF-Dateien oder MS Word über MFC-Programm füllen und ausdrucken



  • In der Überschrift ist der Kern des Problems schon umrissen. Wir haben hier eine Maschinensteuersoftware, die aus einer SQL-Datenbank einige Daten aufruft und auch Prozessdaten dort ablegt. Derzeit werden die Arbeitsschritte über aufwendige Formulare dokumentiert, was ne Menge Schreiberei bedeutet. Jetzt kam der Gedanke, die als PDF-Datei vorliegenden Formulare bereits durch die Software mit den Daten aus der Datenbank zu füllen und darüber auszudrucken.
    Die Quick-And-Dirty-lösung wäre, die Position der Textausgaben per Textout auf dem Papier zu plazieren und im Drucker bereits als Druckpapier Blanko-Formulare zu haben. Sowas verrutscht aber leicht. Gibt es eine Möglichkeit sowas auch aus dem Programm heraus zu drucken? Ich muss nur drucken und nichts abspeichern. Wahlweise liegen die Dateien auch im docx-Format vor, wo ich dann die Formularfelder über MS-Word einfügen könnte. Habe bei codeproject.com ein paar Artikel gefunden, jedoch waren die mit VB geschrieben. Ich muss das zwingend unter C++ (MFC) umsetzen.
    Hat jemand dazu eine zündende Idee bzw. einen Link für mich?



  • Hallo,

    Word-Automation und CustomDocumentProperties könnten dir hier helfen. Du kannst CustomDocumentProperties im Word anlegen und einen Dummy-Text vergeben. Danach kannst du diese Properties in das Dokument einfügen und mittels Automation auslesen und ändern.

    Hier ein Beispiel:

    CustomDocumentProperties anlegen und VB Beispielcode
    https://msdn.microsoft.com/en-us/library/aa537163(v=office.11).aspx

    Beispielcode C++
    https://support.microsoft.com/en-us/kb/238393

    Du musst allerdings nicht unbedingt Invoke usw. verwenden, du kannst dir auch die entsprechenden Klassen aus der Word TypeLib generieren lassen:

    https://support.microsoft.com/en-us/kb/178749
    https://support.microsoft.com/en-us/kb/179494

    Acrobat bietet auch eine TypeLib, mit der du Forms direkt in der PDF Datei bearbeiten kannst, allerdings wird dafür AFAIR Acrobat Professional benötigt.



  • Erst mal vielen Dank für die Links. Hab da erst mal nur diagonal drüber geschaut und muss das alles mal probieren. Da ich nur drucken muss wäre es gut wenn ich eine Möglichkeit finde, das ohne das Öffnen von MS Word auf den Drucker zu schieben.



  • Hi,

    um nochmal eine ganz andere Alternative aufzulisten. Wenn es um Berichte geht, die aus Datenbanken gefüllt werden ... da fand ich damals mal CrystalReports relativ praktisch. Da müsste man halt das PDF-Dokument, bzw. Word-Dokument, erstmal nachbauen, aber die sind halt genau für so einen Zweck gemacht.



  • DBReports schrieb:

    Hi,

    um nochmal eine ganz andere Alternative aufzulisten. Wenn es um Berichte geht, die aus Datenbanken gefüllt werden ... da fand ich damals mal CrystalReports relativ praktisch. Da müsste man halt das PDF-Dokument, bzw. Word-Dokument, erstmal nachbauen, aber die sind halt genau für so einen Zweck gemacht.

    Ja, wir haben hier auch Crystal Reports. Damit arbeitet das ERP-System. Jedoch bleibt auch hier das Problem, den Report aus der Anwendung heraus anzusprechen. Ich hab mich dann immer den Reporting Services der Datenbank bedient und dort was hochgeladen. Jedoch ist das hier keine Alternative. Es geht um freigegebene Produktionsdokumente, die dann aus dem Programm heraus mit Inhalten gefüllt werden müssen. Hab jetzt mal schon die Beispiele im oberen Post nachvollziehen können. Damit kann man ja erst mal Eigenschaftsfelder einer Word-Datei hinzufügen/ändern. Wie das mit den Formularfeldern geht hab ich noch nicht rausgefunden.



  • So, hab jetzt rausgefunden, dass der Aufruf in VBA wie folgt heißen muss:

    ActiveDocument.FormFields("Feldbezeichner").Result = "neuer Wert"
    

    Dann hab ich mich an folgenden Artikel gehalten: http://www.codeproject.com/Articles/34998/MS-Office-OLE-Automation-Using-C

    Jetzt hab ich das Problem, dass beim Übergeben des Wertes in der Klammer der Fehler "unbekannter Name".

    if(!m_pWApp || !m_pActiveDocument) return E_FAIL;
    	IDispatch *pDocApp;
    	{  
    		VARIANT result;
    		VariantInit(&result);
    		m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result, m_pActiveDocument, L"FormFields", 0); //S_OK
    		pDocApp= result.pdispVal;
    	}
    
    	// Get "Fieldname" from ActiveDocument.FormFields("Fieldname")
        IDispatch *pFieldname;
        {
            VARIANT result;
            VariantInit(&result);
            VARIANT x;
            x.vt = VT_BSTR;
            x.bstrVal = ::SysAllocString(L"Tag_Test");
    		m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result,pDocApp, L"FormFields", 1, x); //0x80020006 Unbekannter Name
            pFieldname = result.pdispVal;
            SysFreeString(x.bstrVal);
        }
    

    Weiterhin funktioniert aber folgendes aus dem Artikel https://support.microsoft.com/en-us/kb/238393

    // Get Documents collection
          IDispatch *pDocs;
          {
                VARIANT result;
                VariantInit(&result);
                AutoWrap(DISPATCH_PROPERTYGET, &result, pWordApp, L"Documents", 
                         0);
    
                pDocs = result.pdispVal;
          }
    
          // Call Documents.Open() to open C:\Doc1.doc
          IDispatch *pDoc;
          {
                VARIANT result;
                VariantInit(&result);
                VARIANT x;
                x.vt = VT_BSTR;
                x.bstrVal = ::SysAllocString(L"C:\\Doc1.doc");
    
                AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 1, x);
                pDoc = result.pdispVal;
                SysFreeString(x.bstrVal);
          }
    
          // Get BuiltinDocumentProperties collection
          IDispatch *pProps;
          {
                VARIANT result;
                VariantInit(&result);
                AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc, 
                         L"BuiltinDocumentProperties", 0);
                pProps = result.pdispVal;
          }
    
          // Get "Subject" from BuiltInDocumentProperties.Item("Subject")
          IDispatch *pPropSubject;
          {
                VARIANT result;
                VariantInit(&result);
                VARIANT x;
                x.vt = VT_BSTR;
                x.bstrVal = ::SysAllocString(L"Subject");
                AutoWrap(DISPATCH_PROPERTYGET, &result, pProps, L"Item", 1, x);
                pPropSubject = result.pdispVal;
                SysFreeString(x.bstrVal);
          }
    

    Was mach ich da falsch?


  • Mod

    Du darfst nicht FormFIed im letzen Aufruf verwenden, sondern Du musst Item verwenden.

    Schau Dir dir tlb/odl an.

    ActiveDocument.FormFields("Fieldname").Result ist in Wahrheit ein Shortcut für
    ActiveDocument.FormFields.Item("Fieldname").Result



  • Martin Richter schrieb:

    Du darfst nicht FormFIed im letzen Aufruf verwenden, sondern Du musst Item verwenden.

    Schau Dir dir tlb/odl an.

    ActiveDocument.FormFields("Fieldname").Result ist in Wahrheit ein Shortcut für
    ActiveDocument.FormFields.Item("Fieldname").Result

    Danke erst mal für den Tipp. Jedoch funktionieren die Aufrufe nicht:

    IDispatch *pDocApp;
    	{  
    		VARIANT result;
    		VariantInit(&result);
    		m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result, m_pActiveDocument, L"FormFields", 0); //S_OK
    		pDocApp= result.pdispVal;
    	}
    
    	IDispatch *pDocsApp;
    	{  
    		VARIANT result;
    		VariantInit(&result);
    		m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result, pDocApp, L"Item", 0); //0x0037e830 Empty
    		pDocsApp= result.pdispVal;
    	}
    	// Get "Fieldname" from ActiveDocument.FormFields("Fieldname")
        IDispatch *pFieldname;
        {
            VARIANT result;
            VariantInit(&result);
            VARIANT x;
            x.vt = VT_BSTR;
            x.bstrVal = ::SysAllocString(L"Tag_Test");
    		m_hr=OLEMethod(DISPATCH_PROPERTYGET, &result,pDocApp, L"Item", 1, x); //0x0037e830 Empty
            pFieldname = result.pdispVal;
            SysFreeString(x.bstrVal);
        }
    

    Wenn man es unter VBA als Makro proggt funktioniert das jedoch sauber.


  • Mod

    Was soll der Mittelteil. Das ist doch doppelt gemoppelt.

    Zudem finde ich diesen OLEMethod Quatsch nervend. Verwende einfach einen COM Import.



  • Martin Richter schrieb:

    Was soll der Mittelteil. Das ist doch doppelt gemoppelt.

    Zudem finde ich diesen OLEMethod Quatsch nervend. Verwende einfach einen COM Import.

    Ja es ist zu Testzwecken doppelt. Hab das immer wahlweise auskommentiert. Trotzdem funktionierts nicht. Er scheint das Attribut Item nicht zu kennen, ob nun mit oder ohne Parameter. Ich finde das auch nervig, soll es aber nehmen, da es schon rudimentäre Ansätze in einem bestehenden Programm gibt, die ich nutzen soll.



  • So, ich habe jetzt durch Eibinden der typelib für Word das wie folgt gelöst:

    void CAutoProjectDlg::OnBnClickedRun()
    {
    
    	// TODO: Fügen Sie hier Ihren Kontrollbehandlungscode für die Benachrichtigung ein.
    	CApplication app;  // app is the Word _Application object
    
    	// Start Word and get Application object.
    
    	if(!app.CreateDispatch(_T("Word.Application")))
    	{
    		AfxMessageBox(_T("Cannot start Word and get Application object."));
    		return;
    	}
    	// Make the application visible and give the user control of
    	// Microsoft Word.
    	app.put_Visible(TRUE);
    
    	// get the document list
    	CDocuments docs(app.get_Documents());
    	COleVariant varOPt(DISP_E_PARAMNOTFOUND, VT_ERROR);  // unused param
    
    	// Open a Word doc
        CComVariant Filename = _T("D:\\Doc1.docx");
    
    	// Open the file
    	CDocument0 doc = docs.Open(&Filename, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt);
    
    	// Get the formfield list
    	CFormFields formfields(doc.get_FormFields());
    
    	// Choose a formfield
    	CComVariant Index=_T("Tag_Test");
    	CFormField formfield = formfields.Item(&Index);
    
    	// Change the result
    	formfield.put_Result(_T("Test"));
    
    	// Save Document
    	//doc.Save();
    
    	// Print the Document
    	doc.PrintOut(varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt, varOPt,varOPt,varOPt,varOPt, varOPt,varOPt,varOPt);
    
    	// Close Document
    	doc.Close(varOPt, varOPt, varOPt);
    
    	// Exit Word
    	app.Quit(varOPt, varOPt, varOPt);
    }
    

    Es funktioniert soweit ganz gut.


Log in to reply