[Migrated content. Thread originally posted on 04 January 2011]
Considering that most COBOL apps store customer contact information one way or another, there should be nice opportunities for some CRM tooling, especially when we know how easy it is to interact with Microsoft Office using dot net in VISUAL COBOL.How about using Microsoft Word in conjunction with your customer database to mail a promo? All automatic of course :-)
To use the following source example;
* Make sure you have a C drive, and write access to the root of it. Otherwise change all filenames in the source to your preferred destination.
* Make sure you have Microsoft Office installed.
* Start up Visual COBOL.
* Select File | New | Project.
* Choose COBOL, Managed, Console Application.
* Enter name, Location and Solution name as desired or use the suggested values.
* Click Ok.
* You should now have gotten the source file (Program1.cbl) editor.
* To the right, you should have the Solution Explorer. If not, open it from the menu (View | Solution Explorer)
* Make sure Solution, and project is expanded, there should be an item in the tree named References.
* Right-click References in the Solution Explorer.
* Click Add Reference in the pop-up menu.
* Depending on your computer, it might take some time to populate the .NET tab. Here, scroll for Microsoft.Interop.Word, note that there might be multiple versions installed. This worked with version 11.0.0.0 for me.
* Click Microsoft.Interop.Word to select this and OK.
* Right-Click References again, press Add Reference again, this time look for System-Windows.Forms, select it and click OK. We use this for the message boxes, if you remove the message boxes, you can skip this.
* Select all content in the editor window (Program1.cbl), delete it and replace with the code below.
* Build.
* Run.
You may of course as well step through in the debugger.
When the program is finished, you will find two new files; "c:\merged.doc" and "c:\vcdatasource.doc".
The merged.doc file is the final document to print. The vcdatasource.doc is the datasource we created to merge recepients from.
You may print, fax or even email the documents from your applocation, provided you have the setup for this.
program-id. Program1 as "VCMailMerge.Program1".
working-storage section.
*Word is not native .net, but encapsulated COM. In COM you have the opportunity to
*omitt optional parameters, this is not supported in most .net native languages. Hence,
*we have to provide "dummy" data for these. For this purpose we use the
* "System.Reflection.NullObject::Value
01 NullObject object value type System.Reflection.Missing::Value.
01 my-exception type "System.Exception".
01 orgbold binary-long.
01 dummystr string.
01 listsep string.
01 headerstr string.
01 WrdApp type Microsoft.Office.Interop.Word._Application.
01 WrdDoc type Microsoft.Office.Interop.Word._Document.
01 WrdDDc type Microsoft.Office.Interop.Word._Document.
01 WrdSel type Microsoft.Office.Interop.Word.Selection.
01 WrdMMg type Microsoft.Office.Interop.Word.MailMerge.
01 WrdMMF type Microsoft.Office.Interop.Word.MailMergeFields.
01 WrdTbl type Microsoft.Office.Interop.Word.Table.
01 WrdNoSave object value type Microsoft.Office.Interop.Word.WdSaveOptions::wdDoNotSaveChanges.
01 wdBlank object value type Microsoft.Office.Interop.Word.WdNewDocumentType::wdNewBlankDocument.
01 wdGoToLine object.
01 wdGoToLast object.
procedure division.
*First get the correct list separator character for your culture.
set listsep to type System.Globalization.CultureInfo::CurrentCulture::TextInfo::ListSeparator.
*> launch Word, get an instance to it.
set WrdApp to new Microsoft.Office.Interop.Word.Application()
*> Show Word, so we can see things happen. For production comment out or remove the next line
set WrdApp::Visible to true
try
*> Create master document
invoke WrdApp::Documents::Add(NullObject, NullObject, wdBlank, NullObject) giving WrdDoc
*> Select the document as current.
invoke WrdDoc::Select()
*> Get a reference to the current selection
set WrdSel to WrdApp::Selection
*> Get a reference to the MailMerge object of the master document
set WrdMMg to WrdDoc::MailMerge
*> Create the Data Source file, for the header we use the list separator character we
*> obtained from the current culture on the first line.
set headerstr to string::Concat("FirstName" listsep " LastName" listsep
" City" listsep " Country")
invoke WrdMMg::CreateDataSource(
"c:\vcdatasource.doc" as type System.Object, NullObject, NullObject,
headerstr as type System.Object,
NullObject, NullObject, NullObject, NullObject, NullObject)
*> Open the Data Source file
invoke WrdApp::Documents::Open(
"c:\vcdatasource.doc" as type System.Object,
NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject,
NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject)
giving WrdDDc
*> Get a reference to the table in the Data Source file
set WrdTbl to WrdDDc::Tables::Item(1)
*> The CreateDataSource method insert a table with 2 rows by default, we expand this
*> with another three rows just for having more data. Giving us a total of 5 rows.
*> 1 header row (1) and 4 data rows (2..5)
perform 3 times
Invoke WrdTbl::Rows::Add(NullObject)
end-perform
*> This following sequence could easily be replaced by you reading your data from a COBOL file.
*> Insert Gisle, Forseth, Oslo, Norway in table row 2.
invoke WrdTbl::Cell(2, 1)::Range::InsertAfter("Gisle")
invoke WrdTbl::Cell(2, 2)::Range::InsertAfter("Forseth")
invoke WrdTbl::Cell(2, 3)::Range::InsertAfter("Oslo")
invoke WrdTbl::Cell(2, 4)::Range::InsertAfter("Norway")
*> Insert Mark, Warren, Newbury, England in table row 3.
invoke WrdTbl::Cell(3, 1)::Range::InsertAfter("Mark")
invoke WrdTbl::Cell(3, 2)::Range::InsertAfter("Warren")
invoke WrdTbl::Cell(3, 3)::Range::InsertAfter("Newbury")
invoke WrdTbl::Cell(3, 4)::Range::InsertAfter("England")
*> Insert Piet, Henskens, Amsterdam, Holland in table row 4
invoke WrdTbl::Cell(4, 1)::Range::InsertAfter("Piet")
invoke WrdTbl::Cell(4, 2)::Range::InsertAfter("Henskens")
invoke WrdTbl::Cell(4, 3)::Range::InsertAfter("Amsterdam")
invoke WrdTbl::Cell(4, 4)::Range::InsertAfter("Holland")
*> Insert Nabeen, Jamal, Newbury, England in table row 5
invoke WrdTbl::Cell(5, 1)::Range::InsertAfter("Nabeen")
invoke WrdTbl::Cell(5, 2)::Range::InsertAfter("Jamal")
invoke WrdTbl::Cell(5, 3)::Range::InsertAfter("Newbury")
invoke WrdTbl::Cell(5, 4)::Range::InsertAfter("England")
*> Save the Data Source document
invoke WrdDDc::Save()
*> Close the Data Source document
invoke WrdDDc::Close(WrdNoSave, NullObject, NullObject)
*> Let us start typing the template letter, first we make the header,
*> let us align the header text center.
set WrdSel::ParagraphFormat::Alignment to type
Microsoft.Office.Interop.Word.WdParagraphAlignment::wdAlignParagraphCenter
*> Make a copy of the current font bold state and name
set orgbold to WrdSel::Font::Bold
set dummystr to WrdSel::Font::Name
*> Set the font to bold and the font type to Tahoma, that looks good :-)
set WrdSel::Font::Bold to 1
set WrdSel::Font::Name to "Tahoma"
*> Type in Micro Focus University
invoke WrdSel::TypeText("Micro Focus University")
*> Let us get a new line
invoke WrdSel::TypeParagraph()
*> Department
invoke WrdSel::TypeText("Knowledge department")
*> Restore font to original
set WrdSel::Font::Bold to orgbold
set WrdSel::Font::Name to dummystr
*> Two blank lines
perform 2 times
invoke WrdSel::TypeParagraph()
end-perform
*> Set left alignment
set WrdSel::ParagraphFormat::Alignment to type
Microsoft.Office.Interop.Word.WdParagraphAlignment::wdAlignParagraphLeft
*> Initiate the MailMergeField object
set WrdMMF to WrdMMg::Fields
*> With the MailMergeField object, using the template document (WrdSel) we type in
*> the mailmerge fields.
*> FirstName field
invoke WrdMMF::Add(WrdSel::Range, "FirstName")
*> Add a blank for nice formatting
invoke WrdSel::TypeText(" ")
*> LastName field
invoke WrdMMF::Add(WrdSel::Range, "LastName")
*> New line
invoke WrdSel::TypeParagraph()
*> City field
invoke WrdMMF::Add(WrdSel::Range, "City")
*> New line
invoke WrdSel::TypeParagraph()
*> Country field
invoke WrdMMF::Add(WrdSel::Range, "Country")
*> New line
invoke WrdSel::TypeParagraph()
*> Align right for date
set WrdSel::ParagraphFormat::Alignment to type
Microsoft.Office.Interop.Word.WdParagraphAlignment::wdAlignParagraphRight
*> Insert date object. Behold! Do note that the format here may differ depending
*> on your nationality. Make sure you choose a format appropriate for your target
*> audience :-)
invoke WrdSel::InsertDateTime("dddd, MMMM dd, yyyy" as type System.Object,
false as type "System.Object", NullObject, NullObject, NullObject)
*> New line
invoke WrdSel::TypeParagraph()
*> Restore left alignment for body text
set WrdSel::ParagraphFormat::Alignment to type
Microsoft.Office.Interop.Word.WdParagraphAlignment::wdAlignParagraphLeft
*> Be polite, say Dear ;-)
invoke WrdSel::TypeText("Dear ")
*> Insert FirstName, be personal to make sure you have their attention
invoke WrdMMF::Add(WrdSel::Range, "FirstName")
*> Just a comma
invoke WrdSel::TypeText(",")
*> New line
invoke WrdSel::TypeParagraph()
*> Insert the body text. Note that the concatenation is not just for
*> having the source code look good, but also because the compiler has a
*> limitation of 255 bytes on a single line of source code.
invoke WrdSel::TypeText( "Thank you for your recent r" &
"equest for next semesters' class schedule for the" &
" Visual COBOL Programming Department. Enclosed wi" &
"th this letter is a booklet containing all the cl" &
"asses offered next semester at Micro Focus Univer" &
"sity. Several new classes will be offered in the " &
"dot net Programming Department next semester. The" &
"se classes are listed below.")
*> New line
invoke WrdSel::TypeParagraph()
*> Create a table for entry of the courses, apply table behavior, 9 rows and 4 columns
*> Note the cast to "System.Object", this is due to the type specification of the method Add's
*> parameters being pointer to variant
invoke WrdDoc::Tables::Add(WrdSel::Range, 9, 4,
type Microsoft.Office.Interop.Word.WdDefaultTableBehavior::wdWord8TableBehavior as type System.Object,
type Microsoft.Office.Interop.Word.WdAutoFitBehavior::wdAutoFitFixed as type System.Object)
*> To save typing, get a reference to the table item in the object variable WrdTbl
set WrdTbl to WrdDoc::Tables::Item(1)
*> Set width and alignment for table item 1
invoke WrdTbl::Columns::Item(1)::SetWidth(55,
type Microsoft.Office.Interop.Word.WdRulerStyle::wdAdjustNone)
*> Set width and alignment for table item 2
invoke WrdTbl::Columns::Item(2)::SetWidth(165,
type Microsoft.Office.Interop.Word.WdRulerStyle::wdAdjustNone)
*> Set width and alignment for table item 3
invoke WrdTbl::Columns::Item(3)::SetWidth(100,
type Microsoft.Office.Interop.Word.WdRulerStyle::wdAdjustNone)
*> Set width and alignment for table item 4
invoke WrdTbl::Columns::Item(4)::SetWidth(111,
type Microsoft.Office.Interop.Word.WdRulerStyle::wdAdjustNone)
*> Let us apply a shade to the title row
set WrdTbl::Rows::Item(1)::Cells::Shading::BackgroundPatternColorIndex to
type Microsoft.Office.Interop.Word.WdColorIndex::wdGray25
*> Since we shade the title row, it makes sense to have the text here appear in bold
*> to ease reading
set WrdTbl::Rows::Item(1)::Range::Bold to 1
*> Center the first cell, leave the others left aligned
set WrdTbl::Cell(1, 1)::Range::Paragraphs::Alignment to
type Microsoft.Office.Interop.Word.WdParagraphAlignment::wdAlignParagraphCenter
*> Insert the header row text
invoke WrdTbl::Cell(1, 1)::Range::InsertAfter("Class Number")
invoke WrdTbl::Cell(1, 2)::Range::InsertAfter("Class Name")
invoke WrdTbl::Cell(1, 3)::Range::InsertAfter("Class Time")
invoke WrdTbl::Cell(1, 4)::Range::InsertAfter("Instructor")
*> Insert row for course 1, cell by cell
invoke WrdTbl::Cell(2, 1)::Range::InsertAfter("EE220")
invoke WrdTbl::Cell(2, 2)::Range::InsertAfter("Introduction to VISUAL COBOL programming")
invoke WrdTbl::Cell(2, 3)::Range::InsertAfter("1:00-2:00 M,W,F")
invoke WrdTbl::Cell(2, 4)::Range::InsertAfter("Dr. Bits")
*> Insert row for course 2, cell by cell
invoke WrdTbl::Cell(3, 1)::Range::InsertAfter("EE230")
invoke WrdTbl::Cell(3, 2)::Range::InsertAfter("VISUAL COBOL programming theory")
invoke WrdTbl::Cell(3, 3)::Range::InsertAfter("10:00-11:30 T,T")
invoke WrdTbl::Cell(3, 4)::Range::InsertAfter("Dr. Bytes")
*> Insert row for course 3, cell by cell
invoke WrdTbl::Cell(4, 1)::Range::InsertAfter("EE300")
invoke WrdTbl::Cell(4, 2)::Range::InsertAfter("Modernization theory")
invoke WrdTbl::Cell(4, 3)::Range::InsertAfter("9:00-10:00 M,W,F")
invoke WrdTbl::Cell(4, 4)::Range::InsertAfter("Dr. Integer")
*> Insert row for course 4, cell by cell
invoke WrdTbl::Cell(5, 1)::Range::InsertAfter("EE325")
invoke WrdTbl::Cell(5, 2)::Range::InsertAfter("UI Modernization with PAM")
invoke WrdTbl::Cell(5, 3)::Range::InsertAfter("9:00-10:30 T,T")
invoke WrdTbl::Cell(5, 4)::Range::InsertAfter("Dr. Dword")
*> Insert row for course 5, cell by cell
invoke WrdTbl::Cell(6, 1)::Range::InsertAfter("EE350")
invoke WrdTbl::Cell(6, 2)::Range::InsertAfter("Advanced VISUAL COBOL topics")
invoke WrdTbl::Cell(6, 3)::Range::InsertAfter("9:00-10:30 T,T")
invoke WrdTbl::Cell(6, 4)::Range::InsertAfter("Dr. Gpf")
*> Insert row for course 6, cell by cell
invoke WrdTbl::Cell(7, 1)::Range::InsertAfter("EE400")
invoke WrdTbl::Cell(7, 2)::Range::InsertAfter("To the sky with VISUAL COBOL")
invoke WrdTbl::Cell(7, 3)::Range::InsertAfter("1:00 -2:30 M,W,F")
invoke WrdTbl::Cell(7, 4)::Range::InsertAfter("Dr. Gui")
*> Insert row for course 7, cell by cell
invoke WrdTbl::Cell(8, 1)::Range::InsertAfter("EE450")
invoke WrdTbl::Cell(8, 2)::Range::InsertAfter("UI programming with PAM")
invoke WrdTbl::Cell(8, 3)::Range::InsertAfter("1:00 -2:00 M,W,F")
invoke WrdTbl::Cell(8, 4)::Range::InsertAfter("Dr. Netguru")
*> Insert row for course 8, cell by cell
invoke WrdTbl::Cell(9, 1)::Range::InsertAfter("EE500")
invoke WrdTbl::Cell(9, 2)::Range::InsertAfter("Deploying to multiple targets")
invoke WrdTbl::Cell(9, 3)::Range::InsertAfter("3:00 -4:00 M,W,F")
invoke WrdTbl::Cell(9, 4)::Range::InsertAfter("Dr. Redist")
*> Set value for wdGoToItem to wdGoToLine
set wdGotoLine to type Microsoft.Office.Interop.Word.WdGoToItem::wdGoToLine
*> Set value for wdGoToDirection to wdGoToLast
set wdGoToLast to type Microsoft.Office.Interop.Word.WdGoToDirection::wdGoToLast
*> Let us move to the last line in the document
invoke WrdSel::GoTo(wdGoToLine, wdGoToLast, NullObject, NullObject)
*> Add some more text
invoke WrdSel::TypeText("For additional information r" &
"egarding the Micro Focus University, you can visit our website at ")
*> Today people like links, let us give them the hyperlink to our web site to click on
invoke WrdSel::Hyperlinks::Add(WrdSel::Range, "Http://www.microfocus.com", NullObject,
NullObject, NullObject, NullObject)
*> Conclude the letter, providing a phone number
invoke WrdSel::TypeText(". Thank you for your interest in the classes offered at the Micro Focus Univer" &
"sity. If you have any other questions, please feel free to give us a call at 800 328 4967 (US).")
*> New line
invoke WrdSel::TypeParagraph()
*> Have a polite end of the letter
invoke WrdSel::TypeText("Sincerely, ")
invoke WrdSel::TypeParagraph()
invoke WrdSel::TypeText("John Doe")
invoke WrdSel::TypeParagraph()
invoke WrdSel::TypeText("Micro Focus University")
invoke WrdSel::TypeParagraph()
*> Set the destination of the merge. Here we tell we want it to go to a new document
*> but it might as well be fax or email. These two of course will depend on us providing
*> respectively fax number or email address
set WrdMMg::Destination to type Microsoft.Office.Interop.Word.WdMailMergeDestination::wdSendToNewDocument
*> Now run the merge
invoke WrdMMg::Execute(1 as object)
*> We don't care for the template document, so we tell Word it is already saved to avoid a dialog
*> poping up
set WrdDoc::Saved to true
*> Now close the template document, no dialogs please (WrdNoSave)
invoke WrdDoc::Close(WrdNoSave, NullObject, NullObject)
*> Set WrdDoc object to the new merged document
set WrdDoc to WrdApp::Documents::Item(1)
*> Show a message box, telling we store the merged document in C:\merged.doc
invoke type System.Windows.Forms.MessageBox::Show("Saved mailmerge to c:\merged.doc",
WrdDoc::Name)
*> Save the merged document
invoke WrdDoc::SaveAs("c:\merged.doc" as type System.Object,
NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject,
NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject, NullObject)
*> For some odd reason, we have to tell Word it is saved (!)
set WrdDoc::Saved to true
*> Close the merged document
invoke WrdDoc::Close(WrdNoSave, NullObject, NullObject)
catch my-exception
*> Uh oh, we were not supposed to get here :-(
invoke type System.Windows.Forms.MessageBox::Show(
my-exception::Message::ToString(), "Try Catch")
end-try
*> Close Word, we are done, no save dialogs (WrdNoSave) please
invoke WrdApp::Quit(WrdNoSave, NullObject, NullObject)
*> Clear pointers to indicate we are all set and done
set WrdTbl to NULL
set WrdDDc to NULL
set WrdDoc to NULL
set WrdSel to NULL
set WrdApp to NULL
*> Wow, you just did your first MailMerge from Visual COBOL using dot net and Office Interop!
goback
end program Program1.