I have an application that I have just added ClickOnce to as an update method. I'm about to pull it and do something else, even after working through all the gotchas of dealing with ClickOnce in a moderately complex application. Well, it's not even a complex application, but it's going onto dedicated hardware, so I have a few odd requirements, like completely transparent and automatic updates, no odd little pup-up windows, etc. The main one is that the application starts and takes over the system at boot.
Where this causes trouble for ClickOnce is that when the system first boots, there is no network - the Wi-Fi is still getting started and connecting. The application handles this, checking for the network to get started and then connecting to our server. ClickOnce is a different matter. If there is no network when the application starts, then all the ApplicationDeployment functions will not work, even after the network is started.
So, for example, I use something like this to get the version:
if (ApplicationDeployment.IsNetworkDeployed)
Version = ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();
else
Version = "unknown";
If I run the application at boot (that is, before the network is working), this code will return "unknown" for the rest of the application run, even after the network is up. If I shut down the application and restart it, it shows the deployed version. So technically, the IsNetworkDeployed is returning an incorrect value. The application WAS network deployed; it's just not on a network NOW. I'll probably post this as a bug over on MSDN.
BTW, the application does not actually require a network to run, so at startup, I can't take the path of "wait until the network is ready, then restart the application automatically". The hardware can move around, and may be in the middle of nowhere with no available network at all. I still have to deal with that (and I don't actually return "unknown" for that case, I just pull the version from the assembly). And if the problem was just getting a version, I wouldn't care, but this means that there is no way to ever update the application, since it always starts with no network, and it will never get to my code to check for, download, and auto-update the application.
Before I write off all my ClickOnce work, I was wondering if anyone knew of a way to reinitialize ApplicationDeployment, so that it will figure out that there is a network and enable all that ClickOnce goodness.
This is basically what that check is doing:
private static bool _isNetworkDeployed;
private static bool _isNetworkDeployedChecked;
public static bool IsNetworkDeployed
{
get
{
if (!_isNetworkDeployedChecked)
{
_isNetworkDeployed = (
AppDomain.CurrentDomain != null &&
AppDomain.CurrentDomain.ActivationContext != null &&
AppDomain.CurrentDomain.ActivationContext.Identity != null &&
AppDomain.CurrentDomain.ActivationContext.Identity.FullName != null);
//_isNetworkDeployed = ApplicationDeployment.IsNetworkDeployed;
_isNetworkDeployedChecked = true;
}
return _isNetworkDeployed;
}
}
We ran into the same issue with ClickOnce and reverse engineered the check. You could modify this to do your own checking prior to calling the .NET version.
Related
I wrote an application that use grpc with ASP.NET client/server.
This application is built for watch and inform about some operations.
Because I would to have an application also compatible with linux, I transformed it in a dll, then for Windows I made a little exe that uses it, with an icon in the systray.
It works for Windows, I must test for linux except for a part, I would to get the addresses used by the server after launching... it works if I back the dll in console mode, but with the short "launcher" it's impossible (add by edit) to get the addresses with the code i wrote between balises, BUT you can connect to the server (by example i changed port in config file to verify if it was not an address by default and it works.). My problem is only to retrieve addresses after launch, i need them to add the possibility to receive a mail with addresses to connect, show them in systray..
Yesterday I found this way to get addresses used:
public void Configure(IApplicationBuilder app,...)
{
string ServerAddress = app.ServerFeatures.Get<IServerAddressesFeature().Addresses.FirstOrDefault();
// ---
}
with the short launcher the string is null while it works when I back transform the dll as a program and launch it.
I would to have an easy way, without to make big modifications when I make an app for a system or another, I have chosen what was the more compatible and would to stay in this way if possible. I know i could make an identifier to know on which system I then launch according to the OS, but i hoped to find another way in case I want to make something else with this app.
Edit:
It seems it could be a problem between the fact to launch a systray that launch Kestrel. I'm beginner with Asp .net. I copy/paste contract and constructor to the systray, it seems i have the same problem (i used a different port). I can connect but IApplicationBuilder don't have the list of adresses used.
As part of some work I need to get done for Windows 10, I have written a code in C# that essentially detects every minute whether a PC is in screen saver mode or not, and it writes to a table in MySQL the relevant status ("PC in use" if the screen saver is off, "available PC" if the screen saver is on).
I did this using (full link if required - https://www.codeproject.com/Articles/17067/Controlling-The-Screen-Saver-With-C):
// Returns TRUE if the screen saver is actually running
public static bool GetScreenSaverRunning( )
{
bool isRunning = false;
SystemParametersInfo( SPI_GETSCREENSAVERRUNNING, 0,
ref isRunning, 0 );
return isRunning;
}
The code works flawlessly in console application mode (I made a loop to test it out over a minute with a check up on screen save status every 10 seconds), this means in MySQL the status was set correctly every time, depending on the screen save status at the moment of the check up.
The problem occurs when I use this code for a windows service. The service is installed correctly, the log on tab is set on Local System (I also tried with the logged in user instead, same results) and I allow the service to interact with the desktop, just in case, but the difference here is that no matter if the PC enters screen save or not, it always returns false on GetScreenSaverRunning(), thus setting the status of the PC in MySQL as "PC in use", even if the screen saver is on at the moment of check up.
I get the sense that the problem isn't in the code itself, since it works without any issues as a console application, but perhaps something behind the scenes. I tried to search here and on many other websites, haven't found anything related to such a problem.
Does anyone have any idea at all what might be the issue? Any help and/or suggestions will be greatly appreciated!
Thank you in advance.
(I could post the code if required, but it is pretty much straight forward and the main part of it, controlling the screen save detection, is taken from the website mentioned above, afterwards it's a simple if (GetScreenSaverRunning() == true) )
Ever since Vista, Services are barred from a Interactive Session. Even if they run under the same rights, they do not get a interactive Session. I would guess that is getting in the way here.
While you can overwrite this behavior in the Service settings, this is not adviseable for new code. Consider making this a Background Task started by the Task Sheduler instead.
Because the windows service runs in different session then the windows logon. You can't interact with the desktop related services unless you run the windows service in win logon session. There used to be an option in Windows service manager where you can set the properties to "Interact with desktop session" but I don't think that ever worked.
There's a work around to run the windows service using the win logo session.
See this helper class that can get the current logged on user session and interact with the desktop services. https://github.com/murrayju/CreateProcessAsUser/blob/master/ProcessExtensions/ProcessExtensions.cs
I have an application that takes screenshots from the local computer.
This works since many years correctly until suddenly a colleague reported me that he got an "The handle is invalid" error from my application.
This error came from inside the .NET framework from Graphics.CopyFromScreen().
To work around this I replaced this function with C++ code using GetDC(GetDesktopWindow()) / GetDC(NULL) and BitBlt() to copy the screen into a bitmap. Now I got ERROR_INVALID_HANDLE.
This happens on Windows 7.
What is going on there ?
I can not investigate this problem on my own because I cannot reproduce it and my colleague is in another country.
I searched in Google and lots of people report this error.
But all posts that I found were from people who tried to take a screenshot from a client computer through ASP code on a server. I don't understand how people can have the strange desire to capture the client's computer from a website. It is obvious that this will not work.
But I could not find one single case where someone reports this problem from an application that cannot capture the screen of the SAME computer in the SAME session where the application itself is running.
After investigating more with my colleague and giving him ideas what he can try, he told me that he starts my application through a remote desktop session.
The remote desktop session creates a virtual desktop (you see for example that the desktop wallpaper is missing).
I told my colleague to install a VNC client to remote control the computer instead of a remote desktop session and now all works fine. He installed TightVNC which uses the REAL desktop user session instead of creating a virtual session and locking the screen of the machine.
So if anyone gets reports of "The handle is invalid" while taking a screen capture, ask your users if they use a remote desktop session.
To detect a remote desktop session in code you can write:
in C++:
if (GetSystemMetrics(SM_REMOTESESSION) > 0)
{
MessageBox(m_hWnd, L"This application may not work correctly in a remote desktop session", "Error", MB_ICONSTOP);
}
or in C#:
if (System.Windows.Forms.SystemInformation.TerminalServerSession)
{
Messagebox.Show("This application may not work correctly in a remote desktop session");
}
Note that the problem is not reproducible on all computers. When I test on my own Windows 7 it works. So there are probably any additional system settings or other factors that trigger the "The handle is invalid" error (service packs / hotfixes...?).
But my colleague reports that he has never seen the error again after he stopped using the remote desktop connection.
There are a few reasons this can happen but the underlying theme is that the desktop window isn't available when this method is called.
In addition to the reasons mentioned above, another reason this can happen is if this method is being called when the screen is locked.
The code for CopyFromScreen has this section:
int result = SafeNativeMethods.BitBlt(targetDC, destinationX, destinationY, destWidth, destHeight, screenDC, sourceX, sourceY, (int) copyPixelOperation);
//a zero result indicates a win32 exception has been thrown
if (result == 0) {
throw new Win32Exception();
}
It would seem to me that the safest course of action would be that if you make use of this function, make sure that you also write your code assuming that receiving a Win32Exception or an unavailable Desktop Window is a use case which must be handle so the application doesn't crash.
I am using VS2013 Premium to publish a site to Windows Server 2012.
All files publish ok except these:
SqlServerTypes\x64\msvcr100.dll
SqlServerTypes\x64\SqlServerSpatial110.dll
SqlServerTypes\x86\msvcr100.dll
SqlServerTypes\x86\SqlServerSpatial110.dll
I get this kind of errors for each of the above files I tried to publish:
Web deployment task failed. (The file 'msvcr100.dll' is in use. Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_FILE_IN_USE.)
Interrestingly, these files were published the first time (when they were not on the server), then they are no longer overwritten. Tried with 2 different web servers.
I have followed the guide here:
http://blogs.msdn.com/b/webdev/archive/2013/10/30/web-publishing-updates-for-app-offline-and-usechecksum.aspx
...But it only managed to put the site offline (VS is placing the app_offline.htm) but publish still fails with the same error.
All other files publish perfectly.
Any ideas?
You can take you app offline during publishing which hopefully should free up the lock on the file and allow you to update it.
I blogged about this a while back. The support outlined was shipped inside of the Azure SDK and Visual Studio Update. I don't remember the exact releases but I can find out if needed. Any update dating around/after that blog post should be fine.
Prerequisites:
VS 2012 + VS update / VS 2013 + VS Update / VS2015
MSDeploy v3
Note: if you are publishing from a CI server the CI server will need the updates above as well
Edit the publish profile
In VS when create a Web Publish profile the settings from the dialog are stored in Properties\PublishProfiles\ as files that end with .pubxml. Note: there is also a .pubxml.user file, that file should not be modified
To take your app offline in the .pubxml file add the following property.
<EnableMSDeployAppOffline>true</EnableMSDeployAppOffline>
Notes
ASP.NET Required
The way that this has been implemented on the MSDeploy side is that an app_offline.htm file is dropped in the root of the website/app. From there the asp.net runtime will detect that and take your app offline. Because of this if your website/app doesn't have asp.net enabled this function will not work.
Cases where it may not work
The implementation of this makes it such that the app may not strictly be offline before publish starts. First the app_offline.htm file is dropped, then MSDeploy will start publishing the files. It doesn't wait for ASP.NET to detect the file and actually take it offline. Because of this you may run into cases where you still run into the file lock. By default VS enables retrys so usually the app will go offline during one of the retrys and all is good. In some cases it may take longer for ASP.NET to respond. That is a bit more tricky.
In the case that you add <EnableMSDeployAppOffline>true</EnableMSDeployAppOffline> and your app is not getting taken offline soon enough then I suggest that you take the app offline before the publish begins. There are several ways to do this remotely, but that depends on your setup. If you only have MSDeploy access you can try the following sequence:
Use msdeploy.exe to take your site offline by dropping app_offline.htm
Use msdeploy.exe to publish your app (_make sure the sync doesn't delete the app_offline.htm file_)
Wait some amount of time
Publish the site
Use msdeploy.exe to bring the app online by deleting app_offline.htm
I have blogged how you can do this at http://sedodream.com/2012/01/08/howtotakeyourwebappofflineduringpublishing.aspx. The only thing that is missing from that blog post is the delay to wait for the site to actually be taken offline. You can also create a script that just calls msdeploy.exe directly instead of integrating it into the project build/publish process.
I have found the reason why the solution at
http://blogs.msdn.com/b/webdev/archive/2013/10/30/web-publishing-updates-for-app-offline-and-usechecksum.aspx
did not work for the original poster, and I have a workaround.
The issue with the EnableMSDeployAppOffline approach is that it only recycles the app domain hosting the application. It does not recycle the app pool worker process (w3wp.exe) which the app domain lives in.
Tearing down and recreating the app domain will not affect the Sql Server Spatial dlls in question. Those dlls are unmanaged code which are manually loaded via interop LoadLibray calls. Therefore the dlls live outside the purview of the app domain.
In order to release the files locks, which the app pool process puts on them, you need to either recycle the app pool, or unload the dlls from memory manually.
The Microsoft.SqlServer.Types nuget package ships a class which is used to load the Spatial dlls called SqlServerTypes.Utilities. You can modify the LoadNativeAssemblies method to unload the unmanaged dlls when the app domain is unloaded. With this modification when msdeploy copys the app_offline.htm the app domain will unload and then unload the managed dlls as well.
[DllImport("kernel32.dll", SetLastError = true)]
internal extern static bool FreeLibrary(IntPtr hModule);
private static IntPtr _msvcrPtr = IntPtr.Zero;
private static IntPtr _spatialPtr = IntPtr.Zero;
public static void LoadNativeAssemblies(string rootApplicationPath)
{
if (_msvcrPtr != IntPtr.Zero || _spatialPtr != IntPtr.Zero)
throw new Exception("LoadNativeAssemblies already called.");
var nativeBinaryPath = IntPtr.Size > 4
? Path.Combine(rootApplicationPath, #"SqlServerTypes\x64\")
: Path.Combine(rootApplicationPath, #"SqlServerTypes\x86\");
_msvcrPtr = LoadNativeAssembly(nativeBinaryPath, "msvcr100.dll");
_spatialPtr = LoadNativeAssembly(nativeBinaryPath, "SqlServerSpatial110.dll");
AppDomain.CurrentDomain.DomainUnload += (sender, e) =>
{
if (_msvcrPtr != IntPtr.Zero)
{
FreeLibrary(_msvcrPtr);
_msvcrPtr = IntPtr.Zero;
}
if (_spatialPtr != IntPtr.Zero)
{
FreeLibrary(_spatialPtr);
_spatialPtr = IntPtr.Zero;
}
};
}
There is one caveat with this approach. It assumes your application is the only one running in the worker process that is using the Spatial dlls. Since app pools can host multiple applications the file locks will not be released if another application has also loaded them. This will prevent your deploy from working with the same file locked error.
There are known issues with IIS and file-locks (why they aren't solved yet i dont know).
The question i want to ask however is if you even need to re-deploy these files?
I recognize the file-names and recall them to be system-files which should either already be present on the server or simply not need to be re-deployed.
I am not very experienced when it comes to IIS but i have ran into this problem before and several of my more experienced co-workers have told me that this is as i said a known IIS-issue and i believe the answer to your question is:
Avoid deploying unnecessary files.
try again
Reset website
try again
iisreset
I think what would be the easiest thing to do is to make these dll's as CopyLocal as true. I am assuming these dll's are pulled out from program files folder. Try marking them as copylocal true and do a deployment.Try to stop any IIS local process running in your local machine.
Watch out you don't have one of those new-fangled cloud backup services running that is taking file locks - and also you don't have things open in explorer or a DLL inspection tool.
I think it's kind of ridiculous that MS doesn't make better provisions for this problem. I find that 9 times out of 10 my deployment works just fine, but then as our traffic increases that can become 1 in 10 times.
I am going to solve the problem with :
two applications MySite.A and MySite.B, where only one is running at a time.
I always then deploy to the dormant site.
If there's a problem during the deployment it will never cause the whole site to go down.
If there's a major problem after deployment you can revert back very easily.
Not quite sure how I'm implementing it, but I think this is what I need to do.
I've had a scout around some answers to similar questions but they haven't really helped me much.
I have an app, into which I've embedded some resources. At launch the app checks to see if the resources exist in the appdata folder and if not copies the template files from the embedded resources to the appdata folder before loading them and then using the ones in the appdata folder as the working copies.
I have a helper class which amongst other things returns the appdata and resources subfolder as follows:
class Folders
{
static public String GetUserFolder()
{
return Application.LocalUserAppDataPath;
}
static public String GetResourcesFolder()
{
// If the resources folder does not exist then create it
String userFolder = GetUserFolder();
String resourcesFolder = userFolder + "\\Resources";
if (!Directory.Exists(resourcesFolder))
{
Directory.CreateDirectory(resourcesFolder);
}
return resourcesFolder;
}
...
So my code calls the GetResourcesFolder method which returns the path (creating the folder in the process if it needs to) checks to see if the file exists and if it doesn't tries to write to it using something like:
String filename = Helpers.IO.Folders.GetResourcesFolder() + "\\data.dat";
FileStream outFile = System.IO.File.OpenWrite(filename);
So I've set the scene and this code is working on all the machines I had in the development office. However a couple of off site colleagues have complained it "crashes" on their machines - in each case an XP machine - but otherwise not a lot of useful information coming back from them - working on trying to get something more informative from them. I have XP machines in the office that it has worked on without problems.
After digging out some really old dev machines that were "archived" a while ago, I've managed to have a crash on two xp (sp2) machines also. On both occasions the crash seems to be related to write permissions and running the app using "Run As..." has resolved the problem and it executes correctly. However once the app has been successfully run once the app no longer crashes, even if I delete the files/folders it created from the appdata folder it will still create the successfully on subsequent executions even if I don't elevate permissions.
The problem I have is that I can now no longer repeat the crash on any dev machines available to me and I don't know how to go about putting the machine back into the state where I can.
Anybody got any ideas on what might be causing the problem, or how I may be able to return the machines to a "virgin" state to be able repeat the crash and help me track it down.
One course of action is to create a Virtual Machine of XP. You can save the state of the machine before install for testing. After your install just revert back to the previous state to test again. There are a few Vendors with free Virtual Machines:
http://www.microsoft.com/windows/virtual-pc/
https://www.virtualbox.org/
As too the related problem itself, I don't know a better way than to install VS on a virtual machine for testing purposes.
+1 to Erik's VM solutions for reporducing the issue.
For tracking down permissions issues consider using ProcMon ( http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx ) - will show enverything you ever wanted (and not :) ) about file/regisstry access made by a process. I'd recommend trying it several times first on machine where you program works fine to get filtering setup for your process and get a sense of what should be happening.