Use AssocQueryString() instead of manual accessing registry in wxMSW.

AssocQueryString() is more reliable and should work under all Windows
versions, including Windows 8 for which our previous implementation, reading
the values directly from the registry, had some problems.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@75647 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2014-01-19 13:15:48 +00:00
parent 8aed772f6c
commit 95b5a81c0e
2 changed files with 108 additions and 136 deletions

View File

@@ -25,4 +25,5 @@ wxGTK:
wxMSW: wxMSW:
- Improve wxMimeTypesManager open command detection (Eric Jensen).
- Make wxFILTER_INCLUDE_LIST in wxTextValidator actually usable. - Make wxFILTER_INCLUDE_LIST in wxTextValidator actually usable.

View File

@@ -34,10 +34,12 @@
#include "wx/file.h" #include "wx/file.h"
#include "wx/iconloc.h" #include "wx/iconloc.h"
#include "wx/confbase.h" #include "wx/confbase.h"
#include "wx/dynlib.h"
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include "wx/msw/registry.h" #include "wx/msw/registry.h"
#include "wx/msw/private.h" #include "wx/msw/private.h"
#include <shlwapi.h>
#endif // OS #endif // OS
// other standard headers // other standard headers
@@ -208,101 +210,79 @@ bool wxFileTypeImpl::EnsureExtKeyExists()
// get the command to use // get the command to use
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static wxString wxFileTypeImplGetCurVer(const wxString& progId) // Helper wrapping AssocQueryString() Win32 function: returns the value of the
// given associated string for the specified extension (which may or not have
// the leading period).
//
// Returns empty string if the association is not found.
static
wxString wxAssocQueryString(ASSOCSTR assoc,
wxString ext,
const wxString& verb = wxString())
{ {
wxRegKey key(wxRegKey::HKCR, progId + wxT("\\CurVer")); typedef HRESULT (WINAPI *AssocQueryString_t)(ASSOCF, ASSOCSTR,
if (key.Exists()) LPCTSTR, LPCTSTR, LPTSTR,
DWORD *);
static AssocQueryString_t s_pfnAssocQueryString = (AssocQueryString_t)-1;
static wxDynamicLibrary s_dllShlwapi;
if ( s_pfnAssocQueryString == (AssocQueryString_t)-1 )
{ {
wxString value; if ( !s_dllShlwapi.Load(wxT("shlwapi.dll"), wxDL_VERBATIM | wxDL_QUIET) )
if (key.QueryValue(wxEmptyString, value)) s_pfnAssocQueryString = NULL;
return value; else
wxDL_INIT_FUNC_AW(s_pfn, AssocQueryString, s_dllShlwapi);
} }
return progId;
if ( !s_pfnAssocQueryString )
return wxString();
DWORD dwSize = MAX_PATH;
TCHAR bufOut[MAX_PATH] = { 0 };
if ( ext.empty() || ext[0] != '.' )
ext.Prepend('.');
HRESULT hr = s_pfnAssocQueryString
(
ASSOCF_NOTRUNCATE, // Fail if buffer is too small.
assoc, // The association to retrieve.
ext.t_str(), // The extension to retrieve it for.
verb.empty() ? NULL
: static_cast<const TCHAR*>(verb.t_str()),
bufOut, // The buffer for output value.
&dwSize // And its size
);
// Do not use FAILED() here as S_FALSE can be returned but is still an
// error in this context.
if ( hr != S_OK )
{
wxLogApiError("AssocQueryString", hr);
return wxString();
}
return wxString(bufOut);
} }
wxString wxFileTypeImpl::GetCommand(const wxString& verb) const wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
{ {
// suppress possible error messages wxString command = wxAssocQueryString(ASSOCSTR_COMMAND, m_ext, verb);
wxLogNull nolog;
wxString strKey;
// Since Windows Vista the association used by Explorer is different from
// the association information stored in the traditional part of the
// registry. Unfortunately the new schema doesn't seem to be documented
// anywhere so using it involves a bit of guesswork:
//
// The information is stored under Explorer-specific key whose path is
// below. The interesting part is UserChoice subkey which is the only one
// we use so far but there is also OpenWithProgids subkey which can exist
// even if UserChoice doesn't. However in practice there doesn't seem to be
// any cases when OpenWithProgids values for the given extension are
// different from those found directly under HKCR\.ext, so for now we don't
// bother to use this, apparently the programs registering their file type
// associations do it in both places. We do use UserChoice because when the
// association is manually changed by the user it's only recorded there and
// so must override whatever value was created under HKCR by the setup
// program.
{
wxRegKey explorerKey
(
wxRegKey::HKCU,
wxT("Software\\Microsoft\\Windows\\CurrentVersion\\")
wxT("Explorer\\FileExts\\") +
m_ext +
wxT("\\UserChoice")
);
if ( explorerKey.Open(wxRegKey::Read) &&
explorerKey.QueryValue(wxT("Progid"), strKey) )
{
strKey = wxFileTypeImplGetCurVer(strKey);
}
}
if (!strKey && wxRegKey(wxRegKey::HKCR, m_ext + wxT("\\shell")).Exists())
strKey = m_ext;
if ( !strKey && !m_strFileType.empty())
{
wxString fileType = wxFileTypeImplGetCurVer(m_strFileType);
if (wxRegKey(wxRegKey::HKCR, fileType + wxT("\\shell")).Exists())
strKey = fileType;
}
if ( !strKey )
{
// no info
return wxEmptyString;
}
strKey << wxT("\\shell\\") << verb;
wxRegKey key(wxRegKey::HKCR, strKey + wxT("\\command"));
wxString command;
if ( key.Open(wxRegKey::Read) ) {
// it's the default value of the key
if ( key.QueryValue(wxEmptyString, command) ) {
bool foundFilename = CanonicalizeParams(command); bool foundFilename = CanonicalizeParams(command);
#if wxUSE_IPC #if wxUSE_IPC
// look whether we must issue some DDE requests to the application wxString ddeCommand = wxAssocQueryString(ASSOCSTR_DDECOMMAND, m_ext);
// (and not just launch it) wxString ddeTopic = wxAssocQueryString(ASSOCSTR_DDETOPIC, m_ext);
strKey += wxT("\\DDEExec");
wxRegKey keyDDE(wxRegKey::HKCR, strKey); if ( !ddeCommand.empty() && !ddeTopic.empty() )
if ( keyDDE.Open(wxRegKey::Read) ) { {
wxString ddeCommand, ddeServer, ddeTopic; wxString ddeServer = wxAssocQueryString( ASSOCSTR_DDEAPPLICATION, m_ext );
keyDDE.QueryValue(wxEmptyString, ddeCommand);
// in some cases "DDEExec" subkey exists but has no value, we
// shouldn't use DDE in this case
if ( !ddeCommand.empty() ) {
ddeCommand.Replace(wxT("%1"), wxT("%s")); ddeCommand.Replace(wxT("%1"), wxT("%s"));
wxRegKey keyServer(wxRegKey::HKCR, strKey + wxT("\\Application")); if ( ddeTopic.empty() )
keyServer.QueryValue(wxEmptyString, ddeServer);
wxRegKey keyTopic(wxRegKey::HKCR, strKey + wxT("\\Topic"));
keyTopic.QueryValue(wxEmptyString, ddeTopic);
if (ddeTopic.empty())
ddeTopic = wxT("System"); ddeTopic = wxT("System");
// HACK: we use a special feature of wxExecute which exists // HACK: we use a special feature of wxExecute which exists
@@ -313,10 +293,9 @@ wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
<< wxT('#') << ddeTopic << wxT('#') << ddeTopic
<< wxT('#') << ddeCommand; << wxT('#') << ddeCommand;
} }
}
else else
#endif // wxUSE_IPC #endif // wxUSE_IPC
if ( !foundFilename ) if ( !foundFilename && !command.empty() )
{ {
// we didn't find any '%1' - the application doesn't know which // we didn't find any '%1' - the application doesn't know which
// file to open (note that we only do it if there is no DDEExec // file to open (note that we only do it if there is no DDEExec
@@ -325,9 +304,6 @@ wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
// HACK: append the filename at the end, hope that it will do // HACK: append the filename at the end, hope that it will do
command << wxT(" %s"); command << wxT(" %s");
} }
}
}
//else: no such file type or no value, will return empty string
return command; return command;
} }
@@ -409,20 +385,10 @@ bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
{ {
wxString strIconKey; wxString strIcon = wxAssocQueryString(ASSOCSTR_DEFAULTICON, m_ext);
strIconKey << m_strFileType << wxT("\\DefaultIcon");
// suppress possible error messages if ( !strIcon.empty() )
wxLogNull nolog; {
wxRegKey key(wxRegKey::HKCR, strIconKey);
if ( key.Open(wxRegKey::Read) ) {
wxString strIcon;
// it's the default value of the key
if ( key.QueryValue(wxEmptyString, strIcon) ) {
// the format is the following: <full path to file>, <icon index>
// NB: icon index may be negative as well as positive and the full
// path may contain the environment variables inside '%'
wxString strFullPath = strIcon.BeforeLast(wxT(',')), wxString strFullPath = strIcon.BeforeLast(wxT(',')),
strIndex = strIcon.AfterLast(wxT(',')); strIndex = strIcon.AfterLast(wxT(','));
@@ -433,6 +399,12 @@ bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
strIndex = wxT("0"); strIndex = wxT("0");
} }
// if the path contains spaces, it can be enclosed in quotes but we
// must not pass a filename in that format to any file system function,
// so remove them here.
if ( strFullPath.StartsWith('"') && strFullPath.EndsWith('"') )
strFullPath = strFullPath.substr(1, strFullPath.length() - 2);
if ( iconLoc ) if ( iconLoc )
{ {
iconLoc->SetFileName(wxExpandEnvVars(strFullPath)); iconLoc->SetFileName(wxExpandEnvVars(strFullPath));
@@ -442,7 +414,6 @@ bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
return true; return true;
} }
}
// no such file type or no value or incorrect icon entry // no such file type or no value or incorrect icon entry
return false; return false;