Automatic update a Windows application - c#

How do I develop my Windows application so it will auto update on the client machine, like Firefox, Skype, etc.?
Is there any simple approach or any open source library which help me to do it just following some steps or a few lines of code?

ClickOnce is what you're searching for.
You might also find these SO questions interesting (which offers some different solutions):
Auto update for WinForms application
How do I implement an auto update strategy for my in-house winform app

try microsoft clickonce technology
(in MSDN)

You can use wyUpdate or .NET Application Updater Component

There is also the Update Block in the Ent Lib by msft.

The most popular frameworks are:
Google Omaha - This is what Chrome uses. Very powerful.
Squirrel - This is used in Electron applications. Easy to use but can't update machine-wide installations. Also, no graphical update notifications.
WinSparkle - Gives you graphical update notifications. But less mature than Squirrel.
AutoUpdater.NET - Both graphical and silent updates. Similar to Squirrel and WinSparkle.
I've taken these links from this article. It goes into more details about the pros and cons of each of the frameworks.

Use MD5-Update it easy only need add 5 lines at your application, no configuration need in your app only add library and publish the files.
1. Your need a web server with PHP for publish your files please include updt.exe.
2. Add index.php for make list of update files. aviable on github repository https://github.com/jrz-soft-mx/MD5-Update/blob/main/Tools/Tools.zip o create new app with this code.
<?php
$_dat = array();
$_dir=new RecursiveDirectoryIterator(".");
foreach (new RecursiveIteratorIterator($_dir) as $_itm) {
$_fil = str_replace(".".DIRECTORY_SEPARATOR, "", $_itm);
if(!is_dir($_fil) && $_fil != "index.php"){
$_dat[]=array('StrFil' => "$_fil", 'StrMd5' => strtoupper(md5_file($_fil)), 'lonSiz' => filesize($_fil));
}
}
echo json_encode($_dat, JSON_UNESCAPED_UNICODE);
?>
3. Add nuget repository at your proyect
PM> Install-Package MD5.Update
4. Call the library when your app stars, with your update folder url, update all files and download your new app on updt folder, for replace your app need updt.exe
string strUrl = "http://yourdomain.com/app/";
if (MD5Update.MD5Update.Check(strUrl, true))
{
Process.Start(AppDomain.CurrentDomain.BaseDirectory + #"updt.exe", AppDomain.CurrentDomain.FriendlyName + " " + Process.GetCurrentProcess().ProcessName);
Application.Exit();
}
5. updt.exe for replace the current app with the new app updt folder to app. aviable on github repository https://github.com/jrz-soft-mx/MD5-Update/blob/main/Tools/Tools.zip o create new app with this code.
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
List<string> lisArg = Environment.GetCommandLineArgs().ToList();
if (lisArg.Count < 2)
{
MessageBox.Show("Please provide App Excutable Name and Procees name");
Application.Exit();
return;
}
string strAppName = lisArg[1];
string strAppProcees = lisArg[2];
Process[] lisPro = Process.GetProcessesByName(strAppProcees);
foreach (Process Pro in lisPro)
{
if (Pro.Id != Process.GetCurrentProcess().Id)
{
Pro.Kill();
Thread.Sleep(1000);
}
}
string strAppMain = AppDomain.CurrentDomain.BaseDirectory + strAppName;
string strAppUpdate = AppDomain.CurrentDomain.BaseDirectory + #"updt\" + strAppName;
if (!File.Exists(strAppMain))
{
MessageBox.Show("App Excutable dosent exists");
Application.Exit();
return;
}
if (!File.Exists(strAppUpdate))
{
MessageBox.Show("App Excutable Updated dosent exists");
Application.Exit();
return;
}
File.Copy(strAppUpdate, strAppMain, true);
long fileSize = 0;
FileInfo currentFile = new FileInfo(strAppMain);
while (fileSize < currentFile.Length)
{
fileSize = currentFile.Length;
Thread.Sleep(1000);
currentFile.Refresh();
}
Process.Start(strAppMain);
}
catch (Exception Ex)
{
MessageBox.Show("An error ocurred");
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + #"updt_" + DateTime.Now.ToString("yyyyMMddTHHmmss") + " .txt", Ex.ToString());
Application.Exit();
}

How about System Center 2012 Configuration Manager?

I'd add another possible variation:
https://github.com/synhershko/NAppUpdate
https://github.com/cecon/autoupdatereasy
https://github.com/NetSparkleUpdater/NetSparkle

