Posts: 997
Threads: 239
Joined: Jul 2022
03-12-2024, 11:10 PM
(This post was last modified: 03-20-2024, 08:01 AM by Davider.)
I'm installing LA on a new computer, and the downloading speed of .NET8 is too slow. The window also becomes unresponsive. I need to wait for 30 minutes to complete the installation.
LINQPad8 software also comes with a .NET8 downloader, which has a very fast downloading speed. It only takes 3 seconds to download.
https://www.linqpad.net/GetFile.aspx?LINQPad8.zip
Posts: 12,028
Threads: 139
Joined: Dec 2002
I can't reproduce. Downloads in several seconds.
Posts: 997
Threads: 239
Joined: Jul 2022
I downloaded directly from the .NET website, and the speed was fast. It's just that the download speed during LA installation is slow. I think it might need to use CDN links
Posts: 12,028
Threads: 139
Joined: Dec 2002
The Setup program downloads directly from Microsoft. But the URL is different. The setup program uses this URL because it's the newest .NET 8 version. Else it would have to use a URL of some hardcoded version (8.0.1, 8.0.2, etc), which may be not the newest.
Posts: 997
Threads: 239
Joined: Jul 2022
03-13-2024, 07:07 AM
(This post was last modified: 03-13-2024, 07:22 AM by Davider.)
LINQPad's .NET downloader always manages to detect the latest version and downloads quickly. I'm not sure how it's implemented.
I found the source code of the downloader using ILSpy.
// NetCoreDownloader.MainWindow
using System;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using NetCoreDownloader;
public class MainWindow : Window, IComponentConnector
{
private bool _isBeta;
private bool _is32Bit;
private bool _isArm64;
private string _x64Uri;
private string _x86Uri;
private CancellationTokenSource _cancelSource;
internal TextBlock x64Text;
internal TextBox txtX64Version;
internal TextBox txtX86Version;
internal TextBox txtX64Status;
internal TextBox txtX86Status;
internal TextBox txtDownloadSource;
internal Grid grdDownloadButtons;
internal Button btnDownloadX64;
internal Button btnDownloadX86;
internal GroupBox grpProgress;
internal TextBlock lblAction;
internal Button btnCancel;
internal ProgressBar progressBar;
internal TextBlock txtProgress;
internal GroupBox grpLaunch;
internal Button btnLaunchLINQPadX64;
internal Button btnLaunchLINQPadX86;
private bool _contentLoaded;
public MainWindow()
{
_isBeta = File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BetaUpdateStream"));
_is32Bit = FxChecker.IsX86;
_isArm64 = FxChecker.IsArm64;
InitializeComponent();
base.Title = base.Title + " (LINQPad 8.0.18" + (_isBeta ? ", beta update stream" : "") + ")";
if (_isArm64)
{
x64Text.Text = "ARM64 (64-bit)";
btnDownloadX64.Content = "Download Runtime - ARM64";
btnLaunchLINQPadX64.Content = "Launch LINQPad (ARM64)";
}
grpProgress.Visibility = Visibility.Hidden;
if (_is32Bit)
{
foreach (UIElement child in grdDownloadButtons.Children)
{
if (child is TextBlock || child is Label)
{
child.Visibility = Visibility.Collapsed;
}
}
}
PopulateDownloadSource();
PopulateStatus();
}
private string GetLINQPadPath(bool x86, bool cmd)
{
string text = (cmd ? "LPRun8" : "LINQPad8");
string text2 = ((x86 || _is32Bit) ? "x86" : (_isArm64 ? "arm64" : "x64"));
string text3 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, text + "-" + text2 + ".exe");
if (File.Exists(text3))
{
return text3;
}
text3 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, text + ".exe");
if (File.Exists(text3) && GetArchitectureFromExe(text3) == text2)
{
return text3;
}
return "executable";
}
public static string GetArchitectureFromExe(string path)
{
byte[] array = new byte[4096];
using (FileStream fileStream = File.OpenRead(path))
{
if (fileStream.Length < 4096)
{
return null;
}
fileStream.Read(array, 0, 4096);
}
int num = BitConverter.ToInt32(array, 60);
if (num < 0 || num + 4 > 4090)
{
return null;
}
return BitConverter.ToUInt16(array, num + 4) switch
{
332 => "x86",
43620 => "arm64",
34404 => "x64",
_ => null,
};
}
private async void PopulateStatus()
{
while (true)
{
string text3 = (txtX64Version.Text = (txtX86Version.Text = "?"));
SemanticVersion semanticVersion;
SemanticVersion semanticVersion2;
try
{
semanticVersion = FxChecker.FindHighestValidVersion(for64bit: true);
semanticVersion2 = FxChecker.FindHighestValidVersion(for64bit: false);
}
catch
{
break;
}
txtX64Version.Text = ((semanticVersion != null) ? ("Version " + semanticVersion?.ToString() + " installed") : "Not installed");
txtX86Version.Text = ((semanticVersion2 != null) ? ("Version " + semanticVersion2?.ToString() + " installed") : "Not installed");
PopulateStatusBox(txtX64Status, semanticVersion);
PopulateStatusBox(txtX86Status, semanticVersion2);
btnLaunchLINQPadX64.IsEnabled = FxChecker.MinVersion.CompareTo(semanticVersion) <= 0 && !_is32Bit;
btnLaunchLINQPadX86.IsEnabled = FxChecker.MinVersion.CompareTo(semanticVersion2) <= 0;
await Task.Delay(1000);
}
}
private void PopulateStatusBox(TextBox box, SemanticVersion version)
{
if (version == null)
{
box.Text = "";
return;
}
try
{
if (version.CompareTo(FxChecker.MinVersion) < 0)
{
box.Text = "Update required";
}
else if (version.CompareTo(FxChecker.MinRecommendedVersion) < 0)
{
box.Text = "Newer version available";
}
else
{
box.Text = "You're up to date!";
}
}
catch
{
}
}
private async void PopulateDownloadSource()
{
Task<byte[]> task = new TapWebClient().DownloadDataAsync("https://api.nuget.org/v3/index.json");
task.ContinueWith((Task<byte[]> t) => t.Exception);
bool flag = await Task.WhenAny(task, Task.Delay(8000)) == task && !task.IsFaulted;
string text;
try
{
text = await new TapWebClient().DownloadStringAsync("https://www.linqpad.net/FxDownloadSource.8.0." + (flag ? "desktop" : "sdk") + ".txt");
}
catch (Exception ex)
{
txtDownloadSource.Text = "Cannot contact server: " + ex.Message;
return;
}
string[] array = (from s in text.Split(new string[1] { "\n" }, StringSplitOptions.RemoveEmptyEntries)
select s.Trim()).ToArray();
if (array.Length < 3)
{
txtDownloadSource.Text = "Invalid response from server.";
return;
}
txtDownloadSource.Text = array[0];
FxChecker.MinRecommendedVersion = SemanticVersion.TryParse(array[0]) ?? FxChecker.MinRecommendedVersion;
_x64Uri = (FxChecker.IsArm64 ? array[3] : array[1]);
_x86Uri = array[2];
btnDownloadX64.IsEnabled = !_is32Bit;
btnDownloadX86.IsEnabled = true;
if (App.AutoDownload)
{
Download(_is32Bit ? _x86Uri : _x64Uri);
}
}
private void DownloadX64(object sender, RoutedEventArgs e)
{
Download(_x64Uri);
}
private void DownloadX86(object sender, RoutedEventArgs e)
{
Download(_x86Uri);
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
_cancelSource.Cancel();
}
private async void LlRefresh_Click(object sender, RoutedEventArgs e)
{
TextBox textBox = txtX64Version;
TextBox textBox2 = txtX86Version;
TextBox textBox3 = txtX64Status;
string text2 = (txtX86Status.Text = "");
string text4 = (textBox3.Text = text2);
string text7 = (textBox.Text = (textBox2.Text = text4));
await Task.Delay(300);
PopulateStatus();
}
private void BtnLaunchLINQPadX64_Click(object sender, RoutedEventArgs e)
{
LaunchLINQPad(x86: false);
}
private void BtnLaunchLINQPadX86_Click(object sender, RoutedEventArgs e)
{
LaunchLINQPad(x86: true);
}
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("The X86 runtime lets you run LINQPad8-x86.exe, which launches LINQPad as a 32-bit process.\r\n\r\nThis is required when you need to reference 32-bit native DLLs.");
}
private void LaunchLINQPad(bool x86)
{
string lINQPadPath = GetLINQPadPath(x86, cmd: false);
if (!File.Exists(lINQPadPath))
{
MessageBox.Show("Cannot locate " + lINQPadPath + ".");
return;
}
string lINQPadPath2 = GetLINQPadPath(x86, cmd: true);
if (!File.Exists(lINQPadPath2))
{
MessageBox.Show("Cannot locate " + lINQPadPath2 + ".");
return;
}
using (Process process = Process.Start(new ProcessStartInfo(lINQPadPath2)
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}))
{
StringBuilder errors = new StringBuilder();
process.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs errorArgs)
{
errors.AppendLine(errorArgs.Data);
};
process.BeginErrorReadLine();
int num = 0;
while (process.StandardOutput.ReadLine() != null)
{
num++;
}
int exitCode = process.ExitCode;
if (exitCode != 0 && exitCode != 1)
{
string text = "Unable to start LINQPad.";
if (errors.Length > 0)
{
text = text + "\r\n\r\n" + errors.ToString();
}
MessageBox.Show(text, "LINQPad", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
}
Process.Start(new ProcessStartInfo(lINQPadPath)
{
UseShellExecute = true
}).Dispose();
}
private async void Download(string uri)
{
Button button = btnDownloadX64;
bool isEnabled = (btnDownloadX86.IsEnabled = false);
button.IsEnabled = isEnabled;
grpProgress.Visibility = Visibility.Visible;
_cancelSource = new CancellationTokenSource();
btnCancel.Visibility = Visibility.Visible;
progressBar.IsEnabled = true;
txtProgress.Text = "Connecting...";
try
{
string filename = new Uri(uri).Segments.Last();
lblAction.Text = "Downloading " + filename;
string tempFile = Path.Combine(Path.GetTempPath(), filename);
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
Progress<DownloadProgressChangedEventArgs> progress = new Progress<DownloadProgressChangedEventArgs>(delegate(DownloadProgressChangedEventArgs p)
{
progressBar.Value = p.ProgressPercentage;
txtProgress.Text = p.BytesReceived / 1000000 + "MB of " + (p.TotalBytesToReceive / 1000000 + 1) + "MB";
});
await new TapWebClient().DownloadFileAsync(uri, tempFile, _cancelSource.Token, progress);
txtProgress.Text = "Complete";
progressBar.Value = 100.0;
Process.Start(tempFile);
lblAction.Text = "Running " + filename;
}
catch when (_cancelSource.IsCancellationRequested)
{
txtProgress.Text = "";
progressBar.Value = 0.0;
lblAction.Text = "Operation canceled by user.";
}
catch (Exception ex)
{
txtProgress.Text = "Error: downloading via web browser...";
lblAction.Text = ex.Message;
progressBar.Value = 0.0;
await Task.Delay(2000);
Process.Start(new ProcessStartInfo(uri)
{
UseShellExecute = true
});
}
finally
{
btnDownloadX64.IsEnabled = !_is32Bit;
btnDownloadX86.IsEnabled = true;
btnCancel.Visibility = Visibility.Collapsed;
progressBar.IsEnabled = false;
}
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent()
{
if (!_contentLoaded)
{
_contentLoaded = true;
Uri resourceLocator = new Uri("/Download .NET;component/mainwindow.xaml", UriKind.Relative);
Application.LoadComponent(this, resourceLocator);
}
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
[EditorBrowsable(EditorBrowsableState.Never)]
void IComponentConnector.Connect(int connectionId, object target)
{
switch (connectionId)
{
case 1:
x64Text = (TextBlock)target;
break;
case 2:
txtX64Version = (TextBox)target;
break;
case 3:
txtX86Version = (TextBox)target;
break;
case 4:
txtX64Status = (TextBox)target;
break;
case 5:
txtX86Status = (TextBox)target;
break;
case 6:
txtDownloadSource = (TextBox)target;
break;
case 7:
grdDownloadButtons = (Grid)target;
break;
case 8:
btnDownloadX64 = (Button)target;
btnDownloadX64.Click += DownloadX64;
break;
case 9:
btnDownloadX86 = (Button)target;
btnDownloadX86.Click += DownloadX86;
break;
case 10:
((Hyperlink)target).Click += Hyperlink_Click;
break;
case 11:
grpProgress = (GroupBox)target;
break;
case 12:
lblAction = (TextBlock)target;
break;
case 13:
btnCancel = (Button)target;
btnCancel.Click += BtnCancel_Click;
break;
case 14:
progressBar = (ProgressBar)target;
break;
case 15:
txtProgress = (TextBlock)target;
break;
case 16:
grpLaunch = (GroupBox)target;
break;
case 17:
btnLaunchLINQPadX64 = (Button)target;
btnLaunchLINQPadX64.Click += BtnLaunchLINQPadX64_Click;
break;
case 18:
btnLaunchLINQPadX86 = (Button)target;
btnLaunchLINQPadX86.Click += BtnLaunchLINQPadX86_Click;
break;
default:
_contentLoaded = true;
break;
}
}
}
Accessing Line https://api.nuget.org/v3/index.json will automatically switch to CDN Line https://nuget.cdn.azure.cn/v3/index.json
Posts: 12,028
Threads: 139
Joined: Dec 2002
03-13-2024, 08:28 AM
(This post was last modified: 03-13-2024, 08:37 AM by Gintaras.)
var urlManualDownload = "https://download.visualstudio.microsoft.com/download/pr/51bc18ac-0594-412d-bd63-18ece4c91ac4/90b47b97c3bfe40a833791b166697e67/windowsdesktop-runtime-8.0.3-win-x64.exe";
var urlSetup = "https://aka.ms/dotnet/8.0/windowsdesktop-runtime-win-x64.exe";
perf.first();
var b1 = internet.http.Get(urlManualDownload).Bytes();
perf.next();
var b2 = internet.http.Get(urlSetup).Bytes();
perf.nw();
print.it(b1.Length, b2.Length, b1.SequenceEqual(b2));
On my computer the download times are the same. What are your results?
Posts: 997
Threads: 239
Joined: Jul 2022
Posts: 12,028
Threads: 139
Joined: Dec 2002
New LA will use the fast download URL. Thank you.
Posts: 997
Threads: 239
Joined: Jul 2022
Thanks very much for your hard work, LA is becoming more and more powerful
Posts: 997
Threads: 239
Joined: Jul 2022
03-19-2024, 02:39 PM
(This post was last modified: 03-19-2024, 02:42 PM by Davider.)
Can the download prompt for the .NET 8 runtime be added to the generated EXE file?
Before Run the EXE, if an error occurs, check if the .NET runtime meets the requirements. If it doesn't meet the requirements, Using the link above to download the .NET runtime. Is this possible?
Posts: 12,028
Threads: 139
Joined: Dec 2002
I changed the URL in the prompt too.
Posts: 997
Threads: 239
Joined: Jul 2022
03-19-2024, 11:05 PM
(This post was last modified: 03-19-2024, 11:52 PM by Davider.)
I generated a single .exe file by clicking Run -> Publish (single file). However, I couldn't execute it successfully. It prompted an error as shown in the screenshot, but in reality, the .NET 8 runtime has already been installed.
https://i.ibb.co/1KZ0qfL/A.png
Additionally, the interface in the image is in English. Can I localize the text on the interface?
Posts: 12,028
Threads: 139
Joined: Dec 2002
03-20-2024, 06:13 AM
(This post was last modified: 03-20-2024, 06:14 AM by Gintaras.)
Please run in cmd:
dotnet --info
And post the output text.
----
Localize - no.
Posts: 997
Threads: 239
Joined: Jul 2022
03-20-2024, 07:52 AM
(This post was last modified: 03-20-2024, 08:00 AM by Davider.)
C:\Users\Administrator>dotnet --info
.NET SDK:
Version: 8.0.100
Commit: 57efcf1350
Workload version: 8.0.100-manifests.6c33ef20
运行时环境:
OS Name: Windows
OS Version: 10.0.18363
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\8.0.100\
已安装 .NET 工作负载:
Workload version: 8.0.100-manifests.6c33ef20
没有要显示的已安装工作负载。
Host:
Version: 8.0.3
Architecture: x64
Commit: 9f4b1f5d66
.NET SDKs installed:
8.0.100 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
C:\Users\Administrator>
PS C:\Program Files\PowerShell\7> host
Name : ConsoleHost
Version : 7.4.1
InstanceId : 86c45cbd-0662-4426-af97-356ae713fc37
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : zh-CN
CurrentUICulture : zh-CN
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled : True
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
PS C:\Program Files\PowerShell\7>
The EXE file generated through the compile button can be executed in the Powershell 7 installation directory, but the single EXE file generated through the publish button cannot be executed.
Posts: 12,028
Threads: 139
Joined: Dec 2002
The dotnet --info output is OK.
I tested Publish with various .NET Runtime and SDK versions, but could not reproduce this.
Try to uninstall this .NET SDK version, install the newest SDK version, and create exe with Publish again.
Posts: 997
Threads: 239
Joined: Jul 2022
When installing LA now, if the system lacks the .NET 8 runtime environment, it will automatically download and install silently, and the speed is very fast.
However, when executing the exe file generated by the compile button, it still prompts the following dialog. Can it automatically download and install the .NET 8 runtime silently like LA does? Because this is very convenient.
https://i.ibb.co/Q9Lsyv2/AAA.png
Posts: 12,028
Threads: 139
Joined: Dec 2002
No. Instead can be used menu Run > Publish > Add .NET runtime.
Posts: 997
Threads: 239
Joined: Jul 2022
The generated EXE file has a large size.
Additionally, I do need to localize the text from the image link above because there are many situations where the .NET 8 runtime is not installed.
Posts: 997
Threads: 239
Joined: Jul 2022
08-08-2024, 02:29 AM
(This post was last modified: 08-08-2024, 02:41 AM by Davider.)
After trying various solutions, I no longer use the publish function. Instead, I use the compile button on the toolbar, then package the compiled files into a Zip file.
If the user's computer already has the .NET 8 runtime installed, the program runs without any issues. However, if the runtime is not installed, launching the program will prompt the dialog box shown in Figure 01. Clicking the Yes button will bring up the dialog box shown in Figure 02.
https://i.postimg.cc/P5Ct5VDK/01.png
https://i.postimg.cc/nrVxb3Z4/02.png
The problem is that the dialog boxes and the opened web pages are not in the localized language, which is neither user-friendly nor convenient.
My idea is to display only one dialog box with customizable description text in the localized language. For example:
"To run this program, you need to install the .NET 8 x64 desktop runtime. Do you want to download and install it?"
When I click the Yes button, the .NET 8 runtime files will be silently downloaded and installed, and then the program will launch.
This approach is simple and user-friendly.
I don't want to use third-party packaging programs; the process is somewhat cumbersome.
My idea is to embed the localized UI text as resources in the exe file and read them when needed. I'm not sure if this is possible to implement.
Posts: 12,028
Threads: 139
Joined: Dec 2002
Unfortunately .NET does not provide a good and reliable way to auto-install .NET. It seems .NET developers don't care.
I will not change the current way to display text in the message boxes.
Posts: 997
Threads: 239
Joined: Jul 2022
08-08-2024, 07:31 AM
(This post was last modified: 08-08-2024, 07:32 AM by Davider.)
I have an alternative solution(However, it needs to be predefined.):
If the .NET 8 runtime is not installed on the current system, and there is a file named insNet8RT.exe in the program directory, then launch insNet8RT.exe directly. This file is compiled with QM and does not require a runtime, for example, the code below.
Macro insNet8RT
MES m
m.style="YNn"
m.timeout=6
m.default='C'
int i=mes("downLoading and install .Net8 Runtime" "Tip" m)
;Todo Code...
Posts: 12,028
Threads: 139
Joined: Dec 2002
I avoid anything that may look suspicious to antivirus programs. Launching unknown programs, PowerShell scripts.
Inform users to run insNet8RT.exe if .NET missing.
Posts: 12,028
Threads: 139
Joined: Dec 2002
Posts: 997
Threads: 239
Joined: Jul 2022
08-08-2024, 11:00 AM
(This post was last modified: 08-08-2024, 11:33 AM by Davider.)
Quote:Inform users to run insNet8RT.exe if .NET missing.
The dialog box cannot have customizable localized description text.
Some people, even if they can understand English words, might not know what a runtime is because they are not developers.
Therefore, I believe that having a localized text description here is really necessary.
Additionally, the runtime download page that opens should automatically adjust based on the system language. For example, on a Chinese operating system, it should open the following link.
https://dotnet.microsoft.com/en-us/download/dotnet/8.0
To:
https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0
It would be great if the text in the dialog box and the webpage link could be modified or read from somewhere. This doesn't seem likely to trigger an antivirus alert.
|