DELPHI MessageDlg Call from DLL disrupts Control Focus - dll

Recent upgrade to RAD XE8.
I have a dll with several MessageDLg calls e.g. Warning/Information etc e.g.
procedure Information(Msg: string); stdcall;
begin
MessageDlg(Msg,mtInformation, [mbOK],0);
end;
I noticed last week the following:-
If I double click on a grid with a handler
begin
Information("I am here"); //in the dll
ens
the message is displayed but when I click and the message box disappears I can no longer click on the grid (Mouse wheel does work but not the buttons) (no response). If I click on a second control and then back on the grid all is well again.
I tried adding to the DLL
uses VCL.Forms;
procedure Information(Msg: string); stdcall;
begin
Application.Handle := GetForegroundWindow;
try
MessageDlg(Msg,mtInformation, [mbOK],0);
finally
Application.Handle := 0;
end;
end;
to no avail.
I have established the following:-
ih my app
CanFocus; //DBGrid1
MessageDlg(Msg,mtInformation, [mbOK],0);
CanFocus; //Still DBGrid
but in my app (calling the MessageDlg in the DLL via the function Information())
CanFocus; //DBGrid1
Information('Hi ya');
CanFocus; //MAINFORM
CanFocus only added for debug purposes
So the first dblclick on the DBGrid calls Information but subsequent dblclicks are not detected in the VCL (messages are visible via Windows
Detective).
in VC.LDBGids TCustomDBGrid.AcquireFocus:
Focused is True the first time and False subsequently, until I click a second control and then back on the DBGrid.
Does his make the issue clearer?
TIA
Ephraim

Edit: Setting the global variable UseLatestCommonDialogs to False solved the problem!
I'm having the same issue and posted a question on Embarcadero Forum. I hope the answer help you:
That is because none of the HWNDs that belong to the EXE are being
used as the owner for the modal dialog window, so it can't restore
focus correctly when closed. That is why you have to click on the grid
again to regain focus.
Raymond Chen has a whole series of articles on his "Old New Thing"
blog on MSDN that discuss issues related to modal window ownership (or
lack of).
That is because calling MessageDlg() inside the EXE has access to the
EXE's TApplication and TForm HWNDs to use as modal dialog owners. A
DLL does not have that same access.
I would suggest getting rid of MessageDlg() inside the DLL altogether.
Call the Win32 MessageBox() or TaskDialog() directly instead, then you
can provide whatever HWND you need as the owner window.
Remy Lebeau (TeamB)

Related

Vb.Net: How can I trigger an event when a single instance program is opened again?

I've got a single instance (set up via .net) program that operates mostly from the system tray but also has a window.
Users often lose the program among their other system tray icons and believe the program isn't running, trying to open it again from the executable.
How can I detect, within my running program, that the executable is opened again? (So that I can maximize the window)
Thanks in advance.
Opening another copy of a single instance program will bring your form to the front by default, but when the form isn't visible, this won't have any effect.
This is what I was looking for
The MyApplication_StartupNextInstance event occurs when another instance is started. This can be used to call any additional functions you need.
In Project Properties, You can navigate to Application -> View Application Events and handle Me.StartupNextInstance from within Partial Friend Class MyApplication.
Direct all thanks to the comments.

Do not require attention for ShowDialog

I had a question about Dialogs in VB.NET. I am working on a point of sale program, and at one point during a sale, I have a few windows that pop up. For example, a user will go into a sale that is window A. In window A, they have the option of entering products, etc., and if they choose a 'repair' product, it opens window B, allowing them to choose options. In window B, there is a button that pops up window C that allows them to attach products TO the repair. My issue is with window B opening window C.
Because I open window B as a Dialog (in order to check if DialogResult.OK is true), any window I open with B is non-touchable, as B is a Dialog and requires attention before going to any other windows/forms.
My question is - is there any way to still use a dialog, but allow for manipulating other open forms while the dialog is up, and if not, what would be the best way to check if the user selected OK, or cancelled out of the window?
The only solution I can think of right now would be to open window C as a dialogue as well (it's actually a UserControl, and I'm still trying to find where in the code it's actually getting openned/called), or to create a variable that is passed in to the form, and then passed back out when it's closed, that basically sets a flag to either continue or cancel...
Any advice/ideas??
If I were to explain this using code, this answer would be very long, so instead I'm going to give you a high level overview.
.Show() vs .ShowDialog()
The link below will take you off to Microsofts website to explain the technical differences between these two. However in laymans terms, .ShowDialog() will create the form where it is the only window allowed to have focus in the application. Forms that are called in this instance are hierarchical, in that if you open them in order of 1,2,4,3 then they must be closed in the 3,4,2,1 order. Forms that are opened with just .Show() can be focused at any time.
How to: Display Modal and Modeless Windows Forms
Form.FormBorderStyle property
This property controls how the OS will display the window. The different options under this selection changes the way the window behaves. Depending on the options that are chosen you can make a window that only has a close button on it, or it may not even have a title bar at all. Setting this option to None will take away all controls of the form and only leave you with the Me.ClientArea to work with. When you want a completely custom GUI, this is how you do it but you have to implement your own controls for everything, closing the form, size handles, the ability to move the form on the screen, etc...
Form.FormBorderStyle Property
Passing data between forms
When someone asks how to pass data back and forth between forms, they are usually talking about modeless forms that were created using .Show(). The most common thing I see on SO is to use the tag property of an object (a form is an object that has this property too) to pass data back and forth. While I won't say this is a bad practice, I will recommend creating public properties on your forms. These can be set from a separate form and you can perform additional actions when setting the values (be careful though, this way of doing things isn't thread safe). If you are using a Modeless form as though it were a Modal form, then you can simply override the .Dispose property to return a value or you can create a method named DialogResult that will return the value you need. The caveat to using a DialogResult or similar method is that if the form has been disposed then you can't access the value you wanted to return.
You can use myNewForm.Show(Me) for the Window you want to be shown as a dialog. This will show myNewForm as a child of the current form, but lets you interact with the current form.

