Sign in to follow this  
Followers 0
PsychodelEKS

Creative Audio Control Panel automation

18 posts in this topic

#1 ·  Posted (edited)

Hi everyone,

I'm trying to automate some action in Creative Audio Control Panel (sound card control applet).

Currently I want my script to change speakers configuration.

This is the main window:

Posted Image

And here comes trouble, as you can see there is no Apply/Ok buttons, so changing value in combobox switches the mode instantly.

ComboBox info:

Posted Image

The problem is that its callback is not triggered by:

ControlCommand($winid, '', 1004, 'SelectString', '2/2.1 Speakers')
or

ControlCommand($winid, '', 1004, 'SetCurrentSelection', 0)
The value inside combobox is changed, but it does not actually change the settings =(

I've sniffed the combobox commands sent/recieved with Winspector Spy:

Posted Image

and tried:

$speakers_combobox = ControlGetHandle($winid, "", 1004)
DllCall('user32.dll', 'int', 'SendMessage', 'hwnd', $speakers_combobox, 'uint', 0x0131, 'wParam', 0x02, 'lParam', 0x002000e5)
but no luck, neither combobox value, nor settings change.

If I try:

$CB_SETCURSEL = 0x14E
$speakers_combobox = ControlGetHandle($winid, "", 1004)
DllCall("user32.dll", "int", "SendMessage", "hwnd", $speakers_combobox, "int", $CB_SETCURSEL, "int", 0, "int", 0)
it changes current selection, but not settings.

Any help/advice would be appreciated.

PS: I do not wnt to emulate mouse clicks, as I want to run the applet in hidden mode with Control Panel window also hidden.

Edited by PsychodelEKS

Share this post


Link to post
Share on other sites



#3 ·  Posted (edited)

Send CBN_SELCHANGE by a WM_COMMAND message.

I know it's ignorant, but could you be more specific? :( I've done a lot searching recently, but it seems to be too advanced for me right now :)

Those are my first steps in low-level gui manipulation.

I came up with this code (I'm not even sure it's not total crap), but it only switches the state of combobox, not the actual settings:

#include <GuiConstantsEx.au3>
#include <GuiComboBox.au3>

$WM_COMMAND = 0x0111
$winid = "Audio Control Panel"

