A Generic Error occurred in GDI+ when calling Bitmap.GetHicon - c#

Why I'm getting "A Generic Error occurred in GDI+" Exception ?
IntPtr hicon = tempBitmap.GetHicon();
Icon bitmapIcon = Icon.FromHandle(hicon);
return bitmapIcon;
The error occurred when my application has been running for more than 30 minutes. (I am converting System.Drawing.Bitmap to System.Drawing.Icon every second)

That's caused by a handle leak. You can diagnose the leak with TaskMgr.exe, Processes tab. View + Select Columns and tick Handles, GDI Objects and USER Objects. Observe these columns while your program is running. If my guess is right, you'll see the GDI Objects value for your process steadily climbing. When it reaches 10,000 then the show is over, Windows refuses to allow you to leak more objects.
The Remarks section for Icon.FromHandle says:
When using this method you must dispose of the resulting icon using the DestroyIcon method in the Win32 API to ensure the resources are released.
That's good advice but usually pretty painful to do. You can find a hack to force the Icon object to own the handle, and automatically release it, in this answer. Relevant code is after the "Invoke private Icon constructor" section.

You probably need to clean up your icon.
The example for Icon.FromHandle on MSDN shows you how. Unfortunately it requires PInvoke:
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet=CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);
And then inside your method:
IntPtr hicon = tempBitmap.GetHicon();
Icon bitmapIcon = Icon.FromHandle(hicon);
// And then somewhere later...
DestroyIcon(bitMapIcon.Handle);
If you call DestoryIcon before you use it, it may not work. For my own particular instance of this problem, I ended up keeping a reference to the last icon I created and then called DestroyIcon on it the next time I generated an icon.

Related

Keyboard input lost when using cross-process parent/child windows

I am investigating a bug that results in key-presses being 'lost' when users are providing rapid keyboard input to our application. It seems to happen randomly. If a user writes e.g. "I am writing this text fast on my keyboard" the actual text that ends up in the application's text box looks something like "Ia wrting thistext fstn mykeybrd."
We have managed to find out that the issue seems to be related to how we integrate the text-input component into our main application. Our main app is a .NET Framework 4.8 WinForms process. The text-input component is running as a .NET 4.8 WPF app in a separate process. The WinForms app is responsible for launching the WPF process, passing the window handle of its own main window as an argument to the new process. Once launched, the WPF process uses the provided window handle to reparent itself as a child window of the WinForms app. This is done using Win32 API calls through P/Invoke.
private void Reparent() {
ChangeStyle(this.myHandle);
Win32.SetParent(this.myHandle, this.hostHandle);
}
private static void ChangeStyle(IntPtr myHandle) {
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
const uint WS_CHILD = 0x40000000;
uint style = Win32.GetWindowLong(myHandle, GWL_STYLE);
style |= WS_CHILD;
style &= ~WS_POPUP;
Win32.SetWindowLong(myHandle, GWL_STYLE, style);
}
According to MSDN this seems to be legal.
For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.
We understand that this is a bit erm... 'unorthodox', but the design has been like this for a long time without any other issues.
Interestingly, removing the code that sets the WS_CHILD flag fixes the lost keys problem, but causes a bunch of other issues instead.
According to Raymond Chen
Creating a cross-thread parent/child or owner/owned window relationship implicitly attaches the input queues of the threads which those windows belong to...
We interpret this like AttachThreadInput is called 'under the hood', but do not fully understand what causes keypresses to be lost. Is it some sort of thread-safety issue? A race condition?
By using the AttachThreadInput function, a thread can share its input states (such as keyboard states and the current focus window) with another thread. Keyboard and mouse events received by both threads are processed in the order they were received until the threads are detached by calling AttachThreadInput a second time and specifying FALSE for the fAttach parameter.
We have used Spy++ to monitor the Windows messages that gets sent to the WPF process and it seems that when you type slowly, WM_KEYDOWN is sent together with WM_CHAR. When the input is lost, we still receive the WM_KEYDOWN messages, but no WM_CHAR. This led us to start investigating the .NET framework source. Among other things, we found the following code in HwndSource.cs
switch ((WindowMessage)msgdata.msg.message)
{
case WindowMessage.WM_SYSKEYDOWN:
case WindowMessage.WM_KEYDOWN:
// MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS
// In case a nested message pump is used before we return
// from processing this message, we disable processing the
// next WM_CHAR message because if the code pumps messages
// it should really mark the message as handled.
_eatCharMessages = true;
DispatcherOperation restoreCharMessages = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(RestoreCharMessages), null);
// Force the Dispatcher to post a new message to service any
// pending operations, so that the operation we just posted
// is guaranteed to get dispatched after any pending WM_CHAR
// messages are dispatched.
Dispatcher.CriticalRequestProcessing(true);
The variable named _eatCharMessages could perhaps be related, although debugging this code using dnSpy has not made us any wiser.
We're looking for a Win32-wizard to shed some more light on this. Anything that can point us in the right direction would be greatly appreciated. We need to figure out the root cause of the problem in order to decide how to fix it.

