Show Windows 10 native clock widget programmatically - windows

So I use a clock replacement program. The problem is it also hijack clicks on the clock. So whenever I click on the clock in the notification area, program's popup menu launch rather than the default windows clock widget.
I also tried AHK ControlClick on TrayClockWClass. I still didn't get original widget. Is there any way to launch the original widget programmatically? I use Windows 10 1607.
I am fine with RunDll, API, SendMessage or any other way whatsoever.

Somebody upvoted my question today. So maybe they are looking for the answer. I posted it on autohotkey.com forums back then, and I get the answer there.
https://autohotkey.com/boards/viewtopic.php?t=21274
ControlGet, hClock, Hwnd,, TrayClockWClass1, ahk_class Shell_TrayWnd ; https://autohotkey.com/board/topic/70770-win7-taskbar-clock-toggle/
if (hClock) {
VarSetCapacity(IID_IAccessible, 16), DllCall("ole32\CLSIDFromString", "WStr", "{618736e0-3c3d-11cf-810c-00aa00389b71}", "Ptr", &IID_IAccessible)
if (DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hClock, "UInt", OBJID_CLIENT := 0xFFFFFFFC, "Ptr", &IID_IAccessible, "Ptr*", accTrayClock))
return
VarSetCapacity(variant, A_PtrSize == 8 ? 24 : 16, 0), NumPut(VT_I4 := 3, variant,, "UShort")
if (A_PtrSize == 4) ; https://autohotkey.com/boards/viewtopic.php?p=111355#p111355
DllCall(NumGet(NumGet(accTrayClock+0)+25*A_PtrSize), "Ptr", accTrayClock, "Int64", NumGet(variant, 0, "Int64"), Int64, NumGet(variant, 8, "Int64")) ; IAccessible::DoDefaultAction
else
DllCall(NumGet(NumGet(accTrayClock+0)+25*A_PtrSize), "Ptr", accTrayClock, "Ptr", &variant) ; IAccessible::DoDefaultAction
ObjRelease(accTrayClock)
}

Related

How to get the icon path of an audio device

I am writing an AHK script that uses audio devices. I'm using the Vista Audio library, and I have added some more functions to get additional data about the devices beyond their name. These functions are to get the device description and device icon:
GetDeviceDesc(device)
{
static PKEY_Device_DeviceDesc
if !VarSetCapacity(PKEY_Device_DeviceDesc)
VarSetCapacity(PKEY_Device_DeviceDesc, 20)
,VA_GUID(PKEY_Device_DeviceDesc :="{A45C254E-DF1C-4EFD-8020-67D146A850E0}")
,NumPut(2, PKEY_Device_DeviceDesc, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_Device_DeviceDesc, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceDesc := NumGet(prop,8))
return deviceDesc
}
GetDeviceIcon(device)
{
static PKEY_DrvPkg_Icon
if !VarSetCapacity(PKEY_DrvPkg_Icon)
VarSetCapacity(PKEY_DrvPkg_Icon, 20)
,VA_GUID(PKEY_DrvPkg_Icon :="{CF73BB51-3ABF-44A2-85E0-9A3DC7A12132}")
,NumPut(4, PKEY_DrvPkg_Icon, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_DrvPkg_Icon, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceIcon := NumGet(prop,8))
return deviceIcon
}
Both functions are slightly modified versions of the VA_GetDeviceName function from VA.ahk:
VA_GetDeviceName(device)
{
static PKEY_Device_FriendlyName
if !VarSetCapacity(PKEY_Device_FriendlyName)
VarSetCapacity(PKEY_Device_FriendlyName, 20)
,VA_GUID(PKEY_Device_FriendlyName :="{A45C254E-DF1C-4EFD-8020-67D146A850E0}")
,NumPut(14, PKEY_Device_FriendlyName, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_Device_FriendlyName, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceName := NumGet(prop,8))
return deviceName
}
The first of my functions, to get the device description works, but the other to get the icon path returns nothing. In addition to trying to get the DrvPkg_Icon, I have also attempted to get the DeviceClass_Icon and DeviceClass_IconPath with no success.
I have the PKEY reference here and the IProperty method number reference here.
I can't figure out what I'm doing wrong. Is it possible that the code is working correctly and that these values are empty (according to the WinAPI documentation, a device is only guaranteed to have a friendly name, a description, and an interface friendly description)? In which case is there another way for me to get the device icons?
I just don't get why it's not working. Thank you

