Solved

How can I get the full path to the MS Excel (Excel.exe) from the registry?

Posted on 2011-09-02
4
1,032 Views
Last Modified: 2012-05-12
Hi,

How can I reliably get the full path to the Excel executable from the registry?  The main goal is to get the detailed information about the version from the Excel.exe.

So far, I was successfull using the following approach, but it is not ideal, and there could be something better (symbolically, simplified):

    // Get the CLSID of the Excel.
    RegOpenKeyEx(HKEY_CLASSES_ROOT,  TEXT("Excel.Application\\CLSID"), 0, KEY_READ, &hKey); 
    RegQueryValueEx(hKey, TEXT(""), NULL, &ValueType, buf, &buflen);

    // I have got the value "{00024500-0000-0000-C000-000000000046}" for Excel 2007 in buf.

    // Get the LocalServer value.
    RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID\\" + buf + "\\LocalServer",  0, KEY_READ, &hKey);

    // I have got the value "C:\PROGRA~2\MICROS~1\Office12\EXCEL.EXE /automation"
    // Notice the shortened directory names.

    [Remove the option /automation and text whether the file exist -- I want to check if the file physically 
     exists (not only to check that the info is present in the registry).]

    [call the GetFileVersionInfo() for the filename (version.lib must be linked).]

    // I get the string like "12.0.6557.5000"

Open in new window


Is the HKCR\CLSID\{...}\LocalServer the good or the best way to get somehow the path to the Excel.exe?  Can the path with shortened subdir names be expanded to full path? (I would like to include it to the error log if anything wrong happens.)

Thanks for your time and experience,
    Petr
0
Comment
Question by:pepr
  • 3
4 Comments
 
LVL 31

Accepted Solution

by:
Zoppo earned 500 total points
ID: 36472708
Hi pepr,

an alternative solution could be to search for the executable associated to XLS files instead of using the registry directly. You can use AssocQueryString i.e. like this:
#include <Shlwapi.h>
...
char pszOut[MAX_PATH];
DWORD dwSize = MAX_PATH;
HRESULT hr = AssocQueryString( 0, ASSOCSTR_EXECUTABLE, ".xls", NULL, pszOut, &dwSize );
std::cout << pszOut << std::endl;

Open in new window

(you need to add 'shlwapi.lib' in the linker settings)
This should printout the (complete, not 8.3) file path, i.e. on my machine it's C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE

Hope this helps,

ZOPPO
0
 
LVL 28

Author Comment

by:pepr
ID: 36473171
Hi Zoppo.  Thanks, but I prefer not to use another library.  Also, I am not sure if the .xls must always be associated with the Excel.exe (say it could be the OpenOffice Calc).  This will not run on my machine.

For others, I have found the http://support.microsoft.com/kb/240794/en-us that uses basically the same approach as I did, but with the HKEY_CLASSES_ROOT is replaced by HKEY_LOCAL_MACHINE\Software\Classes\.  I have also found the function GetLongPathName() that is capable to convert the path with 8.3 names to the long path.  I will post the final code when finished (later).
0
 
LVL 28

Assisted Solution

by:pepr
pepr earned 0 total points
ID: 36473450
Here is the code.  It is cut from the bigger file.  Use assert() instead of the ATLASSERT() if you need to. You must touch it a bit:

The first snippet for getting version from the file properties:
    /*! \brief Returns a string with the file version.
    */
    std::string getFileVersion(const std::string & fname)
    {
        string version("unknown version");  // init 

        // Get the pace of the neccessary size.
        //
        DWORD sz = GetFileVersionInfoSize(fname.c_str(), NULL);
        vector<BYTE> v(sz);

        // Get the information for the file name.
        //
        if (GetFileVersionInfo(fname.c_str(), NULL, sz, &v[0]))
        {
            // Get the supported languages identifications
            //
            void * p = NULL;
            UINT len = 0;
            BOOL isOK = VerQueryValue(&v[0], 
                       TEXT("\\VarFileInfo\\Translation"), 
                       &p, &len);
            ATLASSERT(len >= 4);
            DWORD langId = static_cast<DWORD *>(p)[0];

            // Extract the string version identification (dot separated
            // numbers) for the first supported language id and 
            // build the guery string for the version of the file.
            //
            ostringstream oss;
            oss << "\\StringFileInfo\\"
                << hex 
                << setw(4) << setfill('0') << LOWORD(langId) // Microsoft language identifier
                << setw(4) << setfill('0') << HIWORD(langId) // IBM code page number
                << "\\FileVersion";

            // Get the version (it should succeed).
            //
            USES_CONVERSION;
            isOK = VerQueryValue(&v[0], CA2T(oss.str().c_str()), &p, &len);
            ATLASSERT(isOK);
            ATLASSERT(len > 0);
            version = reinterpret_cast<char*>(p);
        }

        return version;
    }