Error on copy some text to clipboard in C# WPF [duplicate]

Why does the following code sometimes causes an Exception with the contents "CLIPBRD_E_CANT_OPEN":
Clipboard.SetText(str);
This usually occurs the first time the Clipboard is used in the application and not after that.
This is caused by a bug/feature in Terminal Services clipboard (and possible other things) and the .NET implementation of the clipboard. A delay in opening the clipboard causes the error, which usually passes within a few milliseconds.
The solution is to try multiple times within a loop and sleep in between.
for (int i = 0; i < 10; i++)
{
try
{
Clipboard.SetText(str);
return;
}
catch { }
System.Threading.Thread.Sleep(10);
}
Actually, I think this is the fault of the Win32 API.
To set data in the clipboard, you have to open it first. Only one process can have the clipboard open at a time. So, when you check, if another process has the clipboard open for any reason, your attempt to open it will fail.
It just so happens that Terminal Services keeps track of the clipboard, and on older versions of Windows (pre-Vista), you have to open the clipboard to see what's inside... which ends up blocking you. The only solution is to wait until Terminal Services closes the clipboard and try again.
It's important to realize that this is not specific to Terminal Services, though: it can happen with anything. Working with the clipboard in Win32 is a giant race condition. But, since by design you're only supposed to muck around with the clipboard in response to user input, this usually doesn't present a problem.
I know this question is old, but the problem still exists. As mentioned before, this exception occurs when the system clipboard is blocked by another process. Unfortunately, there are many snipping tools, programs for screenshots and file copy tools which can block the Windows clipboard. So you will get the exception every time you try to use Clipboard.SetText(str) when such a tool is installed on your PC.
Solution:
never use
Clipboard.SetText(str);
use instead
Clipboard.SetDataObject(str);
I solved this issue for my own app using native Win32 functions: OpenClipboard(), CloseClipboard() and SetClipboardData().
Below the wrapper class I made. Could anyone please review it and tell if it is correct or not. Especially when the managed code is running as x64 app (I use Any CPU in the project options). What happens when I link to x86 libraries from x64 app?
Thank you!
Here's the code:
public static class ClipboardNative
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
[DllImport("user32.dll")]
private static extern bool SetClipboardData(uint uFormat, IntPtr data);
private const uint CF_UNICODETEXT = 13;
public static bool CopyTextToClipboard(string text)
{
if (!OpenClipboard(IntPtr.Zero)){
return false;
}
var global = Marshal.StringToHGlobalUni(text);
SetClipboardData(CF_UNICODETEXT, global);
CloseClipboard();
//-------------------------------------------
// Not sure, but it looks like we do not need
// to free HGLOBAL because Clipboard is now
// responsible for the copied data. (?)
//
// Otherwise the second call will crash
// the app with a Win32 exception
// inside OpenClipboard() function
//-------------------------------------------
// Marshal.FreeHGlobal(global);
return true;
}
}
Actually there could be another issue at hand. The framework call (both the WPF and winform flavors) to something like this (code is from reflector):
private static void SetDataInternal(string format, object data)
{
bool flag;
if (IsDataFormatAutoConvert(format))
{
flag = true;
}
else
{
flag = false;
}
IDataObject obj2 = new DataObject();
obj2.SetData(format, data, flag);
SetDataObject(obj2, true);
}
Note that SetDataObject is always called with true in this case.
Internally that triggers two calls to the win32 api, one to set the data and one to flush it from your app so it's available after the app closes.
I've seen several apps (some chrome plugin, and a download manager) that listen to the clipboard event. As soon as the first call hits, the app will open the clipboard to look into the data, and the second call to flush will fail.
Haven't found a good solution except to write my own clipboard class that uses direct win32 API or to call setDataObject directly with false for keeping data after the app closes.
Use the WinForms version (yes, there is no harm using WinForms in WPF applications), it handles everything you need:
System.Windows.Forms.Clipboard.SetDataObject(yourText, true, 10, 100);
This will attempt to copy yourText to the clipboard, it remains after your app exists, will attempt up to 10 times, and will wait 100ms between each attempt.
Ref. https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setdataobject?view=netframework-4.7.2#System_Windows_Forms_Clipboard_SetDataObject_System_Object_System_Boolean_System_Int32_System_Int32_
This happen to me in my WPF application. I got OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).
i use
ApplicationCommands.Copy.Execute(null, myDataGrid);
solution is to clear the clipboard first
Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
The difference between Cliboard.SetText and Cliboard.SetDataObject in WPF is that the text is not copied to the clipboard, only the pointer. I checked the source code. If we call SetDataObject(data, true) Clipoard.Flush() will also be called. Thanks to this, text or data is available even after closing the application. I think Windows applications only call Flush() when they are shutting down. Thanks to this, it saves memory and at the same time gives access to data without an active application.
Copy to clipboard:
IDataObject CopyStringToClipboard(string s)
{
var dataObject = new DataObject(s);
Clipboard.SetDataObject(dataObject, false);
return dataObject;
}
Code when app or window is closed:
try
{
if ((clipboardData != null) && Clipboard.IsCurrent(clipboardData))
Clipboard.Flush();
}
catch (COMException ex) {}
clipboardData is a window class field or static variable.
That's not a solution, just some additional information on how to reproduce it when all solutions work on your PC and fail somewhere else. As mentioned in the accepted answer - clipboard can be busy by some other app. You just need to handle this failure properly, to explain user somehow why it does not work.
So, just create a new console app with few lines below and run it. And while it is running - test your primary app on how it is handles busy clipboard:
using System;
using System.Runtime.InteropServices;
namespace Clipboard
{
class Program
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
static void Main(string[] args)
{
bool res = OpenClipboard(IntPtr.Zero);
Console.Write(res);
Console.Read();
CloseClipboard();
}
}
}

