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:
@@ -34,10 +34,12 @@
|
||||
#include "wx/file.h"
|
||||
#include "wx/iconloc.h"
|
||||
#include "wx/confbase.h"
|
||||
#include "wx/dynlib.h"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include "wx/msw/registry.h"
|
||||
#include "wx/msw/private.h"
|
||||
#include <shlwapi.h>
|
||||
#endif // OS
|
||||
|
||||
// other standard headers
|
||||
@@ -208,126 +210,100 @@ bool wxFileTypeImpl::EnsureExtKeyExists()
|
||||
// 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"));
|
||||
if (key.Exists())
|
||||
typedef HRESULT (WINAPI *AssocQueryString_t)(ASSOCF, ASSOCSTR,
|
||||
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 (key.QueryValue(wxEmptyString, value))
|
||||
return value;
|
||||
if ( !s_dllShlwapi.Load(wxT("shlwapi.dll"), wxDL_VERBATIM | wxDL_QUIET) )
|
||||
s_pfnAssocQueryString = NULL;
|
||||
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
|
||||
{
|
||||
// suppress possible error messages
|
||||
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);
|
||||
wxString command = wxAssocQueryString(ASSOCSTR_COMMAND, m_ext, verb);
|
||||
bool foundFilename = CanonicalizeParams(command);
|
||||
|
||||
#if wxUSE_IPC
|
||||
// look whether we must issue some DDE requests to the application
|
||||
// (and not just launch it)
|
||||
strKey += wxT("\\DDEExec");
|
||||
wxRegKey keyDDE(wxRegKey::HKCR, strKey);
|
||||
if ( keyDDE.Open(wxRegKey::Read) ) {
|
||||
wxString ddeCommand, ddeServer, ddeTopic;
|
||||
keyDDE.QueryValue(wxEmptyString, ddeCommand);
|
||||
wxString ddeCommand = wxAssocQueryString(ASSOCSTR_DDECOMMAND, m_ext);
|
||||
wxString ddeTopic = wxAssocQueryString(ASSOCSTR_DDETOPIC, m_ext);
|
||||
|
||||
// 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"));
|
||||
if ( !ddeCommand.empty() && !ddeTopic.empty() )
|
||||
{
|
||||
wxString ddeServer = wxAssocQueryString( ASSOCSTR_DDEAPPLICATION, m_ext );
|
||||
|
||||
wxRegKey keyServer(wxRegKey::HKCR, strKey + wxT("\\Application"));
|
||||
keyServer.QueryValue(wxEmptyString, ddeServer);
|
||||
wxRegKey keyTopic(wxRegKey::HKCR, strKey + wxT("\\Topic"));
|
||||
keyTopic.QueryValue(wxEmptyString, ddeTopic);
|
||||
ddeCommand.Replace(wxT("%1"), wxT("%s"));
|
||||
|
||||
if (ddeTopic.empty())
|
||||
ddeTopic = wxT("System");
|
||||
if ( ddeTopic.empty() )
|
||||
ddeTopic = wxT("System");
|
||||
|
||||
// HACK: we use a special feature of wxExecute which exists
|
||||
// just because we need it here: it will establish DDE
|
||||
// conversation with the program it just launched
|
||||
command.Prepend(wxT("WX_DDE#"));
|
||||
command << wxT('#') << ddeServer
|
||||
<< wxT('#') << ddeTopic
|
||||
<< wxT('#') << ddeCommand;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif // wxUSE_IPC
|
||||
if ( !foundFilename )
|
||||
{
|
||||
// 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
|
||||
// subkey)
|
||||
//
|
||||
// HACK: append the filename at the end, hope that it will do
|
||||
command << wxT(" %s");
|
||||
}
|
||||
}
|
||||
// HACK: we use a special feature of wxExecute which exists
|
||||
// just because we need it here: it will establish DDE
|
||||
// conversation with the program it just launched
|
||||
command.Prepend(wxT("WX_DDE#"));
|
||||
command << wxT('#') << ddeServer
|
||||
<< wxT('#') << ddeTopic
|
||||
<< wxT('#') << ddeCommand;
|
||||
}
|
||||
else
|
||||
#endif // wxUSE_IPC
|
||||
if ( !foundFilename && !command.empty() )
|
||||
{
|
||||
// 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
|
||||
// subkey)
|
||||
//
|
||||
// HACK: append the filename at the end, hope that it will do
|
||||
command << wxT(" %s");
|
||||
}
|
||||
//else: no such file type or no value, will return empty string
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -409,39 +385,34 @@ bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
|
||||
|
||||
bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
|
||||
{
|
||||
wxString strIconKey;
|
||||
strIconKey << m_strFileType << wxT("\\DefaultIcon");
|
||||
wxString strIcon = wxAssocQueryString(ASSOCSTR_DEFAULTICON, m_ext);
|
||||
|
||||
// suppress possible error messages
|
||||
wxLogNull nolog;
|
||||
wxRegKey key(wxRegKey::HKCR, strIconKey);
|
||||
if ( !strIcon.empty() )
|
||||
{
|
||||
wxString strFullPath = strIcon.BeforeLast(wxT(',')),
|
||||
strIndex = strIcon.AfterLast(wxT(','));
|
||||
|
||||
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(',')),
|
||||
strIndex = strIcon.AfterLast(wxT(','));
|
||||
|
||||
// index may be omitted, in which case BeforeLast(',') is empty and
|
||||
// AfterLast(',') is the whole string
|
||||
if ( strFullPath.empty() ) {
|
||||
strFullPath = strIndex;
|
||||
strIndex = wxT("0");
|
||||
}
|
||||
|
||||
if ( iconLoc )
|
||||
{
|
||||
iconLoc->SetFileName(wxExpandEnvVars(strFullPath));
|
||||
|
||||
iconLoc->SetIndex(wxAtoi(strIndex));
|
||||
}
|
||||
|
||||
return true;
|
||||
// index may be omitted, in which case BeforeLast(',') is empty and
|
||||
// AfterLast(',') is the whole string
|
||||
if ( strFullPath.empty() ) {
|
||||
strFullPath = strIndex;
|
||||
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 )
|
||||
{
|
||||
iconLoc->SetFileName(wxExpandEnvVars(strFullPath));
|
||||
|
||||
iconLoc->SetIndex(wxAtoi(strIndex));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// no such file type or no value or incorrect icon entry
|
||||
|
Reference in New Issue
Block a user