Squirrel.windows create multi shortcut on desktop - c#

We are trying create an installer using Squirrel.Windows for our .NET application
The application contain multi .exe files.
We using command:
squirrel --releasify BIN_PATH\MyApp.2.0.33404.nupkg
However, when run the setup.exe, it create multi shortcut on desktops correspond to multi .exe files
How to specify create one shortcut only ?

The Squirrel documentation states that creating a shortcut for every EXE in your package is the default behaviour.
That same documentation page explains that to override the default behaviour you need to make at least one of your EXE Squirrel aware and then implement the Squirrel event handlers as you want to.
You better make the one EXE you want a shortcut for Squirrel aware by adding the following to its AssemblyInfo.cs:
[assembly: AssemblyMetadata("SquirrelAwareVersion", "1")]
Then in your EXE implement Squirrel events like that:
static bool ShowTheWelcomeWizard;
...
static int Main(string[] args)
{
// NB: Note here that HandleEvents is being called as early in startup
// as possible in the app. This is very important! Do _not_ call this
// method as part of your app's "check for updates" code.
using (var mgr = new UpdateManager(updateUrl))
{
// Note, in most of these scenarios, the app exits after this method
// completes!
SquirrelAwareApp.HandleEvents(
onInitialInstall: v => mgr.CreateShortcutForThisExe(),
onAppUpdate: v => mgr.CreateShortcutForThisExe(),
onAppUninstall: v => mgr.RemoveShortcutForThisExe(),
onFirstRun: () => ShowTheWelcomeWizard = true);
}
}

Related

ToastActivatorCLSID missing from AppUserModel

im an currently following this guide to add windows toast notifications to my app.
https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop
i need to create a shortcut that contains the System.AppUserModel.ID and System.AppUserModel.ToastActivatorCLSID.
now the website says to just have your installer create this and they recommend using WIX. which is fine but i would rather just create the shortcut from C# code.
so there is this example that creates the shortcut via C#.
https://code.msdn.microsoft.com/windowsdesktop/sending-toast-notifications-71e230a2
but it only shows adding the AppUserModel.ID and not the ToastActivatorCLSID...
here is some of that code...
private void InstallShortcut(String shortcutPath)
{
// Find the path to the current executable
String exePath = Process.GetCurrentProcess().MainModule.FileName;
IShellLinkW newShortcut = (IShellLinkW)new CShellLink();
// Create a shortcut to the exe
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));
// Open the shortcut property store, set the AppUserModelId property
IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;
using (PropVariant appId = new PropVariant(APP_ID))
{
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(System Properties.System.AppUserModel.ID, appId));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
// Commit the shortcut to disk
IPersistFile newShortcutSave = (IPersistFile)newShortcut;
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true));
}
the thing is that the AppUserModel does not have a ToastActivatorCLSID property. seems strange.
i figured i could just add another using block to add the ToastActivatorCLSID property like this
using (PropVariant clsId = new PropVariant(CLSID))
{
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(System Properties.System.AppUserModel.ToastActivatorCLSID, CLSID));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
but the SystemProperties.System.AppUserModel.ToastActivatorCLSID doesnt exist.
right now the appuser model is coming from Microsoft.WindowsAPICodePack.Shell.PropertySystem.
this page shows it should exist
https://learn.microsoft.com/en-us/windows/desktop/properties/props-system-appusermodel-toastactivatorclsid
i would if with that information i could make some kind of interface or something to add in that ToastActivatorCLSID.
there is very little on the internet on this topic. Don't know if there is a different reference or something.
any help would be great
I've been running into the same problem and I've found a work around.
Clone this github repo: https://github.com/aybe/Windows-API-Code-Pack-1.1
open the WindowsAPICodePack12.sln in visual studio.
open Shell > PropertySystem > SystemProperties.cs
find the AppUserModel class on line 2302
add this code to the class:
public static PropertyKey ToastActivatorCLSID
{
get
{
PropertyKey key = new PropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 26);
return key;
}
}
Build the project. Open the bin folder (where the built projects go) and find Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll
Reference these dlls in your project instead of the original code pack.

Create a Shell ContextMenu by right clicking on Desktop or Directory Background

