FocusApp

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Zerosurf
Beiträge: 131
Registriert: 11.10.2005 15:46

FocusApp

Beitrag von Zerosurf »

So, jetzt ist es endlich soweit! Mit viel Hilfe und Unterstützung von einigen sehr netten Forum Mitgliedern konnte ich mein erstes Projekt durchführen.

Ziel war es, eine laufende App durch ein kleines Programm in den Vordergrund zu bringen - z.B. für StreamDeck Hotkeys oder ähnliches.

Zuerst wird der erste Code compiliert (als FocusAppReset.exe) und anschliessend im Zweiten (FocusApp.exe) inkludiert.

Wie immer gilt - falls ihr Verbersserungsvorschläge habt - nur her damit! ;-)
Ich hoffe, es hilft dem Einen oder Anderen so wie es mir geholfen hat!
Vielen Dank nochmal an die Community hier, dich mich sehr unterstützt hat!

Erster Code - "FocusAppReset.exe":

Code: Alles auswählen

If FileSize("AppForFocus.ini") => 0
  ReadFile(0, "AppForFocus.ini")
  App$ = ReadString(0)
  CloseFile(0)
  If MessageRequester("FocusApp Setting", "Reset FocusApp setting?" + Chr(13) + Chr(13) + Chr(34) + App$ + Chr(34), #PB_MessageRequester_YesNo | #PB_MessageRequester_Warning) = #PB_MessageRequester_Yes
    DeleteFile("AppForFocus.ini")
  Else
    End
  EndIf
  MessageRequester("Information", "FocusApp setting reseted!", #PB_MessageRequester_Ok | #PB_MessageRequester_Info)
  RunProgram(GetEnvironmentVariable("comspec"),"/c del "+Chr(34)+ProgramFilename()+Chr(34),"", #PB_Program_Hide)
Else
  MessageRequester("Information", "No FocusApp setting found!"  + Chr(13) + Chr(13) + "Run FocusApp.exe to create setting!", #PB_MessageRequester_Ok | #PB_MessageRequester_Warning)
  RunProgram(GetEnvironmentVariable("comspec"),"/c del "+Chr(34)+ProgramFilename()+Chr(34),"", #PB_Program_Hide)
EndIf 
Zweiter Code - "FocusApp.exe":

Code: Alles auswählen

EnableExplicit

Define Title$, hWndnr, App$
Global PID
Global MODULEENTRY32.MODULEENTRY32
Global Snapshot

Procedure ListWindows(hwnd,lParam)
  Protected.s title = Space(1000),class = Space(1000)
  If GetWindowLongPtr_(hwnd, #GWL_EXSTYLE) ! #WS_EX_TOOLWINDOW And GetWindowLongPtr_(hwnd, #GWL_STYLE) & #WS_VISIBLE
    GetClassName_(hwnd,class,1000)
    GetWindowText_(hwnd,title,1000)
    If title <> ""
      MODULEENTRY32\dwSize = SizeOf(MODULEENTRY32)
      GetWindowThreadProcessId_(FindWindow_(0,title), @PID)
      Snapshot = CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE, PID)
      If Module32First_(Snapshot, MODULEENTRY32)
        AddGadgetItem (0, -1, title+Chr(10)+GetFilePart(PeekS(@MODULEENTRY32\szExePath))+Chr(10)+PeekS(@MODULEENTRY32\szExePath)) 
      EndIf
      CloseHandle_(Snapshot)
    EndIf
  EndIf
  ProcedureReturn #True
EndProcedure

Procedure.I GetSelectedColumn(ListIconID.I)
  Protected CursorLocation.POINT
  Protected HitInfo.LVHITTESTINFO
  GetCursorPos_(CursorLocation)
  MapWindowPoints_(0, GadgetID(ListIconID), CursorLocation, 1)             
  Hitinfo\pt\x = CursorLocation\x
  HitInfo\pt\y = CursorLocation\y
  SendMessage_(GadgetID(ListIconID), #LVM_SUBITEMHITTEST, 0, HitInfo)
  ProcedureReturn HitInfo\iSubItem 
EndProcedure

Procedure OnEvent_SizeWindow()
  Protected WndW, WndH
  WndW = WindowWidth(0)
  WndH = WindowHeight(0)
  ResizeGadget(0, #PB_Ignore, #PB_Ignore, WndW-10, WndH-60)
  ResizeGadget(1, #PB_Ignore, #PB_Ignore, WndW-10, #PB_Ignore)
  ResizeGadget(2, WndW/2-50, WndH-25, #PB_Ignore, #PB_Ignore) 
EndProcedure

Procedure.s GetProcessWindowText(Process.s)
  Protected handle.i
  Protected pid.i
  Protected proc.PROCESSENTRY32
  Protected buffer.s
  Protected check.i
  handle = CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS,#Null);<- snapshot handle holen
  If Not handle = #INVALID_HANDLE_VALUE                        ;<- wenn das handle ok ist weitermachen
    proc\dwSize = SizeOf(PROCESSENTRY32)                       ;<- um die struktur benutzen zu können muss die größe übergeben werden
    If Process32First_(handle,@proc)                           ;<- den ersten eintrag in der prozess liste holen
      Repeat
        If PeekS(@proc\szExeFile) = Process;<- schauen ob der eintrag der gesuchte ist (string vergleich)
          pid = proc\th32ProcessID         ;<- wenn der eintrag der gesuchte ist die process id auslesen
          Break                            ;<- die suche abbrechen da die process id gefunden wurde
        EndIf
      Until Process32Next_(handle,@proc) = #False;<- falscher eintrag? dann weitersuchen
    EndIf
    CloseHandle_(handle);<- handle wieder freigeben
    If pid              ;<- wurde die process id gefunden?
      handle = GetWindow_(GetDesktopWindow_(),#GW_CHILD);<- das erste fenster handle holen (erstes fenster is immer das 'child' des desktops)
      If handle                                         ;<- gibt es ein fenster weitermachen
        Repeat
          GetWindowThreadProcessId_(handle,@check);<- process id des fensters ermitteln
          If check = pid And IsWindowVisible_(handle);<- wenn das fenster sichtbar (reicht meist um das richtige fenster zu bekommen *) ist und die process id die gesuchte ist weitermachen
            check = GetWindowTextLength_(handle)     ;<- die länge des fenster titels ermitteln (in chars)
            If check                                 ;<- gibt es eine länge weitermachen
              buffer = Space(check)                  ;<- speicher für den titel reservieren
              If GetWindowText_(handle,@buffer,check + 1) = check;<- den titel auslesen (wichtig die api will einen char mehr! - siehe msdn)
                ProcedureReturn buffer                           ;<- den titel zurückgeben
              EndIf
            EndIf
          EndIf
          handle = GetWindow_(handle,#GW_HWNDNEXT);<- falsches fenster - weitersuchen
        Until handle = #Null
      EndIf
    EndIf
  EndIf
  ProcedureReturn #Null$;<- nichts wurde gefunden
EndProcedure

Procedure SaveIncludeData(FileName.s, StartOfData, EndOfData)
  Protected r1, file, LenOfData
  file = CreateFile(#PB_Any, FileName)
  If file
    LenOfData = EndOfData - StartOfData
    r1 = WriteData(file, StartOfData, LenOfData)
    CloseFile(file)
  EndIf
  ProcedureReturn r1
EndProcedure

DataSection
  MapDataStart:
  IncludeBinary "FocusAppReset.exe"
  MapDataEnd:
EndDataSection

If FileSize("AppForFocus.ini") => 0
  ReadFile(0, "AppForFocus.ini")
  App$ = ReadString(0)
  CloseFile(0)
  Title$ = GetProcessWindowText(App$)
  hWndnr = FindWindow_(0, Title$)
  If IsIconic_(hWndnr) = 1
    ShowWindowAsync_(hWndnr, #SW_RESTORE)
  EndIf
  SetForegroundWindow_(hWndnr)
  
Else
  If OpenWindow(0, 0, 0, 1010, 250, "App Info:", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
    TextGadget(1, 5, 5, 1000, 20, "RightClick to select app to be focused", #PB_Text_Center)
    ListIconGadget(0, 5, 30, 1000, 190, "Title", 235, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_GridLines)
    AddGadgetColumn(0, 1, "App", 235)
    AddGadgetColumn(0, 2, "Path", 500)
    ButtonGadget(2, 455, 225, 100, 20, "Refresh")
    EnumChildWindows_(FindWindow_("ToolbarWindow32",0),@ListWindows(),0)
    BindEvent(#PB_Event_SizeWindow, @OnEvent_SizeWindow())
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
        Case #PB_Event_Gadget
          If EventGadget() = 0 And EventType() = #PB_EventType_RightClick
            If MessageRequester("Selected App: " + Chr(34) + GetGadgetItemText(0, GetGadgetState(0), 1) + Chr(34), "Next time you run FocusApp.exe, " + Chr(13) + Chr(13) + Chr(34) + GetGadgetItemText(0, GetGadgetState(0), 1) + Chr(34) + " will be focused and setting will be saved to:" + Chr(13) + Chr(13) + Chr(34) + "AppForFocus.ini" + Chr(34) + " in the same directory like " + Chr(34) + "FocusApp.exe" + Chr(34) + "!" + Chr(13) + Chr(13) + "To reset the setting - run " + Chr(34) + "FocusAppReset.exe" + Chr(34) + Chr(13) + Chr(13) + "or just delete " + Chr(34) + "AppForFocus.ini" + Chr(34) + "!", #PB_MessageRequester_YesNo | #PB_MessageRequester_Info)  = #PB_MessageRequester_Yes
              If FileSize("AppForFocus.ini") = -1
                If CreateFile(0, "AppForFocus.ini")
                  WriteString(0, GetGadgetItemText(0, GetGadgetState(0), 1))
                  CloseFile(0)
                  SaveIncludeData("FocusAppReset.exe", ?MapDataStart, ?MapDataEnd)
                Else
                  MessageRequester("Information","Couldn't write .ini file!")
                EndIf
              EndIf
              If IsIconic_(FindWindow_(0, GetProcessWindowText(GetGadgetItemText(0, GetGadgetState(0), 1)))) = 1
                ShowWindowAsync_(FindWindow_(0, GetProcessWindowText(GetGadgetItemText(0, GetGadgetState(0), 1))), #SW_SHOWNORMAL)
              EndIf
              SetForegroundWindow_(FindWindow_(0, GetProcessWindowText(GetGadgetItemText(0, GetGadgetState(0), 1))))
              End
            Else
            EndIf 
          EndIf
          If EventGadget() = 2 And EventType() = #PB_EventType_LeftClick
            ClearGadgetItems(0)
            EnumChildWindows_(FindWindow_("ToolbarWindow32",0), @ListWindows(), 0)
          EndIf
      EndSelect
    ForEver
  EndIf
EndIf
PureBasic 6.00 | Windows 10 pro (x64)
Benutzeravatar
dige
Beiträge: 1182
Registriert: 08.09.2004 08:53

Re: FocusApp

Beitrag von dige »

Was war denn der Grund, warum die Reset Funktion in eine zusätzlich Exe ausgelagert wurde?

Da wird früher oder später auch der VirusSchutz anschlagen.. bspw. mag der Defender nicht,
wenn man RunProgram() verwendet, um nachgelagert ein weiteres Programm zu starten.

Das nervt tierisch. :evil:

Dabei rufe ich in dem Fall nur desk.cpl auf, um den Bildschirmschoner konfigurieren zu können..
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
Benutzeravatar
Zerosurf
Beiträge: 131
Registriert: 11.10.2005 15:46

Re: FocusApp

Beitrag von Zerosurf »

dige hat geschrieben:Was war denn der Grund, warum die Reset Funktion in eine zusätzlich Exe ausgelagert wurde?

Da wird früher oder später auch der VirusSchutz anschlagen.. bspw. mag der Defender nicht,
wenn man RunProgram() verwendet, um nachgelagert ein weiteres Programm zu starten.

Das nervt tierisch. :evil:

Dabei rufe ich in dem Fall nur desk.cpl auf, um den Bildschirmschoner konfigurieren zu können..
Beim Aufrufen vom Hauptprogramm soll nur die eingestellte App in den Vordergrund gesetzt werden und sich dann wieder schließen.
Natürlich würde es reichen nur die "AppForFocus.ini" zu löschen, aber ich wollte es User-freundlich gestalten!

Was wäre denn eine bessere Alternative?
PureBasic 6.00 | Windows 10 pro (x64)
Antworten