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

Console icon? #11759

Open
vefatica opened this issue Nov 14, 2021 · 31 comments
Open

Console icon? #11759

vefatica opened this issue Nov 14, 2021 · 31 comments
Labels
Area-Windowing Window frame, quake mode, tearout Help Wanted We encourage anyone to jump in on these. Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-3 A description (P3) Product-Conhost For issues in the Console codebase
Milestone

Comments

@vefatica
Copy link

I don't know if this has anything to do with conhost, but I can't think of a better place to ask "What's going on here?".

If I give this command to CMD, start "C:\WINDOWS\system32\notepad.EXE" cmd, I get a new instance of CMD in a console with notepad's icon. Why does specifying a title affect the icon?

image

@ghost ghost added Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Tag-Fix Doesn't match tag requirements labels Nov 14, 2021
@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Nov 15, 2021

Seems to be a deliberate feature:

// search for the application along the path so that we can load its icons (if we didn't find one explicitly in
// the shortcut)
const DWORD dwLinkLen = SearchPathW(pwszCurrDir, pwszAppName, nullptr, ARRAYSIZE(wszIconLocation), wszIconLocation, nullptr);
// If we cannot find the application in the path, then try to fall back and see if the window title is a valid path and use that.
if (dwLinkLen <= 0 || dwLinkLen > sizeof(wszIconLocation))
{
if (PathFileExistsW(pwszTitle) && (wcslen(pwszTitle) < sizeof(wszIconLocation)))
{
StringCchCopyW(wszIconLocation, ARRAYSIZE(wszIconLocation), pwszTitle);
}
else
{
// If all else fails, just stick the app name into the path and try to resolve just the app name.
StringCchCopyW(wszIconLocation, ARRAYSIZE(wszIconLocation), pwszAppName);
}
}

The logic there seems somewhat odd though; the comment talks about searching "along the path" but the code calls SearchPathW with only pwszCurrDir as the path.

Plus it uses sizeof(wszIconLocation) in two comparisons where ARRAYSIZE would be correct. Filed as #11761.

@vefatica
Copy link
Author

I can imagine trying to get an icon from STARTUPINFO::lpTitle if STARTUPINFO::dwFlags contains STARTF_TITLEISLINKNAME (or maybe even STARTF_TITLEISAPPID). Otherwise it makes no sense to me. I wouldn't call it a feature.

Of STARUPINFO::lpTitle, the docs say

For console processes, this is the title displayed in the title bar if a new console window is created.

That hardly tells the whole story.

@elsaco
Copy link

elsaco commented Nov 15, 2021

@vefatica if you remove the extension, the icon won't be displayed:

cmd_icon_test

I don't have the knowledge why it behaves like that!

@vefatica
Copy link
Author

In fact, the code posted by @KalleOlaviNiemitalo is done outside the if (pLinkSettings->GetStartupFlags() & STARTF_TITLEISLINKNAME) block. It doesn't seem right. It's just a title.

@KalleOlaviNiemitalo
Copy link

I assume it is a counterpart to some code in cmd.exe or kernelbase.dll that uses the file name as the window title. Which then makes me wonder if the "Administrator:" prefix can make this not find the correct icon. Perhaps not, because it still has pwszAppName.

@vefatica
Copy link
Author

Back to my first post ... setting the icon to that of notepad is just plain wrong. It's a title. It is not a specification of where to look for an icon.

@zadjii-msft
Copy link
Member

Why does specifying a title affect the icon?

honestly, I'm not sure anyone on the team is going to have an answer as to why. This is gonna be one of those "features" of the console that's simply always existed, since before we formally owned the console codebase. I don't think we'd really have an answer other than "Huh, yep, it sure does seem to do that".

I'd be extremely reluctant to change that, undoutably someone somewhere is relying on that behavior to set the icon of their console windows for some reason.

@zadjii-msft zadjii-msft added Issue-Question For questions or discussion Product-Conhost For issues in the Console codebase Resolution-Answered Related to questions that have been answered Needs-Discussion Something that requires a team discussion before we can proceed labels Nov 15, 2021
@vefatica
Copy link
Author

since before we formally owned the console codebase

Maybe so, but it doesn't happen in Windows 7. I wish I had more Windows versions to test on.

undoutably someone somewhere is relying on that behavior to set the icon of their console windows for some reason.

That's humorous.