How to hide GUI when mouse is over?

I create an simple overlaying GUI:
Gui +LastFound +AlwaysOnTop +ToolWindow -Caption
Sometimes there are buttons behind it, and I want the GUI to hide when mouse is over, so that I can see and click buttons behind it. When the mouse in not over anymore, the GUI shows again.
I really can't google out a simple solution for this. Do you have any idea?
I would recommend adapting what Lexikos did with the mouse hook to monitor whether your mouse is hovering over the position where your GUI is/was. There may be more efficient solutions (which I would also like to see), but this will definitely work.
https://autohotkey.com/board/topic/27067-mouse-move-detection/
Here's the example code snippet in case link gets broken:
#Persistent
MouseHook := DllCall("SetWindowsHookEx", "int", 14 ; WH_MOUSE_LL = 14
, "uint", RegisterCallback("MouseProc"), "uint", 0, "uint", 0)
return
MouseProc(nCode, wParam, lParam)
{
global MouseHook
Critical
if wParam = 0x200 ; WM_MOUSEMOVE
{
ToolTip % NumGet(lParam+0,0,"int") ", " NumGet(lParam+4,0,"int")
}
return DllCall("CallNextHookEx", "uint", MouseHook
, "int", nCode, "uint", wParam, "uint", lParam)
}

AutoHotkey: Dynamic resize control based on text

