AutoWin UserLibrary

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: AutoWin UserLibrary

Post by srod »

Kiki, this library, as excellent as it looks, is nothing to do with me!

Sorry for the off-topic posting Thomas.
I may look like a mule, but I'm not a complete ass.
UserOfPure
Enthusiast
Enthusiast
Posts: 469
Joined: Sun Mar 16, 2008 9:18 am

Re: AutoWin UserLibrary

Post by UserOfPure »

KIKI wrote:Purebasic dosen't allows controlling IE or Firefox, it will be very usefull to have these functionality
Don't know if you realise it, but PureBasic can do everything that AutoWin does, natively, without the need for a library. AutoWin is mainly just a bunch of wrapper calls to the Windows API. PureBasic can control IE and Firefox in exactly the same way that AutoWin does.
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Thanks for this library, Thomas!
It's very useful for my current project.

Regards, Little John
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Hi Thomas,

in the current version 1.3, I think there is an issue with the function AW_WinWaitClose2().
As far as I understand, its return value should tell us the reason why the function had terminated, e.g.:

Code: Select all

If AW_WinWaitClose2("My window", 5)
   Debug "Window is closed."
Else
   Debug "Timeout. Window is open."
EndIf
But if the respective window is not open anyway when the function is called, the return value also is #False. So when the return value is #False, the situation is ambiguous: It can mean that the respective Window is closed, or that the window is open, and there was a timeout. For the ease of discussion, I post the code here again:

Code: Select all

ProcedureDLL AW_WinWaitClose2(title.s, timeout)
  Protected time = ElapsedMilliseconds() + (timeout * 1000)
  Protected timebreak

  If Not AW_WinExists(title)
    ProcedureReturn #False
  EndIf
  While AW_WinExists(title)
    If timeout
      If ElapsedMilliseconds() > time
        timebreak = #True
        Break
      EndIf
    EndIf
    Delay(250)
  Wend
  If timebreak
    ProcedureReturn #False
  Else
    ProcedureReturn #True
  EndIf
EndProcedure
For a consistent return value, line 6 should be:

Code: Select all

    ProcedureReturn #True
But then, lines 5-7 are not required at all. :-)

Regards, Little John

//edit:
Similar issue with function AW_WinWaitNotActive2():

Code: Select all

ProcedureDLL AW_WinWaitNotActive2(title.s, timeout)
  Protected time = ElapsedMilliseconds() + (timeout * 1000)
  Protected timebreak
  Protected *param.AW_WinParameter

  *param = AW_FindWindow(title)
  If *param\hWnd
    While GetForegroundWindow_() = *param\hWnd
      If timeout
        If ElapsedMilliseconds() > time
          timebreak = #True
          Break
        EndIf
      EndIf
      Delay(250)
    Wend
    If timebreak
      ProcedureReturn #False
    Else
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure
If *param\hWnd = 0, then the respective Window is not active, and the function should return #True:

Code: Select all

ProcedureDLL AW_WinWaitNotActive2(title.s, timeout)
   Protected time = ElapsedMilliseconds() + (timeout * 1000)
   Protected timebreak
   Protected *param.AW_WinParameter
   
   *param = AW_FindWindow(title)
   If *param\hWnd = 0
      ProcedureReturn #True   ; <---- new
   EndIf

   While GetForegroundWindow_() = *param\hWnd
      If timeout
         If ElapsedMilliseconds() > time
            timebreak = #True
            Break
         EndIf
      EndIf
      Delay(250)
   Wend

   If timebreak
      ProcedureReturn #False
   Else
      ProcedureReturn #True
   EndIf
EndProcedure
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Sending simulated keystrokes to other programs can be very useful. In this context, time is a critical point, and I often made the experience that a Delay() at proper places in the program is crucial -- depending on the situation maybe before sending the keystrokes, or afterwards, or somewhere inbetween. In simple programs, the Delay() can be hard coded into the PB program. However, for more advanced programs this is not satisfying.

With only small changes in the procedure AW_SendKeys(), we'll get the additional keystroke command {DELAY n}, where n denotes the number of milliseconds. Here is the changed procedure:

Code: Select all

