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.
Related
I have a predominantly managed c# .net 4.6 app that is trying to call into a c++ dll (that is also built as part of the solution), Visual Studio 2017.
I get this exception:
"System.IO.FileNotFoundException: 'Could not load file or assembly 'XXX.dll' or one of its dependencies. The specified module could not be found.'"
There doesn't appear to be any more information. The dll is in our binaries directory with all our other dlls that load fine, and the exe that is being run in the debugging session (The start up project).
I've checked the solution configuration manager, its building the dll as x64 in our Debug|Any CPU config, which makes sense.
No one else in the office has this issue, and I didn't have this issue on my old computer (I'm getting now trying to set up my new dev computer, which is the exact same specs as the old one) Windows 10.
I have the WindowsSDK 8.1 and 10 installed, and the vs c++ redistributes.
Any Ideas? Or atleast, and idea how I can get more info about whats actually going wrong? (This exception is frustratingly vague).
You’d want to use Fuslogvw.exe, the assembly binding log viewer. It can be started by launching the Developer Command Prompt for VS. The first thing is to enable logging, and the simplest way is to run as administrator (as a comment to an answer here correctly points out); you can alternatively set the ForceLog regkey specified here if running as a regular user account.
There's also a small video I did a while back to test how this works. You can find this in this article. Just scroll to the bottom, it's within the 2nd question starting from the end.
If that doesn't work, run Process Monitor and set a filter for your specific .dll and see what the CLR does in detail when searching for it.
I've got an application written with C# which is installed via InnoSetup.
With enabled Controlled Folder Access of Windows 10's Defender, the setup fails to create a desktop icon (showing the message PersistFile::Save failed, code 0x80070002) -- despite running with administrative privileges.
Furthermore the installed application (which is NOT run with administrative privileges) fails to write to folders of the user, e.g. Documents.
This happens even for new files or folders, which do not overwrite anything existing. For ransomware protection I'd expect that only modifying existing files is considered dangerous.
The first question that comes to my mind is: Why is my application considered to be harmful and is thus being blocked?
I've done extensive research to get an answer, but could not find anything that helps:
Web resources from Mircosoft describing which applications are considered harmful and why cannot be found -- only guesswork there.
Signing my application with an Extended Validation Certificate (which helps suppressing SmartScreen warnings) does not change Windows Defender's behavior.
I let the Windows App Certification Kit analyze the installer -- which reported several warnings and one critical error. I fixed all issues the Kit complained about (except the warning regarding /SAFESEH which is not possible with InnoSetup), but this did not change Windows Defender's behavior regarding access blocking.
So, my refined question is: How can I bypass the access blocking of Windows Defender for my setup and my application in a valid way?
To give insights to the problem and to provide a basis for experiments I've set up a small sample application in C# which
creates new (and afterwards removes) directories in the public desktop path, user's desktop path, pulic documents path and user's documents path
is bundled by an InnoSetup script to a signed installer
Using Visual Studio to build the solution and InnoSetup to package the build, it should be easy to reproduce the behavior with enabled Controlled Folder Access. (Make sure to have a look at README.md for a description of the build steps!)
Please check out the sample project.
I've developed a tool in C#.NET/Visual Studio, which uses an imported dll.
The dll will be copied to output folder, when the project is built.
It doesn't need to install the application, you can just copy the exe and dll to a local folder to start.
Now I want to start the application from a network share:
\\localhost\program\prog.exe
All is fine while I don't call a function from the external dll. Then it crashes with the following error window:
Program has stopped working...
Question: Is there a way to make it work without copying both to a local folder and without changing policies on client computers?
Answer: Mapping \localhost to Z: solved this problem.
Please feel free to ask, this is my first question ;-)
Best regards,
Christian
This sounds like a "caspol" issue. Network shares like \\localhost\program\ get reduced trust. Interestingly, from (some time ago), named shares actually get more trust - so one simple option is to map, say, z: as \\localhost\program, and access z:\prog.exe - you might find that this makes it work. Beyond that, the options are:
caspol changes to the each client machine
ClickOnce
of those, the latter is simpler. Then you simply run the ClickOnce application (rather than the .exe) - ClickOnce then basically makes it work. The user will, IIRC, need to click an "ok" the first time they run the application, but that's about it. You would need to ensure that the external dll is known the the ClickOnce deployment, i.e. it is in the project and marked to be shipped.
I am working on a Setup program which creates a .msi file. There is a CustomAction program associated with it which is written in C#. There are three COM object references made in this CustomAction program which are necessary in order for it to work. They are as follows.
Active DS IIS Namespace Provide (C:\Windows\System32\inetsrv\adsiis.dll)
Windows Script Host Object Model (C:\Windows\System32\wshom.ocx)
NetFwTypeLib (C:\Windows\System32\FirewallAPI.dll)
As added information, following are the tasks I use these DLLs in my program for.
To set MIME settings for a particular file type.
To create a desktop shortcut to a folder.
To register a program as a firewall exception
.
Now, here is my problem. When I rebuild the C# program and execute the CustomAction program from within Visual Studio, it works fine and I can get the above three tasks done like a charm. However, when I actually build the .msi and try to execute it and install the program on my system, it gives an error. I'm using a Japanese OS so it gives the error in Japanese so I'll try to translate the error message in to English as best as I can. The error is something like this:
*
File or assembly 'Interop.IISOle,
Version = 1.0.0.0, Culture = neutral,
PublicKeyToken = null', or one of its
dependencies could not be loaded.
Cannot find the file specified.
*
This particular error message is given because it relates to the first dll (adsiis.dll) because it is the one which contains the IISOle namespace. Similarly it gives error messages relates to all the three COM objects which I made references to from my program. Now what I can understand from this is, somehow when this code is inside the .msi file it cannot find the appropriate COM objects. I'm really confused why this happens as the Setup gives the same error when I try to install the program even on the same machine which I used to code the CustomAction. Funnily it executes flawlessly from withing Visual Studio Interface. Please do help me with this. Thanks in advance!
Note that I'm working on Windows Vista Business, but I need my Setup to be able to install on a variety of Windows OS ranging from Windows Server 2003 to Windows 7.
Just encountered this today. Go to "Turn Windows features on or off" and scope out everything under IIS, including the IIS 6 stuff.
Make sure that the feature "IIS Metabase and IIS 6 configuration compatibility" is checked, found under "Internet Information Services" > "Web Management Tools" > "IIS 6 Management Compatibility".
Anton, finally I found out what was wrong.
When I reference, for instance, dsiis.dll then Visual Studio places an Interop module, namely Interop.IISOle.dll inside the bin directory of my CustomAction program. Which is where the CustomAction.exe can also be found. It does the same process when I reference the other two COM objects; wshom.ocx and FirewallAPI.dll. But my actual setup program which is written using Wix copies this CustomAction.exe to some other directory and the .msi uses that particular CustomAction.exe file. The problem occurs since Interop.IISOle.dll (and the others as well) cannot be found there. (The error message said what I needed to look for from the beginning it seems; I was foolish not to look for it but thinking there might be bigger problems)
So this is what I did. I instructed the Wix project to copy those Interop DLL files to the same place where the CustomAction.exe which is used by the installer resides. That solved the problem so easily.
I'm totally a newbie when it comes to Wix, plus we needs to use the CustomAction for 2 and 3 because of some reasons.
I'm going to make a desktop application that will run in the background, meaning no visible window, and I'd like an option called: "Upload Text" to appear when a user right clicks a file.
Can someone point me in the right direction? I also have to make sure that if someone wants to uninstall the program at any point, that the shell modification is also cleanly eliminated.
The app will run Windows XP, Windows Vista and Windows 7. How different are these OS's in handling my shell dilemma?
This is a shell extension. You've tagged this question with the C# tag; you should know that writing shell extensions in a managed language is strongly discouraged:
Unfortunately unmanaged C++ is really
the only way to go here.
Writing in-process shell extensions
in managed code is actually a very
dangerous thing to do because it has
the effect of injecting your managed
code (and the .NET Framework) into
every application on the machine that
has a file open dialog.
The problems occur because only one
version of the .NET Framework can be
loaded in a process at any given time
(other shared components such as java
and msxml have the same property and
thus the same restriction).
If you write your shell extension
using the 2.0 .NET Framework and an
application built with the 1.1 .NET
Framework uses a file open dialog,
your shell extension will fail because
it can not run on an earlier version.
Things can get even worse if your
shell-extension manages to get loaded
in a process before another
applications managed code does: your
extension may force an existing
application onto a different runtime
version than the one it was expecting
and cause it to fail.
Because of these problems we strongly
recomend against using any
single-instance-per-process runtime or
library (such as the .NET Framework,
java, or msxml) in an in-process shell
extension.
That said, people have done it.
Here's a guide to creating shell extensions, using C++.
You could add your app to the SendTo folder.
What about a stand-alone program using SendTo?
Install the exe to "Program Files\mycompany\myprogram" and a shortcut to the exe into the SendTo folder. Then when a user right clicks on a file, selects SendTo, and then selects your program, your exe will be executed by Windows and the full path to the filename will be passed in via argv[1]. If they select n files they will be in argv[1]..argv[n].
If you want your program to be invisible then do not make the default form visible. You could optionally place an icon in the tray so the user could double click on it to see the upload progress. When the upload of argv[1] is complete, process argv[2]...argv[n] if they exists and exit. To cleanly uninstall, remove your program and the shortcut from the SendTo folder.