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).aspxBeispielcode C++
https://support.microsoft.com/en-us/kb/238393Du 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/179494Acrobat 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?
-
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").ResultDanke 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.
-
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.