How to keep Excel interop from stealing focus while inserting images - c#

I'm using the Excel COM interop to insert images (specifically EPS) into a spreadsheet. The images are inserted fine, but Excel ignores all the visible/background settings and steals the focus the display a dialog box saying something like "importing image". The dialog box only stays a fraction of a section, but it makes the screen flicker, and worse, when I'm inserting many images at once, it can monopolize the system for several seconds (including stealing keystrokes from the foreground process).
I'm setting the background options as follows:
Excel.Application xlApp = new Excel.Application();
xlApp.Visible = false;
xlApp.ScreenUpdating = false;
xlApp.DisplayAlerts = false;
Excel.Worksheet worksheet;
//....
worksheet.Pictures(Type.Missing).Insert(filename,Type.Missing); //steals focus
How can I get Excel to stay in the background here like it belongs?

I suspect this is caused by a component that Microsoft licensed that is badly behaved.
The only way to deal with situations like this is by intercepting the appropriate low-level Windows message and blocking it using a Win32 hook
The easiest way to do this, and, believe me, it's not pretty, is to use the CBT hook. CBT stands for "Computer Based Training." It is an ancient and nearly obsolete technology intended to make it possible to create training apps which watch what you're doing and respond accordingly. The only thing it's good for any more is hooking and preventing window activation in code you don't have access to. You could intercept the HCBT_ACTIVATE code and prevent a window from activating.
This would probably need to be done in C or C++.

Contrary to common sense, setting xlApp.ScreenUpdating = true; prevents focus steal! (source)

I'm not sure I have a good answer to your question (I would have left a comment, but apparently I don't have enough reputation yet), but in case it's helpful, I often also disable events
xlApp.EnableEvents = False
Obviously you have to be sensitive to this setting when you're relying on Excel events in your code, but just in case there are some unwanted events firing while you're inserting, this might help.
Good luck!

Related

WinForms MdiChildren Selection and Activation

I am a fairly experienced WinForms developer. I have an MdiApplication that used to work well. However, recently the main shell of the application, for which we use ComponentOne RibbonForm, has been updated in a big way. This update did affect some of our other 3rd party components, which we established was due to ComponentOne's use of DoEvents() in their event code. I thought I had cleaned up all of the code causing problems but I now have found another...
When I have multiple MdiChildren open and select one of these in code from an button click event on the ribbon form via
document.Activate();
document.EditorControl.Select();
document.EditorControl.Focus();
the other open MdiChildren documents still have focus, that it the forms are highlighted and input is not set on the document I set in code. Two questions:
How can I ensure that the Form I want to make active is the only one that is active?
Linking to the above; setting one form as active using form.Activate() should deactivate the others MdiChildren, but it is not - how can I deactivate the other windows in code?
Thanks for your time.
[Too long for a comment]
I am sick to the hind teeth with fighting C1. Esp. the Ribbon. I have confirmed with their support that they do use DoEvents() which they use to yield on their Gui threads. I am now going to switch to DevExpress which should be straight forward for my MVC application...
C1's use of DoEvents() messes up the normal flow of your application. DoEvents() is asynchronous which means it terminates before the application has actually processed any outstanding events, so if you're using it in a procedure with many sequential statements, calling DoEvents() causes a huge disturbance whenever it's called. This is what I think we are seeing when we perform our MDI operations, but we can never be sure without the C1 source code.
I hope this helps.

Get Control/Form object from IntPtr Handle

I try to get the managed control from a shown Word Application window using following code:
Process[] processes = null;
processes = Process.GetProcessesByName("WINWORD");
Process wordProc = processes[0];
Control wordControl = Control.FromHandle(wordProc.MainWindowHandle);
unfortunately wordControl is always null... as far as i know, FromHandle returns null, if no handle related control was found. But actually I guess there should be a related control, because I can see the window on my screen.
Therefore my question is, if I'm doing something terribly wrong while trying to get the handle or the control.
Or maybe my general approach will not work for some, at this time unknown, reasons based somewhere in the .NET / Windows environment.
What you are trying to do is not possible. You cannot take an instance of Word running in its own process and cast it as a C# WinForms Control - this would be totally insecure.
Depending on what you want to do, there are two approaches you can take:
If you want to affect the behaviour of an existing Word instance, then you can send it some messages using SendMessage() and other assorted User32.DLL functions. Use Pinvoke / DLL Import to accomplish this.
If you are trying to use Word functionality in an app you have written (e.g. create word document) then use Word interop libraries:
Edit
If you're interested in handling key events in an existing Word instance, you can use a Low Level keyboard hook to deal with key events, specifying the handle of the word procs you're interested in.
Control.FromHandle requires you to pass the handle of the managed control, not the MainWindowHandle of the win32 window...

