notifyIcon in Microsoft.NET.Sdk.Web - c#

i have WASM app to request api to localhost for manage file and folder ...
beside this i have another web application that handle WASM request ....
now I want add notifyIcon to localhost web application so user can see config and port usage for it ...
this is project file for web application
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Update="briefcase-solid.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
and this is program.cs of web application
var builder = WebApplication.CreateBuilder(args);
int port = builder.Configuration.GetValue<int>("Port");
NotifyIcon notifyIcon = new NotifyIcon();
notifyIcon.Visible = true;
notifyIcon.Icon = new Icon("briefcase-solid.ico");
notifyIcon.ShowBalloonTip(5000, "Port", port.ToString(), ToolTipIcon.Info);
StringBuilder sb = new StringBuilder();
sb.AppendLine("localHost Service");
sb.AppendLine("Port : " + port);
notifyIcon.Text = sb.ToString();
notifyIcon.Click += (o, e) =>
{
// not execute At all
};
builder.Services.AddControllers();
var app = builder.Build();
string url = "http://localhost:" + port;
app.UseCors(x => x.AllowAnyOrigin());
app.UseLocalAccessOnlyMiddleware();
app.MapControllers();
app.MapGet("/", () => "Hello World!");
await app.RunAsync(url);
now notifyIcon show Ballon tips and text show OK ...
but i cant use event call back o click on it and so on ....
why ???
notifyIcon.Click not work because this is a web application not a win application

why ???
UI elements require a message loop to process their events. Your code starts a website but no UI processing loop.
At the very least, you would need to add a message loop, e.g., Application.Run. Presumably after you start (not Run) the web app (alternatively, run the web app in a different thread).
You may also need to create the UI elements from within your message loop so they can "see" the correct UI context.
Also, consider how shutdown is going to work; the message loop gives your app a way for the OS to request shutdown, so ideally you should write a lifetime component for the web part that responds to shutdown requests from the UI.

Related

Configuring log4net programmatically, but extra logging going to console

I've taken the code from Can you configure log4net in code instead of using a config file? and put it into my little NET5 application. The log file, thus far, seems okay, but as I haven't configured any appender going to the console, but only the one rolling file appender, I wasn't expecting to get anything going to the console. However, that is exactly what is happening - my messages are going to the console albeit in a different format, it's also not using my pattern layout.
This is what I have for setup at the moment:
private void SetupLog4Net()
{
var hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Root.RemoveAllAppenders();
hierarchy.Root.Level = Level.Debug;
var patternLayout = new PatternLayout
{
ConversionPattern = "%date{yyyy-MM-dd HH:mm:ss.fff} [%logger] %message%newline%exception",
};
patternLayout.ActivateOptions();
var rollingFileAppender = new RollingFileAppender
{
AppendToFile = true,
File = #"Logs/uav.log",
Layout = patternLayout,
MaxSizeRollBackups = 5,
MaxFileSize = 1_000_000,
RollingStyle = RollingFileAppender.RollingMode.Size,
StaticLogFileName = true,
Encoding = System.Text.Encoding.UTF8,
};
rollingFileAppender.ActivateOptions();
hierarchy.Root.AddAppender(rollingFileAppender);
hierarchy.Root.Level = Level.Info;
hierarchy.Configured = true;
BasicConfigurator.Configure(hierarchy);
}
This is called as the first thing in the first thing that Main does (construct the application object, this is called via that constructor), before we get to absolutely anything else.
Then, later, when we call to log stuff, e.g.,
var logger = LogManager.GetLogger(this.GetType());
logger.Info(message.Message, message.Exception);
I get output such as:
1780 [6] INFO MyClass.MainApp (null) - Ready
I can't even make sense of half of that, but making sense isn't the point - clearing out this appender is. Comments on the actual configuration are fine as comments, but the main question is just how to remove that console output.
Using Log4NET 2.0.12 and .NET 5.0.400 on Linux
According to the log4net configuration page, calling BasicConfigurator.Configure() method will "Set up a simple configuration that logs on the console."
Seeing as you've configured things programmatically to write to a file, no need to include the line:
BasicConfigurator.Configure();
OK: so your REAL question is "How do I disable Log4Net console logging?"
Remember: in Java, "log4j"is "the logger". In .Net, however, Log4net coexists with Microsoft.Extensions.Logging. So there's "extra stuff", which might yield "surprises". .Net itself (e.g. Net 5, nee ".Net Core") might also yield "surprises".
Specifically, there are several solutions discussed here:
How do I disable log4net status messages to the console?
a. Change log4net.Config.BasicConfigurator.Configure(); to
log4net.Config.XmlConfigurator.Configure();
b. set debug = false
c. Modify "log4net.Internal.Debug" in your app config file (e.g.
"appsettings.json")
<appSettings>
<add key="log4net.Internal.Debug" value="false"/>
Also look at the Log4Net FAQs:
http://logging.apache.org/log4net/release/faq.html
Internal debugging messages are written to the console and to the
System.Diagnostics.Trace system. If the application does not have a
console the messages logged there will be lost. Note that an
application can redirect the console stream by setting the
System.Console.Out.
As log4net internal debug messages are written to the
System.Diagnostics.Trace system it is possible to redirect those
messages to a local file.

