Well, due to recent discoveries, I have to recant a lot of what I said in Part 1. Embarrassingly, that functionality is a snap to do with QTP’s Window object, and I finally figured out how to successfully call ShowDialog on a .NET Form and pass the QTP window to it making the dialog modal to the QTP window. This is what I was trying to accomplish and the whole reason I began this quest in the first place. In my defense, I have only used QTP on web applications, so I wasn’t familiar with the QTP Window object. All effort was not wasted though, at least I learned about the QTP’s Extern object!
I didn’t realize how easy it was to find windows by their title with Descriptive Programming and the Window object. To easily find the QTP window you can just code: []Window(“RegExpWndTitle:=QuickTest Professional”). QTP only allows one instance running, so I would think this is pretty safe, but you could add more identification properties if you are worried that this could come back identifying more than one window. From there you can get runtime object properties, including the Handle for the QTP window.
Since it is easy to pick out windows this way, Part 1‘s functionality can be much more easily duplicated by finding the window that you want to bring to the front by using the identification properties of the Window object and simply calling the Window().Activate function. This is obviously much easier than defining and calling external methods as presented in Part 1, most embarrassing.
Anyway, on the subject of making the .NET Form modal to the QTP window, one of the strategies I was unsuccessfully trying to implement was to call the FromHandle method on the .NET Control class to get an object that implements the IWin32Window interface to pass to the ShowDialog method on the Form object. The reason I couldn’t get this to work was that I kept attempting to hand the “raw” Handle directly into the method. What made this the most confusing was that .NET objects throw a “Method not found” exception when an argument is of the wrong type, so I was just figuring that the method was not available due to the fact that I instantiated the Control object indirectly with the QTP’s DotNetFactory object.
I found a solution here. This forum post clued me into the piece of the puzzle I was missing. The FromHandle and similar methods are looking for an argument of the IntPtr type. The IntPtr object is an assembly that holds the handle value. The post also clued me into the NativeWindow class and its AssignHandle method. When calling the ShowDialog method, the only thing defined by the IWin32Window interface is that it has a Handle property, so all we need is an object that implements the interface to which we can assign the Handle property.
To make a modal dialog from a .NET Form with the QTP window as the parent:
Dim myForm, hWnd, intPtrHWnd, qtpWnd Set myForm = DotNetFactory.CreateInstance("System.Windows.Forms.Form", "System.Windows.Forms") hWnd = Window("RegExpWndTitle:=QuickTest Professional").GetROProperty("hWnd") Set intPtrHWnd = DotNetFactory.CreateInstance("System.IntPtr", "Mscorlib", hWnd) Set qtpWnd = DotNetFactory.CreateInstance("System.Windows.Forms.NativeWindow", "System.Windows.Forms") qtpWnd.AssignHandle intPtrHWnd myForm.ShowDialog qtpWnd myForm.Dispose Set myForm = Nothing Set intPtrHWnd = Nothing Set qtpWnd = Nothing
The flexibility here is amazing. You can add any .NET form components to the form to build custom dialogs, inputs for passwords, etc. There are even SaveFileDialogs, FolderBrowserDialogs, and ColorDialogs (to name a few), that can be shown modally to the QTP window in the same maner.
As promised, though, I was going to show the Shell.Application BrowseForFolder method, but of course, I will get the QTP window handle using the Window object this time.
Dim shell, hWnd, flder Set shell = CreateObject("Shell.Application") hWnd = Window("RegExpWndTitle:=QuickTest Professional").GetROProperty("hWnd") Set flder = shell.BrowseForFolder(hWnd, "My Folder Browser", 0) 'Third arg is options arg, fourth arg is optional for root directory (Desktop is default, check docs for more info) If flder Is Nothing Then 'Handle error Else MsgBox flder.Self.Path End If Set flder = Nothing Set shell = Nothing