Page 2 of 4

Re: AutoWin UserLibrary

Posted: Sat Apr 03, 2010 10:42 pm
by srod
Kiki, this library, as excellent as it looks, is nothing to do with me!

Sorry for the off-topic posting Thomas.

Re: AutoWin UserLibrary

Posted: Sat Apr 03, 2010 11:43 pm
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.

Re: AutoWin UserLibrary

Posted: Sun Nov 14, 2010 11:26 am
by Little John
Thanks for this library, Thomas!
It's very useful for my current project.

Regards, Little John

Re: AutoWin UserLibrary

Posted: Sun Jan 16, 2011 4:22 pm
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

Re: AutoWin UserLibrary

Posted: Sun Sep 25, 2011 10:01 am
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

Re: AutoWin UserLibrary

Posted: Mon Oct 10, 2011 7:08 am
by Little John
In the help for AW_SendKeys(), {CONTROLUP} is missing. :)

Re: AutoWin UserLibrary

Posted: Mon Oct 10, 2011 8:13 am
by ts-soft
Okay, i will have a look on this on weekend and integrate your suggest.

Greetings
Thomas

Re: AutoWin UserLibrary

Posted: Mon Oct 10, 2011 8:16 pm
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

Re: AutoWin UserLibrary

Posted: Sat Oct 15, 2011 7:13 pm
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

File2PDF

Posted: Sat Oct 15, 2011 9:11 pm
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.

Re: AutoWin UserLibrary

Posted: Sat Nov 26, 2011 12:28 pm
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

Re: AutoWin UserLibrary

Posted: Sat Nov 26, 2011 7:18 pm
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

Re: AutoWin UserLibrary

Posted: Sun Nov 27, 2011 1:33 am
by Little John
Thank you, Thomas.

Good night from Neukölln to Wedding. :-)

Re: AutoWin UserLibrary

Posted: Sun Jan 29, 2012 10:59 pm
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

Re: AutoWin UserLibrary

Posted: Sun Jan 29, 2012 11:43 pm
by ts-soft
Thx for bugreport. Download the fixed version in first post!

PS: AW_FindWindow is not a public function :mrgreen:

Greetings - Thomas