Solved

Maximizing a window with SendKeys

Posted on 2000-03-23
18
1,222 Views
Last Modified: 2006-11-17
I'm using SendKeys to send information to a different application. I've noticed that if that window is minimized the SendKeys will not work. Is there something that I can do to insure that the app that I'm sending to is maximized?
0
Comment
Question by:scuzz1
  • 9
  • 9
18 Comments
 
LVL 9

Accepted Solution

by:
BrianWren earned 150 total points
ID: 2650817
It's very complicated.

You need to use API functions to find the window's handle, then use that handle to ascertain its state, and if minimized, send a Restore or Maximize message to that window...

How is the application being opened that you are using Access to send keystrokes to?

Perhaps there is a better way:  
What are you trying to do by sending keystrokes to this application?
What application is this?

Brian
0
 

Author Comment

by:scuzz1
ID: 2651354
I'm trying to create a print drawing button in Access. The file name to be printed is in the database. One of the problems is the fact that there are three possible file extensions. The other problem is that I have to use "PSLOUT". This is a encyrpted program used to generate postscript files for printing. I don't know if you are familiar with Pslout but once it is executed it takes full control of the window meaning that you can only input the args by keystroke. I tried creating a script to do the entire process but as soon as Pslout is executed it stops and waits for args.

So here is what I'm doing:

I have a Unix script that will determine the file extension needed.  I'm sending keys to an Exceed window (Unix emulator) with that script name and the filename. The script finds the file extension. It then saves the filename with extension to a textfile.Then I sendkeys to a NT text editor that opens the textfile and cuts and pastes the filename back to a blank textbox in Access. Then I sendkeys back to the Exceed window which executes Pslout and sends the keys needed to create the postscript file and print it. Pslout will accept the information this way. A real Hackers solution, But it does work.

Unfortunately, the main problem I'm having is when the window I'm sending keys to is minimized.

We are using NT boxes with a Unix server. We have a database with a form that we call our Inbox. We were experiencing problems with typos of filenames and libraries where we send finished art. With the help of Access and its pulldown boxes we are able to eliminate a lot of these types of errors. I just wanted to go a step farther and have the ability to print right from our inbox.