The .NET Shell extension framework called SharpShell is great; I've developed a right-click file Shell ContextMenu "quite easily" that works selecting both files and directories.
Now I would like to develop a Shell ContextMenu by righ-clicking on an empty space (that is, on the Desktop or on a white spot while I'm inside a folder).
Is it possible do that still using SharpShell? Or do I need to move to a different solution?... and in 2nd case... what do you suggest?
Thanks
The two solutions presented below work, but in the meantime I have found that there is an easier solution that is actually already used in the samples that come with SharpShell.
See the CopyDirectoryLocationHandler class as an example of a context menu handler that is registered for the directory background (and the desktop):
[ComVisible(true)]
[COMServerAssociation(AssociationType.Class, #"Directory\Background")]
public class CopyDirectoryLocationHandler : SharpContextMenu
{
// ...
}
If you want the handler to only handle clicks on the desktop background, use this code instead:
[ComVisible(true)]
[COMServerAssociation(AssociationType.Class, #"DesktopBackground")]
public class CopyDirectoryLocationHandler : SharpContextMenu
{
// ...
}
Old obsolete answer:
You can use SharpShell for this purpose without problem. There are two possible approaches:
Register the Shell Extension to handle the folder background
yourself
or
Modify SharpShell to handle the registration of the
extension for the folder background for you.
Register the Shell Extension to handle the folder background yourself
Your shell extension is a COM server and as such is identified to the system via a GUID. This GUID is then used at places in the registry to register the COM extension for different purposes. When we manually want to register the extension for a purpose such as extending the context menu for folder backgrounds, it is best when our extension has a fixed GUID.
Currently your class looks like this:
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
public class MyContextMenuExtension : SharpContextMenu
{
When compiling, the compiler will automatically generate a GUID to use for that class. But we can specify a specific one to use like this:
[Guid("A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0")]
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
public class MyContextMenuExtension : SharpContextMenu
{
Do not use the same GUID as shown here but create your own unique one in Visual Studio via Menu Tools > Create GUID. Use a different GUID for every shell extension you write.
Then recompile the dll and install and register it again (using regasm or the SharpShell Server Manager tool.
Then create a text file named "registry.reg" with the following content (use your own specific GUID). Instead of "MyContextMenuExtension" specify the name of your extension.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\MyContextMenuExtension]
#="{A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0}"
Install the "registry.reg" file by double clicking. Your extension should now be active when you open the context menu for a folder background or the Desktop.
Instead of using the *.reg file, you can also make the changes manually using registry editor or if you have an installer instruct the installer to make those registry changes.
Modify SharpShell to handle the registration of the extension for the folder background for you
Make the following changes to the SharpShell source code:
In the file AssociationType.cs add a new enum value to the AssociationType enumeration:
/// <summary>
/// Create an association to the unknown files class.
/// </summary>
UnknownFiles,
/// <summary>
/// Create an association to the background of folders and the desktop
/// </summary>
DirectoryBackground
In the file ServerRegistrationManager.cs add a new private string constant:
/// <summary>
/// The 'directory' special class.
/// </summary>
private const string SpecialClass_Directory = #"Directory";
/// <summary>
/// The 'directory background' special class.
/// </summary>
private const string SpecialClass_DirectoryBackground = #"Directory\Background";
Also in the file ServerRegistrationManager.cs in the method CreateClassNamesForAssociations in the big switch statement add a new case like this:
case AssociationType.Directory:
// Return the directory class.
return new[] { SpecialClass_Directory };
case AssociationType.DirectoryBackground:
// Return the directory background class.
return new[] { SpecialClass_DirectoryBackground };
Finally you only have to tell your own extension class to use this new enumeration value:
[Guid("A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0")]
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
[COMServerAssociation(AssociationType.DirectoryBackground)]
public class MyContextMenuExtension : SharpContextMenu
{
I have used SharpShell some time ago, forgotten it since then (because it works flawlessly). I have used it on files and folders, so your question intrigued me. A little research on the tool led me to the answer No(unfortunately).
The binding is done through the com server associations on SharpShell. And by looking at the documentation of the com server associations I am not seeing the way to your desired functionality.
PS: I encourage you to leave a comment on the documentation page, or contact directly with the author of the library. He seems to be really helpful(I've contacted him before).

How to add content to windows taskbar

I have been trying to figure out, for some time, how to add content to the taskbar next to the clock. You can see examples of this in NetSpeedMonitor or NetWorx. I am a noob to taskbar but I cannot find anything no matter how hard I look. I am not interested in ThumbButtonInfo or NotifyIcon. Here are a a couple examples of content in the taskbar(incase I'm not saying this right). I would like so be able to do something similar to the first one:
Thank you,
Rymn
There's an example in codeproject showing Extending Explorer with Band Objects using .NET and Windows Forms
Build a Release version of BandObjectLib and register it in the Global
Assembly Cache. The easiest way to do this is to open
BandObjectLib.sln in Visual Studio, set the active configuration to
Release and select 'Rebuild Solution' from the 'Build' menu. The
second project in the solution - RegisterLib - is a C++ utility
project that performs the 'gacutil /if BandObjectLib.dll' command that
puts assembly into GAC.
As you probably already know, Band Objects are COM components. And for
the .NET framework to find an assembly that implements a COM component
it must be either be registered in the GAC or located in the directory
of the client application. There are two possible client applications
for Band Objects - explorer.exe and iexplorer.exe. Explorer is located
in the windows directory and IE somewhere inside 'Program Files'. So
GAC is actually the only one option in this case. Thus .NET assemblies
that implement Band Objects should be registered in GAC and all
libraries they depend on - like BandObjectLib.dll - should also be
there.
Assemblies in the GAC must have strong names and thus key pairs are
required. I have provided the BandObjects.snk file with a key pair but
I encourage you to replace it with your own. See the sn.exe tool for
more details.
Create a new Windows Control Library project and call it SampleBars.
We are going to rely on the base functionality of BandObjectLib so we
have to add a reference to BandObjectLib\Relase\bin\BandObjectLib.dll.
As we are developing a 'Hello World Bar', rename UserControl1.cs and
the UserControl1 class inside it appropriately - into HelloWolrdBar.cs
and HelloWorldBar. Also put the following lines at the beginning of
HelloWorldBar.cs:
using BandObjectLib;
using System.Runtime.InteropServices;
Make HelloWorldBar class inherit BandObject instead of
System.Windows.Forms.UserControl. As I mentioned earlier, Band Objects
are COM components so we should use the Guid attribute. Use
guidgen.exe to generate your unique GUID or you can use the one I have
generated for you:
[Guid("AE07101B-46D4-4a98-AF68-0333EA26E113")]
We also have to sign our assembly with a strong name. You can do this
by putting the following line into AssemblyInfo.cs file:
[assembly: AssemblyKeyFile(#"..\..\..\BandObjects.snk")]
Now its time to decide what kind of Band Object we want to develop.
Lets make it an Explorer Toolbar as well as a Horizontal Explorer Bar
(also known as a Browser Communication Band). All we need to do to
implement this decision is to add custom BandObject attribute to our
HelloWorldBar class:
[Guid("AE07101B-46D4-4a98-AF68-0333EA26E113")]
[BandObject("Hello World Bar",
BandObjectStyle.Horizontal | BandObjectStyle.ExplorerToolbar,
HelpText = "Shows bar that says hello.")]
public class HelloWorldBar : BandObject
{ ...
That's enough to make our control available through 'View->Explorer
Bars' and 'View->Toolbars' explorer menus. It also takes care of menu
item text - "Hello World Bar", and hen the menu item is highlighted
status bar displays "Shows bar that says hello.". Don't you like
declarative programming and custom attributes?
Now it is time to open HelloWorldBar.cs in the Visual Studio Designer
and put some controls on it. Although in my version of HelloWorldBar I
decided to put a single button with 'Say Hello' caption on it you are
free to do something more personalized. I made the size of the button
equal to the size of the control's client area and also set its Anchor
property to the combination of all possible styles - 'Top, Bottom,
Left, Right'. The background color is 'HotTrack' and ForeColor is
'Info'.
The BandObject control has several properties specific to the Band
Objects (and so classes derived from it) - Title , MinSize, MaxSize
and IntegralSize. I set Title for HelloWorldBar to "Hello Bar" and
both MinSize and Size to '150, 24'. Oh, and in button's On Click event
handler I put code that displays a message box. This is what my final
code looks like (and most of it was generated by VS.Net):
using System;
using System.ComponentModel;
using System.Windows.Forms;
using BandObjectLib;
using System.Runtime.InteropServices;
namespace SampleBars
{
[Guid("AE07101B-46D4-4a98-AF68-0333EA26E113")]
[BandObject("Hello World Bar", BandObjectStyle.Horizontal
| BandObjectStyle.ExplorerToolbar, HelpText = "Shows bar that says hello.")]
public class HelloWorldBar : BandObject
{
private System.Windows.Forms.Button button1;
private System.ComponentModel.Container components = null;
public HelloWorldBar()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#region Component Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Anchor = (((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right);
this.button1.BackColor = System.Drawing.SystemColors.HotTrack;
this.button1.ForeColor = System.Drawing.SystemColors.Info;
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(150, 24);
this.button1.TabIndex = 0;
this.button1.Text = "Say Hello";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// HelloWorldBar
//
this.Controls.AddRange(new System.Windows.Forms.Control[] { this.button1 });
this.MinSize = new System.Drawing.Size(150, 24);
this.Name = "HelloWorldBar";
this.Size = new System.Drawing.Size(150, 24);
this.Title = "Hello Bar";
this.ResumeLayout(false);
}
#endregion
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("Hello, World!");
}
}
}
Ok, now we are ready to build SampleBars.dll but its not enough to see
it in explorer yet. We have to put our assembly into the GAC as well
as register it as a COM server. There are tools - gacutil.exe and
regasm.exe that do just this. The C++ utility project named Register
in my version of the SampleBars solution liberates me from using these
tools manually. It has no files in it, just the following post-build
command (debug version):
cd $(ProjectDir)..\bin\Debug
gacutil /if SampleBars.dll
regasm SampleBars.dll
Of cause you have to make sure that Register project is the last one
to be built in the solution using Project Dependencies / Build Order.
After building the solution, and executing the gacutil and regasm
commands, we are finally ready to start Explorer and see our toolbar
and explorer bar. And if you did everything right you should be able
to see something like the picture at the top of the article. On this
picture you can also see how HelloWorldBar looks in the Windows
Taskbar. To achieve this all you need to do is to modify BandObject
attribute adding the BandObjectStyle.TaskbarToolBar flag.
Also you might want to look at this answer
According to Microsoft, Deskbands are not recommended for Windows
7, although they still work. Also keep in mind that Microsoft
requires that Deskbands support Aero on Windows 7 via IDeskband2
Interface, rather than IDeskband. Also, Micorosft has officially said
that IDeskBand2 may be altered or unavailable in subsequent versions
of the operating system or product.
Finally, be very careful about creating shell extensions in managed
code.

Using Reflection to use a namespace on certain OSs

I'm creating a program that uses the CodeProject CoreAudioApi (pretty popular framework for manipulating audio), but the problem is the CoreAudioApi uses system calls that aren't available in any versions of Windows earlier than Vista. If I run a program with CoreAudioApi compiled with it (using a using statement as normal), the program will crash on anything earlier than Vista.
I've created this function to get the version number of the current environment:
win_version = Environment.OSVersion.Version.Major;
That returns the major version number I need. '6' is Vista/7, anything else is not, which is all I need to determine. Utilizing this, I need to determine whether or not to include the CoreAudioApi namespace if the OS is over or equal to '6'. From research, usings need to be compiled with the program, but I've also read about something called Reflection - which might be what I need.
Once I get the CoreAudioApi namespace using'd (sorry for the lack of terminology), the rest is easy. How can I do this?
TL;DR
I need some form of code that would effectively do this:
using System;
using System.Text;
//etc
if(currentWindowsVersion>=6) using CoreAudioApi;
Except control structures won't work outside of a class, and all namespaces are compiled with the program, not controlled individually.
Thanks!
EDIT: So far, I'm using this to load the CoreAudioApi namespace as a compiled assembly:
if(win_version>=6){
CoreAudioApi = Assembly.LoadFrom("CoreAudio.dll");
CoreAudioApi.GetLoadedModules();
CoreAudioApi.GetTypes();
MessageBox.Show("Loaded CoreAudioApi");
}
From here, what I need to do is actually use the types, and methods from the API. My code that works on Windows Vista/7 is this:
public static MMDeviceEnumerator devEnum;
public static MMDevice defaultDevice;
//later in a mute method:
defaultDevice.AudioEndpointVolume.Mute = true/false;
I don't even really need devEnum AFAIK, so really the only important lines are the last two (besides the comment).
I've just tried the following:
Create a new console application project
Add the CoreAudioApi project from CodeProject to the solution
Add a project reference to CoreAudioApi in my console app
Create the following classes:
interface IAudio { void SetVolume(float level); }
class XpAudio : IAudio {
public void SetVolume(float level) {
// I do nothing, but this is where your old-style code would go
}
}
class VistaAudio : IAudio {
public void SetVolume(float level) {
MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
MMDevice defaultDevice = devEnum
.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
defaultDevice.AudioEndpointVolume.MasterVolumeLevel = level;
}
}
class Program {
static void Main(string[] args) {
IAudio setter = Environment.OSVersion.Version.Major >= 6
? (IAudio)new VistaAudio()
: (IAudio)new XpAudio();
float val = float.Parse(Console.ReadLine());
setter.SetVolume(val);
Console.ReadLine();
}
}
This runs on both my server (~ Windows 7) and local (Windows XP) machines. On my XP machine it'll happily take in a value and ignore it; on my server, it throws an exception, (presumably because I don't have a sound output). If I make my XP machine run the CoreAudioApi, I get an exception when I input a value, not before.
The question is, what are you doing differently to make your application break? Are you using CoreAudioApi code at startup?
EDIT: After seeing your edit, if you do this, you shouldn't need to mess about with Assembly.LoadFrom at all. The framework should dynamically load that assembly if (and only if) and when it needs to.
COREAUDIOAPI.dll does not work on XP or earlier, because they cant handle MMDEVICE API (Device Enumeration). I dont know about Vista.

Why do 'requires' statements fail when loading (iron)ruby script via a C# program?

IronRuby and VS2010 noob question:
I'm trying to do a spike to test the feasibility of interop between a C# project and an existing RubyGem rather than re-invent that particular wheel in .net. I've downloaded and installed IronRuby and the RubyGems package, as well as the gem I'd ultimately like to use.
Running .rb files or working in the iirb Ruby console is without problems. I can load the both the RubyGems package, and the gem itself and use it, so, at least for that use case, my environment is set up correctly.
However, when I try to do the same sort of thing from within a C# (4.0) console app, it complains about the very first line:
require 'RubyGems'
With the error:
no such file to load -- rubygems
My Console app looks like this:
using System;
using IronRuby;
namespace RubyInteropSpike
{
class Program
{
static void Main(string[] args)
{
var runtime = Ruby.CreateRuntime();
var scope = runtime.ExecuteFile("test.rb");
Console.ReadKey();
}
}
}
Removing the dependencies and just doing some basic self-contained Ruby stuff works fine, but including any kind of 'requires' statement seems to cause it to fail.
I'm hoping that I just need to pass some additional information (paths, etc) to the ruby runtime when I create it, and really hoping that this isn't some kind of limitation, because that would make me sad.
Short answer: Yes, this will work how you want it to.You need to use the engine's SetSearchPaths method to do what you wish.
A more complete example
(Assumes you loaded your IronRuby to C:\IronRubyRC2 as the root install dir)
var engine = IronRuby.Ruby.CreateEngine();
engine.SetSearchPaths(new[] {
#"C:\IronRubyRC2\Lib\ironruby",
#"C:\IronRubyRC2\Lib\ruby\1.8",
#"C:\IronRubyRC2\Lib\ruby\site_ruby\1.8"
});
engine.Execute("require 'rubygems'"); // without SetSearchPaths, you get a LoadError
/*
engine.Execute("require 'restclient'"); // install through igem, then check with igem list
engine.Execute("puts RestClient.get('http://localhost/').body");
*/
Console.ReadKey();

Categories