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:
@@ -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.
|
||||||
|
@@ -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,126 +210,100 @@ 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;
|
bool foundFilename = CanonicalizeParams(command);
|
||||||
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);
|
|
||||||
|
|
||||||
#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 ( keyDDE.Open(wxRegKey::Read) ) {
|
|
||||||
wxString ddeCommand, ddeServer, ddeTopic;
|
|
||||||
keyDDE.QueryValue(wxEmptyString, ddeCommand);
|
|
||||||
|
|
||||||
// in some cases "DDEExec" subkey exists but has no value, we
|
if ( !ddeCommand.empty() && !ddeTopic.empty() )
|
||||||
// shouldn't use DDE in this case
|
{
|
||||||
if ( !ddeCommand.empty() ) {
|
wxString ddeServer = wxAssocQueryString( ASSOCSTR_DDEAPPLICATION, m_ext );
|
||||||
ddeCommand.Replace(wxT("%1"), wxT("%s"));
|
|
||||||
|
|
||||||
wxRegKey keyServer(wxRegKey::HKCR, strKey + wxT("\\Application"));
|
ddeCommand.Replace(wxT("%1"), wxT("%s"));
|
||||||
keyServer.QueryValue(wxEmptyString, ddeServer);
|
|
||||||
wxRegKey keyTopic(wxRegKey::HKCR, strKey + wxT("\\Topic"));
|
|
||||||
keyTopic.QueryValue(wxEmptyString, ddeTopic);
|
|
||||||
|
|
||||||
if (ddeTopic.empty())
|
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
|
||||||
// just because we need it here: it will establish DDE
|
// just because we need it here: it will establish DDE
|
||||||
// conversation with the program it just launched
|
// conversation with the program it just launched
|
||||||
command.Prepend(wxT("WX_DDE#"));
|
command.Prepend(wxT("WX_DDE#"));
|
||||||
command << wxT('#') << ddeServer
|
command << wxT('#') << ddeServer
|
||||||
<< wxT('#') << ddeTopic
|
<< wxT('#') << ddeTopic
|
||||||
<< wxT('#') << ddeCommand;
|
<< wxT('#') << ddeCommand;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
#endif // wxUSE_IPC
|
||||||
#endif // wxUSE_IPC
|
if ( !foundFilename && !command.empty() )
|
||||||
if ( !foundFilename )
|
{
|
||||||
{
|
// 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
|
// subkey)
|
||||||
// subkey)
|
//
|
||||||
//
|
// 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,39 +385,34 @@ 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);
|
wxString strFullPath = strIcon.BeforeLast(wxT(',')),
|
||||||
|
strIndex = strIcon.AfterLast(wxT(','));
|
||||||
|
|
||||||
if ( key.Open(wxRegKey::Read) ) {
|
// index may be omitted, in which case BeforeLast(',') is empty and
|
||||||
wxString strIcon;
|
// AfterLast(',') is the whole string
|
||||||
// it's the default value of the key
|
if ( strFullPath.empty() ) {
|
||||||
if ( key.QueryValue(wxEmptyString, strIcon) ) {
|
strFullPath = strIndex;
|
||||||
// the format is the following: <full path to file>, <icon index>
|
strIndex = wxT("0");
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// no such file type or no value or incorrect icon entry
|
||||||
|
Reference in New Issue
Block a user