ShowWindowAsync not work in all case

ShowWindowAsync won't work in all case. It works when I try it with notepad, task managger, or visual studio, it's just do the job, restore them when they minimized, but when I try it with windows explorer or another external process, it won't work. In all case, the GetProcessesByName find them, just won't come back from minimized.
PInvokeFunctions class
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(HandleRef hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr WindowHandle);
Method
public static void FocusProcess(string procName)
{
Process[] objProcesses = Process.GetProcessesByName(procName);
if (objProcesses.Length > 0)
{
IntPtr hWnd = IntPtr.Zero;
hWnd = objProcesses[0].MainWindowHandle;
PInvokeFunctions.ShowWindowAsync(new HandleRef(null, hWnd), Constants.PInvokeConstants.SW_RESTORE); // SW_RESTORE = 9
PInvokeFunctions.SetForegroundWindow(hWnd);
}
}
What you want is actually not possible. You are trying to find the 'main window' of a process and that is an non-existing concept. The topic is discussed at length here: There can be more than one (or zero): Converting a process to a window
When you try to explain this to people, you sometimes get stuck in the Why won't you answer my question? cycle.
"I have a thread ID. How do I get the corresponding window?"
You can use the EnumThreadWindows function to get all the windows on the thread.
"Yes, I know about EnumThreadWindows, but how do I get the window that I want?"
Well, you haven't said what you wanted yet.
"I want the window that corresponds to the thread."
But which one? How will you decide among all the windows?
"That's what I'm asking you!"
But you haven't yet described what you want.
"I want the window corresponding to the thread. Why won't you answer my question?"
Note that saying, "I am looking for the top-level unowned window" is a step forward, but it still doesn't uniquely identify a window. There can be multiple top-level unowned windows in a process. For example, Explorer typically has lots of top-level unowned windows. There's the desktop, the taskbar, your open folder windows, and property sheets. If you ask for "the" top-level unowned window of Explorer, which one do you want?
And then Raymond ads the coup de grĂ¢ce:
Perhaps people are getting the idea that there is a way to uniquely specify "the" window for a process because the System.Diagnostics.Process object has a property called MainWindowHandle. The documentation for that property doesn't do anything to dispel the notion, either. I have no idea how that property decides among multiple top-level unowned windows.
So, as you see, you need to unask the question. What you want is fundamentally incorrect, since there is no 'main' window for a process like Chrome (remember, all modern browsers isolate browsing tabs in their own process). If you followed the discussion above, you'll know what to do.