Error when calling two fullTrust apps on the load of the page

I have a UWP app that does the recording and calling functionality. For this I have added two FullTrust Apps using Desktop bridge application. When I call just one fullTrust app everything works perfectly, but when I call two FullTrust apps by passing parameters (of the FullTrust apps to be started) the the first app that was started behaves incorrectly. For these two FullTrust apps I have used two different Appservice names declared in the Package.Manifest file of the Windows Packaging Project.
I have noticed that whenever I switch the position of the Fulltrust app call the last application that is called always remains active(has the priority of the Appservice connection) even if both has different app service names.
Here is the code I have added when user opens a page in UWP that starts Win32 app and background App
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Win32");
}
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Background");
}
In the above code, the first app that is Started calls my Win32.exe and second background.exe.
How can I call these two apps independently? whenever we want to start them and close whenever required or may be in future I would like to start two apps at the same time but also I may need to close any app whenever required. Can anyone tel me how can I handle the correct communication path when calling two fullTrust apps at the same time?
How can I call these two apps independently?
For launching multiple desktop app, we suggest to make Launcher app to manage multiple apps, then call LaunchFullTrustProcessForCurrentAppAsync(string parameterGroupId) and pass GroupId parameter. And at first we need add the group in the desktop bridge appxmanifes file.
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Launcher\Launcher.exe">
<desktop:FullTrustProcess>
<desktop:ParameterGroup GroupId="Background" Parameters="/background" />
<desktop:ParameterGroup GroupId="Win32" Parameters="/win32" />
</desktop:FullTrustProcess>
</desktop:Extension>
</Extensions>
Then use the Launcher to start all apps with parameter
static void Main(string[] args)
{
// determine the package root, based on own location
string result = Assembly.GetExecutingAssembly().Location;
int index = result.LastIndexOf("\\");
string rootPath = $"{result.Substring(0, index)}\\..\\";
// process object to keep track of your child process
Process newProcess = null;
if (args.Length > 2)
{
// launch process based on parameter
switch (args[2])
{
case "/background":
newProcess = Process.Start(rootPath + #"FullTrust_Background\FullTrust_Background.exe");
break;
case "/win32":
newProcess = Process.Start(rootPath + #"FullTrust_Win32\FullTrust_Win32.exe");
break;
}
}
}
For more detail please refer this tutorial.

UWP FullTrustProcess - actually controlling the process

In WPF, I have used this code to control the process of an external .exe file:
ProcessStartInfo info = new ProcessStartInfo()
{
Verb = "runas",
FileName = executePath,//Client.exe path within the WPF application
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
};
Process scanProcess = new Process
{
StartInfo = info,
EnableRaisingEvents = true
};
int procId = Process.GetCurrentProcess().Id;
Process[] existingProcess = Process.GetProcessesByName("Client.exe");
foreach (Process process in existingProcess)
{
if (process.Id == procId)
{
continue;
}
return;//don't run, might have tried to start double
}
scanProcess.Start();
scanProcess.Exited += (s, e) => GoBack();
windowHandle = IntPtr.Zero;
IntPtr buttonHandle = IntPtr.Zero;
while (buttonHandle == IntPtr.Zero)//wait till process is fully started, we wait for a button the be available because then we know it is ready
{
windowHandle = HardwareApi.GetForegroundWindow();
buttonHandle = HardwareApi.FindWindowEx(windowHandle, IntPtr.Zero, "TPanel", "Start");
}
Rectangle rect = new Rectangle(0, 0, 0, 0);
HardwareApi.GetWindowRect(windowHandle, ref rect);
//move window to correct position
var tempX = 500;
var tempY = 600;
HardwareApi.SetWindowPos(windowHandle, new IntPtr(-1), 0, 0, 0, 0, 3u);
HardwareApi.MoveWindow(windowHandle, (int)tempX, (int)tempY, rect.Width, rect.Height - 55, true);
As you see, quite a bit of intense process controlling. HardwareApi is just a collection of user32.dll methods using DllImport.
Now I don't mind if not all of these functionalities are possible, but at least I need to be able to start the process and stop it/kill it from out the new UWP application.
So I went through the process of creating an UWP application and a Package application, I followed this blog, provided by this answer on stackoverflow. What is importantly different is that I do not have a separate Visual Studio Project of the external Client.exe, so I did what the mentioned answer said: Copy the Client.exe file (and the dependent files) into the Package project.
Solution view:
Package.appxmanifest of the Package project includes this part:
<Applications>
<Application Id="App"
....
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Rss\Hardware\Software\RedFolder\Client.exe"/>
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="allowElevation" />
</Capabilities>
And within the UWP application, I somewhere start the FullTrustProcess
Don't forget to go to Add Reference-> Universal Windows -> Extensions -> Windows Desktop Extensions for the UWP (latest version) and add the reference
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
}
So this all works fine, so now the question is how I can do the following things:
Check if the process is running
Close the process at a certain time from within the UWP application
Move the window to a certain on-screen position (or use other user32.dll-like functions.
As per my understanding, App Service within UWP doesn't seem to be a solution, I can't modify the external .exe in such a way that it would receive/connect. If an App Service can be used from only the UWP-side to control a process then that would be great.
To solve your scenario you need to add a full-trust launcher/controller EXE to your package. Launch this from the UWP via FullTrustProcess launcher and communicate with it via AppServiceConnection. From the controller EXE you can then use all the System.Diagnostis.Process* APIs to start, check and kill your actual app process (Client.exe in your example) - and also use all the user32 function for window manipulation etc.
More info and a sample app on handling multiple processes here:
https://stefanwick.com/2018/04/06/uwp-with-desktop-extension-part-2/
Info and sample on how to do the communication here:
https://stefanwick.com/2018/04/16/uwp-with-desktop-extension-part-3/
A pure UWP app runs in a sandbox and cannot kill processes using the System.Diagnostics.Process type in .NET.
What you probably want to is to package your current WPF application as a UWP using a Windows Application Packaging project.
You will then be able to run it as a full-trust application but still take advantage of the streamlined deployment and update experience that UWP brings, as well as using modern UWP APIs side-by-side with your current .NET code.

c# Form Project won't save Setting changes of Console Project

Background
Console Application:
I have a console application that retrieves data from a spreadsheet using the google sheets API. This application is automated by running it every 5 minutes with windows scheduler.
Form Application:
In the same solution I have created a windows form project that can be run manually, outside of the automation process to tweak any settings without disturbing the 5 minute process. (i.e. If we want to change spreadsheet ID to fetch data form a different spreadsheet, or if i want to change the output folder of where the data is being fetched)
My Goal
I'm trying to develop a form project that will edit the "settings.settings" file of another project in the same solution. Below is a screenshot of how i have my solution laied out:
What I've done so far
I've already added a reference from my sheetstocsv project in my SettingsUI Project, and i've successfully created a form that accesses sheetstocsv's Settings and edits them when a save button is clicked. Shown below is the function that's supposed to save the new settings from the form.
private void Save_Button_Click(object sender, EventArgs e)
{
sheetstocsv.Properties.Settings.Default.outputdir = OutputDirectory_TextBox.Text;
sheetstocsv.Properties.Settings.Default.spreadsheetID = SpreadsheetID_Textbox.Text;
sheetstocsv.Properties.Settings.Default.Entity = Entity_Texbox.Text;
sheetstocsv.Properties.Settings.Default.headernum = (int)Columns_NumericUpDown.Value;
string headers = "";
for (int i = 0; i < (int)Columns_NumericUpDown.Value; i++)
{
headers = headers + Columns_DataGridView.Rows[0].Cells[i].Value.ToString() + ",";
}
sheetstocsv.Properties.Settings.Default.headers = headers;
sheetstocsv.Properties.Settings.Default.configured = true;
sheetstocsv.Properties.Settings.Default.Save();
MessageBox.Show("Saving Complete!", "Settings",
MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
How I'm trying to do it
Below is a code snippet of how i'm only letting the sheetstocsv project continue if the configuration file has configured it first
//Check for if configured
if (Properties.Settings.Default.configured == false)
{
Console.WriteLine("Program has not been configured yet! Please Run SettingsUI first to start this program.");
Console.ReadLine();
Environment.Exit(0);
}
My Problem
Whenever I run my settingsUI, save, and then run my sheetstocsv project the changes that were supposed to be saved are not. and it will not allow the program move foward.
Edit
Below is the full form .cs that shows how i'm editing the properties of the other project
using sheetstocsv;
namespace SettingsUI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Columns_DataGridView.RowCount = 1;
Columns_DataGridView.ColumnCount =(int)Columns_NumericUpDown.Value;
}
// Event handlers for other UI elemets here.......
private void Save_Button_Click(object sender, EventArgs e)
{
sheetstocsv.Properties.Settings.Default.outputdir = OutputDirectory_TextBox.Text;
sheetstocsv.Properties.Settings.Default.spreadsheetID = SpreadsheetID_Textbox.Text;
sheetstocsv.Properties.Settings.Default.Entity = Entity_Texbox.Text;
sheetstocsv.Properties.Settings.Default.headernum = (int)Columns_NumericUpDown.Value;
string headers = "";
for (int i = 0; i < (int)Columns_NumericUpDown.Value; i++)
{
headers = headers + Columns_DataGridView.Rows[0].Cells[i].Value.ToString() + ",";
}
sheetstocsv.Properties.Settings.Default.headers = headers;
sheetstocsv.Properties.Settings.Default.configured = true;
sheetstocsv.Properties.Settings.Default.Save();
MessageBox.Show("Saving Complete!", "Settings", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
}
}
I don't quite have enough info from your question (see comment from Rufus), but I am guessing that your console app launches your WinForm? If this is the case, what is likely happening is this:
1) Console app starts, and loads the config file into memory.
2) Your Winform launches and changes the config file.
3) Console app overwrites the changes to the config file on exit since it read it before the changes were made.
You could just move the winform into your console project, then just do something like this:
Properties.Settings.Default.outputdir = OutputDirectory_TextBox.Text;
...
Adding More Info (Oct 1):
I must be honest, doing what you're doing is a little strange, and I would really rethink referencing an executable to change its default settings. Just because VS lets you do this, doesn't necessarily mean you should. But if you must, there are a couple other things you can try:
1) You can simply parse the xml settings file and change node values of the settings you want to change. System.Xml namespace will make very short work of this. The post below has good examples:
Change xml node value
2) Try wrapping the code that is currently in your button click event handler with some method inside the console project. This way, you're calling a method that changes the settings, rather than trying to change the settings from an external assembly.
Note: neither of those methods will work if your console app is running while you're making changes to its config file. On shut down, the console app will overwrite the settings file with the settings it loaded into memory when it started up. If the console app isn't running, 1) will definitely work.