How do I create a WinForms application that locks/freezes every other application and can't be closed?

I am writing an application in c# to lock or freeze all programs untill user enters a value in the app's textbox and clicks ok.
The purpose of the app would be to get people to enter their time.
As far as I know you can set it to top most but they can end the app with task manager so am stuck here..
formName.TopMost = true;
Any help would be appreciated
Yes, that's correct. The Windows operating system allows multiple programs to run at one time. What you're experiencing is entirely by design.
If I remember correctly, the TopMost property applies only to windows in your process, and as you mention, it's all quite irrelevant: the user can still kill your application using the Task Manager.
There's no legitimate way of getting around that. It's not a "limitation", it's a feature. Any app that prevents itself from being closed by the Task Manager is treading dangerously closely on the category of software that we call malware. Nothing good can come out of pursuits like this.
Relevant reading: The arms race between programs and users
Perhaps a good compromise solution is to make your window/form actually top-most and disable the Close button so that the user knows they shouldn't try and close it. This is almost always enough to stop a user that is not determined to end your application by any means necessary, and that's about all you should ever be concerned with.
See the sample code here for how to make your window/form always appear on top of other running applications by setting the WS_EX_TOPMOST flag or toggling HWND_TOPMOST.
I've also already written a detailed answer here about disabling the Close button the correct way by setting the CS_NOCLOSE class style.

Prevent Excel from quitting

