Graphics.CopyFromScreen() and GetDC(0) fail with "The handle is invalid" - c#

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.

Related

Why can't a windows service created in C# detect that a screen saver is currently running?

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

Performance considerations when getting license information for WP app

Introduction
I have a Windows Phone 8.1 Silverlight (WP8.1 SL) based app in the store. Some users complain about performance issues when they have a bad network connection. I searched a bit and came up with the idea that it might be related to new LicenseInformation() that gives me the information of whether the app is running in Trial mode or not. The question is, whether this requires network information or not, and whether CurrentApp.LicenseInformation is a suitable replacement for a WP8.1 SL app.
Background and What I did so far
In general, the app does not need a network connection (no data to load, no advertisements, ...). To confirm that I used Fiddler to watch over the network sent by my phone. The result was that no network traffic is generated. However, the problem still persists.
After a lot of research and playing around I got the feeling that this issue might be related to the code part that checks on whether the app is in trial mode or not. I use the following code to check that.
var li = new LicenseInformation();
if (li.IsTrial()) {
...
}
I do this a couple of times during startup. So in case IsTrial() requires a network connection this could be the actual issue when there is only a bad connection available. But again, I couldn't find anything using Fiddler. The documentation (see here) for LicenseInformation does not mention whether a network connection is required or not.
Searching around I found that there is an updated interface available for both WP 8.1 SL and also W10M UWP.
var li = CurrentApp.LicenseInformation;
if (li.IsTrial) {
...
}
Its documentation clearly states that there is no network connection required for that (see here).
Even though the docs say that CurrentApp.LicenseInformation is also available on WP8 I also found some references that say that you only get a reliable answer for the IsTrial-question when using new LicenseInformation() (e.g. here).
Actual Questions
Is new LicenseInformation() required on WP8.1 SL, or can I use CurrentApp.LicenseInformation as well?
Does new LicenseInformation() require a network connection compared to CurrentApp.LicenseInformation?

Programmatically enabling Windows 8 Pro mobile broadband device

Background
The company I work on is developing a kiosk-like application for tablets running Windows 8 Pro (on desktop mode). The user shouldn't be able to access anything that isn't the application itself: charms will be disabled, the taskbar will be hidden behind the application, etc.
This also means the user shouldn't be able to change network settings, leaving the responsability to keep the device always connected to us. Up to now, I had success using the Mobile Broadband API to assure the device is connected whenever there's a mobile network available. It'll detect disconnect events and try to connect again.
The Problem
Although the user shouldn't be able to do it, I'm considering the case where the user follows this steps:
User opens right-side charm,
clicks on Settings,
clicks on Network,
clicks on More PC Settings,
clicks on Wireless, and
disables the mobile broadband device.
I would like to be able to revert this programmatically and enable it again.
The Attempts
I have tried some different ways to force 3G being reenabled. Most of them give me the same result: they supposedly enable the device without errors, but I still cannot use it. Enable-NetAdapter in Powershell doesn't throw errors, and the Enable method of Win32_NetworkAdapter appears to work, but no dice.
I thought maybe the method IMbnRadio::SetSoftwareRadioState could be what I'm after, but I can't get to it when the device is disabled. The method IMbnInterfaceManager::GetInterfaces throws a COMException claiming the element could not be found (HRESULT = 0x80070490).
MbnInterfaceManager mbnInterfaceManager = new MbnInterfaceManager();
IMbnInterfaceManager interfaceManager = (IMbnInterfaceManager)mbnInterfaceManager;
// The following line throws a COMException:
IMbnInterface[] interfaces = (IMbnInterface[])interfaceManager.GetInterfaces();
mobileInterface = interfaces[0];
mobileRadio = (IMbnRadio)mobileInterface;
uint requestId;
mobileRadio.SetSoftwareRadioState(MBN_RADIO.MBN_RADIO_ON, out requestId);
Is there a way to override user preferences set on "More PC Settings?"
I found a sketchy way to solve this. Keep in mind this is undocumented, wrong, shameless and immoral, and will probably break eventually. The client is aware of this, but prefers to keep the access to the OS limited.
The setting in case is saved in the Registry. At least in the computers I've checked, it's stored in HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\0022 in a key named RadioOff.
The Airplane Mode setting is also stored in the Registry, but in a different place. It's at HKLM\SYSTEM\CurrentControlSet\Control\RadioManagement\SystemRadioState in a key named (Default).
After changing these keys and rebooting, everything seems to work fine. I'll repeat though: you really shouldn't be doing this, especially the Airplane Mode thing.

