From 0ed580f45173f0703a01a4a00fa6021a82df72ce Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 2 Dec 2015 23:15:25 +0100 Subject: [PATCH] Fix modifying file type associations under MSW as non-admin The existing code tried writing to HKEY_CLASSES_ROOT which doesn't work, in general, for normal, i.e. non administrator, users, under any post-XP versions of Windows. Fix it to write to HKEY_CURRENT_USER\Software\Classes, which is the part of HKEY_CLASSES_ROOT for the current user and to which we do have write access, and only continue to use HKEY_CLASSES_ROOT itself for reading. --- src/msw/mimetype.cpp | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/msw/mimetype.cpp b/src/msw/mimetype.cpp index ce7280ad4f..0b7e6b0671 100644 --- a/src/msw/mimetype.cpp +++ b/src/msw/mimetype.cpp @@ -81,6 +81,17 @@ class WXDLLIMPEXP_FWD_CORE wxIcon; // to open/print the file (the positional parameters are introduced by %1, // %2, ... in these strings, we change them to %s ourselves) +// Notice that HKCR can be used only when reading from the registry, when +// writing to it, we need to write to HKCU\Software\Classes instead as HKCR is +// a merged view of that location and HKLM\Software\Classes that we generally +// wouldn't have the write permissions to but writing to HKCR will try writing +// to the latter unless the key being written to already exists under the +// former, resulting in a "Permission denied" error without administrative +// permissions. So the right thing to do is to use HKCR when reading, to +// respect both per-user and machine-global associations, but only write under +// HKCU. +static const wxChar *CLASSES_ROOT_KEY = wxS("Software\\Classes\\"); + // although I don't know of any official documentation which mentions this // location, uses it, so it isn't likely to change static const wxChar *MIME_DATABASE_KEY = wxT("MIME\\Database\\Content Type\\"); @@ -205,7 +216,7 @@ size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs, bool wxFileTypeImpl::EnsureExtKeyExists() { - wxRegKey rkey(wxRegKey::HKCR, m_ext); + wxRegKey rkey(wxRegKey::HKCU, CLASSES_ROOT_KEY + m_ext); if ( !rkey.Exists() ) { if ( !rkey.Create() || !rkey.SetValue(wxEmptyString, m_strFileType) ) @@ -553,11 +564,11 @@ wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) extWithDot = wxT('.'); extWithDot += ext; - // start by setting the HKCR\\.ext entries + // start by setting the entries under ".ext" // default is filetype; content type is mimetype const wxString& filetypeOrig = ftInfo.GetShortDesc(); - wxRegKey key(wxRegKey::HKCR, extWithDot); + wxRegKey key(wxRegKey::HKCU, CLASSES_ROOT_KEY + extWithDot); if ( !key.Exists() ) { // create the mapping from the extension to the filetype @@ -605,7 +616,7 @@ wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) // create the MIME key wxString strKey = MIME_DATABASE_KEY; strKey << mimetype; - wxRegKey keyMIME(wxRegKey::HKCR, strKey); + wxRegKey keyMIME(wxRegKey::HKCU, CLASSES_ROOT_KEY + strKey); ok = keyMIME.Create(); if ( ok ) @@ -626,7 +637,7 @@ wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) extWithDot = wxT('.'); extWithDot += ext; - wxRegKey key2(wxRegKey::HKCR, extWithDot); + wxRegKey key2(wxRegKey::HKCU, CLASSES_ROOT_KEY + extWithDot); if ( !key2.Exists() ) key2.Create(); key2.SetValue(wxEmptyString, filetype); @@ -643,7 +654,7 @@ wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) // create the MIME key wxString strKey = MIME_DATABASE_KEY; strKey << mimetype2; - wxRegKey keyMIME(wxRegKey::HKCR, strKey); + wxRegKey keyMIME(wxRegKey::HKCU, CLASSES_ROOT_KEY + strKey); ok = keyMIME.Create(); if ( ok ) @@ -654,11 +665,11 @@ wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) } } - } // end of for loop; all extensions now point to HKCR\.ext\Default + } // end of for loop; all extensions now point to .ext\Default // create the filetype key itself (it will be empty for now, but // SetCommand(), SetDefaultIcon() &c will use it later) - wxRegKey keyFT(wxRegKey::HKCR, filetype); + wxRegKey keyFT(wxRegKey::HKCU, CLASSES_ROOT_KEY + filetype); keyFT.Create(); wxFileType *ft = CreateFileType(filetype, extWithDot); @@ -686,7 +697,7 @@ bool wxFileTypeImpl::SetCommand(const wxString& cmd, if ( !EnsureExtKeyExists() ) return false; - wxRegKey rkey(wxRegKey::HKCR, GetVerbPath(verb)); + wxRegKey rkey(wxRegKey::HKCU, CLASSES_ROOT_KEY + GetVerbPath(verb)); // TODO: // 1. translate '%s' to '%1' instead of always adding it @@ -704,7 +715,8 @@ bool wxFileTypeImpl::SetDefaultIcon(const wxString& cmd, int index) if ( !EnsureExtKeyExists() ) return false; - wxRegKey rkey(wxRegKey::HKCR, m_strFileType + wxT("\\DefaultIcon")); + wxRegKey rkey(wxRegKey::HKCU, + CLASSES_ROOT_KEY + m_strFileType + wxT("\\DefaultIcon")); return rkey.Create() && rkey.SetValue(wxEmptyString, @@ -719,7 +731,7 @@ bool wxFileTypeImpl::SetDescription (const wxString& desc) if ( !EnsureExtKeyExists() ) return false; - wxRegKey rkey(wxRegKey::HKCR, m_strFileType ); + wxRegKey rkey(wxRegKey::HKCU, CLASSES_ROOT_KEY + m_strFileType ); return rkey.Create() && rkey.SetValue(wxEmptyString, desc); @@ -754,7 +766,7 @@ bool wxFileTypeImpl::RemoveCommand(const wxString& verb) wxCHECK_MSG( !m_ext.empty() && !verb.empty(), false, wxT("RemoveCommand() needs an extension and a verb") ); - wxRegKey rkey(wxRegKey::HKCR, GetVerbPath(verb)); + wxRegKey rkey(wxRegKey::HKCU, CLASSES_ROOT_KEY + GetVerbPath(verb)); // if the key already doesn't exist, it's a success return !rkey.Exists() || rkey.DeleteSelf(); @@ -764,7 +776,7 @@ bool wxFileTypeImpl::RemoveMimeType() { wxCHECK_MSG( !m_ext.empty(), false, wxT("RemoveMimeType() needs extension") ); - wxRegKey rkey(wxRegKey::HKCR, m_ext); + wxRegKey rkey(wxRegKey::HKCU, CLASSES_ROOT_KEY + m_ext); return !rkey.Exists() || rkey.DeleteSelf(); } @@ -773,7 +785,8 @@ bool wxFileTypeImpl::RemoveDefaultIcon() wxCHECK_MSG( !m_ext.empty(), false, wxT("RemoveDefaultIcon() needs extension") ); - wxRegKey rkey (wxRegKey::HKCR, m_strFileType + wxT("\\DefaultIcon")); + wxRegKey rkey (wxRegKey::HKCU, + CLASSES_ROOT_KEY + m_strFileType + wxT("\\DefaultIcon")); return !rkey.Exists() || rkey.DeleteSelf(); } @@ -782,7 +795,7 @@ bool wxFileTypeImpl::RemoveDescription() wxCHECK_MSG( !m_ext.empty(), false, wxT("RemoveDescription() needs extension") ); - wxRegKey rkey (wxRegKey::HKCR, m_strFileType ); + wxRegKey rkey (wxRegKey::HKCU, CLASSES_ROOT_KEY + m_strFileType ); return !rkey.Exists() || rkey.DeleteSelf(); }