I'm missing an Excel.Application.Quit or an Excel.Application.BeforeQuit event. Does anybody know a workaround to mimic these events?
I access Excel from a C# WinForms application via COM Interop. Given an Excel.Application object, how can I:
Preferrably prevent Excel from quitting?
If this is not possible, how can I at least notice when Excel is quit?
Please note: Since I have a COM reference to the Excel.Application, the Excel process does not exit when Excel is "quit" by the user. Although this sounds contradictory, that's how it is. By "quit" I mean that the user hits "Quit" or the "cross button" at the top right corner of the window. The window closes, the files are unloaded, the add-ins are unloaded and whatever stuff Excel does apart from that which I have no clue of. But I can still use the Application object to "revive" the process and make Excel visible again, though the add-ins are then missing, and I have no certainty about what else is in an undefined state.
To get rid of this problem, I would like to either Cancel the Quit at the very start (Think of a BeforeQuit Cancel = true if it existed), or at least be notified when Excel is quit, so I can release the COM objects and make the process really exit, and next time I need Excel again, I will know that I need to start it up first.
Unfortunately it's a vicious circle: As long as Excel runs, I need the COM objects. So I can't dispose of them before Excel is quit. On the other hand, as long as the COM objects are there, the process doesn't exit even if Excel pretends to quit, so I cannot wait for a process exit event or similar.
I have the unpleasing feeling that I'm going to bash my head against a brick wall...
Please note that I haven't tried this.
Create a workbook which has code in it on BeforeClose.
for e.g.
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Cancel = True
End Sub
Open this workbook alongwith other workbooks that you have & it doesn't have to be hidden (if the entire application is invisible).
So, if you try to quit the excel instance, it will force closing of this hidden workbook, which will raise its BeforeClose event & you can write code to stop it from closing.
Note that above code is in VB6 (VBA) and it will need converting into c#.
Post a comment, if you find any difficulty converting.
If you want to hide a workbook, you could do
Workbooks("my workbook").Windows(1).Visible = False
Note: Workbook has a Windows collection. The code above tries to hide the 1st window.
I don't know, can a workbook have more than 1 window? if so, how?
It's a hack of course, but couldn't you use the Windows SetWindowsHookEx API with WH_SHELL or WH_CBT at least to get notified of the Excel's main window being destroyed?
NOTE: It certainly has security implications, i.e. need some admin rights to do cross-process magic.
The problem you are trying to solve here is not going to be solved by monitoring for program exit.
Before you say that I am not answering your question, you state in the question that you are able to revive excel even after the user quits excel. Therefore the excel.exe process is still in play because you have a .net object with a com interop reference to excel.application.
So you have three options:
Avoid the user exiting Excel.
As you have stated keep Excel from quitting, however I am unaware of a way that you can prevent the user from causing Excel to quit, thus as you have correctly noted unloading your and any other addins, etc. Bare in mind that Microsoft specifically design the user interaction this way, they want users to have the ability to close their apps. your addin needs to be able to deal with this, if it can't I'd say thats a problem with your addin not Excel. I might be wrong since i dont know enough about your apps requirements.
Cleanup all unmanaged resources BEFORE the user quits.
What you need to do is cleanup your references to alll Excel and Office unmanaged resources before the user manually quits Excel so that when they do quit your application code is not left with any leftover resources that are now pointing to an instance of excel that no longer has addins etc loaded. Step (a) should be performed as you go, as soon as you no longer need a particular resource or even when reusing it for something else (i.e. a Excel.Range type) whereas step (b) should be used less often however if its a win app and not a addin, probably alot more frequently, it all depends on your app and the window of oppurtunity that you have (time) before the user is likely to complete there tasks an shutdown. Obviously with an addin you can just put it in the shutdown event, or arbitarily in your code.
a. As noted by Otaku, use Marshal.FinalReleaseCOMObject on each unmanaged resource that is != null after use.
if (ComObject != null)
{
Marshal.FinalReleaseComObject(ComObject);
ComObject = null;
}
b. use the GC cleanup pattern for COM resources.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Reload addins
If your not interested in fully tracking down and unloading all unmanaged resources due to the complexity of that task, time constraints (although I'd recommend it), you could look at reloading any required addins that you are presumably already aware of in your environment. This only works if you control the environment.
there are techniques for loading both Excel and COM addins manually. As for other stuff, I'm not aware of that but perhaps its possible if you are using XLLs or maybe XLT in startup/XLSTART dirs but that would load anyways.
There is a KB article, How to automate Excel and then know the user closed it, in C++. I haven't ported this to C#, but it's likely not a lot of work.
Why don't you just execute a System.Diagnostics.Process.Start(#"SomeWorkbook.xlsx"); to ensure that Excel is started. If it already has been started then this will not create a new process.
Why not just use the Application.ApplicationExit event to know when it is closed?

Windows Terminal - Windows Form to embed cmd

I'm looking for a way to embed a cmd "Shell" into a Form. I want to build a C# based application that acts as a Terminal, better than the Powershell window (no tabs) or cmd (no nothing). Just start these interpreters in the backend.
I guess MS never thought of doing this. Any ideas what From elements I could use?
Thanks,
i/o
That's not a trivial task you're undertaking. I know of one project (Console2) which basically polls the screen buffer of the underlying console window and displays in its own. You certainly will have trouble coping with interactive applications like Far and the like as they (a) rely on getting keyboard events and (b) on manipulating their screen buffer. Both are icky things if you want a suitable wrapper around the console window functionality. Mouse input is possible as well (unless Quick Edit mode is enabled) which could give you further headaches.
I doubt you can use a ready-made control for this. Basically you need to display a grid of cells each of which has a foreground and background color. You could probably use a RichTextBox for this but I'd guess it's far from ideal.
Also I don't think no one at MS ever thought of this. It's just that there's a limited budget for new features and every one of them needs to be specified, implemented, tested, tested more for regressions with millions of applications out there, etc. It's just a freaking expensive thing (if you don't want to misuse your customers as testers, which they aren't).
It would propably be the easiest to extend the Textbox class and add logic so that it behaves like a console (respond to the KeyPressed/KeyUp/KeyDown events or similar). You can also add events for those things that your console needs to respond to. For example, add a CommandEntered event.
Basing your new console on a TextBox gives you the editing and display features of the textbox "for free", so you do not need to re-implement that.
You could use a richtext box, and set the background to black and foreground to white. RTB instead of text box to handle larger amounts of data.
You would have to write an awful lot of code to simulate the terminal though.

Categories