Run("C:\Program Files (x86)\Creative\AudioCS\CTAudCS.exe", "C:\Program Files (x86)\Creative\AudioCS\")
WinWaitActive($winid, '', 20)

$hCntl = ControlGetHandle($winid, "", 1004)
_GUICtrlComboBox_SetCurSel($hCntl, 0)
$iCode = $CBN_SELCHANGE
$val = BitShift($iCode, -16)
$handle = WinGetHandle($winid, "")
_SendMessage($handle, $WM_COMMAND, $val, $hCntl)

Thanks for your help in advance.

Edited by PsychodelEKS

Share this post


Link to post
Share on other sites

Try this, it works for me:

#include <WinAPI.au3>

Local $hWnd,$ctSpeakerSel,$iControlID

$hWnd=WinGetHandle("[TITLE:Audio Control Panel; CLASS:#32770]")
If $hWnd=0 Then Exit

$ctSpeakerSel=ControlGetHandle($hWnd,"","[CLASS:ComboBox; INSTANCE:1]")
ConsoleWrite("Control Handle:"&$ctSpeakerSel)
; Get the Control ID # (GWL_ID = -12)
$iControlID=_WinAPI_GetWindowLong($ctSpeakerSel,-12)
ConsoleWrite(" Control ID:"&$iControlID&@CRLF)

ControlCommand($hWnd,"",$ctSpeakerSel,"SelectString","2/2.1 Speakers")

;WM_COMMAND = 0x111, CBN_SELCHANGE  1 (upper word) + Control ID
_WinAPI_PostMessage($hWnd,0x0111,0x00010000+$iControlID,$ctSpeakerSel)

Share this post


Link to post
Share on other sites

 

Try this, it works for me

Awesome, man, thx a lot. At least it switches the mode, hopefully I'll be able to fully understand it and add everything else =)

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

 

Try this, it works for me

I've faced another problem, as I understand, GUI is created with all objects (by IDs) available, but seems like it is not - if the window is switched to another tab (as it is by default), the ComboBox can not be found =(

Is there any way to overcome this, or should I manually switch the tab each time? Are there any dummy-guides on how to do this (not through TabRight/TabLeft)?

Here goes, tab info:

Posted Image

Also, the window is not hidden by @SW_HIDE, does it have any limitations?

Edited by PsychodelEKS

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

This is a way to set the tab:

$ctTabControl=ControlGetHandle($hWnd,"","[CLASS:SysTabControl32; INSTANCE:1]")

;#define TCM_FIRST 0x1300   // Tab control messages
;#define TCM_SETCURFOCUS (TCM_FIRST + 48)
_SendMessage($ctTabControl,0x1300+48,1,0)

Note that the wParam parameter (2nd to last) of SendMessage/PostMessage is the index of the Tab. I've assumed for you it is 1, but it might be another number (it was 2 on my Creative Audio Control Panel, but its layout is different). So you'll have to experiment a bit to see which one is the correct one. (They start at index #0)

This gives me an idea to create an enumeration function for a Tab control.. it looks possible, so I will have to try my luck at it...

Edited by Ascend4nt

Share this post


Link to post
Share on other sites

 

This is a way to set the tab

Thank Ascend4nt, works nice, but my version seems to be written by some hardcore coders, seems like it needs some event triggered or command sent to get and render the info. Now it just renders empty tab after switching =(

Posted Image

The ComboBox is empty and other controls (radiobuttons) are missing from it. :(

Share this post


Link to post
Share on other sites

I'm just curious, what Creative SB X-Fi are you using, which drivers, and what OS? I wonder if it would be simpler to call the drivers directly without messing with the Creative Audio Control Panel GUI.

Share this post


Link to post
Share on other sites

I'm just curious, what Creative SB X-Fi are you using, which drivers, and what OS?  I wonder if it would be simpler to call the drivers directly without messing with the Creative Audio Control Panel GUI.

It is X-Fi Titanium PCI-E version. Under Win7 x64. 

Actually using drivers directly would've been perfect, but I have totally no idea on how and where to start from. I'm more of a web coder, not C/C++ =)

But as far as I know, windows, starting from vista, does not provide any simple built in interface for this.

Share this post


Link to post
Share on other sites

Thank Ascend4nt, works nice, but my version seems to be written by some hardcore coders, seems like it needs some event triggered or command sent to get and render the info. Now it just renders empty tab after switching =(

The ComboBox is empty and other controls (radiobuttons) are missing from it. :)

Same problem as before, but different message. From the help file under _GuiCtrlTab_SetCurSel():

Remarks

A tab control does not send a $TCN_SELCHANGING or $TCN_SELCHANGE notification message when a tab is selected using this function.

:(


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

PsaltyDS, that's actually exactly how its listed on the MSDN page (without the $'s). Even with those messages though, it doesn't seem to do any good, probably because of the pointers being passed.

I found code that attempts to activate the tab using the messages here:

_GUICtrlTab_ActivateTab

Problem is - GUI isn't updated, just the button. I'm guess its got to do with the pointers..

Oh, and PsychodelEKS - you are absolutely right, I didn't realize that TCM_SETCURFOCUS leaves the form empy. The problem was that I was testing an already-open Control Panel where I had clicked the 'Speakers' tab myself, then clicked another tab. But I started it up from scratch and got the same problem you did.

So after looking at the options, I figure the easiest method, though it looks like a sort of hack, is to cycle through the tabs one-by one until we get to the right one. We'll grab the current tab, knowing already what the target tab is ($iSpeakerTab, index #1?), and 'spin' the tabs in the right direction using ControlCommand() until we are on the right tab. Notice that "CurrentTab" through ControlCommand is 1-based, whereas getting it through Windows methods is 0-based (hence my -1 on the first line):

$iCurSelTab=ControlCommand($hWnd,"",$ctTabControl,"CurrentTab","")-1

If $iSpeakerTab>$iCurSelTab Then
    $sControlDir="TabRight"
    $iDiff=$iSpeakerTab-$iCurSelTab
Else
    $sControlDir="TabLeft"
    $iDiff=$iCurSelTab-$iSpeakerTab
EndIf
ConsoleWrite("Current tab#"&$iCurSelTab&" Target Tab #"&$iSpeakerTab&" Tab Dir:"&$sControlDir&" Difference:"&$iDiff&@CRLF)

For $i=1 To $iDiff
    ControlCommand($hWnd,"",$ctTabControl,$sControlDir,"")
Next

Oh - and my idea on enumerating tabs.. that's another problem dealing with pointers. All info passed through messages to other processes expects pointers to be accessing its own memory space (TCN_SEL* messages require pointers).

My thinking on this wasn't clear until I got a grip on that - DLL calls with pointers work because DLL's are mapped into the address space of the given process. But separate processes - the same memory pointer will mean something completely different since each process lives in its own virtual bubble and can only see 'out' of it through calls to Windows to read other process's memory..

*edit: up too late, not thinking straight. gotta go now, mate

Edited by Ascend4nt

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

Well, as expected, by writing to the process memory, I was able to enumerate the tabs and send the necessary messages. Even though I simplified it a lot in my code (_DLLStructInject/Sync functions), there's still a lot of steps to go through to get simple text, indexes, or to select a tab. For these purposes, I think my 'hack' solution is actually the best method for what we need it for. It works quickly enough, and doesn't require the screen to have focus (it can even be minimized).

However, something like the enumeration of Tabs could be incorporated into say, the AutoIT Window Info Tool. The problem of course would be the applications that need higher access privileges to write to their process memory.

Its certainly been enlightening for me in messing around with this stuff.

Edited by Ascend4nt

Share this post


Link to post
Share on other sites

 

Its certainly been enlightening for me in messing around with this stuff.

Glad to here that :) Wish Creative support were half that responsive in this problem resolution ;) I`ll definitely give your solution a try.

Some things in AutoIt are really for pros only :)

PsaltyDS,

Your ":("s make me shy  Posted Image

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

omg, don't get me started on Creative support! I identified a real bug in their drivers but they gave me a huge run-around and treated me as though someone like me couldn't possibly find a bug.

But hey.. I've been toying around with this new functionality I have due to this mess, and I'm wondering if there can be some cool practical uses for it.. I just activated your tab another way using this code utilizing my _DllStructInject functions:

Func _TabClick($hProcess,$ctTabControl,$iIndex=0)
    If Not IsHWnd($ctTabControl) Or Not IsPtr($hProcess) Or $hProcess=0 Then Return SetError(1,0,"")
    Local $stRect,$pRemoteRect,$bSuccess
    Dim $aTabRect[4]
    $stRect=DllStructCreate("long;long;long;long")
    $pRemoteRect=_DLLStructInject($hProcess,$stRect)
    ; TCM_GETITEMRECT (TCM_FIRST + 10) ; #define TCM_FIRST 0x1300   // Tab control messages
    $bSuccess=_SendMessage($ctTabControl,0x1300+10,$iIndex,$pRemoteRect)
    _DllStructInjectSyncDown($hProcess,$pRemoteRect,$stRect)
    _DllStructUnInject($hProcess,$pRemoteRect)
    If Not $bSuccess Then Return SetError(5,0,"")
    For $i=1 To 4
        $aTabRect[$i-1]=DllStructGetData($stRect,$i)
    Next
    ControlClick($ctTabControl,"","","primary",1,$aTabRect[0]+1,$aTabRect[1]+1)
    Return True
EndFunc

The code for enumerating is a bit confusing to post, as it deals with pointers to buffers inside the structure, but its pretty neat now using this interface. I'm such a geek :(

*edit: found out there are already data-injection functions in the standard UDF's which can be used for these things! d'oh.. gotta know where to look. But FYI - those particular injection functions can potentially crash AutoIT due to the lack of proper error checking (as of v3.3.6.0)

Edited by Ascend4nt

Share this post


Link to post
Share on other sites

Hi guys,

I'm trying to do exactly the same here and I was trying to make this work. Im not familliar with all this, have you been succesful with the tab selection?? Is it possible to have this part of your code??

Share this post


Link to post
Share on other sites

Check the help guide for _GUICtrlTab* functions. (_GUICtrlTab_FindTab, _GUICtrlTab_GetItemCount, _GUICtrlTab_SetCurSel/Focus, etc etc)

Share this post


Link to post
Share on other sites

#18 ·  Posted (edited)

Check the help guide for _GUICtrlTab* functions. (_GUICtrlTab_FindTab, _GUICtrlTab_GetItemCount, _GUICtrlTab_SetCurSel/Focus, etc etc)

I'm not familliar with the handle thing, Im looking in winspector spy but Im not shure how to find or where to find the handle of the tab for those function. Can you help me with this?

Thank you.

Edited by schtroumph

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0