I'm still having reports that doc2any.exe is not detecting Microsoft Word installations correctly. These are beta users using our product so I can't give you access to their systems and I personally can't duplicate the errors on my XP, Vista or Windows 7 test machines.
I think the best approach is for me to understand how doc2any.exe checks the Windows registry for Word. I have pasted below a C++ header file I wrote a few days ago to detect Office installations myself using various recommended articles from Microsoft. I wanted to provide you with this so you don't think I'm trying to pry into the doc2any code, but rather to try and align my code checking with yours so that I can work out whether or not to call doc2any i.e. I want to call doc2any when I detect Word and need to be assured that doc2any will find Word too. That's why I'm sharing as there's no point in me detecting Word and calling doc2any and having it fail. This way I can provide better error message feedback to the user rather than the limited messages that come from doc2any.
The basic approach I have used successfully is to look in the HKEY_CLASSES_ROOT and look for the key: Word.Application then get the CurVer to get the version of Word and the CLSID - which with another lookup to HKEY_LOCAL_MACHINE\Software\Classes\CLSID\ will give me the executable path to a valid version of Word installed.
What I'd like to understand is how doc2any detects for a Word install. If you could share the approach that would really help me resolve these issues I'm facing with beta users.
I have highlighted the relevant parts of the code below in bold for your reference.
#ifndef OFFICEDETECTIVE_H
#define OFFICEDETECTIVE_H
#include <Windows.h>
#include "SCRWindowsTypeConverter.h"
#include <QDebug>
// Class is based on the following Microsoft Knowledge article and references:
// http://support.microsoft.com/kb/240794
// RegOpenKeyEx: http://msdn.microsoft.com/en-us/library/ms724897(VS.85).aspx
// RegQueryValueEx: http://msdn.microsoft.com/en-us/library/ms724911(VS.85).aspx
class OfficeDetective
{
public:
enum OfficeVersion
{
OfficeVersion_Unknown,
OfficePath_Unknown,
OfficeVersion_95,
OfficeVersion_97,
OfficeVersion_2000,
OfficeVersion_XP,
OfficeVersion_2003,
OfficeVersion_2007,
OfficeVersion_2010
};
enum OfficeApp
{
OfficeApp_Word,
OfficeApp_Excel,
OfficeApp_Outlook,
OfficeApp_Access,
OfficeApp_PowerPoint
};
static QString toString(const OfficeVersion officeVersion)
{
switch(officeVersion) {
case OfficeVersion_Unknown: { return QString("->Version Not found"); }break;
case OfficePath_Unknown: { return QString("->Path Not found"); }break;
case OfficeVersion_95: { return QString("->Office 95"); }break;
case OfficeVersion_97: { return QString("->Office 97"); }break;
case OfficeVersion_2000: { return QString("->Office 2000"); }break;
case OfficeVersion_XP: { return QString("->Office XP"); }break;
case OfficeVersion_2003: { return QString("->Office 2003"); }break;
case OfficeVersion_2007: { return QString("->Office 2007"); }break;
case OfficeVersion_2010: { return QString("->Office 2010"); }break;
default: { Q_ASSERT(false); return QString(""); }break;
}
}
static QString applicationAsString(const OfficeApp officeApp)
{
switch(officeApp) {
case OfficeApp_Word: { return QString("Word"); }break;
case OfficeApp_Excel: { return QString("Excel"); }break;
case OfficeApp_Outlook: { return QString("Outlook"); }break;
case OfficeApp_Access: { return QString("Access"); }break;
case OfficeApp_PowerPoint: { return QString("Powerpoint"); }break;
default: { Q_ASSERT(false); return QString(""); }break;
}
}
static QString progID(const OfficeApp officeApp)
{
switch(officeApp) {
case OfficeApp_Word: { return QString("Word.Application"); }break;
case OfficeApp_Excel: { return QString("Excel.Application"); }break;
case OfficeApp_Outlook: { return QString("Outlook.Application"); }break;
case OfficeApp_Access: { return QString("Access.Applicationdsde"); }break;
case OfficeApp_PowerPoint: { return QString("Powerpoint.Application"); }break;
default: { Q_ASSERT(false); return QString(""); }break;
}
}
static OfficeVersion stringToVersion(const QString versionString)
{
if(QString("7") == versionString){
return OfficeVersion_95;
}else if(QString("8") == versionString){
return OfficeVersion_97;
}else if(QString("9") == versionString){
return OfficeVersion_2000;
}else if(QString("10") == versionString){
return OfficeVersion_XP;
}else if(QString("11") == versionString){
return OfficeVersion_2003;
}else if(QString("12") == versionString){
return OfficeVersion_2007;
}else if(QString("14") == versionString){
return OfficeVersion_2010;
}else{
return OfficeVersion_Unknown;
}
}
static QString getVersionAndPath(OfficeApp appToCheck)
{
// The Return string of this method will look something like these:
/**
"Word->Path Not found"
"Word->Version Not found
"Word->Office 2010->C:\PROGRA~2\MICROS~4\Office14\WINWORD.EXE"
"Excel->Office 2010->C:\PROGRA~2\MICROS~4\Office14\EXCEL.EXE"
"Outlook->Office 2007->C:\PROGRA~2\MICROS~4\Office12\OUTLOOK.EXE"
"Access->Office 2000->C:\PROGRA~2\MICROS~4\Office9\MSACCESS.EXE"
"Powerpoint->Office XP->C:\PROGRA~2\MICROS~4\Office1o\POWERPNT.EXE"
**/
const QString progID( progID(appToCheck) );
HKEY hROOTKey( NULL);
HKEY hLOCALKey( NULL);
HKEY hVersionKey(NULL);
HKEY hCLSIDKey(NULL);
//STEP ONE - Find Application version and CLSID
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, win32::SCRWindowsTypeConverter::QString_To_LPCTSTR(progID), 0, KEY_READ, &hROOTKey) ){
return toString(OfficeVersion_Unknown);
}
if(ERROR_SUCCESS != RegOpenKeyEx(hROOTKey, win32::SCRWindowsTypeConverter::QString_To_LPCTSTR(QString("CurVer")), 0, KEY_READ, &hVersionKey)) {
RegCloseKey(hROOTKey);
return toString(OfficeVersion_Unknown);
}
if(ERROR_SUCCESS != RegOpenKeyEx(hROOTKey, win32::SCRWindowsTypeConverter::QString_To_LPCTSTR(QString("CLSID")), 0, KEY_READ, &hCLSIDKey)) {
RegCloseKey(hROOTKey);
return toString(OfficePath_Unknown);
}
// Get the Version information
const int BUFFER_SIZE(255);
ULONG cSize(BUFFER_SIZE);
ULONG cSize1(BUFFER_SIZE);
ULONG cSize2(BUFFER_SIZE);
TCHAR szVersion[BUFFER_SIZE];
TCHAR szCLSID[BUFFER_SIZE];
TCHAR szCLSIDPath[BUFFER_SIZE];
// Query values and place in buffers
const LONG lVersionRet( RegQueryValueEx(hVersionKey, NULL, NULL, NULL, (LPBYTE)szVersion, &cSize) );
const LONG lCLSIDRet( RegQueryValueEx(hCLSIDKey, NULL, NULL, NULL, (LPBYTE)szCLSID, &cSize1) );
// Close the registry keys
RegCloseKey(hCLSIDKey);
RegCloseKey(hVersionKey);
RegCloseKey(hROOTKey);
// Error while querying for version and CLSID
if(ERROR_SUCCESS != lVersionRet){
return toString(OfficeVersion_Unknown);
}
if(ERROR_SUCCESS != lCLSIDRet){
return toString(OfficePath_Unknown);
}
// Parse buffer to extract version number as string
QString programAndVersion(win32::SCRWindowsTypeConverter::TChar_To_QString(szVersion));
int lastIndex = programAndVersion.lastIndexOf(".", -1, Qt::CaseInsensitive);
if (lastIndex == -1)
return toString(OfficeVersion_Unknown);
int length = programAndVersion.length();
QString version = programAndVersion.mid((lastIndex+1), (length-(lastIndex+1)));
bool ok;
int isNumber = version.toInt(&ok, 10);
if (!isNumber)
return toString(OfficeVersion_Unknown);
QString detectedVersion = toString(stringToVersion(version));
// STEP TWO - Construct registry path and open HKEY_LOCAL_MACHINE to find exe path for CLSID
QString localPath = "Software\\Classes\\CLSID\\";
QString CLSID_Value = win32::SCRWindowsTypeConverter::TChar_To_QString(szCLSID);
localPath.append(CLSID_Value).append("\\LocalServer32");
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, win32::SCRWindowsTypeConverter::QString_To_LPCTSTR(localPath), 0, KEY_READ, &hLOCALKey) ){
return toString(OfficePath_Unknown);
}
const LONG szPathRet(RegQueryValueEx(hLOCALKey, NULL, NULL, NULL, (LPBYTE)szCLSIDPath, &cSize2));
RegCloseKey(hLOCALKey);
// Error while querying for path value
if(ERROR_SUCCESS != szPathRet){
return toString(OfficePath_Unknown);
}
// Remove trainling /commands from end of path string
QString detectedPath = win32::SCRWindowsTypeConverter::TChar_To_QString(szCLSIDPath);
lastIndex = detectedPath.lastIndexOf("/", -1, Qt::CaseInsensitive);
if (lastIndex != -1)
detectedPath = detectedPath.left(lastIndex).trimmed();
// Return version and path
return detectedVersion + "->" + detectedPath;
}
};
#endif // OFFICEDETECTIVE_H
All the best,
=======================================
We are not read Word path from registry, we are using CreateDispatch or CreateObject to create the "Word.Application" application, please refer to VB source code at below,
Sub AutoWord()
' Declare the variable.
Dim oWord As Word.Application
' Set the variable (runs new instance of Word).
Set oWord = CreateObject("Word.Application")
' Add a new document.
oWord.Documents.Add
' Add some text.
oWord.Selection.TypeText "This is some text."
' Save the document.
oWord.ActiveDocument.SaveAs Filename:="mydoc.doc"
' Quit Word.
oWord.Quit
' Clear the variable from memory.
Set oWord = Nothing
End Sub
You can also refer to a VC++ example at following web page,
http://www.codeguru.com/cpp/misc/misc/article.php/c6091
#include "AutoWord.h"
int PrintWordDocument(CString szFilePath)
{
// Instantiate an object of CAutoWord
CAutoWord AutoWord;
// Set filepath
int iRetVal = AutoWord.InitAutomation();
if (iRetVal != 0)
{
printf("LoadDocument operation failed.");
}
// Set filepath
char* strFilePath = szFilePath.GetBuffer(szFilePath.GetLength));
// Print the WORD document
iRetVal = AutoWord.PrintDocument(strFilePath);
if (iRetVal == 0)
{
AfxMessageBox(szFilePath+" was sent to printer.");
}
else
{
AfxMessageBox("Print operation failed.");
}
return iRetVal;
}
Can you create word object properly with above example source code in your system?
VeryDOC
=======================================
Thanks for that.
I'm assuming doc2any is a vb6 application from the source code provided?
Can you confirm that you check for the following in bold as I did not see this in your code:
Set oWord = CreateObject("Word.Application")
If oWord is nothing then
'flag as word not installed
End
Also, how do you test the version of Word to determine if you can use docx?
All the best,
=======================================
>>I'm assuming doc2any is a vb6 application from the source code provided?
doc2any is written in VC++.
>>Can you confirm that you check for the following in bold as I did not see this in your code:
>>Set oWord = CreateObject("Word.Application") If oWord is nothing then
>> 'flag as word not installed
>>End
In our doc2any.exe application, CreateDispatch("Word.Application") function is failed, so it can't create Word instance or object at all.
>>Also, how do you test the version of Word to determine if you can use docx?
Our doc2any.exe does support DOCX format automatically if your system has Word 2007 or Word 2010 installed, you need to do nothing in your side.