Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SHELL32][BROWSEUI] Implement ShellBrowser Apply/Reset default folder settings #6912

Merged
merged 8 commits into from
Jun 4, 2024

Conversation

whindsaks
Copy link
Contributor

@whindsaks whindsaks commented May 20, 2024

Notes:

  • I don't know the format of the Windows "Settings" stream. I made our version incompatible on purpose, it is much smaller and uses a special signature.
  • DefView only understands the Reset part, I'm not sure how/what to store as its settings yet.
  • SHELL_ErrorBox is a template to make it harder to pass in a NULL HWND on purpose.

@github-actions github-actions bot added the shell All PR's related to the shell (and shell extensions) label May 20, 2024
@whindsaks whindsaks added the enhancement For PRs with an enhancement/new feature. label May 20, 2024
@HBelusca
Copy link
Contributor

HBelusca commented May 20, 2024

I don't know the format of the Windows "Settings" stream. I made our version incompatible on purpose, it is much smaller and uses a special signature.

Well that's precisely what we try to avoid doing in ReactOS (making the exported interfaces incompatible on purpose).
Also, it's 99% possible the stream is of the usual IStream format: https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=grep&s=IStream

Maybe one of the other shell guys could help you determine what the actual format is.

dll/win32/shell32/precomp.h Show resolved Hide resolved
dll/win32/shell32/dialogs/view.cpp Outdated Show resolved Hide resolved
FWF_NOSUBFOLDERS | FWF_NOSCROLL | FWF_NOENUMREFRESH | FWF_NOVISIBLE);
}

static void EnsureValid(DEFFOLDERSETTINGS &dfs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this EnsureValid helper meant to be used in more than one place in the future? (So far it's only used in CGlobalFolderSettings::Load )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static void EnsureValid(DEFFOLDERSETTINGS &dfs)
static void EnsureValid(DEFFOLDERSETTINGS& dfs)

Copy link
Contributor Author

@whindsaks whindsaks May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No sure yet. Not in this PR anyway. External components should probably use IGlobalFolderSettings if they want to read but so far I think only CShellBrowser needs this data and I made its SBFOLDERSETTINGS look like the other structs it is currently reading with a .Load method.

dll/win32/browseui/globalfoldersettings.cpp Show resolved Hide resolved
dll/win32/browseui/globalfoldersettings.cpp Show resolved Hide resolved
FWF_NOSUBFOLDERS | FWF_NOSCROLL | FWF_NOENUMREFRESH | FWF_NOVISIBLE);
}