I will be glad to increase points if you could think of a better way of performing this task (short of new hard and/or software).

                                             Regards,
                                             Scuzz1
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2654175
How are you executing PSLOut?  (I'm thinking that maybe you can get the handle to the window as you open it.)

Why do you need to use the Unix script?  It seems that you could fill the Access text box without it, (or am I missing something...)

0
 

Author Comment

by:scuzz1
ID: 2654563
The field in the database that contains the drawing name does not have the extension with it. Pslout needs the extension. The script I wrote just determines what extension I need to send with the filename.

I execute Pslout with SendKeys.
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2654882
Actually I was wondering how that script makes this determination of what the extension should be.

When you run PSLOut, is that sending keys to an already-open window of 'Exceed'?  Is that the one that might be minimized, the 'Exceed' window?

If so, how was THAT window opened?
0
 

Author Comment

by:scuzz1
ID: 2654940
Yes, the Exceed and the text editor windows are already open before function is executed.

The script that determines the extension simply uses "if file.ext exists, then" statements.
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2655552
I'm working on how to get the actual handle to the Unix widow, so that you can find out if it is minimized, and so that you can change its state, and give it the focus.  (Using APIs, and crashed my machine about 7 times so far...  But I can get the handle to every top-level window...)

Brian
0
 

Author Comment

by:scuzz1
ID: 2661016
Ok Brian,

       Don't want you to have a meltdown trying to get this one.

       I'm not familiar with API's. Is this something a novice hacker such as myself fool with?
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2661188
OK.  I have the APIs to find out everything you need to know about all of the open windows.

Create a form with one object on it:  A textbox wit a vertical scrollbar.  Call it lstWindows.

In the Form's Open event, put this  code:
________________________________________________________________________________

Option Compare Database
Option Explicit

Private Sub Form_Load()
  Getem Me!lstWindows
End Sub
________________________________________________________________________________



In a module put this code.  (You should be able to cut and paste):

________________________________________________________________________________

Option Compare Database
Option Explicit

Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Declare Function GetDesktopWindow Lib "user32" () As Long
Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long
Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long

Public Const GW_HWNDFIRST = 0
Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2
Public Const GW_HWNDPREV = 3
Public Const GW_OWNER = 4
Public Const GW_CHILD = 5
Public Const GW_MAX = 5

Public Const GWL_WNDPROC = (-4)
Public Const GWL_HINSTANCE = (-6)
Public Const GWL_HWNDPARENT = (-8)
Public Const GWL_STYLE = (-16)
Public Const GWL_EXSTYLE = (-20)
Public Const GWL_USERDATA = (-21)
Public Const GWL_ID = (-12)

Sub Getem(c As Control)

    ' Makes 3 columns...
    Dim hwnd&
    Dim s As String

    hwnd& = GetDesktopWindow()          ' The desktop is the highest window

    hwnd& = GetWindow(hwnd&, GW_CHILD)  ' It's 1st child is the 1st top lev. win.

    Do                                  ' Now load all top level windows
        s = s & GetWindowDesc$(hwnd&) & vbCrLf
        hwnd& = GetWindow(hwnd&, GW_HWNDNEXT)
    Loop While hwnd& <> 0
   
    c = Left$(s, Len(s) - 1)

End Sub

Public Function GetWindowDesc$(hwnd&)

    Dim desc$
    Dim tbuf$
    Dim inst&
    Dim dl&
    Dim hWndProcess&

    desc$ = """&H" & Hex$(hwnd) & "    "                 ' Include the windows handle 1st

    ' Get name of source app
    tbuf$ = String$(256, 0)                             ' Predefine string's length

    dl& = GetWindowThreadProcessId(hwnd, hWndProcess)
   
    If hWndProcess = GetCurrentProcessId() Then
        inst& = GetWindowLong(hwnd&, GWL_HINSTANCE)     ' Get instance for window
        dl& = GetModuleFileName(inst, tbuf$, 255)       ' Get the module filename
        tbuf$ = GetBaseName(tbuf$)

        If InStr(tbuf$, Chr$(0)) Then tbuf$ = Left$(tbuf$, InStr(tbuf$, Chr$(0)) - 1)
    Else
        tbuf$ = "Foreign Window"
    End If
   
    ' And add it to the description
    desc$ = desc$ & tbuf$ & "    "

                                            ' Add the class name
    tbuf$ = String$(256, 0)                 ' Initialize space again
    dl& = GetClassName(hwnd&, tbuf$, 255)
    If InStr(tbuf$, Chr$(0)) Then tbuf$ = Left$(tbuf$, InStr(tbuf$, Chr$(0)) - 1)
    desc$ = desc$ & tbuf$

    GetWindowDesc$ = desc$    ' And return the description

End Function

Private Function GetBaseName$(ByVal source$)

    Do While InStr(source$, "\") <> 0
        source$ = Mid$(source$, InStr(source$, "\") + 1)
    Loop
   
    If InStr(source$, ":") <> 0 Then source$ = Mid$(source$, InStr(source$, ":") + 1)
    GetBaseName$ = source$

End Function
________________________________________________________________________________



When you open the form, it will have in the text box a list of all of the open window's handles, (In hexadecimal notation), the Module name of the window, and the class name of the window.

From this hopefully you will be able to ascertain which of the windows is the one that you need maximized, (or at least restored),  and we can proceed from there.

Brian
0
Complete Microsoft Windows PC® & Mac Backup

Backup and recovery solutions to protect all your PCs & Mac– on-premises or in remote locations. Acronis backs up entire PC or Mac with patented reliable disk imaging technology and you will be able to restore workstations to a new, dissimilar hardware in minutes.

 

Author Comment

by:scuzz1
ID: 2661575
Ok, got it

"&H20434    Foreign Window    Exceed

This is what is listed for Exceed.



0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2661980
OK.

Knowing the actual title of the window, modify the code that I gave you to return just that number, &H20434.  (Search for the 3rd part: " = Exceed".  That number will change from instance to instance...)

By the way, you can get the actual value with Val("&H20434").

Using this number, you can send an message to the window to maximize itself.  That number would go where the 'hWnd' (a Long integer) is in the following:


Add this to the modlue where you get the window's handle:

___________________________________________________________

Public Const GWL_STYLE = (-16)
Public Const WS_MAXIMIZE = &H1000000

Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

___________________________________________________________

In the code where you need the window maximized add:
___________________________________________________________


     ' This is where the number goes that you get from the modification described above.
     SetWindowLong(hWnd, GWL_STYLE, WS_MAXIMIZE)

___________________________________________________________


(I can't find the value for restored...)

This can be a risky API call.  Save your work first--all of it.

Brian
0
 

Author Comment

by:scuzz1
ID: 2674542
Hi Brian,

     Sorry it took so long to get back to you. We've been pretty swamped around here. Not much time to hack.
     I got the handle seperated from the list generated by the code you gave me. I also added the modification. Should I be calling the SetWindowLong function from the module that is sending keys? i.e.

    WinPRAtitle = "Exceed"
   
    Call SetWindowLong(hwnd, GWL_STYLE, WS_MAXIMIZE)

    AppActivate WinPRAtitle, True
           
    SendKeys "^C" + Chr(vbKeyReturn), True

Where is it passing the handle at? (hwnd?)

              Confused,
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2681610
Yes, that's the place.  Using the post you made, the call would be

     Dim hwnd as Long

     hwnd = GetWindow(hwnd&, GW_CHILD)  ' The line here that gets the "Exceed" window Handle.

     Call SetWindowLong(hwnd, GWL_STYLE, WS_MAXIMIZE)

You could use a  Do / Exit Do / Loop  to find the right handle, (it is never guaranteed to be the same from day to day;  handles are generated as needed), then use the right one to maximize the window that you are after.

Brian


0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2681624
I mean

     Dim hwnd as Long

     hwnd = GetWindow(hwnd, GW_CHILD)  ' The line here that gets the "Exceed" window Handle.

     Call SetWindowLong(hwnd, GWL_STYLE, WS_MAXIMIZE)

That '&' is the type specifier for type Long Integer.  The references I consulted like type specifiers, I like keywords...

Brian

0
 

Author Comment

by:scuzz1
ID: 2693562
Hi Brian,

      I got it working I think. It's hard to tell. It still seems that after I first log on the Exceed window dosn't want to maximize. When I hit the button to execute I get a lot of blips and beeps like it's sending keys to either a wrong window or nothing at all. Then I'll go into the Exceed window. I'll do a list dir or something. Minimize the window. Go back into Access and it will work.  I'm not sure if it's just a glich or what. Is there a way that I can tell for sure that it is working or whether it is just maximizing due to the sendkeys?
0
 
LVL 9

Expert Comment

by:BrianWren
ID: 2701073
This kind of operation is rife with pitfalls.  (This is why they pay us the big bucks!)

Testing this sort of thing in a meaningful way often involves things like intercepting Windows messages to an dfrom the window, etc.  In a situation that involves an emulator like Exceed, that can be a real hair puller.

If I were you, and I got it working, I'd document what I did and leave it at that unless it ceases working.  (That's the reason for the documenting I mentioned.)

The Exceed window might not perform like a standard window, (it might be subclassed, with some properties eliminated, and others added), the title might change the first time it is used, etc.   It's hard to say by 'remote control,' which is basically the position that I am in on this question, not having Exceed myself . . .

Brian
0
 

Author Comment

by:scuzz1
ID: 2701262
Adjusted points from 25 to 150
0
 

Author Comment

by:scuzz1
ID: 2701263
Yeah, I was afraid of that.

I think that is exactly what I'm going to do. I've got to keep this one in development until I can get it a little more reliable.

I'm pretty sure that it does work. I'm just going to have to work with it for a while and keep my eye on it.

I want to thank you for all your efforts. I can appreciate you position. Trying to develop and/or debug code from a remote position must be frustrating at times, to say the least.

                          Again, thanks for your time.
                          Scuzz1
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

This article is a continuation or rather an extension from Cascading Combos (http://www.experts-exchange.com/A_5949.html) and builds on examples developed in detail there. It should be understandable alone, but I recommend reading the previous artic…
Most if not all databases provide tools to filter data; even simple mail-merge programs might offer basic filtering capabilities. This is so important that, although Access has many built-in features to help the user in this task, developers often n…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
With Microsoft Access, learn how to start a database in different ways and produce different start-up actions allowing you to use a single database to perform multiple tasks. Specify a start-up form through options: Specify an Autoexec macro: Us…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now