Open in new window


The snippet for getting the version of the installed Excel:
    std::string getExcelVersion()
    {
        HKEY hKey = NULL;   // handle k register key

        LONG res = RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            TEXT("Software\\Classes\\Excel.Application\\CLSID"),
            0, KEY_READ, &hKey);

        // If not installed, return the empty string.
        //
        if (res != ERROR_SUCCESS)
            return "";

        // The buffer for reading the strings from the registry.
        //
        DWORD ValueType = 0;
        const int bufsize = 1000;  // ad hoc
        BYTE buf[bufsize];
        ZeroMemory(&buf, bufsize);
        DWORD buflen = bufsize;    // input value of the argument...

        // Get the CLSID. 
        //
        ATLASSERT(res == ERROR_SUCCESS);
        ATLASSERT(hKey != NULL);

        res = RegQueryValueEx(hKey, TEXT(""), NULL,      // default
                              &ValueType, buf, &buflen);

        // Return empty string on error.
        //
        if (res != ERROR_SUCCESS)
            return "";

        // Close the key, find the new one.
        //
        ATLASSERT(ValueType == REG_SZ);

        RegCloseKey(hKey);

        string subKey("Software\\Classes\\CLSID\\");
        subKey += reinterpret_cast<char*>(buf);
        subKey += "\\LocalServer32";

        res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                           subKey.c_str(),
                           0, KEY_READ, &hKey);

        if (res != ERROR_SUCCESS)
            return "";

        ATLASSERT(res == ERROR_SUCCESS);
        ATLASSERT(hKey != NULL);

        ValueType = 0;
        ZeroMemory(&buf, bufsize);
        buflen = bufsize;   // input value of the argument

        // Full path possibly with options.
        //
        res = RegQueryValueEx(hKey, TEXT(""), NULL, 
                              &ValueType, buf, &buflen);

        if (res == ERROR_SUCCESS)
        {
            ATLASSERT(ValueType == REG_SZ);
            string app_file_name(reinterpret_cast<char*>(buf));

            // Find the ".EXE", cut the tail.
            //
            string s(LCase(app_file_name));  // app dependent lowercase
            string::size_type pos = s.find(".exe");
            ATLASSERT(pos != string::npos);
            app_file_name.erase(pos + 4);  // 4 = len(".exe")
            
            if ( ! isfile(app_file_name))  // app dependent test for the physical existence of the file
                return "";

            // Short path to long path.
            //
            ZeroMemory(&buf, bufsize);
            DWORD len = GetLongPathName(app_file_name.c_str(), 
                                        reinterpret_cast<char*>(buf),
                                        bufsize);
            if (len <= bufsize)
                app_file_name = reinterpret_cast<char*>(buf);
            // else -- left it in the short form
        }

        // Close the key.
        //
        RegCloseKey(hKey);

        // Find the version and return it.
        //
        return getFileVersion(app_file_name);
    }

Open in new window

0
 
LVL 28

Author Closing Comment

by:pepr
ID: 36494055
Thanks for the help.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Macro Capitalize 4 52
splitting text of cell to columns 14 24
Excel 2010 Text Formatting placing a hyphen in front of text 3 20
Excel - find text within text? 1 21
Introduction This Article is a follow-up to my Mappit! Addin Article (http://www.experts-exchange.com/A_2613.html), it was inspired by an email posting I made to EUSPRIG (http://www.eusprig.org/index.htm), I will briefly cover: 1) An overvie…
Approximate matching with VLOOKUP and MATCH seems to me to be a greatly under-used technique, and one which is vital for getting good performance out of large lookups. Until recently I would always have advised using an exact match for simplicity an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

914 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now