ProcedureDLL AW_SendKeys (keys.s)
   ; Sends simulated keystrokes to the active window
   Protected r, s, notakey, alted, vk, ctrled, shifted
   Protected vk$, s$

   For r = 1 To Len(keys)
      vk$ = Mid(keys, r, 1)
      If vk$ = "{"                      ; Special key found.
         s       = FindString(keys, "}", r + 1) - (r + 1)  ; Get length of special key.
         s$      = Mid(keys, r + 1, s)  ; Get special key name.
         notakey = #False
         Select s$                      ; Get virtual key code of special key.
            Case "ALTDOWN" : alted = 1 : keybd_event_(#VK_MENU, 0, 0, 0) ; Hold ALT down.
            Case "ALTUP" : alted = 0 : keybd_event_(#VK_MENU, 0, #KEYEVENTF_KEYUP, 0) ; Release ALT.
            Case "BACKSPACE" : vk = #VK_BACK
            Case "CONTROLDOWN" : ctrled = 1 : keybd_event_(#VK_CONTROL, 0, 0, 0) ; Hold CONTROL down.
            Case "CONTROLUP" : ctrled = 0 : keybd_event_(#VK_CONTROL, 0, #KEYEVENTF_KEYUP, 0) ; Release CONTROL.
            Case "DELETE" : vk = #VK_DELETE
            Case "DOWN" : vk = #VK_DOWN
            Case "END" : vk = #VK_END
            Case "ENTER" : vk = #VK_RETURN
            Case "F1" : vk = #VK_F1
            Case "F2" : vk = #VK_F2
            Case "F3" : vk = #VK_F3
            Case "F4" : vk = #VK_F4
            Case "F5" : vk = #VK_F5
            Case "F6" : vk = #VK_F6
            Case "F7" : vk = #VK_F7
            Case "F8" : vk = #VK_F8
            Case "F9" : vk = #VK_F9
            Case "F10" : vk = #VK_F10
            Case "F11" : vk = #VK_F11
            Case "F12" : vk = #VK_F12
            Case "ESCAPE" : vk = #VK_ESCAPE
            Case "HOME" : vk = #VK_HOME
            Case "INSERT" : vk = #VK_INSERT
            Case "LEFT" : vk = #VK_LEFT
            Case "PAGEDOWN" : vk = #VK_NEXT
            Case "PAGEUP" : vk = #VK_PRIOR
            Case "PRINTSCREEN" : vk = #VK_SNAPSHOT
            Case "RETURN" : vk = #VK_RETURN
            Case "RIGHT" : vk = #VK_RIGHT
            Case "SHIFTDOWN" : shifted = 1 : keybd_event_(#VK_SHIFT, 0, 0, 0) ; Hold SHIFT down.
            Case "SHIFTUP" : shifted = 0 : keybd_event_(#VK_SHIFT, 0, #KEYEVENTF_KEYUP, 0) ; Release SHIFT.
            Case "TAB" : vk = #VK_TAB
            Case "UP" : vk = #VK_UP
            Default
               If Left(s$, 5) = "DELAY"   ; ** neu (JL)
                  Delay(Val(Mid(s$, 6)))  ; Führende Leerzeichen stören Val() nicht.
               Else
                  notakey = #True
               EndIf
         EndSelect
         If notakey = #False
            If Left(s$, 3) <> "ALT" And Left(s$, 7) <> "CONTROL" And Left(s$, 5) <> "SHIFT" And Left(s$, 5) <> "DELAY"
               keybd_event_(vk, 0, 0, 0) : keybd_event_(vk, 0, #KEYEVENTF_KEYUP, 0)  ; Press the special key.
            EndIf
            r = r + s + 1  ; Continue getting the keystrokes that follow the special key.
         Else
            vk = VkKeyScanEx_(Asc(vk$), GetKeyboardLayout_(0)) ; Normal key found
            keybd_event_(vk, 0, 0, 0) : keybd_event_(vk, 0, #KEYEVENTF_KEYUP, 0) ; Press the normal key.
         EndIf
      Else
         vk = VkKeyScanEx_(Asc(vk$), GetKeyboardLayout_(0)) ; Normal key found
         If (vk & $0100) <> 0 And shifted = 0 : keybd_event_(#VK_SHIFT, 0, 0, 0) : EndIf ; Due to shifted character.
         If (vk & $0200) <> 0 And ctrled = 0 :  keybd_event_(#VK_CONTROL, 0, 0, 0) : EndIf
         If (vk & $0400) <> 0 And alted = 0 :   keybd_event_(#VK_MENU, 0, 0, 0) : EndIf
         keybd_event_(vk, 0, 0, 0) : keybd_event_(vk, 0, #KEYEVENTF_KEYUP, 0) ; Press the normal key.
         If (vk & $0100) <> 0 And shifted = 0 : keybd_event_(#VK_SHIFT, 0, #KEYEVENTF_KEYUP, 0) : EndIf ; Due to shifted character.
         If (vk & $0200) <> 0 And ctrled = 0  : keybd_event_(#VK_CONTROL, 0, #KEYEVENTF_KEYUP, 0) : EndIf
         If (vk & $0400) <> 0 And alted = 0   : keybd_event_(#VK_MENU, 0, #KEYEVENTF_KEYUP, 0) : EndIf
      EndIf
   Next

   keybd_event_(#VK_MENU, 0, #KEYEVENTF_KEYUP, 0)     ; Release ALT key if user forgot.
   keybd_event_(#VK_CONTROL, 0, #KEYEVENTF_KEYUP, 0)  ; Release CONTROL key if user forgot.
   keybd_event_(#VK_SHIFT, 0, #KEYEVENTF_KEYUP, 0)    ; Release SHIFT key if user forgot.
EndProcedure
Regards, Little John
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

In the help for AW_SendKeys(), {CONTROLUP} is missing. :)
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: AutoWin UserLibrary

Post by ts-soft »

Okay, i will have a look on this on weekend and integrate your suggest.

Greetings
Thomas
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: AutoWin UserLibrary

Post by infratec »

@Little John

Have a look at
http://www.purebasic.fr/english/viewtop ... 12&t=47802

I use also the 'newer' SendInput_() instead of the 'older' (maybe deprecated) keybd_event().

Bernd
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

@infratec,

thank you, I'm aware that SendInput_() is now the recommended method for simulating keystrokes. The disadvantage is, that it doesn't work on older systems. It would be best to use both functions, and call them accordingly:

Code: Select all

If #PB_Compiler_OS < #PB_OS_Windows_2000
   SimulateKeystrokesBy_keybd_event("Hello")
Else
   SimulateKeystrokesBy_SendInput("Hello")
EndIf
Regards, Little John
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

File2PDF

Post by Little John »

At our office, we often want to share the contents of particular Excel worksheets with some other people (e.g. by copying files into a Dropbox shared folder). In order to avoid confusion, we don't want them to change the contents of the respective Excel files -- so we give them the data in the form of PDF files.

Doing this over and over again every day is just boring. So I wrote a little AutoWin "script" that does the job. The prerequisite is, that the program PDFCreator is installed.

The code sends simulated keystrokes to German GUIs, in other languages other keys must be sent, and also some window titles will be different (see comments in the code).

Code: Select all

EnableExplicit

XIncludeFile <path> + "AutoWin_Include.pbi"

Procedure.s rExtract (source$, s$)
   ; returns all characters of source$ from 1 to the
   ; RIGHTMOST position of s$.
   Protected p, sLen

   sLen = Len(s$)
   For p = Len(source$)-sLen+1 To 1 Step -1
      If Mid(source$, p, sLen) = s$
         ProcedureReturn Left(source$, p-1)
      EndIf
   Next

   ProcedureReturn source$
EndProcedure


;-- Initialise
#ProgName$ = "File2PDF"
#ProgVersion$ = #ProgName$ + " 0.60"
#PdfPrinter$ = "PDFCreator"

AW_ChangeMatchMode(#AW_MatchAnySubString)

Define InFile$, InFileName$, OutDir$, OutFile$, Msg$

InFile$ = ProgramParameter(0)
OutDir$ = ProgramParameter(1)

;-- Check InFile$ and OutDir$
If InFile$ = "" Or OutDir$ = ""
   Msg$ + "Usage: " + #ProgName$ + " <input file> <output directory> [/auto]" + #LF$
   Msg$ + #LF$
   Msg$ + "( " + #DQUOTE$ + RTrim(#PdfPrinter$) + #DQUOTE$ + " must be installed on the system. )"
   MessageRequester(#ProgVersion$, Msg$)
   End
EndIf
If FileSize(InFile$) < 0
   MessageRequester(#ProgVersion$, "Input file '" + InFile$ + "' not found.")
   End
EndIf
If FileSize(OutDir$) > -2
   MessageRequester(#ProgVersion$, "Output directory '" + OutDir$ + "' not found.")
   End
EndIf

;-- Get file part of input file, and create name of output file
InFileName$ = rExtract(GetFilePart(InFile$), ".")

If (OutDir$ <> "") And (Right(OutDir$, 1) <> "\")
   OutDir$ + "\"
EndIf
OutFile$ = OutDir$ + InFileName$

;-- Prompt the user if required
If ProgramParameter(2) <> "/auto"
   Msg$ = "     Convert" + #LF$ + InFile$ + #LF$ + "     to" + #LF$ + OutFile$ + ".pdf ?"
   If MessageRequester(#ProgVersion$, Msg$, #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
      End
   EndIf
EndIf

;-- Open input file with associated program
RunProgram(InFile$)
If AW_WinWaitActive(InFileName$, 30) = 0
   MessageRequester(#ProgVersion$, "Error opening '" + InFile$ + "'.")
   End
EndIf

;-- Type CTRL+p, and wait for the "Print" dialog become active
AW_SendKeys("{CONTROLDOWN}p{CONTROLUP}")
AW_WinWaitActive("Drucken")

;-- Start PDFCreator (*only* printer in the list here that begins with "p")
AW_SendKeys("{ALTDOWN}m{ALTUP}p{ENTER}{ENTER}")  ; Excel
; AW_SendKeys("{ALTDOWN}d{ALTUP}p{ENTER}")       ; Planmaker

;-- Wait for PDFCreator window become active
If AW_WinWaitActive(#PdfPrinter$, 30) = 0
   MessageRequester(#ProgVersion$, "Printer '" + #PdfPrinter$ + "' not found.")
   End
EndIf

;-- Close PDFCreator window and start printing
AW_SendKeys("{ENTER}")

;-- Wait for the "Save as" dialog become active
AW_WinWaitActive("Speichern unter")

;-- Save the file
AW_SendKeys(OutFile$)
AW_SendKeys("{ENTER}{ALTDOWN}j{ALTUP}")            ; Overwrite? > "Yes"

;-- Give focus again to window with input file, then close it
If AW_WinActivate(InfileName$)
   AW_SendKeys("{ALTDOWN}{F4}{ALTUP}")
EndIf
With small adjustments, the program also works with other programs than Excel. Maybe it's useful for someone else, too.

Regards, Little John

//edit 2012-05-05: Code slightly improved.
Last edited by Little John on Sat May 05, 2012 8:58 am, edited 1 time in total.
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Hi Thomas,

the procedure AW_WinWaitActive2() does not work as I would expect it to do.

For instance, the following AutoIt program runs without an error (on a German Windows version):

Code: Select all

ShellExecute("C:\Windows\notepad.exe")
If WinWaitActive("Unbenannt - Editor", "", 30) Then
   Send("Hello World")
Else
   MsgBox(0, "Error", "Timeout")
EndIf
Using PureBasic and the AutoWin library, the code looks like this:

Code: Select all

XIncludeFile <path> + "AutoWin_Include.pbi"

RunProgram("C:\Windows\notepad.exe")
If AW_WinWaitActive("Unbenannt - Editor", 30)
   AW_SendKeys("Hello World")
Else
   MessageRequester("Error", "Timeout")
EndIf
This code snippet launches Notepad, and then immediately ends with the "Timeout" error message. It neither sends the wanted text to Notepad nor waits 30 seconds.

Looking at the current (AutoWin version 1.3) source code of AW_WinWaitActive2() explains why the procedure behaves this way:

Code: Select all

ProcedureDLL AW_WinWaitActive2(title.s, timeout)
  Protected time = ElapsedMilliseconds() + (timeout * 1000)
  Protected timebreak
  Protected *param.AW_WinParameter

  *param = AW_FindWindow(title)
  If *param\hWnd
    While GetForegroundWindow_() <> *param\hWnd
      If timeout
        If ElapsedMilliseconds() > time
          timebreak = #True
          Break
        EndIf
      EndIf
      Delay(250)
    Wend
    If timebreak
      ProcedureReturn #False
    Else
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure
I'd suggest to change the procedure like this:

Code: Select all

ProcedureDLL.i AW_WinWaitActive2 (title.s, timeoutSec)
   ; Pauses execution of the program until the requested window is active
   Protected endTime, timebreak=#False
   Protected *param.AW_WinParameter

   endTime = ElapsedMilliseconds() + timeoutSec*1000

   *param = AW_FindWindow(title)
   While *param\hWnd <> GetForegroundWindow_()
      If (timeoutSec > 0) And (ElapsedMilliseconds() > endTime)
         timebreak = #True
         Break
      EndIf
      Delay(250)
      *param = AW_FindWindow(title)
   Wend

   If timebreak
      ProcedureReturn 0
   Else
      ProcedureReturn *param\hWnd
   EndIf
EndProcedure
This code works for me as expected.
(This is also the code which is used by the program in my previous post -- unfortunately I forgot to mention that.)

Regards, Little John
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: AutoWin UserLibrary

Post by ts-soft »

Hallo Jürgen,

Update: Version 1.4
added all suggestions by Little John
Userlib update to work with PB4.60

The helpfile is untouched, i will update this in a later release.

Greetings
Thomas
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Thank you, Thomas.

Good night from Neukölln to Wedding. :-)
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: AutoWin UserLibrary

Post by Little John »

Hello Thomas,

I think there is a bug in the procedure AW_FindWindow() -- also in the current version 1.4 -- when a window handle is passed as parameter.
The first following code runs as expected, that means after closing the window, hWnd will be 0. The second code still shows the same hWnd as before, even when the respective window does not exist anymore.

Code: Select all

XIncludeFile #PB_Compiler_FilePath + "AutoWin_Include.pbi"

Define *param.AW_WinParameter, hWnd
Define msg$

hWnd = OpenWindow(0, 50, 50, 200, 100, "MyWindow")
*param = AW_FindWindow("MyWindow")
msg$ = Str(*param\hWnd) + #LF$

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
CloseWindow(0)

*param = AW_FindWindow("MyWindow")
msg$ + Str(*param\hWnd)

MessageRequester("Test", msg$)

Code: Select all

XIncludeFile #PB_Compiler_FilePath + "AutoWin_Include.pbi"

Define *param.AW_WinParameter, hWnd
Define msg$

hWnd = OpenWindow(0, 50, 50, 200, 100, "MyWindow")
*param = AW_FindWindow("MyWindow")
msg$ = Str(*param\hWnd) + #LF$

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
CloseWindow(0)

AW_ChangeMatchMode(#AW_MatchParaIsHwnd)
*param = AW_FindWindow(Str(hWnd))
msg$ + Str(*param\hWnd)

MessageRequester("Test", msg$)
The problem is, that in the second example the procedure AW_FindWindow() just returns the handle that has been passed as parameter ... it does not actually search whether or not that window does exist. :-)

I think the procdure should look somehow like this:

Code: Select all

Procedure AW_FindWindow (title.s)
   Static param.AW_WinParameter
   Protected hWnd, text.s{260}

   ClearStructure(@param, AW_WinParameter)
   If AW_MatchMode = #AW_MatchParaIsHwnd
      hWnd = Val(title)
      If IsWindow_(hWnd)
         GetWindowText_(hWnd, @text, 260)
         param\hWnd = hWnd
         param\Title = text
      EndIf
   Else
      param\Title = title
      EnumWindows_(@AW_EnumWindowsProc(), @param)
   EndIf

   ProcedureReturn @param
EndProcedure
Best regards, Jürgen
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: AutoWin UserLibrary

Post by ts-soft »

Thx for bugreport. Download the fixed version in first post!

PS: AW_FindWindow is not a public function :mrgreen:

Greetings - Thomas
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
Post Reply