VB6 - reading from keyboard in an ActiveX DLL

I have an ActiveX DLL which currently reads from a serial port. Now I want it to accept input from a USB device.
The USB device functions as a standard Human Interface Device. That is to say, if I open Notepad then the device's output will appear in Notepad as if it were typed on a keyboard.
Normally, I would capture Key Up/Down events, but I think that I need a form for that and my DLL does not have a form.
How can I capture that input?
[Update] I found this http://us.generation-nt.com/answer/anyone-know-how-read-keyboard-input-within-an-activex-dll-help-7934442.html# which claims to do it, but the code there won't work as is uses the AddressOf operator, which can only be used in a .BAS file, so not in an DLL .CLS
I am not even sure if I am looking for a system wide hook or application specific.
Hmmm, http://www.xtremevbtalk.com/showthread.php?t=77216 says "You can't implement a global WH_KEYBOARD hook in VB - it requires a standard (non ActiveX dll) as it has to be loaded into the address space of all the running applications."
[Upper date] So, maybe I can a form, make it 1x1 pixel and invisible and have a function GetTheData which shows the form modally and collects and returns the data - either getting keyboard input at form level or into a (n invisible) control then closes the form returning the input.
Would that work? If anyone posts a working example I will award a bounty (I would prefer that the form not be visible on the task bar and have no close button; the user should not be aware of it, or able to close it, it should close itself when it receives enough input from the USB attached HDI).
You can use RegisterRawInputDevices to monitor HID devices' input but this requires a window to listen for WM_INPUT message which means subclassing the window.
Here is a working sample project: UsbBarcodeSanner.zip
I think you have better option,
using uesr32.dll you can do this task easily,
refer this link
you will be able to use this function
Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Long) As Integer
This dll handles anything you want for user in windows.. refer Old Post
I hope this will help..

System.Threading.ThreadstateException

I'm developing an adding for office powerpoint application.
I'm trying to display a description of the object(Customized object) currently dropped on the powerpoint slide in design mode(Design mode of the powerpoint).
When i click on my addin the related object description will be displayed on a tabbed window as the first tabpage.
There is a button on the tab page, and when i click on it i need the description to get copied to windows clipboard.
I tried this using clipboardclass it throws the following exception,
System.Threading.ThreadstateException
{"Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it."}
Code for clipboard:
Clipboard.Clear()
Clipboard.SetText(lblObjectID.Text)
I searched the net for a solution and got couple of answers like,
1. Put [STAThread] in the main function
2. Thread.CurrentThread.SetApartmentState(ApartmentState.STA) Immediately before your call to SetDataObject.
But I'm not sure where to put the 1st one and the 2nd option didn't work.
Can anyone help me please.
Thanks.
WinForms are STA by default. Are you creating another thread or using a BackgroundWorker? Run this code to determine what mode you're in:
MessageBox.Show(System.Threading.Thread.CurrentThread.GetApartmentState().ToString())
Edit:
But maybe you could also try using this command before calling the clipboard functions:
Application.OleRequired()

VB.Net MessageBox.Show() moves my form to the back

I have an MDI application. When I show a message box using MessageBox.Show(), the entire application disappears behind all of my open windows when I dismiss the message box.
The code is not doing anything special. In fact, here is the line that invokes the message box from within an MDI Child form:
MessageBox.Show(String.Format("{0} saved successfully.", Me.BusinessUnitTypeName), "Save Successful", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly)
Me.BusinessUnitTypeName() is a read only property getter that returns a string, depending upon the value of a member variable. There are no side effects in this property.
Any ideas?
Remove the last parameter, MessageBoxOptions.DefaultDesktopOnly.
From MSDN:
DefaultDesktopOnly will cause the
application that raised the MessageBox
to lose focus. The MessageBox that is
displayed will not use visual styles.
For more information, see Rendering
Controls with Visual Styles.
The last parameter allows communication of a background Windows Service with the active desktop through means of csrss.exe! See Bart de Smet's blog post for details.
Remove the MessageBoxOptions.DefaultDesktopOnly parameter and it will work correctly.
DefaultDesktopOnly specifies that "The message box is displayed on the active desktop" which causes the focus loss.
These answers are correct, but I wanted to add another point. I came across this question while working with someone else's code. A simple message box was causing the front most window to move to the back:
MessageBox.Show("Hello").
Turns out, there was a BindingSource.Endedit command before the MessageBox. The BindingSource wasn't connected to any controls yet, but it caused the window to change z-positions.
I am only including this note since my search brought me to this question and I thought it might be helpful to someone else.