Weired QAX setProperty() behavior
-
I use Qt's dumpcpp tool dump the msword.olb to cpp files. There is one method dumped as
inline void Paragraph::SetWidowControl(int value){ setProperty("WidowControl", QVariant(value)); }
And in my code, I tried three different ways
-
Invoke the method with "SetWidowControl(true)", don't work, and the debug shows:
QAxBase: Error calling IDispatch member WidowControl: Unknown error
-
Invoke "setProperty("WidowControl", true)" directly, show the same error message (as it's basically same with case 1).
-
Invoke "dynamicCall("SetWidowControl(bool)", true)", no error message showed and all are OK.
I've two questions:
- Why 1 and 2 not work? I think they are the 'normal' methods, but they don't work.
- According to: Paragraph.WidowControl property. It doesn't mentioned there is a method "SetWidowControl" and the dumpcpp tool didn't dump one. So, why case 3 works? I'm not familiar with COM tech, so what Qt's 'setProperty' does? I guess it must use similar ways as 'dynamicCall()' to direct MSWord to do something.
My toolchain is Qt 5.7.0 and VS2015, and the word edition is office365 (office 2016).
-
-
Hi, the reason #3 flavor works but not #1 and #2 I think occurs because you don't have the correct COM interface instantiated in your QAxBase class. If you check the control property of that class does it say IDispatch or Paragraph?
It's usually easier (and slower) to call using dynamicCall() to call into COM.
You can call directly (e.g. ... ->SetWindowControl(true); but then you have to set that Control property first. (It's a bit like comparing virtual calls or normal function calls in C++). -
@hskoglund Thanks for your reply. The word.h and word.cpp are dumpped using command line "dumpcpp msword.olb". The tool automatically generates the classes with methods.
class WORD_EXPORT Paragraphs : public QAxObject { public: Paragraphs(IDispatch *subobject = 0, QAxObject *parent = 0) : QAxObject((IUnknown*)subobject, parent) { internalRelease(); } ......... inline int WidowControl() const; //Returns the value of WidowControl inline void SetWidowControl(int value); //Sets the value of the WidowControl property .......... }
Can you give me more informations about where the property defined?
-
Hi, I think the properties are mostly used from Visual Basic and C# and SetWidowControl() function are used from C++
I also have dumpcpp.exe and msword.olb somewhere in my computer, I can make a simple demo/test program using method #1 (or #2) later tonight if you need.
-
@hskoglund I'm really appreciated if you can have try it for me :). Now I'm not if it's Qt's bug or MS's bug. And I find that Qt's method accept a 'int', it seems MS use 'bool', can this be the reason?
BTW, Qt's dumpcpp is not perfect yet, when generating the method, it sometimes omit the class qualifier with the first parameter. such as it generate 'Range' instead of 'Word::Range', so you may need manually add 'Word::' to them :(. Maybe a brute 'using namespace Word;' should work too.
-
Hi, yesterday I tested running dumpcpp on the msword.olb on my computer (I have Office 2010 installed). Sure enough I got a word.cpp and word.h but the generated .cpp file was 60 MB! When I tried to compile in using Qt 5.7+MSVC2015 I got errors on stuff like ListTemplate and Range :-(
And trying to edit that 60 MB word.cpp file ---> Qt Creator crashes..
So I spent some time splitting in up and getting it compile cleanly. But try as I might, I could only get #2 and #3 not #1 (which I think is the superior one).
Finally I realized that the dumpcpp program had maybe misparsed something in that huge msword.olb. Since the object model for Office has been more or less the same the last 20 years, if I could find an older version of Office perhaps that msword.olb would be a better match for dumpcpp. On my old portable I had Office 2003, run dumpcpp and voila a word.cpp only 30 MB (a midget), and most important no errors when compiling :-) You have Office 2016 perhaps that msword.olb also gives better word.cpp and word.h files? Anyway, I think it doesn't matter what version of Office you use, as long as the compilation runs ok.I'll post now 2 versions of my test program, one using dynamic calls (or rather the usual querySubObject() flavor and the other using more fancy C++ direct calls.
-
First version: create a vanilla Qt widgets app, in the .pro file insert "axcontainer" after "...gui" on the top line.
Then in mainwindow.cpp, insert "#include qaxobject.h" at the top and
after ui->setupUi(this); insert:auto wApp = new QAxObject("Word.Application"); if (nullptr == wApp) return; wApp->setProperty("Visible",true); auto doc = wApp->querySubObject("Documents")->querySubObject("Add()"); for (int p = 0; (p < 100); ++p) { auto para = doc->querySubObject("Content")->querySubObject("Paragraphs")->querySubObject("Add()"); para->querySubObject("Range")->setProperty("Text","Four score and seven years ago our fathers " "brought forth on this continent a new nation, conceived in liberty, and dedicated to the " "proposition that all men are created equal."); para->querySubObject("Format")->setProperty("SpaceAfter",20); para->setProperty("WidowControl",-1); // for true // para->setProperty("WidowControl",0); // for false para->querySubObject("Range")->querySubObject("InsertParagraphAfter()"); }
This is the style I think 99% of QAxObject programs are written.
Advantages: you don't need to run dumpcpp and you don't need to include any humongous word.* files in your project.
Disadvantage: just like JS, no compile time checks, surprises can await you when you start the program.
-
2nd version, a bit more fancy, requiring you to run dumpcpp to get 2 files: word.cpp and word.h. Create a vanilla widget app, insert " axcontainer" in the .pro file (same as above). Add word.cpp into your project (so it wil compiled along with main.cpp and mainwindow.cpp.
Then in mainwindow.cpp insert "#include word.h" at the top, and after ui->setupUi(this); insert:
auto wApp = new Word::Application; if (nullptr == wApp) return; wApp->SetVisible(true); auto doc = wApp->Documents()->Add(); for (int p = 0; (p < 100); ++p) { auto para= doc->Content()->Paragraphs()->Add(); para->Range()->SetText("Four score and seven years ago our fathers " "brought forth on this continent a new nation, conceived in liberty, and dedicated to the " "proposition that all men are created equal. Now we are engaged in a great civil war, " "testing whether that nation, or any nation so conceived and so dedicated, can long endure."); para->Format()->SetSpaceAfter(20); para->SetWidowControl(0); // for false // para->setWidowControl(-1); // for true para->Range()->InsertParagraphAfter(); }
These calls will be changed to dynamic calls inside word.h but it's nice to have command completions and syntax checking by the compiler.
Note: when dealing with COM, thanks to it being used first by Visual Basic and VBA for Office, it's sometimes problems with the boolean chaps true and false, so I prefer to use 0 for false and -1 for true, same as in the Visual Basic.