SendMessage disappears into the ether

I'm attempting to pass messages between two applications - one of them is a plugin, and the other is a standalone configuration utility. When my plugin detects an event, I want to send a message to my utility and prompt the user to reconfigure.
The code I'm using is as follows:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int MESSAGE_UNAUTH = 0x401;
[... misc logic here, function def, etc]
Process[] processes = Process.GetProcessesByName("MyConfigurationApplication");
if (processes.Length > 0)
{
foreach (Process p in processes)
{
SendMessage(p.MainWindowHandle, MESSAGE_UNAUTH, IntPtr.Zero, IntPtr.Zero);
}
}
And then in my receiving process, I have the following code (I also defined MESSAGE_UNAUTH in this class):
protected override void WndProc(ref Message message)
{
if (message.Msg == MESSAGE_UNAUTH)
{
MessageBox.Show("Message received");
}
base.WndProc(ref message);
}
Things I have already verified with the debugger:
The message is getting sent. All the code in the Sender, including the SendMessage call, is executing.
The message is not getting received (obviously).
The WndProc() function is not getting called at all when the message is sent. It is, however, getting called a whole bunch of times when the configuration utility is launched (I'm assuming this is Windows' behavior).
I've gone through enough online tutorials to need eyedrops, and as far as I know, everything here is syntax-correct and "proper," but for some reason, between when I send the message and when the receiver's WndProc() should be called, black magic is happening.
Any ideas would be greatly appreciated.
Update: Using Marshal.GetLastWin32Error(), I am getting an error #1400, which appears to correspond to an invalid window handle. However, I don't see why that would be the case, since the process was found and we entered the for each loop successfully. The one caveat I can think of is that my configuration utility is presented as a taskbar icon, and doesn't necessarily have a visible window at all times - would this prevent p.MainWindowHandle from being valid? If so, how can I work around this to pass a message to the process instead of the window?
Update: Process.MainWindowHandle is 0, so it looks like that is indeed the problem - when the form in my configuration utility is not visible, no valid window handler is returned, even though my utility icon is visible in the notification bar. Is there any way I can send a message to the process, or even to the taskbar icon?
You can try to enumerate all windows associated with the process. See How to enumerate all windows belonging to a particular process using .NET?
Depending on the .NET framework you are using, this will help resolve your issues.
There was a bug in the old .NET frameworks (2.0 I think) where calling to Process.MainWindowHandle when the process starts up returns 0. Any subsequent call will also result in a 0. This is due to caching the main window handle, which should have been fixed in .NET 3.0 and later.
You might also try giving full trust to your WndProc which might help. Something like:
[System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
//...
}
On a side note, if you can change your implementation then I strongly suggest you go for better inter process communication means such as sockets, TCPChannel (which I think is replaced by WCF), named pipes...
The message might not be sent, it might be blocked. See here: When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).
Use Windows Registermessage in bothe sender and receiver end will resolve the problem
Problem was that the process I was sending the message to only existed as a tooltip icon and not as an active, open window. Turns out the windows message functionality is designed for window-to-window messages, not process-to-process messages.
Solution was aforementioned kludgy system of file IO handlers.