C# uwp launch apps

With this code below:
await Windows.System.Launcher.LaunchUriAsync(new Uri(#"ms-windows-store://review/?ProductId=9wzdncrfj2wl"));
I am opening Facebook app in Microsoft Store. There is a Launch button on that page. With it user runs the application.
How can I run the application with it's product ID?
I managed to open app with this code:
await Windows.System.Launcher.LaunchUriAsync(new Uri("fb:post?text=foo"));
But I want to open it with ID.
I have found this code, but it is not working:
await Windows.System.Launcher.LaunchUriAsync(new Uri(#"ms-windows-store://pdp/?ProductId=9wzdncrfj2wl"));
How to open installed app with ID? Or, if there is another way to check if App is installed, if it is then launch it, if it is not then show it in store, so user can install it manually. The app I am developing is Windows 10 UWP...
I have a situation where there is no URI for application, so I have to open it via it's ProductID or ProductFamily...
So, this is the shortcut's target that is opening that app:
C:\Windows\explorer.exe shell:AppsFolder\A88BB54F.N1info_gvc78jvcn5cg0!App
Is there any chance I can use this in UWP app to launch app?
Does anyone got the link from LAUNCH button in Windows Store? So, I would put that in URI, just like link from GET button:
ms-windows-store:PDP?PFN=A88BB54F.N1info_gvc78jvcn5cg0&referrer=unistoreweb&webig=39694073-f9af-436f-a82b-abb9d9f644f0&muid=097C7AA3CA2C6EE22D237359CE2C689A&websession=c9916902dd014ec2b5a9e0390a28c26d
I am using it like this:
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-windows-store:PDP?PFN=A88BB54F.N1info_gvc78jvcn5cg0&referrer=unistoreweb&webig=39694073-f9af-436f-a82b-abb9d9f644f0&muid=097C7AA3CA2C6EE22D237359CE2C689A&websession=c9916902dd014ec2b5a9e0390a28c26d"));
and it is showing app in store.
Thanx.
It is possible by using Package Manager:
using Windows.Management.Deployment;
var app = await GetAppByPackageFamilyNameAsync("Microsoft.WindowsCalculator_8wekyb3d8bbwe");
if(app != null)
{
await app.LaunchAsync();
}
static async Task<AppListEntry> GetAppByPackageFamilyNameAsync(string packageFamilyName)
{
var pkgManager = new PackageManager();
var pkg = pkgManager.FindPackagesForUser("", packageFamilyName).FirstOrDefault();
if (pkg == null) return null;
var apps = await pkg.GetAppListEntriesAsync();
var firstApp = apps.FirstOrDefault();
return firstApp;
}
And add one capability to the Package.appxmanifest :
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns:...
xmlns:rescap = "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="... rescap">
...
<Capabilities>
...
<rescap:Capability Name="packageQuery" />
</Capabilities>
</Package>
Learn more about restricted capabilities: https://learn.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations#restricted-capabilities
If you want to launch one app from another, the target app must have registered URI activation and handle that case. More about that you can read at MSDN.
Lots of apps in the store has registered URI scheme, there are some lists over the internet, like this one, however I'm not sure if it's actual and which apps work with UWP.
Or, if there is another way to check if App is installed, if it is then launch it, if it is not then show it in store, so user can install it manually.
You could call Launcher.QueryUriSupportAsync to see if the app is installed. This method will return LaunchQuerySupportStatus enumeration value, you could decide to open the app or windows store by this value.
Please check the following code for details:
var ret = await Windows.System.Launcher.QueryUriSupportAsync(new Uri("fb:post?text=foo"), Windows.System.LaunchQuerySupportType.Uri);
if (ret == LaunchQuerySupportStatus.Available)
{
await Windows.System.Launcher.LaunchUriAsync(new Uri("fb:post?text=foo"));
}
else
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(#"ms-windows-store://pdp/?ProductId=9wzdncrfj2wl"));
}

Categories