ClickOnce fails to initialize with no network

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.

.Net Crystal Report printing application running on termianal service connection errors when session is disconnected

I have created a .Net application to run on an App Server that gets requests for a report and prints out the requested report.
The C# application uses Crystal Reports to load the report and subsequently print it out.
The application is run on Server which is connected to via a Remote Desktop connection under a particular user account (required for old apps). When I disconnect from the Remote Session the application starts raising exceptions such as:
Message: CrystalDecisions.Shared.CrystalReportsException: Load report failed
This type of error is never raised when the Remote Session is active. The server running the app is running Windows Server 2003, my box which creates the connection is Windows XP.
I appreciate this is fairly weird, however I cannot see any problem with the application deployment I have created.
Does anyone know what could be cause this issue?
EDIT: I bit the bullet and created the application as a windows service, obviously this doesn't take long I just wasn't convinced it would solve the problem. Anyway it doesn't!!!
I have also tried removing the multi-thread code that was calling the print function asynchronously. I did this in order to simply the app and narrow down the reason it could fail. Anyway, this didn't improve the situation either!
EDIT: The two errors I get are:
System.Runtime.InteropServices.COMException
(0x80000201): Invalid printer
specified. at
CrystalDecisions.ReportAppServer.Controllers.PrintOutputControllerClass.ModifyPrinterName(String
newVal) at
CrystalDecisions.CrystalReports.Engine.PrintOptions.set_PrinterName(String
value) at
Dsa.PrintServer.Service.Service.PrintCrystalReport(Report
report)
The printer isn't invalid, this is confirmed when 60 seconds later the time ticks and the report is printed successfully.
And
The request could not be submitted for
background processing. at
CrystalDecisions.ReportAppServer.Controllers.ReportSourceClass.GetLastPageNumber(RequestContext
pRequestContext) at
CrystalDecisions.ReportSource.EromReportSourceBase.GetLastPageNumber(ReportPageRequestContext
reqContext) --- End of inner
exception stack trace --- at
CrystalDecisions.ReportAppServer.ConvertDotNetToErom.ThrowDotNetException(Exception
e) at
CrystalDecisions.ReportSource.EromReportSourceBase.GetLastPageNumber(ReportPageRequestContext
reqContext) at
CrystalDecisions.CrystalReports.Engine.FormatEngine.PrintToPrinter(Int32
nCopies, Boolean collated, Int32
startPageN, Int32 endPageN) at
CrystalDecisions.CrystalReports.Engine.ReportDocument.PrintToPrinter(Int32
nCopies, Boolean collated, Int32
startPageN, Int32 endPageN) at
Dsa.PrintServer.Service.Service.PrintCrystalReport(Report
report)
EDIT:
I ran filemon to check if there were any access issue. At the point when the error occurs file mon reports
Request: OPEN | Path: C:\windows\assembly\gac_msil\system\2.0.0.0__b77a5c561934e089\ws2_32.dll | Result: NOT FOUND | Other: Attributes Error
Our particular problem has been solved. Basically when the reports were created they were saved with information about printers. Basically a particular printer had been set for the report and saved.
This printer no longer exists which is why the report had started failing. Basically we had to open the report designer and remove any association with printers in the report.
We have encountered these errors several times in the past few years. Many times I wished for some more specific error message.
First, I would verify that there aren't multiple Crystal Reports versions installed. In our expereince, we found that Crystal Reports 9.0 doesn't seem to play well with 10. Uninstalling version 9 seemed to help some of our customers. If both are installed, I highlly recommend uninstalling both, then re-installing Crystal Reports 10.
One of our earliest Crystal Reports errors was the dreaded "The request could not be submitted for background processing." Reports would work fine for a while, then suddenly they would stop. After looking at the code, I found a place where we were not disposing of a ReportDocument. Correctly disposing this document fixed the issue.
Lately, we hit a spat of "The request could not be submitted for background processing." and "Invalid Printer" errors. One customer's server had several network printers defined by IP address. Printing would work just fine for a while, then suddenly, bam, the customer got the "Invalid Printer" error and called our support.
A fellow developer fixed the "Invalid Printer" problem by doing all of the following things:
Edit and save the .rpt file in Visual Studio 2005. We had been keeping the report format compatible with Crystal Reports 9, because we wanted our changes to be distributable to our customers still using older versions.
Save Crystal Reports files with the "No Printer" option. From the Visual Studio 2005 menu, select Crystal Reports/Design/Printer Setup, then select the "No Printer" check box.
We changed a formula that displayed "Page N of M" from something strange like this:
"Page " + Left (CStr (PageNumber), Length (CStr (PageNumber)) - 3) +
" of " + Left (CStr (TotalPageCount), Length (CStr (TotalPageCount)) - 3)
to this:
"Page " + CStr(PageNumber,0) + " of " + CStr(TotalPageCount,0)
Unfortunately, I have no idea which (any or all) of the above actions may fixed the error. I say "may", because for all I know, the error magically went away because of some IT network change.
Finally, have you read this white paper? While it did not necessarily solve our problems, it was very interesting.
If you create a window service wrapper for your application, it means that it will continue to run regardless of users logged in. It can also be set to start up automatically. I think that because you are using an actual application, and not a windows service it is not actually running once you logout of a session on the server.
Here is an example on Code Project:
http://www.codeproject.com/KB/system/WindowsService.aspx
Are you sure you are "disconnecting' you remote session as opposed to "logging off". I disconnect my sessions to a nt server 2003 all the time and everything to continues to run just fine. Logging off on the other hand will kill the apps running in that session.
What is your o/s server running?
In Order to solve the problem that you have described you need to focus in on what your problem is.
I have recieved the
Message: CrystalDecisions.Shared.CrystalReportsException: Load report failed
a number of times and it is due to the fact that you can't access the report. Either it doesn't exist or in your situation the you are not connected to the machine. The error is not due to anything printing related.
I had an obscure issue that was similar to this, except it was a (shock-horror) automation issue with Excel. I found the application worked fine when I had my remote desktop session connected, but once disconnected, it would give automation errors relating to the default printer.
I believe the issue relates to how the default printer is set when you connect with remote desktop. Assuming you have a .rdp file for initiating your remote desktop session, if you edit it, then select the "Local Resources" tab, you can see whether or not you share your local printer on the remote session.
If it is enabled, this means when you connect, your local default printer becomes the default printer on the remote machine. When you disconnect, your application's default printer will still possibly refer to the now unavailable default printer from your local machine.
You should be able to solve this, by ensuring you don't share your local printer, by deselecting the Printer checkbox from the "Local Resources" tab.
An easier solution would be to disable "printer redirection" on the server. See https://serverfault.com/questions/5646/how-do-i-disable-remote-printers-using-group-policy for more information.
If you are looking in detail how to
add checkboxes into crystal reports on a boolean feild and if the boolean field is false
uncheck else check the checkbox look at this article
http://checkboxcrystalreport.blogspot.com/2009/07/printing-checkbox-in-crystal-report-for.html

Categories

Resources