static void EnsureValid(DEFFOLDERSETTINGS &dfs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static void EnsureValid(DEFFOLDERSETTINGS &dfs)
static void EnsureValid(DEFFOLDERSETTINGS& dfs)

dll/win32/browseui/shellbrowser.cpp Show resolved Hide resolved
FIXME("Save current as default\n");
break;
case DVCMDID_RESETDEFAULTFOLDERSETTINGS:
wsprintfW(SubKey, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wsprintfW(SubKey, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
StringCchPrintfW(SubKey, _countof(SubKey), L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? The string will always fit.

@whindsaks
Copy link
Contributor Author

whindsaks commented May 20, 2024

Well that's precisely what we try to avoid doing in ReactOS (making the exported interfaces incompatible on purpose).

I see two options:

A) Try to make it Windows compatible. The problem with this is that the registry value is 40 bytes and I'm not going to be able to figure out all the bytes. Making it 40 bytes and let Windows load garbage/unknown data is not a great option. This value is also 4 bytes smaller on 2000 than on XP and there is no size of it stored inside like a classic "cbSize". Every time I Apply/Reset, a member is incremented as some sort of counter, I assume so Windows knows if it has loaded the up to date version or a stale one.

B) Be incompatible on purpose. This is not a problem. The only scenario where something goes "wrong" is when you first use ROS browseui and then Windows browseui on the same system (or vice versa). In this case it will fail to read the users default preference and you will end up with the "system defaults". When Windows reads our "corrupt" value, I believe it ends up in either VIEW_PRIORITY_STALECACHEHIT or VIEW_PRIORITY_CACHEMISS mode.

Just being able to load/save this value is a big win for ROS, it means you can set a different view mode as the default for all FS folders if you don't like ListView report mode.

Also, it's 99% possible the stream is of the usual IStream format

IStream is not a format! That is like saying "file X is probably of CreateFile format". It is not an OLE stream with the class CLSID stored first.

whindsaks added a commit to whindsaks/reactos that referenced this pull request May 20, 2024
Save/Restore the state of the ShellBrowser toolbar/addressbar/statusbar. This is related to CORE-9094.

Windows shares the state of the Go button and the locked state between Explorer and Internet Explorer but the bar states are not shared.

Notes:
 - This is not really related to PR reactos#6912. This PR deals with the ShellBrowser state that is saved every time you change the rebar while the other is only about the default folder settings.
whindsaks added a commit to whindsaks/reactos that referenced this pull request May 21, 2024
Save/Restore the state of the ShellBrowser toolbar/addressbar/statusbar. This is related to CORE-9094.

Windows shares the state of the Go button and the locked state between Explorer and Internet Explorer but the bar states are not shared.

Notes:
 - This is not really related to PR reactos#6912. This PR deals with the ShellBrowser state that is saved every time you change the rebar while the other is only about the default folder settings.
whindsaks added a commit to whindsaks/reactos that referenced this pull request May 21, 2024
Save/Restore the state of the ShellBrowser toolbar/addressbar/statusbar. This is related to CORE-9094.

Windows shares the state of the Go button and the locked state between Explorer and Internet Explorer but the bar states are not shared.

Notes:
 - This is not really related to PR reactos#6912. This PR deals with the ShellBrowser state that is saved every time you change the rebar while the other is only about the default folder settings.
whindsaks added a commit to whindsaks/reactos that referenced this pull request May 21, 2024
Save/Restore the state of the ShellBrowser toolbar/addressbar/statusbar. This is related to CORE-9094.

Windows shares the state of the Go button and the locked state between Explorer and Internet Explorer but the bar states are not shared.

Notes:
 - This is not really related to PR reactos#6912. This PR deals with the ShellBrowser state that is saved every time you change the rebar while the other is only about the default folder settings.
@JoachimHenze
Copy link
Contributor

JoachimHenze commented May 23, 2024

I don't know the format of the Windows "Settings" stream. I made our version incompatible on purpose, it is much smaller and uses a special signature.

Well that's precisely what we try to avoid doing in ReactOS (making the exported interfaces incompatible on purpose). Also, it's 99% possible the stream is of the usual IStream format: https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=grep&s=IStream

Maybe one of the other shell guys could help you determine what the actual format is.

I do second @HBelusca . We do want to be compatible.
There are 3rd-party-tools for tweaking that stuff, and if we implemented incompatible, ros would be incompatible to those tools. Not good.

Many ShellBags-feature-reversing/documentation can be found in the description of the following ticket. https://jira.reactos.org/browse/CORE-9283
and the following link will turn out to be exceptionally useful in your context:
https://superuser.com/questions/253249/windows-registry-entries-for-default-explorer-view/253267

@tkreuzer
Copy link
Contributor

I don't know the format of the Windows "Settings" stream. I made our version incompatible on purpose, it is much smaller and uses a special signature.

Well that's precisely what we try to avoid doing in ReactOS (making the exported interfaces incompatible on purpose). Also, it's 99% possible the stream is of the usual IStream format: https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=grep&s=IStream

What we should also try to avoid, is painstakingly trying to be "binary compatible" with some internal interface that some version of Windows used to have 30 years ago and that not a single application outside of internal MS tools ever used.
That would prevent code like this in our code base.

    if ((param->dwFlags & 0x80000) == 0 && param->offset80 != NULL)
        ILFree(param->offset80);

@learn-more
Copy link
Member

learn-more commented May 24, 2024

As I said on mattermost:
If the choice is made to make this incompatible with MS's interface (which should be fine I guess), reflect this in the name so that it is clearly not compatible. (f.e. "ReactOS-Streams", or drop it under the ReactOS subkey instead of Microsoft)

@whindsaks
Copy link
Contributor Author

I can use a different name for the value but it should still be in the same subkey so that anyone nuking the key used by Windows also nukes us.

@whindsaks whindsaks requested a review from HBelusca May 30, 2024 15:55

static void InitializeDefaults(DEFFOLDERSETTINGS &dfs)
{
C_ASSERT(FIELD_OFFSET(DEFFOLDERSETTINGS, FolderSettings) == 4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this 4 corresponding to a sizeof(DWORD)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what the Windows struct has as the first member, a DWORD or bit-field or two WORDs etc. I just know the ListView view mode is 4 bytes into the struct.

dll/win32/browseui/shellbrowser.cpp Outdated Show resolved Hide resolved
sdk/include/reactos/shlobj_undoc.h Outdated Show resolved Hide resolved
dll/win32/browseui/globalfoldersettings.cpp Outdated Show resolved Hide resolved
SHELL_ErrorBoxHelper(HWND hwndOwner, UINT Error)
{
WCHAR buf[400];
UINT cch, u32_errstr = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 ? What's this error corresponding to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the resource id for the string that says "Error" (and only used when there is no other option if FormatMessage fails). This id is the same on Windows.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do UINT cch, u32_errstr = IDS_ERROR;
Then at the top of the file you can add:

#ifndef IDS_ERROR
#define IDS_ERROR 2
#endif // IDS_ERROR

I'm guessing we call it IDS_ERROR in our resources, but double check that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is shellutils.h, I don't want to pollute globally with macros that may cause conflicts. I will however use a more descriptive name that includes "IDS_ERROR".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will not cause an issue. It's encouraged in fact.
From Microsoft's MSVC documentation:

"However, you can redefine the macro with exactly the same definition. Thus, the same definition may appear more than once in a program."

From GNU's GCC documentation:

"If the new definition is effectively the same, the redefinition is silently ignored. This allows, for instance, two different headers to define a common macro. The preprocessor will only complain if the definitions do not match."

https://learn.microsoft.com/en-us/cpp/preprocessor/macros-c-cpp?view=msvc-170
https://gcc.gnu.org/onlinedocs/cpp/Undefining-and-Redefining-Macros.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe MSI shouldn't redefine IDS_ERROR? MSI is winesynced, it would never use shellutils.h. And if you did end up forking it to use shellutils.h, you should give that resource a different name; it's poorly named.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are fair points.

I still don't understand how a define is better than something contained inside the function in question.

No matter how we look at it, I'm relying on a user32 implementation detail and that is slightly hacky. But again, it's the best I can do...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The define points to why we set it to that value; it corresponds with IDS_ERROR resource in user32. You can add a comment to the top of the define block that says // From user32 resource IDS_ERROR or something similar. It may be similarly hacky, but it is more apparent that it is deliberate this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about an enum (inside function) with a comment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's better, I would dismiss my rejection if you did that. But I still think the define is a better way to do this.

if (!cch)
{
cch = LoadStringW(LoadLibraryW(L"USER32"), u32_errstr, buf, _countof(buf));
wsprintfW(buf + cch, L"\n\n%#x (%d)", Error, Error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a candidate for StringCchPrintfW here, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is shellutils.h, I don't want to pull in anything. Besides, the string "Error" in any language + two 32-bit formatted numbers is going to fit in WCHAR buf[400];.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? It's not just about fitting into your buffer. Using unsafe buffer functions can lead to security issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not just about fitting into your buffer. Using unsafe buffer functions can lead to security issues.

It is only about fitting it into the buffer. We know the format string is safe because we control it here.

@@ -98,7 +98,7 @@ static inline UINT
SHELL_ErrorBoxHelper(HWND hwndOwner, UINT Error)
{
WCHAR buf[400];
UINT cch, u32_errstr = 2;
UINT cch, user32_IDS_ERROR = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a magic constant. Please also see my other comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would it be less magic if it was a define?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use a newly-added parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use a newly-added parameter

I have no idea what this even means in this context

Copy link
Contributor

@HBelusca HBelusca May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this needs to be reused elsewhere, adding a #define USER32_ERROR 2 in our sdk/include/reactos/undocuser.h would make sense (+ a comment about what this define is for).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this needs to be reused elsewhere

That is a big if.

Kernel32 and advapi32 should not be displaying a lot of message boxes. Other components higher up?

Cross that bridge when we get there?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed 👍

dll/win32/shell32/CFolderOptions.cpp Outdated Show resolved Hide resolved
dll/win32/shell32/dialogs/view.cpp Outdated Show resolved Hide resolved
dll/win32/shell32/dialogs/view.cpp Outdated Show resolved Hide resolved
@@ -98,7 +98,7 @@ static inline UINT
SHELL_ErrorBoxHelper(HWND hwndOwner, UINT Error)
{
WCHAR buf[400];
UINT cch, u32_errstr = 2;
UINT cch, user32_IDS_ERROR = 2;
Copy link
Contributor

@HBelusca HBelusca May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this needs to be reused elsewhere, adding a #define USER32_ERROR 2 in our sdk/include/reactos/undocuser.h would make sense (+ a comment about what this define is for).

@whindsaks whindsaks merged commit c97c1ad into reactos:master Jun 4, 2024
34 checks passed
@whindsaks whindsaks deleted the ShFolderoptsSetDefSettings branch June 4, 2024 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement For PRs with an enhancement/new feature. shell All PR's related to the shell (and shell extensions)
Projects
None yet
8 participants