.NET (C#): Getting child windows when you only have a process handle or PID?

Kind of a special case problem:
I start a process with System.Diagnostics.Process.Start(..)
The process opens a splash screen -- this splash screen becomes the main window.
The splash screen closes and the 'real' UI is shown. The main window (splash screen) is now invalid.
I still have the Process object, and I can query its handle, module, etc. But the main window handle is now invalid.
I need to get the process's UI (or UI handle) at this point. Assume I cannot change the behavior of the process to make this any easier (or saner).
I have looked around online but I'll admit I didn't look for more than an hour. Seemed like it should be somewhat trivial :-(
If you don't mind using the Windows API, you could use EnumWindowsProc, and check each of the handles that that turns up using GetWindowThreadProcessId (to see that it's in your process), and then maybe IsWindowVisible, GetWindowCaption and GetWindowTextLength to determine which hWnd in your process is the one you want.
Though if you haven't used those functions before that approach will be a real pain, so hopefully there's a simpler way.
#ageektrapped is on the right track, however FindWindow will not search child windows.
For that you will need to use FindWindowEx
Thank you for your answers. Thanks to you here, I figured out how to know if the main window of a process is in front or not:
N.B : of course this needs System.Diagnostic and System.Runtime.Interrop
public bool IsWindowActive(Int32 PID)
{
return IsWindowActive(Process.GetProcessById(PID));
}
[DllImport("user32.dll")]
private static extern
IntPtr GetForegroundWindow();
public bool IsWindowActive(Process proc)
{
proc.Refresh();
return proc.MainWindowHandle.Equals(GetForegroundWindow());
}
You may find that if you call .Refresh() that you get the new top-level window.
If you know the window's title, you can use the Win32 call, FindWindow, through P/Invoke.
You can find the signature here on pinvoke.net
From what I understand MainWindowHandle property of the process you are starting is not valid. If that's the case, you can use FindWindow function (from Win32 SDK) which returns the window handle you need. All you need is the class name of target application's main window. You can obtain it using Spy++ or Winspector. You also need to ensure you have the right window by checking that window's process id using GetWindowThreadProcessId.
At last, I have to say I am not an expert on Win32 and there might be a better solution for your case.
Use Process.GetProcessById(proc.Id); where proc was your splash screen.
Works for me.
Now, how do you get to main window properties in System.Windows.Forms to give it focus w/o using win32?
After all .net is supposed to be a one-stop solution - is it not?
Somewhere in the code, the "real" main window is created. You can just save the window handle at that time and then after the splash screen closes you can set Application.MainWindow to the real window.
The MainWindowHandle property is cached after it is first accessed which is why you don't see it changing even after the handle becomes invalid. GregUzelac's information is correct. Calling Proces.Refresh will causes the next call to Process.MainWindowHandle to re-do the logic to find a new main window handle. Michael's logic also works because the new Process doesn't have a cached version of the MainWindowHandle.

Categories

Resources