QAxObject & MS Word 2016+
-
Qt Creator 4.4.1
There is a program for working with a word document.#include <ActiveQt/QAxObject> #include <ActiveQt/qaxbase.h> #include <QDir> QString VbaScriptLine = "text text text"; QString SaveFilePath = "text text text"; QAxObject *word, *objVBComp, *objCodeMod; word = new QAxObject( "Word.Application" ); word->querySubObject( "Documents()" )->dynamicCall( "Open(FileName, ConfirmConversions, ReadOnly )", QDir::toNativeSeparators(QDir::currentPath() + "\\" + OpenFilePath), false, false ); objVBComp = word->querySubObject( "ActiveDocument" )->querySubObject( "VBProject" )->querySubObject( "VBComponents" )->querySubObject( "Add(ComponentType)", 1 ); objCodeMod = objVBComp->querySubObject("CodeModule"); quint32 lLineNum = objCodeMod->property( "CountOfLines" ).toInt() + 1; objCodeMod->querySubObject( "InsertLines(Line, String)", lLineNum, VbaScriptLine ); word->querySubObject( "Application" )->dynamicCall( "Run(MacroName)", MacroName ); word->querySubObject( "ActiveDocument" )->dynamicCall( "SaveAs( FileName, FileFormat )", QDir::toNativeSeparators(QDir::currentPath() + "\\" + SaveFilePath), 0 ); word->dynamicCall( "Quit()" ); delete word, objVBComp, objCodeMod;
The program worked with word 2003-2013 installed. When installing word version 2016 and higher, when calling the InsertLines function, the error "Error calling IDispatch member InsertLines: Unknown error" occurs.
What could be the problem? -
@UkoZL
This sounds like a Word VBA question, not a Qt one. Given you say it worked on old Word versions and gives the error it does on Word 16, where is your reference link page for usingCodeModule.InsertLines(Line, String)
under Word 16? Have you tested whether it works if you go into Word 16 and enter the corresponding VBA into its VBA/macro editor? -
Yes, I checked through the VBA editor in Word 16 and there are no errors.
Sub Test() ActiveDocument.VBProject.VBComponents.Add (1) VBCount = ActiveDocument.VBProject.VBComponents.Count LinesCount = ActiveDocument.VBProject.VBComponents.Item(VBCount).CodeModule.CountOfLines + 1 ActiveDocument.VBProject.VBComponents.Item(VBCount).CodeModule.InsertLines Line:=LinesCount, String:="text text text" End Sub
Works in old and new versions Word.
-
@UkoZL
Yeah, this is not good, I looked it up too, and it does not seem to have changed! Which would have been "easy"....I'm afraid I think you are going to struggle to get an answer here. There is not going to be something obvious to say why it does not work from your Qt program when it works outside. As you know,
QAxObject
is really just a thin wrapper to the COM/Automation/VBA layer, it's not going to be apparent why this one call should seem to be failing.One thought is that somehow there is an Automation installation issue, like it's picking up a pre-Word-16 for this call. I don't know how likely and I don't know how you would determine. Do you have more than one Word version installed? Are you able to test your code on another machine? Given "Unknown Error", have you tested with simple values for
Line
&String
? Like0
(or1
if that's what it counts from) and"Hello"
, in case the problem is in the parameter values? Verify what value yourobjCodeMod->property( "CountOfLines" ).toInt()
has?Your test is
ActiveDocument.VBProject.VBComponents.Item(VBCount).CodeModule.InsertLines Line:=LinesCount, String:="text text text"
. I don't know whetherQAxObject
does parameters by position or name, ought you testActiveDocument.VBProject.VBComponents.Item(VBCount).CodeModule.InsertLines(LinesCount, "text text text")
? Test some other methods likeDeleteLines
andReplaceLine
?You may be able to answer a question I have wondered about, and would try out if I had an error like
Error calling IDispatch member InsertLines
: there must be some call which would list/enumerate all the available methods/properties on an object, perhaps even to include their parameters? I should like to know what that would be if you know? -
Hi, just guessing, but perhaps in the Trust Center has a stricter setting in your Word 2016, so that vba code inside Word still works with R/W access but external code like Q only has read-only acess.
If you try other properties on the CodeModule (the objCodeMod pointer) for example changing the Width, does it also crash in Qt? -
In the document in the VBA editor, I created a CodeModule and added 4 lines there. In the program, I removed the addition of a new CodeModule to link to a pre-created one.
and I want to check what options are available CodeModule
int Ztmp = word->querySubObject( "ActiveDocument" )->querySubObject( "VBProject" )->querySubObject( "VBComponents" )->property( "Count" ).toInt(); QVariant Z = word->querySubObject( "ActiveDocument" )->querySubObject( "VBProject" )->querySubObject( "VBComponents(index)", Ztmp )->querySubObject( "CodeModule" )->propertyBag();
Result:
Word 2007 (Win10)
Word 2016/2019/2021 (Win7/Win10)
The add module function works correctly and adds the module.
word->querySubObject( "ActiveDocument" )->querySubObject( "VBProject" )->querySubObject( "VBComponents" )->querySubObject( "Add(ComponentType)", 1 );
But when accessing the module, it seems to be absent.
All other methods CodeModule give the same error "Error calling IDispatch member Method Name: Unknown error". -
Indeed it seems something's changed in Word's COM interface.
Your Qt code might need some new (perhaps security-flavored) addition to enable CodeModule access.You could try running dcomcnfg.exe to see if there are some new security stuff that needs to enabled for Word 2016.
Also you can try running the same VBA test() module from your Word 2007 but accessing Word 2016? That means you'll have to start with something like
Dim WordApp As Word.Application2016 ' or similar incantation Set WordApp = New Word.Application ...
to "reach across" from Word 2007 to Word 2016. (If they are on different PCs you can use dcomcnfg.exe to enable DCOM).
If that VBA code also fails to access CodeModule properties then Qt is not the problem.Another approach is to check the COM interfaces to Word2007 and 2016 using OleView.exe and see if anything differs. Note it's an old program (I used it heavily in the previous century) and to make it work on Windows 10, first time you have to run it as an Administrator (after that it's fine).
-
Noticed another feature (word 2007).
Created this app:#include "mainwindow.h" #include "ui_mainwindow.h" #include <ActiveQt/QAxObject> #include <ActiveQt/qaxbase.h> #include <QDir> QAxObject *word, *objVBComp, *objCodeMod; QString VbaScriptLine = "Sub MacroCreatedByUser() : ActiveDocument.VBProject.References.AddFromGuid \"{00020802-0000-0000-C000-000000000046}\", 1, 5 : End Sub"; QString OpenFilePath = "input.doc"; QString SaveFilePath = "output.doc"; QString MacroName = "Test macros"; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_pressed() { word = new QAxObject( "Word.Application" ); word->setProperty("DisplayAlerts", - 1); word->setProperty("Visible", true); QString PathTmp = QDir::toNativeSeparators(QDir::currentPath() + "\\" + OpenFilePath); word->querySubObject( "Documents()" )->dynamicCall( "Open(FileName, ConfirmConversions, ReadOnly )", QDir::toNativeSeparators(QDir::currentPath() + "\\" + OpenFilePath), false, false ); objCodeMod = word->querySubObject( "ActiveDocument" )->querySubObject( "VBProject" )->querySubObject( "VBComponents" )->querySubObject("Item(Index)", 1)->querySubObject( "CodeModule" ); objCodeMod->dynamicCall( "AddFromString(QString)", VbaScriptLine ); word->querySubObject( "Application" )->dynamicCall( "Run(MacroName)", MacroName ); word->querySubObject( "ActiveDocument" )->dynamicCall( "SaveAs( FileName, FileFormat )", QDir::toNativeSeparators(QDir::currentPath() + "\\" + SaveFilePath), 0 ); word->querySubObject( "ActiveDocument" )->dynamicCall( "Close(SaveChanges)", 0 ); word->dynamicCall( "Quit()" ); delete word; }
While executing the line: «objCodeMod->dynamicCall( "AddFromString(QString)", VbaScriptLine );» Windows OS fixes an error in the WINWORD.EXE application:
Despite the error, the macro is added and called as expected.