I developed a .NET windows application which worked both on Windows 7 and 8.1. Then I added the Toast notification feature that came with Windows 8 (from this question: How can I use the Windows.UI namespace from a regular (Non-Store) Win32 .NET application?).
This also worked, I just had to add:
<PropertyGroup>
<TargetPlatformVersion>8.0</TargetPlatformVersion>
</PropertyGroup>
to the project file.
As I referenced the Windows.winmd file from the Windows 8.1 SDK C:\Program Files (x86)\Windows Kits\8.1\References\CommonConfiguration\Neutral\Windows.winmd, the executable does not start on Windows 7 anymore! I double-click and that's it. No errors, no messages.
As I did not find any solution online, that's where my question comes up: How do I manage to do both: Offer the toast feature to my users AND make the same .exe run on Windows 7?
Thank you in advance!
EDIT
It turns out that though TargetPlatformVersion is set to 8.0, the executable starts on Windows 7 anyway, but crashes as soon as the program tries to load the Windows 8 libraries:
An unhandled exception of type 'System.TypeLoadException' occurred in ToastTester.exe.
Additional information: Could not find Windows Runtime type 'Windows.UI.Notifications.ToastNotificationManager'.
on line Application.Run(new Form1());
In Form1.cs in line 9 I've got using Windows.UI.Notifications;
What is the best way to avoid this exception during runtime, even though it is expected that this executable will run in environments like Windows 7 where the Windows.UI.Notifications namespace is definitely not available?
I designed my own workaround for being able to support Windows 8 toasts and at the same time prevent application crashes due to missing libraries when running on Windows 7. Note: I am using the Singleton design pattern (member INSTANCE), but you can always do it otherwise.
ShellLink.cs is taken from here
Win8Toaster.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
namespace ToastManager
{
class Win8Toaster
{
public const string APPUSERMODELID = "YourCompany.YourApplicationName";
public static string ShortcutLocation;
public static ToastNotifier ToastNotifier;
private static Win8Toaster _INSTANCE = null;
public static Win8Toaster INSTANCE
{
get
{
if (_INSTANCE == null)
{
_INSTANCE = new Win8Toaster();
}
return _INSTANCE;
}
}
public Win8Toaster()
{
ShortcutLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Microsoft\Windows\Start Menu\Programs\YourCompany\YourApplication.lnk");
//We need a start menu shortcut (a ShellLink object) to show toasts.
if (!File.Exists(ShortcutLocation))
{
string directory = Path.GetDirectoryName(ShortcutLocation);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (ShellLink shortcut = new ShellLink())
{
shortcut.TargetPath = System.Reflection.Assembly.GetEntryAssembly().Location;
shortcut.Arguments = "";
shortcut.AppUserModelID = APPUSERMODELID;
shortcut.Save(ShortcutLocation);
}
}
ToastNotifier = ToastNotificationManager.CreateToastNotifier(APPUSERMODELID);
}
public void ShowToast(ToastContent Content)
{
XmlDocument ToastContent = new XmlDocument();
ToastContent.LoadXml("<toast><visual><binding template=\"ToastImageAndText02\"><image id=\"1\" src=\"file:///" + Content.ImagePath + "\"/><text id=\"1\">" + Content.Text1 + "</text><text id=\"2\">" + Content.Text2 + "</text></binding></visual></toast>");
ToastNotification thisToast = new ToastNotification(ToastContent);
ToastNotifier.Show(thisToast);
}
}
}
Toaster.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace ToastManager
{
public static class Toaster
{
private static Win8Toaster ActiveToaster;
public static bool Win8ToasterAvailable = true;
public static void ShowToast(ToastContent Content)
{
if (Win8ToasterAvailable)
{
if (ActiveToaster == null)
{
if (Environment.OSVersion.Version.Major > 6 || Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)
{
try
{
ActiveToaster = Win8Toaster.INSTANCE;
}
catch (Exception ex)
{
Win8ToasterAvailable = false;
}
}
else
{
Win8ToasterAvailable = false;
}
}
ActiveToaster.ShowToast(Content);
}
else
{
//Use alternative notifications because Windows 8 Toasts are not available
}
}
}
//I also wrote my own toast content structure:
public class ToastContent
{
public string ImagePath, Text1, Text2;
public ToastContent(string ImagePath, string Text1, string Text2)
{
this.ImagePath = ImagePath;
this.Text1 = Text1;
this.Text2 = Text2;
}
}
}
Now that you've got the necessary classes, here is how to use it (pretty simple, huh?):
ToastManager.Toaster.ShowToast(new ToastManager.ToastContent(#"..\path\toyour\image.png", "Your Application Name", "Time: " + DateTime.Now.ToLongTimeString()));
This example shows a toast notification with the current system time or nothing if you are on Windows 7.
A design suggestion:
I used WinForms to design a notification window which looks similar to that in Windows 8 and simulates the same functions, just with my own forms. Alternatively you can also implement a tray icon and show some notification bubbles.
Related
I'm attempting to make a C# script to send CPU temp and usage statistics to a raspberry pi (it's an LED cube project). I tried to use Python to do it, but the library it used, psutil, does not support sensor readings on Windows.
I'm using the OpenHardwareMonitorLib dll file to try and get the CPU stats. owever, it throws an error won the lines "Computer computer = new Computer(); computer.Open();". The error is:
"System.MissingMethodException: 'Method not found: 'System.Threading.Mutex System.Threading.Mutex.OpenExisting(System.String, System.Security.AccessControl.MutexRights)'.'"
I've tried everything I can think of and everything I have found on google. I can't remember them all, but these are some of them:
Installing Powershell 7
Adding the SystemManagement.dll file to the project (which got me past the previous error to this one)
Installing the newest .NET 4.8 and all of the extras around it.
Added a requirement to run as Admin to the app manifest.
I've put the code on Github (https://github.com/verdammte/led_cube), but here's the C# code in question:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using OpenHardwareMonitor.Hardware;
namespace Get_CPU_Temp5
{
class Program
{
public class UpdateVisitor : IVisitor
{
public void VisitComputer(IComputer computer)
{
computer.Traverse(this);
}
public void VisitHardware(IHardware hardware)
{
hardware.Update();
foreach (IHardware subHardware in hardware.SubHardware) subHardware.Accept(this);
}
public void VisitSensor(ISensor sensor) { }
public void VisitParameter(IParameter parameter) { }
}
static void GetSystemInfo()
{
UpdateVisitor updateVisitor = new UpdateVisitor();
Computer computer = new Computer();
computer.Open();
computer.CPUEnabled = true;
computer.Accept(updateVisitor);
for (int i = 0; i < computer.Hardware.Length; i++)
{
if (computer.Hardware[i].HardwareType == HardwareType.CPU)
{
for (int j = 0; j < computer.Hardware[i].Sensors.Length; j++)
{
if (computer.Hardware[i].Sensors[j].SensorType == SensorType.Temperature)
Console.WriteLine(computer.Hardware[i].Sensors[j].Name + ":" + computer.Hardware[i].Sensors[j].Value.ToString() + "\r");
}
}
}
computer.Close();
}
static void Main(string[] args)
{
while (true)
{
GetSystemInfo();
}
}
}
}
All I want to do is get the CPU usage and CPU temperature and send them to a remote IP. I feel like this shouldn't be this hard.
Based off your repository it looks like your console app is targeting .NET Core but that .dll you are referencing is in .NET Framework. Try creating your project in .NET Framework 4.5+.
I'm trying to Create an installer for my CAD plugin, and need to get the AutoCAD install location. but the return values of RegistryKey.GetSubKeyNames() is different from what I see in Registry Editor.
string registry_key = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
Console.WriteLine(subkey_name);
}
}
output:
AddressBook
Autodesk Application Manager
Autodesk Content Service
Autodesk Material Library 2015
Autodesk Material Library Base Resolution Image Library 2015
Connection Manager
DirectDrawEx
DXM_Runtime
f528b707
Fontcore
...
In Registry Editor:
animizvideocn_is1
AutoCAD 2015
Autodesk 360
Connection Manager
...
AutoCAD 2015 is what i need
Your installer seems to be a 32 bit application, or at least runs as a 32 bit process.
Therefore, Windows redirects
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
to
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
To access the non redirected node, follow the instructions here.
This might be not a direct answer to your question, but i had to do the same thing. I was not looking at the registry, but the Program Files directory. It will then add the netload command to the autoload lisp file. It will install a list of Plugin dlls to all installed autocad versions. This can easily be changed... Hopefully it helps.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace AMU.AutoCAD.Update
{
public class AutoCadPluginInstaller
{
private static readonly Regex AutoloadFilenameRegex = new Regex("acad([\\d])*.lsp");
public void Install(IEnumerable<string> pluginFiles)
{
var acadDirs = this.GetAcadInstallationPaths();
var autoloadFiles = acadDirs.Select(this.GetAutoloadFile);
foreach (var autoloadFile in autoloadFiles)
this.InstallIntoAutoloadFile(autoloadFile, pluginFiles);
}
private void InstallIntoAutoloadFile(string autoloadFile, IEnumerable<string> pluginFiles)
{
try
{
var content = File.ReadAllLines(autoloadFile).ToList();
foreach (var pluginFile in pluginFiles)
{
var loadLine = this.BuildLoadLine(pluginFile);
if(!content.Contains(loadLine))
content.Add(loadLine);
}
File.WriteAllLines(autoloadFile, content);
}
catch (Exception ex)
{
//log.Log();
}
}
private string BuildLoadLine(string pluginFile)
{
pluginFile = pluginFile.Replace(#"\", "/");
return $"(command \"_netload\" \"{pluginFile}\")";
}
private IEnumerable<string> GetAcadInstallationPaths()
{
var programDirs =
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
var autoDeskDir = Path.Combine(programDirs, "Autodesk");
if (!Directory.Exists(autoDeskDir))
return null;
return Directory.EnumerateDirectories(autoDeskDir)
.Where(d => d.Contains("AutoCAD"));
}
private string GetAutoloadFile(string acadDir)
{
var supportDir = Path.Combine(acadDir, "Support");
var supportFiles = Directory.EnumerateFiles(supportDir);
return supportFiles.FirstOrDefault(this.IsSupportFile);
}
private bool IsSupportFile(string path)
=> AutoloadFilenameRegex.IsMatch(Path.GetFileName(path));
}
}
(see here: https://gist.github.com/felixalmesberger/4ff8ed27f66f872face4368a13123fff)
You can use it like this:
var installer = new AutoCadPluginInstaller();
installer.Install(new [] {"Path to dll"});
Have fun.
I want to make a USSD call in a xamarin crossplatform app using C# and i have no idea where to start. All the examples i have seen is done in java. Is it possible to successfully dial a USSD code like *270# within my app without opening the dialer? If yes, please how? I'll be very grateful for any help
To do it with Xamarin forms you have to create a custom renderer for android :
Create an interface in your shared project :
public interface IUssdRenderer
{
void StartTransaction();
}
then in your android project :
using System;
using Android.Content;
using Android.OS;
using ussd.Renderers;
[assembly: Xamarin.Forms.Dependency(typeof(IUssdRenderer))]
namespace ussd.Droid.Renderers
{
public class UssdRenderer : IUssdRenderer
{
public Android.Net.Uri createUriFromString(string ussd)
{
String uri = "tel:";
foreach (char c in ussd.ToCharArray())
{
if (c == '#')
{
uri += Android.Net.Uri.Encode("#");
}
else
{
uri += c;
}
}
return Android.Net.Uri.Parse(uri);
}
public void StartTransaction()
{
var intent = new Intent(Intent.ActionCall, createUriFromString("*270#"));
Context ctx = Xamarin.Forms.Forms.Context;
ctx.StartActivity(intent);
}
}
}
You'll also have to register you interface, I'm using prism :
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register< IUssdRenderer, UssdRenderer>();
}
The last bit is to make sure you have Call permissions enabled :
Right click on Android Project > Options > Android application
make sure CallPhone is selected
You may use Xamarin.Essentials PhoneDialer, to make a call, note that since USSD contains '#' you need to URL-encode it. example:
string code = "*123#";
PhoneDialer.Open(HttpUtility.UrlEncode(code));
After collecting from left and right I finally found the solution to my question with CrossMessaging plugin.
The steps are as follow:
Create your project and give the name
Install from nuget Xam.Plugins.Messaging in your project
Add below line in Android project MainActivity's OnCreate method:
CrossMessaging.Current.Settings().Phone.AutoDial = true;
Add android.permission.CALL_PHONE to the manifest file.
Make calls as follow
try
{
var phonedialer = CrossMessaging.Current.PhoneDialer;
if (phonedialer.CanMakePhoneCall)
{
//Ussd call's
phonedialer.MakePhoneCall(HttpUtility.UrlEncode("#150#"));
//For normal calls
phonedialer.MakePhoneCall("9111111111")
}
}
catch (Exception exc)
{
await DisplayAlert("Error!!!!", exc.ToString(), "ok");
}
I want to add a Product Version to a Form.[assembly: AssemblyVersion("1.0.*")]
string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
This is the solution I found, and which will work. But the Version will be like "1.0.6262.26540".
Can I change the Rule or can I get the Publish Version which Visual Studio generates programmatically?
You could use ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString(). However, this will only work if you are running a version of your program that was installed by the ClickOnce publisher installer (ApplicationDeployment.IsNetworkDeployed returns true).
When you start the compiled assembly directly (e.g. during debugging), you will get an InvalidDeploymentException when trying to access the CurrentDeployment property. To safeguard against this, you can use something like this:
string CurrentVersion
{
get
{
return ApplicationDeployment.IsNetworkDeployed
? ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString()
: "1.0.0.0"; // Fallback version string, or retrieve from assembly as in your question
}
}
If you are not using the ClickOnce Publish function to distribute your software I am not sure that you can expect to access the "Publish Version".
using System;
using System.IO;
using System.Linq;
namespace MyAssemblyInfoPatcher
{
internal class Program
{
static void Main(string[] args)
{
if (args.Length > 0)
{
string path = args[0].ToString();
Console.WriteLine(string.Format("Current App version is set to: {0}", path));
string now_date = DateTime.Now.ToString("yyyy.MM.dd.HHmm");
if (File.Exists(path))
{
string _AssemblyVersion = string.Empty;
string _AssemblyFileVersion = string.Empty;
var lines = File.ReadLines(string.Format(path));
for (int i = 0; i < lines.Count(); i++)
{
if (lines.ElementAt(i).ToString().StartsWith("[assembly: AssemblyVersion"))
{
_AssemblyVersion = lines.ElementAt(i).ToString();
}
else if (lines.ElementAt(i).ToString().StartsWith("[assembly: AssemblyFileVersion"))
{
_AssemblyFileVersion = lines.ElementAt(i).ToString();
}
}
string _replace_assembly = File.ReadAllText(path);
if (_AssemblyVersion != string.Empty)
{
_replace_assembly = _replace_assembly.Replace(_AssemblyVersion, string.Format("[assembly: AssemblyVersion(\"{0}\")]", now_date));
}
if (_AssemblyFileVersion != string.Empty)
{
_replace_assembly = _replace_assembly.Replace(_AssemblyFileVersion, string.Format("[assembly: AssemblyFileVersion(\"{0}\")]", now_date));
}
File.WriteAllText(path, _replace_assembly);
}
}
}
}
}
Above the programs code, you can create a console application and in Project Properties > Build Events, add a "Pre-build event command line" like this: "D:\SomePath\MyAssemblyInfoPatcher.exe" "$(ProjectDir)Properties\AssemblyInfo.cs"
I want to run the Skeinforge slicer program written in Python inside my Windows Phone 8 C# application. I have determined that I should probably use IronPython to do this, I have already determined that I can run Skeinforge inside the ipy.exe terminal I got when I installed IronPython. My problem though is that I am struggling to figure out how to host and run a Python script with IronPython inside Windows Phone 8. I have also already managed to get a simple hello world script running inside a Desktop Windows Forms application that transfers the applications console output to the Debug console with the following code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DebugWriter debugW = new DebugWriter();
Console.SetOut(debugW);
}
private void button1_Click(object sender, EventArgs e)
{
TextWriter tw = new StreamWriter("Test.py");
tw.Write(scriptBox.Text);
tw.Close();
try
{
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("Test.py");
}
catch (Exception ex)
{
Debug.WriteLine("Error: " + ex.Message);
}
}
}
}
And this is the DebugWriter:
class DebugWriter : TextWriter
{
private StringBuilder content = new StringBuilder();
public DebugWriter()
{
Debug.WriteLine("Writing console to debug");
}
public override void Write(char value)
{
base.Write(value);
if (value == '\n')
{
Debug.WriteLine(content.ToString());
content = new StringBuilder();
}
else
{
content.Append(value);
}
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
I have no idea how to even add the IronPython libraries to my Windows Phone 8 application though as the standard libraries won't import. I have though tried compiling the apparently now defunct Windows Phone 7 libraries with the master source code and I can import these libraries, but I get absolutely no response on the debug terminal when I try to run my hello world script.
Do any of you have any idea how to get this woring in Windows Phone 8, if you know how to do this in Windows 8/Metro/RT then that would also probably work for WP8.
UPDATE:
I have looked at the debug output again and I seem to get this error when trying to use the WP7 libraries to run a hello world script:
A first chance exception of type 'System.NotImplementedException' occurred in Microsoft.Scripting.DLL
Error: The method or operation is not implemented.
I managed to get Skeinforge running on a modified version of IPY. You can get the source for my application here: http://skeinforgewp8.codeplex.com/