Consider the following snippet:
FormatTime, time,
Gui, Add, Text, vcTime,
GuiControl, , cTime, % time
Gui, Show, NoActivate Center AutoSize
AutoSize is based on the initial value from Gui, Add, Text, vcTime and not on the new value set by GuiControl, , cTime, % time. Depending on months etc., %time% length can vary. How can I automatically resize the window to adapt to updated values of %time%?
`
AutoSize is actually based on the current control sizes when Gui Show is called. The issue is that the "blank" subcommand to GuiControl for text controls does not automatically resize the control; it just changes the text and you still have to call GuiControl Move with a new size yourself. So, in your example, if you replace AutoSize with w200 the text will still be cut off at the same point.
As far as I know, there's not really a "built-in" automatic way to resize the text control based on the new text. The closest way is to use AHK's initial size calculation when creating a text control: create a new text control with the desired text, use GuiControlGet to get the size of the new control, and finally set the size of the original control to that size using GuiControl Move. Here's an example function which does this, adapted from here:
SetTextAndResize(controlHwnd, newText, fontOptions := "", fontName := "") {
Gui 9:Font, %fontOptions%, %fontName%
Gui 9:Add, Text, R1, %newText%
GuiControlGet T, 9:Pos, Static1
Gui 9:Destroy
GuiControl,, %controlHwnd%, %newText%
GuiControl Move, %controlHwnd%, % "h" TH " w" TW
}
Which would fit into your example like so:
FormatTime, time,
Gui, Add, Text, HwndTimeHwnd vcTime,
SetTextAndResize(TimeHwnd, time)
Gui, Show, NoActivate Center AutoSize
Now whenever you use SetTextAndResize instead of just setting the text you can use Gui Show, AutoSize to automatically resize the window correctly. Note that if you changed the font with Gui Font before adding the text control you would have to pass those same options to SetTextAndResize.
Alternatively, I looked at how AHK itself calculates the initial size of a text control for Gui Add, Text when none is provided and found it uses the Windows API function DrawText with DT_CALCRECT. Here's another implementation of SetTextAndResize I wrote using this directly:
SetTextAndResize(controlHwnd, newText) {
dc := DllCall("GetDC", "Ptr", controlHwnd)
; 0x31 = WM_GETFONT
SendMessage 0x31,,,, ahk_id %controlHwnd%
hFont := ErrorLevel
oldFont := 0
if (hFont != "FAIL")
oldFont := DllCall("SelectObject", "Ptr", dc, "Ptr", hFont)
VarSetCapacity(rect, 16, 0)
; 0x440 = DT_CALCRECT | DT_EXPANDTABS
h := DllCall("DrawText", "Ptr", dc, "Ptr", &newText, "Int", -1, "Ptr", &rect, "UInt", 0x440)
; width = rect.right - rect.left
w := NumGet(rect, 8, "Int") - NumGet(rect, 0, "Int")
if oldFont
DllCall("SelectObject", "Ptr", dc, "Ptr", oldFont)
DllCall("ReleaseDC", "Ptr", controlHwnd, "Ptr", dc)
GuiControl,, %controlHwnd%, %newText%
GuiControl Move, %controlHwnd%, % "h" h " w" w
}
I'm not sure how it compares to the first method in terms of performance, but one advantage is that it gets the font based on the control itself rather than having to provide it to the function yourself.

Output Sound To Audio Device

I'm trying to create a script that will trigger a sound effect when a hotkey is pressed, this might not seem too difficult, but I also want to be able to output it to a specific audio device, a virtual audio cable in my case.
I've looked around on the internet a bit and I saw many possible solutions, but none have worked out for me (yet). It appears that the normal SoundPlay function in AHK can not output to a specific audio device, but I'm also looking into other options, like playing the sound via a batch script with wmplayer or other media players, but I can't find a solutions that will let me output the sound to a specific audio devices...
So my question is, what's the best way to play sound to a specific non-default audio device (a virtual audio cable) which can be done inside a command prompt or inside autohotkey?
So I managed to do what I was trying to accomplish. I'll tell you how I did it:
After some searching around on the internet I came across a C# library called IrrKlang. Using IrrKlang I made a little console app program which I can call by playsound soundfile.mp3 0, playsound is the the name of the .exe, the first parameter is the path to the soundfile from the location of the playsound.exe, and the last parameter is a number which is used to choose the audio device, which number this should be is still guess work but after some trial and error you can find the number of your virtual audio cable or other audio device.
For the people who come here in the future I've put my code up on github.
Everything you need is here:
Lexikos's Vista Audio Control Functions
Note: SoundSet and SoundGet on AutoHotkey v1.1.10 and later support Vista and later natively. You don't need VA.ahk unless you want to use advanced functions not supported by SoundSet/SoundGet.
https://autohotkey.com/board/topic/21984-vista-audio-control-functions/
To change the default output device, you can script the Sound properties in cpanel:
Run, mmsys.cpl
WinWait, Sound
ControlSend, SysListView321,{Down num} ' num matches device position
Sleep, 100
ControlClick, &Set Default
Sleep, 100
ControlClick, OK
WinWaitClose, Sound
Hth,
I modified one code i found, and add volume up and down.
You have to chance device1 and device2 names
Alt + Wheel Up = Volume Up
Alt + Wheel Down = Volume Down
Ctrl + F12 = Chance Device
glhf!
device1:="Speakers / Headphones"
device2:="Communications Headphones"
; http://www.daveamenta.com/2011-05/programmatically-or-command-line-change-the-default-sound-playback-device-in-windows-7/
Devices := {}
IMMDeviceEnumerator := ComObjCreate("{BCDE0395-E52F-467C-8E3D-C4579291692E}", "{A95664D2-9614-4F35-A746-DE8DB63617E6}")
; IMMDeviceEnumerator::EnumAudioEndpoints
; eRender = 0, eCapture, eAll
; 0x1 = DEVICE_STATE_ACTIVE
DllCall(NumGet(NumGet(IMMDeviceEnumerator+0)+3*A_PtrSize), "UPtr", IMMDeviceEnumerator, "UInt", 0, "UInt", 0x1, "UPtrP", IMMDeviceCollection, "UInt")
ObjRelease(IMMDeviceEnumerator)
; IMMDeviceCollection::GetCount
DllCall(NumGet(NumGet(IMMDeviceCollection+0)+3*A_PtrSize), "UPtr", IMMDeviceCollection, "UIntP", Count, "UInt")
Loop % (Count)
{
; IMMDeviceCollection::Item
DllCall(NumGet(NumGet(IMMDeviceCollection+0)+4*A_PtrSize), "UPtr", IMMDeviceCollection, "UInt", A_Index-1, "UPtrP", IMMDevice, "UInt")
; IMMDevice::GetId
DllCall(NumGet(NumGet(IMMDevice+0)+5*A_PtrSize), "UPtr", IMMDevice, "UPtrP", pBuffer, "UInt")
DeviceID := StrGet(pBuffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", pBuffer)
; IMMDevice::OpenPropertyStore
; 0x0 = STGM_READ
DllCall(NumGet(NumGet(IMMDevice+0)+4*A_PtrSize), "UPtr", IMMDevice, "UInt", 0x0, "UPtrP", IPropertyStore, "UInt")
ObjRelease(IMMDevice)
; IPropertyStore::GetValue
VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
VarSetCapacity(PROPERTYKEY, 20)
DllCall("Ole32.dll\CLSIDFromString", "Str", "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", "UPtr", &PROPERTYKEY)
NumPut(14, &PROPERTYKEY + 16, "UInt")
DllCall(NumGet(NumGet(IPropertyStore+0)+5*A_PtrSize), "UPtr", IPropertyStore, "UPtr", &PROPERTYKEY, "UPtr", &PROPVARIANT, "UInt")
DeviceName := StrGet(NumGet(&PROPVARIANT + 8), "UTF-16") ; LPWSTR PROPVARIANT.pwszVal
DllCall("Ole32.dll\CoTaskMemFree", "UPtr", NumGet(&PROPVARIANT + 8)) ; LPWSTR PROPVARIANT.pwszVal
ObjRelease(IPropertyStore)
ObjRawSet(Devices, DeviceName, DeviceID)
}
ObjRelease(IMMDeviceCollection)
Return
$!WheelUp::Send {Volume_Up 5}
$!WheelDown::Send {Volume_Down 5}
currentDevice:=false
^F12::
currentDevice:=!currentDevice
if currentDevice
SetDefaultEndpoint( GetDeviceID(Devices, device1) )
else
SetDefaultEndpoint( GetDeviceID(Devices, device2) )
return
SetDefaultEndpoint(DeviceID)
{
IPolicyConfig := ComObjCreate("{870af99c-171d-4f9e-af0d-e63df40c2bc9}", "{F8679F50-850A-41CF-9C72-430F290290C8}")
DllCall(NumGet(NumGet(IPolicyConfig+0)+13*A_PtrSize), "UPtr", IPolicyConfig, "UPtr", &DeviceID, "UInt", 0, "UInt")
ObjRelease(IPolicyConfig)
}
GetDeviceID(Devices, Name)
{
For DeviceName, DeviceID in Devices
If (InStr(DeviceName, Name))
Return DeviceID
}

How to debug variables in Autohotkey without using MsgBox?

When debugging scripts, you often need to know the value of your variables to know exactly what is going on. Outputting variables with MsgBox to tackle this problem is annoying and inefficient.
Is there a function that can help me with debugging variables?
I want it to write down all my local variables, their names and corresponding values as well as the name of the current procedure being executed to an ini file. Are there debugging tools that can provide this functionality, possibly in real time?
The name of the procedure may be challenging to do, but is there a way that at least all the local variables can get enumerated and written away?
Take a look at the ListVars command. It lists all of the variables.
You can also get more extensive information using a method like this:
MsgBox, % GetAhkStats("lines")
MsgBox, % GetAhkStats("variables")
MsgBox, % GetAhkStats("hotkeys")
Stat1 := GetAhkStats("history")
MsgBox, %Stat1%
Return
a::a
b::b
c::c
d::d
GetAhkStats(xxSection="", xxUseWindow=99, xxDestroyAfter=1)
{
xxSectionN = Lines|Variables|Hotkeys|History
If xxSection=
xxSection = History
Loop, Parse, xxSectionN, |
IfInString, A_LoopField, %xxSection%
xxSection = %A_Index%
DetectHiddenWindows, On
SetTitleMatchMode, 2
Gui, %xxUseWindow%:Show, Hide
xxHidWin := WinExist(A_ScriptFullPath " - AutoHotkey v")
xxOldpar := DllCall("GetParent", "UInt", xxHidWin)
DllCall("SetParent", "UInt", xxHidWin, "UInt", (GuiGetHWND("", xxUseWindow)))
WinMenuSelectItem, ahk_id %xxHidWin%,, View, %xxSection%&
Loop {
Sleep, 50
ControlGetText, xxOut1, Edit1, ahk_id %xxHidWin%
If xxOut1<>
break
}
WinHide, ahk_id %xxHidWin%
DllCall("SetParent", "UInt", xxHidWin, "UInt", xxOldpar)
If (xxDestroyAfter)
Gui, %xxUseWindow%:Destroy
Return, xxOut1
}
GuiGetHWND(xxClassNN="", xxGUI=1)
{
If (xxGUI)
Gui, %xxGUI%:+LastFound
xxGui_hwnd := WinExist()
If xxClassNN=
Return, xxGui_hwnd
ControlGet, xxOutputVar, Hwnd,, %xxClassNN%, ahk_id %xxGui_hwnd%
Return, xxOutputVar
}
Source
listvars does the trick for me.

Resources