@zadjii-msft
Copy link
Member

undoutably someone somewhere is relying on that behavior to set the icon of their console windows for some reason.

That's humorous.

When you've been here as long as us, you can be 100% sure that someone, somewhere, is relying on all sorts of wacky bugs in your codebase 😄

@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Nov 15, 2021

Changing this seems unlikely to halt a production line though. Unless they are then calling GetConsoleWindow and extracting the icon from there. Or they are using an UNC path as the title and waking up a network device that way.

On the other hand, the current strange use of the title shouldn't really have a business impact, either.

@DHowett
Copy link
Member

DHowett commented Nov 15, 2021

It is too early for me to be reading code -- this is all wrong.

Kept for posterity

It looks like this was an "intent error" (like, we may not have understood the original code.) when we brought the v2 console online!

The v1 console loads the icon from:

  1. The title, treated as a link, and resolved via the shell APIs
  2. Resolving the App Name ("base DLL name", likely the PE file that caused console allocation) by SearchPath against the current directory, and using that as the icon source if it exists
  3. The app name directly (if it couldn't be found in the search path)

The v2 console does the exact same thing, except in step 2 it overwrites the resolved app name with the title. Oops.

Here's the v2 code-

const DWORD dwLinkLen = SearchPathW(pwszCurrDir, pwszAppName, nullptr, ARRAYSIZE(wszIconLocation), wszIconLocation, nullptr);
// If we cannot find the application in the path, then try to fall back and see if the window title is a valid path and use that.
if (dwLinkLen <= 0 || dwLinkLen > sizeof(wszIconLocation))
{
if (PathFileExistsW(pwszTitle) && (wcslen(pwszTitle) < sizeof(wszIconLocation)))
{
StringCchCopyW(wszIconLocation, ARRAYSIZE(wszIconLocation), pwszTitle);
}

Note the copy from title into IconLocation just after IconLocation was filled and validated, on line 181.

Here's the v1 code-

        dwLinkLen = RtlDosSearchPath_U(CurDir,
                                       AppName,
                                       NULL,
                                       sizeof(LinkName),
                                       LinkName,
                                       NULL);
        if (dwLinkLen > 0 && dwLinkLen < sizeof(LinkName)) {
            pszIconLocation = LinkName; // there is no equivalent in v2-- the else block is omitted for linkLen being valid
        } else {
            pszIconLocation = AppName;

@eryksun
Copy link

eryksun commented Nov 15, 2021

Maybe so, but it doesn't happen in Windows 7. I wish I had more Windows versions to test on.

This bug only occurs when conhost.exe is the default terminal application, and it uses the V2 implementation. It doesn't occur with V1, i.e. when "HKCU\Console\ForceV2" is 0.

I don't know why the fallback to the STARTUPINFO title was added in the V2 console. Yes, CreateProcessW() uses the application path as the default title, but the API has no flag to indicate the title is the executable path, i.e. something like STARTF_TITLEISEXENAME.

Anyway, the strange fallback code is not actually the bug here. This looks to be a bug in Windows. ConsoleAllocateConsole() is being called with a relative path in p->AppName, but it's supposed to be the fully-qualified path of the executable. It is highly unlikely that the SearchPathW(pwszCurrDir, pwszAppName, ...) call will resolve the relative name against the application's working directory. It used to work because pwszAppName was already a fully qualified path instead of a relative path.

@eryksun
Copy link

eryksun commented Nov 15, 2021

ConsoleAllocateConsole() is being called with a relative path in p->AppName

I checked the initialization of kernelbase!ExeNameBuffer. It's copied from the base module name in the PEB loader data. It's just this base name that gets sent in the extended-attribute buffer of the NtCreateFile() call in kernelbase!ConsoleCreateConnectionObject(). This seems intentional, so I suppose at this level the app name was never meant to be used to actually find the application executable. It's probably meant just for just the command-line editor's per-app history and input aliases. Thus the SearchPathW(pwszCurrDir, pwszAppName, ...) call has always been strange. An application can have any arbitrary initial working directory. It has nothing at all to do with the executable path. For example, passing /D "C:\Windows\System32" to CMD's start command allows the console's SearchPathW() call to resolve the correct path of the executable, and thus to load the correct icon.

@DHowett
Copy link
Member

DHowett commented Nov 15, 2021

@vefatica just a question: how do you find this stuff? How did you find this one?

@vefatica
Copy link
Author

vefatica commented Nov 15, 2021

@vefatica just a question: how do you find this stuff? How did you find this one?

I can't reconstruct the whole route but it started with my investigating #11704. That led me to mess around with the START command. It is also vaguely connected to a TCC issue wherein the initialization directive "UpdateTitle=No" is not always honored.

@malxau
Copy link
Contributor

malxau commented Nov 15, 2021

The way I read this, szAppName is probably fully qualified. If it's not fully qualified, SearchPathW is used to resolve it to something fully qualified. It's intentionally not searching the PATH because the real purpose is relative to absolute name resolution.

The interesting part in this issue isn't the SearchPathW, it's the fallback to use the title as a path. That code appears to have been added in 2017, before the Github release, but after the code was maintained outside the Windows tree, so I don't have visibility into why it was done. Since it's 2017 I don't think it's true to say that this is ancient behavior that the current console team inherited, or that it will be widely depended on. It's very unclear to me why this would be useful - before that change, the code tried SearchPath, and if that failed, fell back to szAppName, which I understand. Falling back to the title, particularly in preference to szAppName, seems like a curious choice.

@DHowett
Copy link
Member

DHowett commented Nov 15, 2021

Here you go!

Merged PR 624729: WSL distros don't show icon when running

Make the console host try using the window title as a search path for the icon.
Fix up the window message loop icon patch to directly use the HWND
(it was broken after the onecore refactor).

Cherry picked from Pull request !615061.

Related work items: MSFT-11750393


If I had to guess: something about app execution aliases made this really wig out. This would be especially true if the AppName/AppPath was to the app execution alias (though I suspect it would not be, given that this data originates from within conclnt after the process has already started.)

@DHowett
Copy link
Member

DHowett commented Nov 15, 2021

That bug states:

Title: Console doesn't pick up the icon of Distro Launcher exe's

Body:
The exe has an icon, but for some reason, when launched from the start menu, the console can't find the appropriate icon.

It is quite terse. 😁

@malxau
Copy link
Contributor

malxau commented Nov 15, 2021

Does the current start menu use STARTF_TITLEISLINKNAME? If so, the title value is (re)initialized from the shortcut, and I think my later change in #6277 will fix it, by taking the icon from the shortcut target. If it doesn't, then, uhh, sad.

@DHowett
Copy link
Member

DHowett commented Nov 15, 2021

If it doesn't, then, uhh, sad.

Alas. I think this applied to package launches, which don't even have a link and likely don't pass STARTF_TITLEISLINKNAME. There's some annoyingly complicated machinery that launches a package with package identity that wasn't part of CreateProcess until late Windows 11... and it's been the source of no end of trouble.

@eryksun
Copy link

eryksun commented Nov 16, 2021

The way I read this, szAppName is probably fully qualified.

At first, it looked like it was supposed to be, but it would have been obvious that it's not had I checked the definition of CONSOLE_API_CONNECTINFO.AppName, which is limited to just 128 characters instead of MAX_PATH + 1.

The app name was intended for the console's per-executable command-line history and aliases. Code was added some versions later to read the icon on the host side by resolving the base executable name against the client's current working directory, i.e. SearchPathW(pwszCurrDir, pwszAppName, ...) (or equivalent NT RtlDosSearchPath_U call). That's nonsense. It could use a handle to the client process to query the executable path with QueryFullProcessImageNameW(). Or update the client-side code to send the full path of the executable instead of the base name.

It's very unclear to me why this would be useful - before that change, the code tried SearchPath, and if that failed, fell back to szAppName, which I understand. Falling back to the title, particularly in preference to szAppName, seems like a curious choice.

Falling back to just szAppName is not understandable to me. There's no reason to assume the base executable name will be found by the implicit SearchPathW(NULL, ...) call in ExtractIconExW(). For example, if I remove all Python installation directories from PATH, then start "spam" "C:\Program Files\Python38\python.exe" creates a console that has the default icon instead of Python's icon. If I add the Python 3.8 installation directory to PATH, then the ExtractIconExW() call in the console host finds "python.exe", and the console obtains the correct icon. But actually it's going to use any "python.exe" that it finds in PATH. No one should be satisfied with that behavior.

Using the title sometimes works because CreateProcessW() fills in the fully qualified executable path as the default lpTitle. For example, the console created by start "" "C:\Program Files\Python38\python.exe" always finds the correct icon. But this is unreliable because there is no STARTF_TITLEISEXENAME flag that indicates to the console that a path in the title is intentionally the executable path.

@elsaco

This comment has been minimized.

@vefatica

This comment has been minimized.

@DHowett
Copy link
Member

DHowett commented Nov 16, 2021

It'll be best for us to keep this issue on its original: conhost's erroneous selection of icon based on the title. 😄

All discussion of windows terminal as the default terminal will need to be moved to a new thread where we can triage it independently.

@zadjii-msft zadjii-msft added Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-3 A description (P3) Area-Windowing Window frame, quake mode, tearout and removed Issue-Question For questions or discussion Resolution-Answered Related to questions that have been answered Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Discussion Something that requires a team discussion before we can proceed labels Dec 8, 2021
@zadjii-msft zadjii-msft added this to the Console Backlog milestone Dec 8, 2021
@sredna
Copy link

sredna commented Dec 9, 2021

The funny thing is that way back when we had SEE_MASK_ICON for ShellExecuteEx and I believe it was used to change the console icon but that has not worked for a very long time, NT4 was maybe the last time it actually did anything. Going back even further, Progman in 16-bit Windows had a undocumented way of assigning icons (STARTUPINFO still has a member for it).

I wish I had more Windows versions to test on.

Windows 8.1 does not use the icon from the title path.

@zadjii-msft zadjii-msft modified the milestones: Console Backlog, 22H1 Jan 4, 2022
@zadjii-msft zadjii-msft modified the milestones: 22H1, Terminal v1.14 Feb 2, 2022
@zadjii-msft zadjii-msft added the Help Wanted We encourage anyone to jump in on these. label Mar 10, 2022
@ghost ghost removed the Needs-Tag-Fix Doesn't match tag requirements label Mar 10, 2022
@zadjii-msft zadjii-msft modified the milestones: Terminal v1.14, Backlog Mar 10, 2022
@jredfox

This comment was marked as off-topic.

@zadjii-msft
Copy link
Member

Hmmm. Okay, so breaking this thread down:

  • The console currently uses the "title" as a fallback location to load the icon from, which can be weird.
  • The console does that as of 2017, seemingly to help support WSL distros.
  • This creates a new bug where start "C:\WINDOWS\system32\notepad.EXE" cmd now loads notepad's icon, because we're using the title as a potential icon search path.
  • (TODO) I don't know off hand if there's a better way to support the icons in the WSL distro case (app execution aliases)

Did I miss anything? I'm down in these weeds as a part of #9458, so this is a prime opportunity to resolve this, if resolvable at all.

@LinHeLurking

This comment was marked as off-topic.

@DHowett

This comment was marked as off-topic.

@LinHeLurking
Copy link

LinHeLurking commented Oct 21, 2022

If I open Windows Terminal first then select some predefined profile to open my WSL, it shows correct icon. But if I open corresponding distro xxx.exe, it launches in Windows Terminal without icon, like this:
image
And the profile command line is just set as the distro exe. image
I've tested 2 distros and both of them show wrong icon. Maybe this is related with Windows Terminal?

I think this is (unfortunately) unrelated to the current issue. However, I'll suggest checking out the icon field in your opensuse-tumbleweed profile settings.

image

Apologies first if I misunderstanding current issue 😢

I've set the Icon field. If I start Windows Terminal first then it shows correct icon like this:
image

But if I start Ubuntu directly from start menu, it shows wrong icon:
image

@NewtonChutney
Copy link

NewtonChutney commented Jan 5, 2024

* [ ]  (**TODO**) I don't know off hand if there's a better way to support the icons in the WSL distro case (app execution aliases)

Does WT use openconsole/conhost under the hood? If not, we could revert the behaviour regressed just for WSL support from conhost/openconsole? As anyone on WSL would also be on WT imo..

I've set the Icon field. If I start Windows Terminal first then it shows correct icon like this: image

But if I start Ubuntu directly from start menu, it shows wrong icon: image

@LinHeLurking, this is related to: #12582 #12755

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Windowing Window frame, quake mode, tearout Help Wanted We encourage anyone to jump in on these. Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-3 A description (P3) Product-Conhost For issues in the Console codebase
Projects
None yet
Development

No branches or pull requests