Change Windows 7 taskbar location automatically ba

2020-06-14 09:07发布

问题:

Are the following things possible using VBScript or any other programming language:

  • detect screen shape - or whether computer is docked
  • change the Windows taskbar location

What I am trying to achieve:

My laptop has a 14" widescreen: pretty wide, but not very high. I find it most convenient to have the Windows taskbar located on the left of the screen, since I can spare the width but not the vertical space.

However, when in the office, my computer sits in a docking station and is hooked up to a nice big squarish screen. Here I much prefer to have the taskbar in its default location i.e. at the bottom.

I know how to switch between the two taskbar locations manually in Taskbar Properties, of course. But I do this a few times daily, which is rather annoying. My question is: can I have the taskbar location change automatically?

For example, at startup (or wake up from hibernation) a script would run which detects either:

  • Is screen shape taller than 4:3? (or whatever number)
  • Is computer docked in docking station?

If yes, put taskbar at bottom, else at left.

Anyone know how to do this or can put me on the right track? Or is there already a utility out there that can do this?

回答1:

//Normal augment on why this is not a good idea on someone else's machine omitted

A scripting language may not be a good choice here, you need something that pumps the message to listen to WM_DISPLAYCHANGE.

When you get the message you need to calculate the desired orientation of the task bar based on the resolutions of your monitors. Then you use RmShutdown to close Windows Explorer.

//undocumented behavior begins, may break anytime

The taskbar docking edge is stored in byte 13 (as one of the ABE values from APPBARDATA ) and the position is stored in byte 25-40 as a win32 RECT. You can modify the setting before restarting the explorer.

//undocumented behavior ends

Sample code (full source at https://github.com/jiangsheng/Samples/tree/master/AppBarTest):

//returns the process id and create time for the oldest explorer.exe 
RM_UNIQUE_PROCESS GetExplorerApplication()
{
    RM_UNIQUE_PROCESS  result={0};
    DWORD bytesReturned=0;
    DWORD processIdSize=4096;
    std::vector<DWORD> processIds;
    processIds.resize(1024);
    EnumProcesses(processIds.data(),processIdSize,&bytesReturned);
    while(bytesReturned==processIdSize)
    {
        processIdSize+=processIdSize;
        processIds.resize(processIdSize/4);
        EnumProcesses(processIds.data(),processIdSize,&bytesReturned);
    }

    std::for_each(processIds.begin(), processIds.end(), [&result] (DWORD processId) {
         HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
                                   FALSE, processId);
         if (hProcess) {
            std::wstring imageName;
            imageName.resize(4096);
            if(GetProcessImageFileName (hProcess,(LPWSTR)imageName.data(),4096)>0)
            {
                if(wcscmp(L"explorer.exe",PathFindFileName(imageName.data()))==0)
                {
                    //this is assmuing the user is not running elevated and won't see explorer processes in other sessions
                    FILETIME ftCreate, ftExit, ftKernel, ftUser;
                    if (GetProcessTimes(hProcess, &ftCreate, &ftExit,&ftKernel, &ftUser))
                    {
                        if(result.dwProcessId==0)
                        {
                            result.dwProcessId=processId;
                            result.ProcessStartTime=ftCreate;
                        }
                        else if(CompareFileTime(&result.ProcessStartTime,&ftCreate)>0)
                        {
                            result.dwProcessId=processId;
                            result.ProcessStartTime=ftCreate;
                        }
                    }
                }
            }
            CloseHandle(hProcess);
         }
    });
    return result;
}
    //taskbar position calculating code omitted
    DWORD dwSession=0;
    WCHAR szSessionKey[CCH_RM_SESSION_KEY+1] = { 0 };
    DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
    if (dwError == ERROR_SUCCESS) {
        RM_UNIQUE_PROCESS rgApplications[1]={GetExplorerApplication()};
        dwError=RmRegisterResources(
            dwSession,0,NULL,1,rgApplications,0,NULL);
        DWORD dwReason;
        UINT nProcInfoNeeded;
        UINT nProcInfo = 10;
        RM_PROCESS_INFO rgpi[10];
        dwError = RmGetList(dwSession, &nProcInfoNeeded,
                       &nProcInfo, rgpi, &dwReason);
        if(dwReason==RmRebootReasonNone)//now free to restart explorer
        {
            RmShutdown(dwSession,RmForceShutdown,NULL);//important, if we change the registry before shutting down explorer will override our change
            //using undocumented setting structure, could break any time
            //edge setting is stored at HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2!Settings
            HKEY hKey={0};
            DWORD result=0;
            result=::RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"),
                    0, KEY_READ|KEY_WRITE, &hKey) ;
            if (result== ERROR_SUCCESS)
            {
                std::vector<BYTE> data;
                data.resize(256);
                TCHAR settingValue[]= _T("Settings");
                DWORD dwKeyDataType=0;
                DWORD dwDataBufSize=data.size();
                result=::RegQueryValueEx(hKey,settingValue, NULL, &dwKeyDataType,
                    (LPBYTE) data.data(), &dwDataBufSize);
                while(ERROR_MORE_DATA==result)
                {
                    data.resize(256+data.size());
                    dwDataBufSize=data.size();
                    result=::RegQueryValueEx(hKey,settingValue, NULL, &dwKeyDataType, 
                        (LPBYTE) data.data(), &dwDataBufSize);
                }
                data.resize(dwDataBufSize);
                if(result==ERROR_SUCCESS)
                {
                    switch ( dwKeyDataType )
                    {
                        case REG_BINARY:
                            if(data.size()==40)
                            {
                                BYTE taskbarPosition=data[12];
                                taskbarPosition=edge;
                                data[12]=taskbarPosition;
                                RECT* taskbarRect=(RECT*)&data[24];
                                CopyRect (taskbarRect,&abd.rc);
                                result=::RegSetValueEx(hKey,settingValue,0,REG_BINARY,(LPBYTE) data.data(), dwDataBufSize);
                            }
                            break;
                    }
                }
                ::RegCloseKey( hKey );
            }
            RmRestart (dwSession,0,NULL);
        }
    }
    RmEndSession(dwSession);


