1
Announcements / Re: BCX Documentation Version 8.2.0, January 15, 2025
« on: Today at 09:48:22 AM »
Awesome 👍
This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
I am impressed with BCX!Me too! Wait until you see how it can call COM!
#include <shellapi.h>
Gui "BatteryMeterWndClass"
Const ID_TrayIcon = 101
Enum
ID_TrayCallback = WM_USER + 1
IDT_CheckBattery
ID_MenuSettings
ID_MenuExit
ID_IntervalLabel
ID_IntervalField
ID_RunAtStartupBox
End Enum
Global Note As NOTIFYICONDATA
Global MainForm As HWND
Global ReportInterval = 10
Global RunAtStartup As Bool
Global Voice As Object
Sub Formload
MakeSingleInstance("BatteryMeter")
MainForm = Bcx_Form("BatteryMeter", 0, 0, 150, 100)
With Note
.cbSize = Sizeof(Note)
.hWnd = MainForm
.uID = ID_TrayIcon
.uFlags |= NIF_MESSAGE | NIF_TIP
.uCallbackMessage = ID_TrayCallback
lstrcpy(.szTip, TEXT("Battery Meter"))
End With
Shell_NotifyIcon(NIM_ADD, &Note)
Voice = Com("SAPI.SpVoice")
Dim Power As SYSTEM_POWER_STATUS
GetSystemPowerStatus(&Power)
' Currently commented out to allow for testing of code on desktop computer
' If Power.BatteryFlag = 128 Then ' No system battery
' Voice.Speak "This computer does not appear to have a battery. Exiting..."
' Uncom(Voice)
' Shell_NotifyIcon(NIM_DELETE, &Note)
' End
' End If
If Exist(Curdir$ & "\\BatteryMeter.ini") Then
ReportInterval = GetPrivateProfileInt("Settings", "Interval", 10, Curdir$ & "\\BatteryMeter.ini")
RunAtStartup = GetPrivateProfileInt("Settings", "RunAtStartup", False, Curdir$ & "\\BatteryMeter.ini")
Dim LinkPath$, AppPath$
LinkPath$ = StartupFolder$() & "BatteryMeter.lnk"
AppPath$ = Appexepath$ & Appexename$
If RunAtStartup Then
CreateLink(AppPath$, LinkPath$, "", "Battery Meter")
Else
If Exist(LinkPath$) Then
Kill LinkPath$
End If
End If
End If
Beep(440, 100)
Voice.Speak "Running"
SetTimer(MainForm, IDT_CheckBattery, 100, NULL)
End Sub
Begin Events
Select Case Cbmsg
Case WM_DESTROY, WM_QUIT, WM_CLOSE
Uncom(Voice)
Shell_NotifyIcon(NIM_DELETE, &Note)
End
Exit Function
Case ID_TrayCallback Then
If Cblparam = WM_LBUTTONDOWN Then
PostMessage(hWnd, WM_QUIT, 0, 0)
Exit Function
Else If Cblparam = WM_RBUTTONDOWN Then
Dim Menu As HMENU
Menu = CreatePopupMenu()
InsertMenu(Menu, -1, MF_BYPOSITION | MF_STRING, ID_MenuSettings, "&Settings")
InsertMenu(Menu, -1, MF_BYPOSITION | MF_STRING, ID_MenuExit, "E&xit")
Dim pt As POINT
Hide (MainForm)
GetCursorPos(&pt)
SetForegroundWindow(hWnd)
TrackPopupMenuEx (Menu, TPM_LEFTALIGN Or TPM_RIGHTBUTTON, pt.x, pt.y, hWnd, NULL)
PostMessage(hWnd, WM_NULL, 0, 0)
End If
Case WM_TIMER Then
Call CheckBattery
Case WM_COMMAND
Select Case Cbctl
Case ID_MenuExit
If IsObject(Voice) Then Uncom(Voice)
PostMessage(hWnd, WM_QUIT, 0, 0)
Exit Function
Case ID_MenuSettings
Bcx_Dialog(SettingsDlgProc, "Settings", MainForm, 0, 0, 300, 150)
Exit Function
End Select
End Select
End Events
Begin Dialog As SettingsDlgProc
Static As HWND IntervalLabel, IntervalField, RunAtStartupBox, SaveBtn, CancelBtn
Select Case Cbmsg
Case WM_INITDIALOG
IntervalLabel = Bcx_Label("&Interval to report battery percentage in", hWnd, ID_IntervalLabel, 5, 5, 30, 5)
IntervalField = Bcx_Input(Str$(ReportInterval, 1), hWnd, ID_IntervalField, 5, 15, 50, 20)
RunAtStartupBox = Bcx_Checkbox("&Run at startup", hWnd, ID_RunAtStartupBox, 100, 5, 20, 20)
SaveBtn = Bcx_Button("&OK", hWnd, IDOK, 100, 10, 30, 30)
CancelBtn = Bcx_Button("&Cancel", hWnd, IDCANCEL, 100, 50, 30, 30)
Show(hWnd)
SetFocus(IntervalField)
Case WM_COMMAND
Select Case Cbctl
Case IDCANCEL
Closedialog
Case IDOK
ReportInterval = Val(Bcx_Get_Text$(IntervalField))
WritePrivateProfileString("Settings", "Interval", Str$(ReportInterval, 1), Curdir$ & "\\BatteryMeter.ini")
Closedialog
End Select
End Select
End Dialog
Sub CheckBattery
Static LastReported = 0
Dim Percentage
Dim Power As SYSTEM_POWER_STATUS
GetSystemPowerStatus(&Power)
Percentage = Power.BatteryLifePercent
If Percentage % ReportInterval = 0 And LastReported <> Percentage Then
Dim Frequency = 260 + ((Percentage - 10) / (100 - 10)) * (440 - 260)
Beep(Frequency, 700)
Dim ToSpeak As String
If Percentage = 100 Then
ToSpeak$ = "Battery is full"
Else
ToSpeak$ = "Battery " & Str$(Percentage, 1) & "%"
End If
Voice.Speak ToSpeak$
LastReported = Percentage
End If
End Sub
Sub MakeSingleInstance(AppID As String)
Local As HANDLE hMutex
hMutex = OpenMutex(MUTEX_ALL_ACCESS, 0, AppID + "_IsAlreadyRunning")
If Not hMutex Then
hMutex = CreateMutex(0, 0, AppID + "_IsAlreadyRunning")
Else
Msgbox "Another instance of " & AppID & " is already running.", "Error", MB_ICONERROR
End
End If
End Sub
Function StartupFolder$()
Local Res$, WShell As Object
Comset WShell = CreateObject("Wscript.Shell")
Res$ = WShell.SpecialFolders("Startup") & "\\"
Comset WShell = Nothing
Return Res$
End Function
Function CreateLink(Exe$, Link$, Args$, Desc$) As HRESULT
Local Res As HRESULT
Local PSL As IShellLink Ptr
Dim PPF As IPersistFile Ptr
Local W[MAX_PATH] As WORD
CoInitialize(NULL)
$Ifdef __cplusplus
Res = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&PSL)
$Else
Res = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&PSL)
$Endif
If SUCCEEDED(Res) Then
PSL->lpVtbl->SetPath(PSL, Exe$)
PSL->lpVtbl->SetDescription(PSL, Desc$)
PSL->lpVtbl->SetShowCmd(PSL, SW_SHOWNORMAL)
PSL->lpVtbl->SetArguments(PSL, Args$)
$Ifdef __cplusplus
Res = PSL->lpVtbl->QueryInterface(PSL, IID_IPersistFile, (LPVOID*)&PPF)
$Else
Res = PSL->lpVtbl->QueryInterface(PSL, &IID_IPersistFile, (LPVOID*)&PPF)
$Endif
If SUCCEEDED(Res) Then
MultiByteToWideChar(CP_ACP, 0, Link$, -1, (LPWSTR)W, MAX_PATH)
Res = PPF->lpVtbl->Save(PPF, (LPCOLESTR)W, True)
PPF->lpVtbl->Release(PPF)
End If
PSL->lpVtbl->Release(PSL)
End If
CoUninitialize()
Return Res
End Function
Hi, Quin.Hi Airr,
I tried this:Code: [Select]For I = 0 To NumSelections - 1
Dim CurIndex
CurIndex = Val(ToHide[I])
If CurIndex <= 0 Or CurIndex > NumUpdates Then Continue
Comset Update = Updates.Item(CurIndex - 1)
If IsObject(Update) Then
Dim Title$
Title$ = Update.Title
Update.IsHidden = True
Print "Hid " & Title$
Uncom(Update)
End If
Next
And it successfully executed with no issue. I ran the resulting binary as Admin (I didn't include the .res file that configures the .exe for elevation, for testing purposes).
AIR.
Edit: Compiled with the UAC res file, no apparent issues. Ran it through WinDBG, no crashes.
#include <wuapi.h>
BCX_Show_COM_Errors(True)
Dim As Object Session, Searcher, SearchResult, Update, Updates
Dim NumUpdates, I
Comset Session = Com("Microsoft.Update.Session")
Comset Searcher = Session.CreateupdateSearcher()
Searcher.ClientApplicationID = "uphide"
Print "Checking for updates..."
Comset SearchResult = Searcher.Search("IsInstalled=0 And IsHidden=0")
NumUpdates = SearchResult.Updates.Count
If NumUpdates = 0 Then
Print "No updates were found."
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
End
End If
Print "Enter the numbers of the updates you want to hide, seperated by spaces, and press enter. Leave blank to exit."
Dim Index = 1
Comset Updates = SearchResult.Updates
For Each Item In Updates
Dim Title$
Title$ = Item.Title
Print Str$(Index, 1) & ": " & Title$
Index++
Next
Dim Input$
Input Input$
If Input$ = "" Then
Uncom(Updates)
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
End
End If
Dim NumSelections
Dim ToHide[100] As String
NumSelections = Split(ToHide, Input$, " ", 0)
For I = 0 To NumSelections - 1
Dim CurIndex
CurIndex = Val(ToHide[I])
If CurIndex <= 0 Or CurIndex > NumUpdates Then Continue
Comset Update = Updates.Item(CurIndex - 1)
If IsObject(Update) Then
Dim Title$
Title$ = Update.Title
Update.IsHidden = True
Print "Hid " & Title$
Uncom(Update)
End If
Next
Pause
Uncom(Updates)
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
Thanks as always for all your help!#include <wuapi.h>
BCX_Show_COM_Errors(True)
Dim As Object Session, Searcher, SearchResult, Update, Updates
Dim NumUpdates, I
Comset Session = Com("Microsoft.Update.Session")
Comset Searcher = Session.CreateupdateSearcher()
Searcher.ClientApplicationID = "uphide"
Print "Checking for updates..."
Comset SearchResult = Searcher.Search("IsInstalled=0 And IsHidden=0")
NumUpdates = SearchResult.Updates.Count
If NumUpdates = 0 Then
Print "No updates were found."
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
End
End If
Print "Enter the numbers of the updates you want to hide, seperated by spaces, and press enter. Leave blank to exit."
Dim Index = 1
Comset Updates = SearchResult.Updates
For Each Item In Updates
Dim Title$
Title$ = Item.Title
Print Str$(Index, 1) & ": " & Title$
Index++
Next
Dim Input$
Input Input$
If Input$ = "" Then
Uncom(Updates)
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
End
End If
Dim NumSelections
Dim ToHide[100] As String
NumSelections = Split(ToHide, Input$, " ", 0)
For I = 0 To NumSelections - 1
Dim CurIndex
CurIndex = Val(ToHide[I])
If CurIndex <= 0 Or CurIndex > NumUpdates Then Continue
Comset Update = Updates.Item(CurIndex - 1)
Dim Title$
Title$ = Update.Title
Update.Ishidden = True
Print "Hid " & Title$
Next
Pause
Uncom(Update)
Uncom(Updates)
Uncom(SearchResult)
Uncom(Searcher)
Uncom(Session)
Did I miss something?'Thanks,Hi Robert,Thanks, Robert.Oh that's neat, my days of sifting through Microsoft docs paid off in more ways than just an application.
For the Windows Update COM example, Quin was the one who kicked that all off, just want to make sure they are recognized for that....
AIR.
Hi Quin:
I apologize for not posting an acknowledgment for your contribution to the BCX Documentation Version 8.1.7 example showing how to call the Windows Update using COM.
I will revise the list of acknowledgments to credit you and Armando, both, for the creation of that example.
Thank you.
The Voice Object is already a global.Very interesting! I have utterly no idea why that would be, but thanks for your help! I wonder if the same thing would apply to the update hiding utility?
Going back to your original code, I tried this:Code: [Select]Case ID_MenuExit
If IsObject(Voice) Then UNCOM(Voice)
PostMessage(hWnd, WM_QUIT, 0, 0)
Exit Function
And the crashing went away...
AIR.