While ClickOnce is simple and it resurrected for .NET 5, it still has a lot of limitations, so I found out that nowadays better option exists: you could use included in Windows 10 mechanism for app delivery called AppInstaller by packaging your app in MSIX bundle or package.
I covered my findings related to the topic in this answer

Related

Read audio file duration in C# on Linux with .net 6

I have an asp.net core API that was recently updated from .net5 to .net6.
There is a piece of code that should read a duration of an audio file. The code that seems to have worked on previous versions was this:
try
{
//
// NAudio -- Windows only
//
using var fileReader = new AudioFileReader(filePath);
return Convert.ToInt32(Math.Ceiling(fileReader.TotalTime.TotalSeconds));
}
catch (DllNotFoundException)
{
try
{
//
// LibVLCSharp is crossplatform
//
using var libVLC = new LibVLC();
using var media = new Media(libVLC, filePath, FromType.FromPath);
MediaParsedStatus parsed = Task.Run(async () => await media.Parse(MediaParseOptions.ParseNetwork, timeout: 2000).ConfigureAwait(false)).Result;
if (parsed != MediaParsedStatus.Done) throw new ArgumentException("Could not read audio file");
if (!media.Tracks.Any(t => t.TrackType == TrackType.Audio) || (media.Duration <= 100)) throw new ArgumentException("Could not read audio from file");
return Convert.ToInt32(Math.Ceiling(TimeSpan.FromMilliseconds(media.Duration).TotalSeconds));
}
catch (Exception ex) when (ex is DllNotFoundException || ex is LibVLCSharp.Shared.VLCException)
{
try
{
using var fileReader = new Mp3FileReader(filePath);
return Convert.ToInt32(Math.Ceiling(fileReader.TotalTime.TotalSeconds));
}
catch (InvalidOperationException)
{
throw new ArgumentException("Could not read audio file");
}
}
}
The application was deployed on Linux and, I don't know which part of the code did the exact calculation (I am assuming the VLC part), but since the update to .NET6, all of these fail, and since the last fallback is NAudio, we get the following exception:
Unable to load shared library 'Msacm32.dll' or one of its dependencies.
I am using Windows, but I tried running the app with WSL, and I can't get the VLC part to run either - it always throws the following exception (even after installing vlc and vlc dev SDK):
LibVLC could not be created. Make sure that you have done the following:
Installed latest LibVLC from nuget for your target platform.
Unable to load shared library 'libX11' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibX11: cannot open shared object file: No such file or directory at LibVLCSharp.Shared.Core.Native.XInitThreads()
at LibVLCSharp.Shared.Core.InitializeDesktop(String libvlcDirectoryPath)
at LibVLCSharp.Shared.Helpers.MarshalUtils.CreateWithOptions(String[] options, Func`3 create)
Is there any clean way to read a duration of an audio file on all platforms?
Needless to say, NAudio works like a charm on Windows, and so does the VLC (with the proper nuget package).
If you install ffmpeg, you can do this quite easily. ffmpeg comes installed in most linux distros by default, but in case it isn't, you can install it with your favorite package manager.
sudo apt install ffmpeg
To install it in windows, you'll need to download the build files, extract it, and add it to the PATH.
Next, install Xabe.FFMpeg package in your project.
Finally, you can call the static method Xabe.FFMpeg.FFMpeg.GetMediaInfo() to get all information regarding your audio file. Here is a sample snippet that I tested on my linux machine.
using System;
using System.IO;
using Xabe.FFmpeg;
namespace Program;
public static class Program
{
public static void Main(string[] args)
{
string filename;
if (args.Length == 0)
{
Console.WriteLine("No arguments found! Provide the audio file path as argument!");
return;
}
else if (File.Exists(filename = args[0]) == false)
{
Console.WriteLine("Given file does not exist!");
return;
}
try
{
var info = FFmpeg.GetMediaInfo(filename).Result;
TimeSpan duration = info.Duration;
Console.WriteLine($"Audio file duration is {duration}");
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
The error you are seeing is because we were assuming that you would display a video on linux, using X11, so we are always initializing X11. See here.
We shouldn't do that for your use case(because you may not have a GUI available). Please report the issue here : https://code.videolan.org/videolan/LibVLCSharp/-/issues
or even better, submit a pull request on github or gitlab.
As for your question of why did it work on .net 5 and not anymore, I'm not sure we have enough info to tell why, because you didn't send us the error message from that machine.
I would encourage you to take a look at atldotnet. It is a small, well maintained completely managed code / cross platform library without any external dependencies and was accurate detecting audio file duration in all of my test cases (more accurate than ffmpeg). Most common audio formats are supported.
var t = new Track(audioFilePath);
// Works the same way on any supported format (MP3, FLAC, WMA, SPC...)
System.Console.WriteLine("Duration (ms) : " + t.DurationMs);

How to find and list all UWP apps / packages?

My goal is to find and launch a UWP app by name (e.g. Twitter). I'm currently using an elevated desktop extension, following a guide by Stefan Wick.
In my full-trust Win32 console process, I'm currently using the PackageManager to find and list all the UWP apps, and it works on my machine. However, when I send my finalized app package to another user, nothing appears on his screen, even after running elevated.
Here's my current code:
var PkgMgr = new PackageManager();
var currUserPkgs = PkgMgr.FindPackagesForUser(string.Empty);
foreach (Package pkg in currUserPkgs)
{
string pkgName = pkg.DisplayName;
if (pkgName == "")
{
continue;
}
if (pkgName.Contains(appName) || appName.Contains(pkgName) ||
percentSimilarity(appName, pkgName) >= 0.50)
{
// we found it
appPkgName = pkg.Id.FamilyName;
break;
}
}
Why does this not bring up any packages on another user's machine? There's no error message that's called.
Also, is there another solution that can locate all UWP packages? Thank you!

How to print thing in for loop Console.WrileLine() in aspx

This is my code i want to print these array in for loop on aspx page
for (int i = 0; i < notes.Count; i++)
{
if (noteCounter[i] != 0)
{
Console.WriteLine(notes[i].Col2 + " : "
+ noteCounter[i]);
}
}
It was working fine in Console Application i just changed it to the Web application Web form and now i want to print it on screen just without html and css any
First Solution:
You can use Log.Write() instead of Console.Write() then check your logs on the Output window; Of course, you should run your app in debugging mode.
first import System.Diagnostics
using System.Diagnostics;
// skip
for (int i = 0; i < notes.Count; i++)
{
if (noteCounter[i] != 0)
{
Debug.WriteLine(notes[i].Col2 + " : "
+ noteCounter[i]);
}
}
Second Solution
You can log everything and save it on a separate file, then check it whenever you want to... Don't need to import anything, just write this method:
public void Logger(string lines)
{
//Write the string to a file.append mode is enabled so that the log
//lines get appended to test.txt than wiping content and writing the log
using(System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\path\\to\\log\\file.txt", true))
{
file.WriteLine(lines);
}
}
Third Solution
Logging in .NET Core and ASP.NET Core
NET Core supports a logging API that works with a variety of built-in and third-party logging providers. This article shows how to use the logging API with built-in providers.
Most of the code examples shown in this article are from ASP.NET Core apps. The logging-specific parts of these code snippets apply to any .NET Core app that uses the Generic Host. The ASP.NET Core web app templates use the Generic Host.
Read the full article at Microsoft
change Console.WriteLine to document.write

Enable ASP.NET in IIS6 Programmatically

Is there a way to enable the ASP.NET Web Service Extension in IIS6 via C#? I'm trying to simplify a website setup program for people who haven't used IIS before.
C# NET. Framework usage:
Process.Start(#"C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis", "-i -enable");
CMD usage:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -i -enable
It's useful.
Source: https://serverfault.com/questions/1649/why-does-iis-refuse-to-serve-asp-net-content
You could call out to WMI easily enough (System.Management namespace, IIRC) and I believe you can set it from there. However, it may well be much simpler to set it manually, you can't do it from within an ASP.NET site since your server won't be able to run it until it is set...
Principles of doing something similar may be found here
Looking around all the examples of this are written in vbscript. So I cheated and came up with this function:
static void EnableASPNET()
{
var file = "wmi.vbs";
using (var writer = new StreamWriter(file))
{
writer.WriteLine("Set webServiceObject = GetObject(\"IIS://localhost/W3SVC\")");
writer.WriteLine("webServiceObject.EnableWebServiceExtension \"ASP.NET v2.0.50727\"");
writer.WriteLine("webServiceObject.SetInfo");
}
var process = Process.Start("cscript", file);
process.WaitForExit();
File.Delete(file);
}
// if windows 2003
if (Environment.OSVersion.Version.Major == 5 &&
Environment.OSVersion.Version.Minor == 2)
{
DirectoryEntry folderRoot = new DirectoryEntry("IIS://localhost/W3SVC");
folderRoot.Invoke("EnableWebServiceExtension", "ASP.NET v2.0.50727");
}
Copied from: http://lastdon.blogspot.com/2006/12/setup-web-application-on-windows-2003.html
I believe you can also run the following command line:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -s W3SVC
And this will recursively enable the AND.NET framework v2.0.50727 for all configured websites.

Use C# to interact with Windows Update

Is there any API for writing a C# program that could interface with Windows update, and use it to selectively install certain updates?
I'm thinking somewhere along the lines of storing a list in a central repository of approved updates. Then the client side applications (which would have to be installed once) would interface with Windows Update to determine what updates are available, then install the ones that are on the approved list. That way the updates are still applied automatically from a client-side perspective, but I can select which updates are being applied.
This is not my role in the company by the way, I was really just wondering if there is an API for windows update and how to use it.
Add a Reference to WUApiLib to your C# project.
using WUApiLib;
protected override void OnLoad(EventArgs e){
base.OnLoad(e);
UpdateSession uSession = new UpdateSession();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
uSearcher.Online = false;
try {
ISearchResult sResult = uSearcher.Search("IsInstalled=1 And IsHidden=0");
textBox1.Text = "Found " + sResult.Updates.Count + " updates" + Environment.NewLine;
foreach (IUpdate update in sResult.Updates) {
textBox1.AppendText(update.Title + Environment.NewLine);
}
}
catch (Exception ex) {
Console.WriteLine("Something went wrong: " + ex.Message);
}
}
Given you have a form with a TextBox this will give you a list of the currently installed updates. See http://msdn.microsoft.com/en-us/library/aa387102(VS.85).aspx for more documentation.
This will, however, not allow you to find KB hotfixes which are not distributed via Windows Update.
The easiest way to do what you want is using WSUS. It's free and basically lets you setup your own local windows update server where you decide which updates are "approved" for your computers. Neither the WSUS server nor the clients need to be in a domain, though it makes it easier to configure the clients if they are. If you have different sets of machines that need different sets of updates approved, that's also supported.
Not only does this accomplish your stated goal, it saves your overall network bandwidth as well by only downloading the updates once from the WSUS server.
If in your context you're allowed to use Windows Server Update Service (WSUS), it will give you access to the Microsoft.UpdateServices.Administration Namespace.
From there, you should be able to do some nice things :)
P-L right. I tried first the Christoph Grimmer-Die method, and in some case, it was not working. I guess it was due to different version of .net or OS architecture (32 or 64 bits).
Then, to be sure that my program get always the Windows Update waiting list of each of my computer domain, I did the following :
Install a serveur with WSUS (may save some internet bandwith) : http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5216
Add all your workstations & servers to your WSUS server
Get SimpleImpersonation Lib to run this program with different admin right (optional)
Install only the administration console component on your dev workstation and run the following program :
It will print in the console all Windows updates with UpdateInstallationStates.Downloaded
using System;
using Microsoft.UpdateServices.Administration;
using SimpleImpersonation;
namespace MAJSRS_CalendarChecker
{
class WSUS
{
public WSUS()
{
// I use impersonation to use other logon than mine. Remove the following "using" if not needed
using (Impersonation.LogonUser("mydomain.local", "admin_account_wsus", "Password", LogonType.Batch))
{
ComputerTargetScope scope = new ComputerTargetScope();
IUpdateServer server = AdminProxy.GetUpdateServer("wsus_server.mydomain.local", false, 80);
ComputerTargetCollection targets = server.GetComputerTargets(scope);
// Search
targets = server.SearchComputerTargets("any_server_name_or_ip");
// To get only on server FindTarget method
IComputerTarget target = FindTarget(targets, "any_server_name_or_ip");
Console.WriteLine(target.FullDomainName);
IUpdateSummary summary = target.GetUpdateInstallationSummary();
UpdateScope _updateScope = new UpdateScope();
// See in UpdateInstallationStates all other properties criteria
_updateScope.IncludedInstallationStates = UpdateInstallationStates.Downloaded;
UpdateInstallationInfoCollection updatesInfo = target.GetUpdateInstallationInfoPerUpdate(_updateScope);
int updateCount = updatesInfo.Count;
foreach (IUpdateInstallationInfo updateInfo in updatesInfo)
{
Console.WriteLine(updateInfo.GetUpdate().Title);
}
}
}
public IComputerTarget FindTarget(ComputerTargetCollection coll, string computername)
{
foreach (IComputerTarget target in coll)
{
if (target.FullDomainName.Contains(computername.ToLower()))
return target;
}
return null;
}
}
}

Categories

Resources