Runtime: Improve UI of error dialog for missing framework/runtime

Created on 21 Oct 2019  ·  21Comments  ·  Source: dotnet/runtime

For 3.1, a basic error dialog was added to inform the user about a missing runtime/framework (#8222). In 5.0, we should investigate if we can make it nicer / more user-friendly through Win32 Task Dialogs.

See https://github.com/dotnet/core-setup/issues/8222#issuecomment-539669978

area-Host

Most helpful comment

FYI: On improving the dialog in the future, the message on this dialog should also be improved as well. It should contain the exact version and bitness of the .NET Core that is missing. If somehow the message fails when a user clicks on "Yes" (see https://github.com/dotnet/core-setup/issues/8902#issuecomment-567844216) or this user decided to download it later / somehow else, he still might up ending picking the wrong version. Because currently the apphost knows what exactly is missing but don't tell it the user. And also because user may report issues to the developers about "Hey I got .NET Core installed but your app says its missing. Please fix" and it turns out he is missing a specific version.

Currently it only says:

"To run this application, you must install .NET Core. Would you like to download it now?"

Expected it to say in the future:

"To run this application, you must install .NET Core Desktop Runtime 3.1 (x64). Would you like to download it now?"

And to give it a more trustful experience maybe also (might be obsolete if the dialog becomes a task dialog and reveal its link like in https://github.com/dotnet/core-setup/issues/8222#issuecomment-539669978):

"To run this application, you must install .NET Core Desktop Runtime 3.1 (x64). Would you like to visit dotnet.microsoft.com and download it now ?"

All 21 comments

I have tested this feature in 3.1 and the message box directs to the page pictured below.

I think this should be presented better. If I were a novice computer user or someone in a hurry, I may accidentally download the wrong thing, like the .NET Framework runtime. I think the message box should direct users to a page that is more clear and provides a single, easy to see download button.
Or, alternatively, have the message box launch a direct link to download the installer in the browser.

Alternatively, just link to this page - I think it is better and more clear to people: https://dotnet.microsoft.com/download/dotnet-core/current/runtime

2019-12-19_17-53-17

@EatonZ this is still being worked on as per https://github.com/dotnet/core-setup/issues/8902. @rowanmiller @richlander may be able to provide more details.

@vitek-karas Thanks, looks like I'm not alone (:

FYI: On improving the dialog in the future, the message on this dialog should also be improved as well. It should contain the exact version and bitness of the .NET Core that is missing. If somehow the message fails when a user clicks on "Yes" (see https://github.com/dotnet/core-setup/issues/8902#issuecomment-567844216) or this user decided to download it later / somehow else, he still might up ending picking the wrong version. Because currently the apphost knows what exactly is missing but don't tell it the user. And also because user may report issues to the developers about "Hey I got .NET Core installed but your app says its missing. Please fix" and it turns out he is missing a specific version.

Currently it only says:

"To run this application, you must install .NET Core. Would you like to download it now?"

Expected it to say in the future:

"To run this application, you must install .NET Core Desktop Runtime 3.1 (x64). Would you like to download it now?"

And to give it a more trustful experience maybe also (might be obsolete if the dialog becomes a task dialog and reveal its link like in https://github.com/dotnet/core-setup/issues/8222#issuecomment-539669978):

"To run this application, you must install .NET Core Desktop Runtime 3.1 (x64). Would you like to visit dotnet.microsoft.com and download it now ?"

Hi,

In 5.0, we should investigate if we can make it nicer / more user-friendly through Win32 Task Dialogs.

See https://github.com/dotnet/core-setup/issues/8222#issuecomment-539669978

It might be a bit "dirty", but would it be possible to use a manifest of an existing Windows binary, e.g. control.exe, to load common controls v6 and show the task dialog?
That file is available in the System32 directory (so it exists for each architecture including ARM64), and is available also e.g. on Windows Server 2008 R2 in "Server Core" mode where other GUI EXEs like explorer.exe or winver.exe are not available.

It contains a manifest resource (with ID 1) that includes a dependency on common controls v6 (this one is from the x86 variant):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    name="Microsoft.Windows.Shell.control"
    processorArchitecture="x86"
    version="5.1.0.0"
    type="win32"/>
<description>Windows Shell</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
        <requestedPrivileges>
            <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
        </requestedPrivileges>
    </security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware  xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
</application>
</assembly>

The manifest could be used with the activation context APIs to load common controls v6 and then call TaskDialogIndirect, without the need to deploy an extra binary or to have any resources in the current apphost EXE.

For example, with a code like the following, showing the task dialog works for me without using an app.mainfest (note: I'm not a C++ programmer, so the code might look a bit ugly):

HRESULT TaskDialogIndirectWithControlExeManifest(_In_ const TASKDIALOGCONFIG* pTaskConfig, _Out_opt_ int* pnButton, _Out_opt_ int* pnRadioButton, _Out_opt_ BOOL* pfVerificationFlagChecked) {
    typedef HRESULT (WINAPI* PFN)(_In_ const TASKDIALOGCONFIG* pTaskConfig, _Out_opt_ int* pnButton, _Out_opt_ int* pnRadioButton, _Out_opt_ BOOL* pfVerificationFlagChecked);

    HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
    PFN taskDialogIndirectPtr = (PFN)GetProcAddress(hComctl, "TaskDialogIndirect");

    if (taskDialogIndirectPtr != nullptr) {
        return taskDialogIndirectPtr(pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
    }
    else {
        // Try to use the manifest from control.exe to load common controls v6.
        ACTCTXW actCtx = { 0 };
        actCtx.cbSize = sizeof(ACTCTXW);
        // TODO: Get actual system directory with kernel32's GetSystemDirectoryW()
        actCtx.lpSource = L"C:\\Windows\\System32\\control.exe";
        actCtx.lpResourceName = MAKEINTRESOURCEW(1);
        actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;

        // TODO: Call ReleaseActCtx() once the context is no longer needed
        HANDLE ctx = CreateActCtxW(&actCtx);
        if (ctx == INVALID_HANDLE_VALUE)
            return E_FAIL;

        ULONG_PTR cookie;
        if (ActivateActCtx(ctx, &cookie) == FALSE)
            return E_FAIL;

        // Try to get TaskDialogIndirect again.
        hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
        taskDialogIndirectPtr = (PFN)GetProcAddress(hComctl, "TaskDialogIndirect");

        HRESULT result;
        if (taskDialogIndirectPtr != nullptr)
            result = taskDialogIndirectPtr(pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
        else
            result = E_FAIL;

        DeactivateActCtx(0, cookie);

        return result;
    }
}

A downside is that this would rely on control.exe being present and embedding a manifest (with resource ID 1) that uses common controls v6, but there could be a fallback to MessageBox if TaskDialogIndirect is still not available for some reason.
(Additionally, I think many other components already depend on control.exe being present (for example, sconfig.vbs uses control timedate.cpl to show the date/time settings, and there is control userpasswords2 etc.), so I think it is safe to assume control.exe won't go away for a long enough time.)

Thanks a lot for the suggestion. I think this could be made to work even without the dependency on control.exe. I tried a simple console app which had no default manifest:

  • By default the task dialog can't be imported and it doesn't work (as expected)
  • I added an RT_MANIFEST resource with ID 101 with the necessary assembly dependency to Common-Controls
  • I tried again without any special code- still doesn't work - this is important, adding a "random" manifest resource does not enforce a manifest on the app
  • I then added the code you have above pointing to the exe of the app itself and loading resource id 101 and now the dialog works

So the solution might be to add a custom manifest resource to apphost with some "weird" ID (probably even a string and not a number) which would enable Common-Controls. This in itself should not have any effect on apps as Windows doesn't seem to load that manifest if none other is available. I also tried adding a normal manifest to the app - that one is picked up over the hidden one. And then use the code above to activate that manifest for the duration of the call to TaskDialog. We will have to test how big is the size impact of such change (apphost.exe is very size sensitive), but it looks promising.

验证正确,远比提示更重要。在.NetCore3.1WinForm应用中,频繁出现此提示,然而它验证又不正确。导致许多最终用户被迫中止使用。这个问题,很严重。
并且,它还经常的发生。还有的时候发生,一台电脑上的两个应用,一个应用出现此提示,而另一个应用则可正常使用。
最好3.1就能改掉它。损伤太大!!!

好多天以前,我就提出了这个问题。似乎它应该从这里提交更合适
https://github.com/dotnet/winforms/issues/3028

Verification is more important than prompt. In the application of. Netcore3.1 WinForm, this prompt appears constantly, but its verification is not correct. As a result, many end users were forced to suspend their use. This problem is very serious. And

It also happens frequently. There are also times when two applications on one computer appear this prompt, and the other can be used normally.

3.1 is the best way to get rid of it. Damage is too big!!!

I asked this question many days ago. It seems more appropriate that it should be submitted from here

@199621616 Thanks for the report, but it's really hard to tell what's wrong. Per the issue in winforms you have a single-file application. Is it also self-contained? (Easiest way to tell is by size, self-contained apps will be much larger - cca 70-100 MB). How do you build/publish the app (the command line and any publish related settings in the project file would help).

We haven't seen reports like yours yet (where it would work and then suddenly stop working). If you can get this to repro, can you please try this from a command line (on a machine where it fails):

set COREHOST_TRACE=1
set COREHOST_TRACEFILE=host_trace.txt
<run the application>

This should produce the file host_trace.txt which will contained detailed information about what the application tried to do and why it failed. Please note that the file contains information about the machine, potentially the user running it and some local paths and names. If you're comfortable please share this file with us. If not, you can try to see what's wrong by looking into the file.

@vitek-karas 感谢这么快看到的回复。因为这个提示的出现过于频繁,并且一旦出现最终用户就不能继续使用,我们已调整部署模式,由原来的框架依赖调整为独立。这样调整后未见反映出现此提示,但是出现了新的问题,就是刚刚部署完成时应用可以正常使用,但不知道会在什么情况下出现双击桌面图标应用不启动,无任何反映。只能重新安装新的安装包才可以使用应用。
两种部署模式,都是生成单个文件。我们会记录下你所说的这个生成host_trace.txt报告的方式,但这需要修改部署模式再部署到用户使用环境中再出现此情况时,才可能得到它。需要多长时间才能得到它我们不确定。

@Vitek Karas thanks for the quick response. Because this prompt appears too frequently, and once it appears, the end user cannot continue to use it. We have adjusted the deployment mode from the original framework dependency to independence. After such adjustment, this prompt does not appear, but there is a new problem, that is, when the deployment is just completed, the application can be used normally, but under what circumstances, the double-click desktop icon application will not start without any reflection. The app can only be used by reinstalling a new installation package.

Both deployment modes generate a single file. We will record what you said about how to generate the host_trace.txt report, but we need to modify the deployment mode and deploy it to the user's environment before we can get it. We're not sure how long it will take to get it.

项目情况及依赖项:

image

原来的发布方式:
image

调整后的发布方式:
image

@vitek卡拉斯 你好,我们重新改用框架依赖部署模式后今天有用户反映再次出现找不到.NetCore3.1提示。我们远程连接到客户电脑分别执行了set COREHOST_TRACE=1和
set COREHOST_TRACEFILE=host_trace.txt。执行后再次启动应用,没有找到host_trace.txt文件。
改用管理员身份执行这两个命令,也没有生成此文件。
我们改用新版本(同样是框架依赖、生成单个文件),可以正常启动

Hello, we changed to framework dependency deployment mode again. Today, some users reported that they could not find. NETCORE 3.1 again. We remotely connected to the client computer and executed set corehost ﹐ trace = 1 and
set COREHOST_TRACEFILE=host_trace.txt。 Start the application again after execution. The file host_trace.txt was not found.
The two commands were executed as administrators instead, and the file was not generated.
We use the new version (also framework dependency, single file generation), which can be started normally

Maybe I should have been clearer, the env. variable setting and running the app must happen from the same command line window, so that the env. variables are seen by the application. The file will be created in the "current directory" as seen by the application when started. So assuming your application lives in C:\MyApp\myapp.exe opening a command line (no need for admin) and running:

cd C:\Temp
set COREHOST_TRACE=1
set COREHOST_TRACEFILE=host_trace.txt
C:\MyApp\myapp.exe

This should produce C:\Temp\host_trace.txt.

收到一个host_trace.txt文件:
host_trace.txt

@vitek-karas The installation path and name of this application are::C:Program Fiels (x86)聊城市泽方软件开发有限公司住院护士工作站标准版.住院护士工作站31.exe。

@199621616 I suspect you may be hitting #3778. That should have been addressed in the .NET Core 3.1.4 release; I would suggest upgrading to that version re-building/publishing.

Thanks

host_trace.txt
今天在.NetCore3.1.7环境中,再次出现此情况

编译环境、运行环境都是.NetCore3.1.7,可是host_trace.txt中的3.1.2从何而来?

@agocke I see you moved this to 6.0. Does that mean .NET 5 will not feature any changes or improvements to this dialog?

Probably not. Unfortunately we didn't have time to make significant improvements here and the bar for making changes is very high right now.

I recently observed similar UX issues. While the download button in the dialg points you to the correct site it lacks giving you the infomration which architecture of the runtime you need to install (x64, x86 or ARM).

In the long term however i think it would be best for end-users when the x64 runtime is capable of running x86 compiled apps as well. I guess the other way around is impossible but that should'nt be any real world issue anyway nowdays.

My scenario is a WPF (single file; framework-dependant) app targeting win-x86 running on Windows 10 x64 with no .NET installed. After installing .NET 5 x64 the app still wouldn't run bus showed the download dialog again.

For end-users that would be a very frustrating experience.

@AnakinSklavenwalker This has actually worked the way you want in the past but it got broken on final release. There is a tracking issue #36765 but it seems that it's still not fixed.

Was this page helpful?
0 / 5 - 0 ratings