回答2:

You can do this in a simple batch or from a script. Set the registry value to position the taskbar based on the current resolution of your screen (if in the docking it will be higher) and then restart explorer.exe. So eg a batch to set the taskbar at the left of your screen would be (assuming you have the bottom.reg file in the d:\scripts folder)

reg add d:\scripts\Bottom.reg
@echo off taskkill /f /IM explorer.exe
explorer.exe

The contents of bottom.reg are

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2]
"Settings"=hex:28,00,00,00,ff,ff,ff,ff,02,00,00,00,03,00,00,00,3e,00,00,00,2e,\
  00,00,00,00,00,00,00,82,04,00,00,80,07,00,00,b0,04,00,00

and for left.reg

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2]
"Settings"=hex:28,00,00,00,ff,ff,ff,ff,02,00,00,00,00,00,00,00,3e,00,00,00,2e,\
  00,00,00,00,00,00,00,00,00,00,00,3e,00,00,00,b0,04,00,00

You will have some flickering but since you will do this when you start windows that won't be a problem i suppose. I tested this on Windows 7.

EDIT: made a vbscript that does the same thing based on screen resolution

HKEY_CURRENT_USER = &H80000001
Set WshShell = CreateObject("WScript.Shell")
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set ObjRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")

'Get curr. user name
Set colItems = objWMIService.ExecQuery("Select * From Win32_ComputerSystem")
For Each objItem in colItems
  strCurrentUserName = objItem.UserName
Next

Set colItems = objWMIService.ExecQuery("Select * From Win32_DesktopMonitor where DeviceID = 'DesktopMonitor1'",,0) 
For Each objItem in colItems 
  intHorizontal = objItem.ScreenWidth 
  intVertical = objItem.ScreenHeight 
Next 

bottom = Array(&H28,&H00,&H00,&H00,&Hff,&Hff,&Hff,&Hff,&H02,&H00,&H00,&H00,&H03,&H00,&H00,&H00,&H3e,&H00,&H00,&H00,&H2e,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H82,&H04,&H00,&H00,&H80,&H07,&H00,&H00,&Hb0,&H04,&H00,&H00)
left_   = Array(&H28,&H00,&H00,&H00,&Hff,&Hff,&Hff,&Hff,&H02,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H3e,&H00,&H00,&H00,&H2e,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H3e,&H00,&H00,&H00,&Hb0,&H04,&H00,&H00)

if intHorizontal >= 1920 then
  regdata = bottom
else
  regdata = left_
end if

ObjRegistry.SetBinaryValue HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2\", "Settings", regdata

'Restart user shell
Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where Name = 'Explorer.exe'")
For Each objProcess in colProcessList
    colProperties = objProcess.GetOwner(strNameOfUser,strUserDomain)
  wscript.echo colProperties
    If strUserDomain & "\" & strNameOfUser = strCurrentUserName then
    wscript.echo "restarting"
        objProcess.Terminate()
    end if
Next