My goal is to actually achieve launching my ClickOnce application in one click (or two I guess). The application has some prerequisites which need to be installed. The normal way of ensuring they are installed that Microsoft provides involves having the user decide whether he has the prerequisites or not and downloading and installing a "setup.exe" which installs them and runs the ClickOnce application. This involves downloading the EXE file (one click), running it (two clicks), then after prerequisites are installed, clicking again to run the ClickOnce application.
I'm trying to reduce this process to one or two clicks:
- Click a link on my website to the ClickOnce .application file.
- Click again to run it.
I have made ANOTHER ClickOnce application, which includes a setup.exe. It checks if the prerequisites are installed, and if they are it runs the other ClickOnce application automatically. If not, it runs the included setup.exe and then runs the other ClickOnce application.
My problem is that when I try to run the other ClickOnce application from this one, it simply opens my web browser and downloads the .application file without running it.
I'm trying to use the following to start the ClickOnce application from inside my C# code:
Process.Start(ApplicationURL);
I just want this to automatically launch the application at ApplicationURL. Is there a way to skip the browser involvement that I'm seeing?
(My question is very similar to Stack Overflow question Run a ClickOnce application from a webpage without user action).
As pointed out in the comments, you can start the iexplore.exe process to launch a ClickOnce application without any dependency on the default browser. You can also launch the ClickOnce application the same way Windows Explorer launches it, using dfshim.dll.
Process.Start("rundll32.exe", "dfshim.dll,ShOpenVerbApplication " + ApplicationURL);
There are a few other articles online that discuss using this strategy, but I did not find any official documenation of dfshim.dll,ShOpenVerbApplication.
Another Stack Overflow question mentions using a custom .exe to install the .NET Framework and then launch a ClickOnce application via ShOpenVerbApplication.
Scott Hanselman discusses ShOpenVerbApplication as the default file mapping for files with the application/x-ms-application MIME type in a post about Firefox and ClickOnce.
Update
As the other Stack Overflow question mentions, you can also use dfshim.dll's LaunchApplication command, which is documented on Microsoft's site. However, that command is not available in some older versions of the .NET Framework.
Have a look at the Microsoft walkthrough for installing manually via InPlaceHostingManager. You have the ability to customize programmatically.
There are at least 2 other methods for launching ClickOnce applications.
One simple method is Process.Start("PresentationHost.exe", "-launchApplication " + ApplicationURL); as documented by Microsoft here.
A more sophisticated method is calling ShellExecuteEx() Win32 API with code like this:
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
info.lpFile = ApplicationURL;
info.nShow = SW_SHOWNORMAL;
info.fMask = SEE_MASK_CLASSNAME;
info.lpClass = "Application.Manifest";
ShellExecuteEx(ref info);
Required Win32 API import and structure definitions can be found here. This method will query registry and run "rundll32.exe dfshim.dll,ShOpenVerbApplication" (or anything else that is configured under HKEY_CLASSES_ROOT\Application.Manifest).
Following are the ways to launch clickonce.application
Approach 1: Launch from URL
Method 1:
string app = "http://domain.xyz/ClickOnce.application";
Process.Start("rundll32.exe", "dfshim.dll,ShOpenVerbApplication " + app);
Method 2:
string app = "http://domain.xyz/ClickOnce.application";
Process.Start("IExplore.exe", app);
Approach 2: Directly launch ClickOnce.application from .application file
string app = "file://C:/ClickOnce.application";
Process.Start("rundll32.exe", "dfshim.dll,ShOpenVerbApplication " + app);
And another option is using Launcher::LaunchUriAsync with LauncherOptions.ContentType = "application/x-ms-application". This is known a "uri direct invoke"
Related
I'm having a hard time trying to figure out what is causing this odd issue.
So I've created a very basic .msi by following the WiX v3 Setup Project documentation, pretty much just the provided template, but no matter what i try, whenever i go to uninstall it via the Windows 10 Apps & Features interface, i keep getting this:
However, when i uninstall via Control Panel\Programs\Programs and Features or by right-clicking and hitting Uninstall in the .msi's Context Menu, it uninstalls fine.
What am i missing here, should i be implementing something within the installer project to support the Apps & Features uninstall?
Thanks in advance.
AVG Anti Virus: In this case it was AVG anti virus. Uninstalling this product made the uninstall run correctly when
invoked from the new settings GUI in Windows 10.
Blocking: Errors like these often mean something is blocking the uninstall. Please disable or uninstall any anti-virus and / or security software and try again.
General Check List: Please check this answer for a "deployment check list" or "mnemonic" to debug similar problems: Deployment Mnemonic (mid-page). Also see comments above. Sweep for malware with Windows Defender Offline.
Microsoft Tips: Windows cannot access the specified device, path, or file...
Make Log File: To debug, always make an MSI log file:
FireGiant: MSI log "how-to"
Installsite: MSI log "how-to"
Exact Error Message: "Error: Windows cannot access the specified device, path, or file. You may not have the appropriate permission to access the item"
I have a click-once application, which is correctly signed, correctly configured and installs itself without any problem.
It is set to run offline, but install from a specific URL, and if I download and run the setup.exe, it installs updates.
So, it's basically all working... except I cannot print out the version number, or trigger an update from in code. If I try, I get the dreaded: 'Application identity is not set.'
2017-01-10 13:43:14.8367 ERROR System.Deployment.Application.InvalidDeploymentException: Application identity is not set.
at System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment()
at LibDataAgent.Internal.Services.UpdateService.Deployment() System.Deployment.Application.InvalidDeploymentException: Application identity is not set.
at System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment()
at LibDataAgent.Internal.Services.UpdateService.Deployment()
I am not running in debug mode, or using a debug build.
So here's my actual question:
How, does the click-once code in System.Deployment.Application, at runtime, determine what the application identity is?
So, there are whole lot other questions around this, but please don't close this as a duplicate, as far as I can tell it is not one.
Here's a list of things I do not want answers for:
How to sign a click-one application.
How to set the application identity as you build.
How to find where the click-once application is installed.
How to make a click-once application work while debugging.
How to check for updates using ApplicationDeployment.
Just very plainly, exactly what does a click-once application do, at runtime that lets it determine the application identity.
Help!
Notes
My (thus far fruitless) attempts to solve this have yielded these notes:
I'm certain this has something to do with how the application is launched, because executing applications from the command line has never worked with click-once; but executing the same application from the start menu will correctly return IsNetworkDeployed as true.
However, I've not been able to determine what the technical difference is, or why one detects the install correctly and one doesn't. (or indeed, why this specific application doesn't work from the start menu, when others with no obvious difference do).
Things I've tried that make no difference include:
the working directory for the application.
launching the application .exe directly or via a shell
launching the application from a new shortcut
There is some kind magic to the 'MyApplication.appref-ms' that goes into the start menu; the appref-ms is just a url to the install path:
http://s3-ap-southeast-1.amazonaws.com/blahblah/Dev/MyApplication.application#MyApplication.application, Culture=neutral, PublicKeyToken=fdasdfsafads, processorArchitecture=x86
...which somehow launches a 'click once aware' instance of the application. But how?
I'll still happily accept an answer that explains how the guts of launching the application actually sets the application context up with an identity, but for now here's my best stab at what's going on for anyone else who finds this question later:
ClickOnce applications are launched by hitting the install url, or by using the .appref-ms file on the start menu, which contains the url.
The application/x-ms-application MIME type handler is invoked for the downloaded file, which launches the 'ClickOnce aware' instance of the application.
At runtime, the ApplicationContext.Identity is used to determine what the ClickOnce details are, and setup the CurrentDeployment object.
As far as anyone knows, directly launching the deployed executable for a ClickOnce application (in C:\Users\Administrator\AppData\Local\Apps\2.0\b107ee1... or whatever the install folder is) will always return IsNetworkDeployed as false, and will not be able to self update.
Practically speaking, this means:
You're looking for the install folder and path to your .exe? Don't bother. Even when you know where it is, it won't have the correct ApplicationContext when you run it.
To spawn a new 'ClickOnce aware' instance of your application launch internet explorer at its install url. You cannot pass command line arguments to it.
eg.
var url = "http://s3-ap-southeast-1.amazonaws.com/blahblah/Dev/MyApplication.application#MyApplication.application, Culture=neutral, PublicKeyToken=fdasdfsafads, processorArchitecture=x86";
var psi = new ProcessStartInfo
{
FileName = #"iexplore",
Arguments = $"\"{url}\"",
};
Process.Start(psi);
(if you want to find the URL, hunt through the start menu for the appref-ms file for the application; the url is contained in it)
...and how does the executable get launched with an identity?
No idea; but this is as far as anyone seems to ever have got with understanding it:
(tldr; magic. Probably something to do with how CreateProcess is invoked to spawn the appliciation using the ApplicationContext api, in some combination of DFsvc.exe. DFshim.dll and DFdll.dll)
(The rest of this information is taken from this old blog post by Ian Picknel: http://ianpicknell.blogspot.com.au/2010/03/launching-clickonce-application.html)
Once the deployment manifest has been
stored in the Temporary Internet Files folder, Internet Explorer then
attempts to establish how it should handle the file with the (assumed
and actual) .application extension. It checks the user-specific file
types at HKCU\Software\Classes and, if it fails to find an
.application sub-key there, checks for machine-specific file types at
HKCR. Via this means it establishes that the .application extension
denotes an Application.Manifest file. It then uses this information to
check the user-specific HKCU\Software\Classes\Application.Manifest and
machine-specific HKCR\Application.Manifest keys to establish the CLSID
of a library which handles Application.Manifest files and yields the
result {98af66e4-aa41-4226-b80f-0b1a8f34eeb4}. Finally, it looks up
this CLSID in the user-specific
HKCU\Software\Classes\CLSID{98af66e4-aa41-4226-b80f-0b1a8f34eeb4} and
machine-specific HKCR\CLSID{98af66e4-aa41-4226-b80f-0b1a8f34eeb4}
paths to establish that .application files are handled by
C:\WINDOWS\system32\DFshim.dll.
This is where things start to get a little complicated. I said earlier
that no 'magic' was happening behind the scenes. Well, whilst that was
true for the process of retrieving the deployment manifest it most
certainly is not true of the process of actually launching the
ClickOnce application once the deployment manifest has been retrieved
and the handler, DFshim.dll, has been identified.
DFshim.dll is described in the registry as the 'Manifest mime handler'
although its file properties describe it as the 'Application
Deployment Support Library'. It implements the Internet Explorer MIME
handler COM interface. It is a native 32-bit DLL, written in Microsoft
Visual C++ 2005, and was installed into C:\Windows\system32 when the
.NET Framework 2.0 was installed.
DFshim.dll has a hard-coded reference to DFdll.dll, which it locates
by checking the values of
HKLM\SOFTWARE\Microsoft.NETFramework\InstallRoot (typically
C:\Windows\Microsoft.NET\Framework) and the keys beneath
HKLM\SOFTWARE\Microsoft.NETFramework\Policy\AppPatch (typically
v2.0.50727, even if a later version of the Framework is installed).
DFdll.dll too is a native 32-bit DLL written in Microsoft Visual C++
2005.
DFdll.dll uses COM services exposed by DFsvc.exe, which is also
located in the .NET Framework folder. DFsvc.exe is a standard .NET
MSIL assembly. DFsvc contains a single Main method (within the
System.Deployment.Application namespace) which simply calls the
internal System.Deployment.Application.DFServiceEntryPoint.Initialize
method within System.Deployment.dll. The Initialize method registers
System.Deployment.Application.DeploymentServiceCom with COM via
System.Runtime.InteropServices.RegistrationServices (i.e. it performs
the equivalent of CoRegisterClassObject in COM) using the CLSID
{33246f92-d56f-4e34-837a-9a49bfc91df3}. This is the means by which its
services are made available to DFdll.dll.
The COM service exposed by
System.Deployment.Application.DeploymentServiceCom delegates the
majority of its methods to other non-ComVisible classes within the
System.Deployment.Application namespace. For example, the public
ActivateDeployment method calls ActivateDeployment on a new
System.Deployment.Application.ApplicationActivator instance, the
public CheckForDeploymentUpdate method calls CheckForDeploymentUpdate
on System.Deployment.Application.SubscriptionStore, etc.
It is clear that the vast majority of the work surrounding the actual
installation and launch of the ClickOnce application is undertaken by
classes within the System.Deployment namespace, hosted within
DFsvc.exe. DFshim.dll and DFdll.dll appear to primarily be responsible
for arbitrating between the COM-based world of Internet Explorer and
the .NET-based world of ClickOnce.
I build a C# Console application that takes a file in parameter, format it and save the result in an other file. I've built the application successfully using ClickOnce. Now I want to be able to click right on a given file and "Send To" my application. As for other application, I've sent the shortcut to the C:\Users\MyUser\AppData\Roaming\Microsoft\Windows\SendTo repository but unlike other shortcut, my application is hidden from the menu. I've tested on few other PC (also running under Windows 7) and I always get the same behavior.
Do you know if it's caused by ClickOnce? Is there a way to solve this issue?
FYI, I finally bypass the ClickOnce solution and use the plain .exe instead. The post below describe how to achieve it:
Why are my binaries not placed in the /bin/release folder when I build a Windows Service in C#?
I have a C# application, and a Visual Studio (2010) Setup Project with it. In my Setup Project, I am using InstallAllUsers = True. Additionally, my application needs to launch at Startup, so a shortcut is being added to the User's Startup Folder.
So I install my application as user Bob (from a share), and then I log off and log on as user Alice. Alice gets a dialogue that says:
The feature you are trying to use is on a network resource that is unavailable.
It provides the network path from which the MSI was installed. So, basically, it's looking for the MSI on a share that Alice doesn't have access to. This is repeatedly reproducible if Alice tries to manually launch the Startup link. However, Alice can directly launch the exe from the installation location, which works fine.
I don't fully understand why it is looking for an MSI, but I guess it makes sense that an application can not be completely installed by one user for another user, so the MSI is needed to complete the installation.
But if that is the case, then I don't understand why the MSI only seems to be required when launching from the Startup link, and not from the exe directly.
My question has several parts:
Is what I am experiencing expected behaviour for the InstallAllUsers option?
If so, how can I ensure that my application is fully installed for all users?
Is it necessary to copy the msi locally, and ensure that it does not get deleted before each user has had a chance to log on?
This is happening because the shortcuts are "advertised shortcuts" hence the greyed out target box.
More info here...
http://www.advancedinstaller.com/user-guide/advertised-shortcuts.html
When an advertised shortcut is launched it validates the checks
associated with the key resources. If any is missing it will fix it by
running the installation package and installing again all information
from the .msi file.
This is why its trying to run the msi.
You can turn off advertised shortcuts by reading this article...
DISABLEADVTSHORTCUTS=1 disables all shortcuts
If its an all users install, then the shortcut will probably want to reside in the all users start menu. Win 7: C:\ProgramData\Microsoft\Windows\Start Menu.
Eric, I would recommend taking a look at the shortcut file contents. You can do this by using Powershell, and there is a SO link to help you on your way:
Editing shortcut (.lnk) properties with Powershell
So here's a brief summary of the problem:
Summary:
I set fileAssociations of my ClickOnce applications, however, they are not registering when I run/update my program (as in, the .bvr files I am trying to associate have no icon and I can't double-click them to start my application).
Extra Info:
I first tried going to Properties -> Publish -> Options -> File Associations and setting my associations from there. After that failed attempt, I tried setting it directly in app.manifest:
<fileAssociation
xmlns="urn:schemas-microsoft-com:clickonce.v1"
extension=".bvr"
description="Behavior File"
progid="GGS.Behavior"
defaultIcon="bvrico.ico"
/>
I've read so many articles about this that I am starting to get frustrated. Some information:
I've set it to a full-trust application (Under security -> Enable ClickOnce security settings -> This is a full trust application)
I am using .NET 4.0 and Visual Studio 2010
Enable "offline" mode
I also handle the data passed through AppDomain, but I doubt that really changes anything.
I was wondering maybe file associations are only set on install.
Anyways, I would really appreciate some insight on this problem. I would really like file associations in my project.
Thanks everyone in advance.
PS:
Tested on Windows XP and Windows 7.
Edit: I also have posted this on Microsoft, btw.
http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/d610cd55-f3c7-4775-a417-251261832200
If anyone would like to post an answer on there as well. I really can't figure this one out. :D
If the filetype you are trying to register has previously been registered with another program (such as notepad), or even opened in notepad, then the only way ive found to get it working is to delete from the registry the filetype in 2 locations, and then reboot. After that publish an update for your app, and if its that sort of problem your in good shape.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.bvr
HKEY_CLASSES_ROOT\.bvr
Reboot
Publish an update to your app.
Are you sure that the following item didn't appear in Open With... file options - Clickonce Application Deployment Support Library with question mark icon? Try setting it as a default.
I had the same problem as yours, but I noticed this strange option appears after I install my application. After so much trying and googling I tried a simple thing - agreed to make this Clickonce Application Deployment Support Library a default for desired file type. And ta da! The file's icon turned into my app icon and it launched my app. I guess the thing was my desired file type was already associated with Notepad, so ClickOnce refused to override this setting.
The only way to get this to work after a ClickOnce deployment is to add/update several registry entries in code. See below.
It's been 8 years, and even in the latest version of Visual Studio 2019 with Windows 10, the file association settings in ClickOnce deployments still don't work. I suspect this is a Windows version issue, as Microsoft seems to change how file associations are stored in the registry with every new version of Windows. But regardless, Visual Studio and ClickOnce should work for the current Windows release. And it does not.
After completing a clean install of my ClickOnce app, I found this one registry entry for my file extension:
Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xxxxxx
And it added this registry entry for the ProgID:
Computer\HKEY_CLASSES_ROOT\xxxxxxxxxxx
But it did not add a reference from the file extension to the ProgID.
These two entries do nothing in Windows 10. It does not show the icon, does not open the ClickOnce app when you open the file with that extension, and does not show any ClickOnce option in the Open With lists (as a commenter suggested). Note that the registry entry above was created by the deployment but contains no reference to the ClickOnce application or the ProgID that was set up in the ClickOnce properties. In fact, the only application referenced in that file extension registry entry is to "OpenWith.exe".
Solution
After spending 2 days researching this issue and piecing together various ideas, the solution below is the only one I found that works. Simply execute SetAssociation() every time your app starts (or at least immediately after an install/upgrade), and it will solve the file association problem in all versions of Windows.
using Microsoft.Win32;
using System;
using System.Collections.Generic;
namespace MyLibrary
{
public class FileAssociations
{
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
private const int SHCNE_ASSOCCHANGED = 0x8000000;
private const int SHCNF_FLUSH = 0x1000;
public static bool SetAssociation(string extension, string progId, string fileTypeDescription, string applicationFilePath)
{
bool madeChanges = false;
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + extension, progId);
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + progId, fileTypeDescription);
madeChanges |= SetKeyDefaultValue($#"Software\Classes\{progId}\shell\open\command", "\"" + applicationFilePath + "\" \"%1\"");
return madeChanges;
}
private static bool SetKeyDefaultValue(string keyPath, string value)
{
using (var key = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (key.GetValue(null) as string != value)
{
key.SetValue(null, value);
return true;
}
}
return false;
}
}
}
Then execute this on program start. The first item in Environment.GetCommandLineArgs() contains the full path to your exe:
string[] args = Environment.GetCommandLineArgs();
FileAssociations.SetAssociation(FileExtension, ProgID, FileExtensionDescription, args[0]);
The only problem I found with this approach is if your file association executes your .exe directly, the ClickOnce automatic updates will not work because the update info is stored in the Start Menu shortcut, not with the installed application files.
That is a different problem than the question you asked, but to get around that, if the app starts up with the .exe directly, I programmatically locate the Start Menu shortcut to the ClickOnce app (.appref-ms extension), then restart the app using that path (instead of the .exe), pass it the original command-line arguments that were sent to your .exe, then when it restarts, I retrieve THOSE arguments using AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData.
This happened when we increased the version number and published the app under a different sub folder.
For example
Version 1.0 was published to \\server\product\version 1.0\
Version 2.0 was published to \\server\product\version 2.0\
I followed the answer of #EvAlex, and a dialog was prompted and I clicked the "Details" button, which opened up a log file. In the log file, I noticed that the program tried to download from a path of a old version, the path it was looking for is still \\server\product\version 1.0\.
Then I realized it should be something in the registry.
I opened up regedit, and did a search for product (which is my product name), and found the path that the log error complained under a few keys like followings:
HKEY_CURRENT_USER\Software\Classes.myext
HKEY_CURRENT_USER\Software\Classes\MyProgId
HKEY_CLASSES_ROOT\MyProgId
I deleted those keys, and repeated the Open With action, the icon is back and it then worked like a charm.
So looks like the app was not cleanly uninstalled or there's bug of ClickOnce updating these keys for some unknown reasons.
ClickOnce does not pass command line arguments the usual way, but through
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData
So in your application startup you will need to check weather it holds any strings and modify your startup-behavior accordingly.
This is because ClickOnce applications are started through the deployment manifest on the server and not through an .exe. Btw: Setting your file associations via Properties -> Publish -> Options -> File Associations should work fine.
Edit: (how I created my sample app)
create windows forms app
activate ClickOnce-security settings, select fully trusted application
in ClickOnce options -> descriptions, set publisher, suite and product name
in ClickOnce options -> file associations add a new extension
publish the app, install the app, test weather it starts when opening a file (it does)
All settings not mentioned have not been touched ... that is all I have done!
Seems like that is exactly what you did - maybe try creating a simple test application and see if that works for you. As this answer does not really answer your question please let me know when you are done so I can delete it.
I found Alan's post above most helpful. It worked as it was written for me on one of my computers, but on another it didn't quite get me there. I was hoping to just add a comment to his post, but Stack Overflow says I don't have the reputation points for that.
If you need it, hope you can still find it here.
I also had to remove a key like the following for each extension mapping:
HKEY_CLASSES_ROOT\AppName.FileType.0
Adding this to Adam's removal of the other .ext keys fixed the problem on my other machine.