I have a Surface Pro that I need to "lock down" as a type of Kiosk mode. I am aware that an update is on its way for "Kiosk Mode".. however I need to do this before that.
I have searched all over the internet, but it appears that you cannot disable the Charm bar from a swipe on the screen. I have found ways to disable trackpads.. but unfortunately, since this tablet will be used without a keyboard, I need to disable the Charm bar.
My new thought is to move the entire Charm bar HWND thousands of pixels off screen, or, maybe set a windows style on it so that it doesn't appear. I have attempted to use Spy++ and a custom EnumWindows
based console app to give me some Window handles.. however I can't keep the Charm bar open long enough for these to take effect.
So my question is: How can I locate the Window Handle (HWND
) for the Charm Bar in Windows 8? Or, how can I get a reference to the Charm bar some other way in order to throw a SetWindowLong
or SetWindowPos
at it?
In fact, I have found a way to do this (apparently, no one else has :/).
For those wondering, software like "Start8" and "SkipMetroSuite" poll key presses to stop the Charm Bar. They literally simulate the keypresses to close it within a tight loop.
I have found (what I think is) a better way.
First up... some WinAPI functions:
using System.Runtime.InteropServices;
....
private enum WindowShowStyle : uint
{ // find more info at http://stackoverflow.com/a/8210120/1245420
Hide = 0, ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
ShowNormalNoActvate = 4, Show = 5, Minimize = 6, ShowNoActivate = 8,
Restore = 9, ShowDefault = 10, ForceMinimized = 11
}
[DllImport("user32.dll", SetLastError = true)]
static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(System.IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(System.IntPtr hWnd, WindowShowStyle nCmdShow);
So the problem first up, is the Charm Bar. The Window title for this turns out to be, Charm Bar
. Spawning a thread that continually looks for this window and hides it works beautifully. So I spawn a thread, and continually poll it:
System.Threading.Tasks.Task.Factory.StartNew(() => {
while (true) {
System.IntPtr hWndCharmBar = FindWindowByCaption(System.IntPtr.Zero, "Charm Bar");
ShowWindow(hWndCharmBar, 0);
System.Threading.Thread.Sleep(100); // sleep for a bit
}
});
This works well and gives the added benefit of making the Charm Bar continue to work when the app is closed. The Thread.Sleep
is there to stop the thread smashing the CPU - but the delay also allows for the Charm Bar to appear for a split second. I am yet to successfully open the Charm Bar and press a button quick enough before the thread hides it again though, so this is fine. Lowering the sleep time obviously makes this quicker.
The other issue with Windows 8 is that if you have some sort of slider (in my app, I have a ListBox
containing images for a gallery) then you can actually slide to the side of the screen... hold your finger there, and access the taskbar...
So.. the next part is closing the task bar:
IntPtr hWndTray = FindWindow("Shell_TrayWnd", null);
ShowWindow(hWndTray, 0);
..then I show it again on app close:
IntPtr hWndTray = FindWindow("Shell_TrayWnd", null);
ShowWindow(hWndTray, 1);
Functionally, this is all I needed for my app. Hope that helps someone.
I would also like to note that the clock is contained in a window with the caption "Clock and Date".
IntPtr hWndCharmClock = FindWindowByCaption(IntPtr.Zero, "Clock and Date");
I have made a small application to toggle the charms bar: https://bitbucket.org/darkwingduck/charmsbartoggle/overview
This is how to disable the charm bar when the app is maximized using SHGetPropertyStoreForWindow interop. Should be easy to convert to C#:
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Public Class EdgeGestureUtil
Private Shared DISABLE_TOUCH_SCREEN As Guid = New Guid("32CE38B2-2C9A-41B1-9BC5-B3784394AA44")
Private Shared IID_PROPERTY_STORE As Guid = New Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")
Private Shared VT_BOOL As Short = 11
#Region "Structures"
<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Public Structure PropertyKey
Public Sub New(guid As Guid, pid As UInt32)
fmtid = guid
Me.pid = pid
End Sub
<MarshalAs(UnmanagedType.Struct)> _
Public fmtid As Guid
Public pid As UInteger
End Structure
<StructLayout(LayoutKind.Explicit)> _
Public Structure PropVariant
<FieldOffset(0)> _
Public vt As Short
<FieldOffset(2)> _
Private wReserved1 As Short
<FieldOffset(4)> _
Private wReserved2 As Short
<FieldOffset(6)> _
Private wReserved3 As Short
<FieldOffset(8)> _
Private cVal As SByte
<FieldOffset(8)> _
Private bVal As Byte
<FieldOffset(8)> _
Private iVal As Short
<FieldOffset(8)> _
Public uiVal As UShort
<FieldOffset(8)> _
Private lVal As Integer
<FieldOffset(8)> _
Private ulVal As UInteger
<FieldOffset(8)> _
Private intVal As Integer
<FieldOffset(8)> _
Private uintVal As UInteger
<FieldOffset(8)> _
Private hVal As Long
<FieldOffset(8)> _
Private uhVal As Long
<FieldOffset(8)> _
Private fltVal As Single
<FieldOffset(8)> _
Private dblVal As Double
<FieldOffset(8)> _
Public boolVal As Boolean
<FieldOffset(8)> _
Private scode As Integer
'CY cyVal;
<FieldOffset(8)> _
Private [date] As DateTime
<FieldOffset(8)> _
Private filetime As System.Runtime.InteropServices.ComTypes.FILETIME
'CLSID* puuid;
'CLIPDATA* pclipdata;
'BSTR bstrVal;
'BSTRBLOB bstrblobVal;
<FieldOffset(8)> _
Private blobVal As Blob
'LPSTR pszVal;
<FieldOffset(8)> _
Private pwszVal As IntPtr
'LPWSTR
'IUnknown* punkVal;
'IDispatch* pdispVal;
' IStream* pStream;
' IStorage* pStorage;
' LPVERSIONEDSTREAM pVersionedStream;
' LPSAFEARRAY parray;
' CAC cac;
' CAUB caub;
' CAI cai;
' CAUI caui;
' CAL cal;
' CAUL caul;
' CAH cah;
' CAUH cauh;
' CAFLT caflt;
' CADBL cadbl;
' CABOOL cabool;
' CASCODE cascode;
' CACY cacy;
' CADATE cadate;
' CAFILETIME cafiletime;
' CACLSID cauuid;
' CACLIPDATA caclipdata;
' CABSTR cabstr;
' CABSTRBLOB cabstrblob;
' CALPSTR calpstr;
' CALPWSTR calpwstr;
' CAPROPVARIANT capropvar;
' CHAR* pcVal;
' UCHAR* pbVal;
' SHORT* piVal;
' USHORT* puiVal;
' LONG* plVal;
' ULONG* pulVal;
' INT* pintVal;
' UINT* puintVal;
' FLOAT* pfltVal;
' DOUBLE* pdblVal;
' VARIANT_BOOL* pboolVal;
' DECIMAL* pdecVal;
' SCODE* pscode;
' CY* pcyVal;
' DATE* pdate;
' BSTR* pbstrVal;
' IUnknown** ppunkVal;
' IDispatch** ppdispVal;
' LPSAFEARRAY* pparray;
' PROPVARIANT* pvarVal;
'
''' <summary>
''' Helper method to gets blob data
''' </summary>
Private Function GetBlob() As Byte()
Dim Result As Byte() = New Byte(blobVal.Length - 1) {}
Marshal.Copy(blobVal.Data, Result, 0, Result.Length)
Return Result
End Function
''' <summary>
''' Property value
''' </summary>
Public ReadOnly Property Value() As Object
Get
Dim ve As VarEnum = vt
Select Case ve
Case VarEnum.VT_I1
Return bVal
Case VarEnum.VT_I2
Return iVal
Case VarEnum.VT_I4
Return lVal
Case VarEnum.VT_I8
Return hVal
Case VarEnum.VT_INT
Return iVal
Case VarEnum.VT_UI4
Return ulVal
Case VarEnum.VT_LPWSTR
Return Marshal.PtrToStringUni(pwszVal)
Case VarEnum.VT_BLOB
Return GetBlob()
End Select
Throw New NotImplementedException("PropVariant " + ve.ToString())
End Get
End Property
End Structure
Friend Structure Blob
Public Length As Integer
Public Data As IntPtr
'Code Should Compile at warning level4 without any warnings,
'However this struct will give us Warning CS0649: Field [Fieldname]
'is never assigned to, and will always have its default value
'You can disable CS0649 in the project options but that will disable
'the warning for the whole project, it's a nice warning and we do want
'it in other places so we make a nice dummy function to keep the compiler
'happy.
Private Sub FixCS0649()
Length = 0
Data = IntPtr.Zero
End Sub
End Structure
#End Region
#Region "Interfaces"
<ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Interface IPropertyStore
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub GetCount(<Out> ByRef cProps As UInteger)
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub GetAt(<[In]> iProp As UInteger, ByRef pkey As PropertyKey)
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub GetValue(<[In]> ByRef key As PropertyKey, ByRef pv As PropVariant)
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub SetValue(<[In]> ByRef key As PropertyKey, <[In]> ByRef pv As PropVariant)
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub Commit()
<MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
Sub Release()
End Interface
#End Region
#Region "Methods"
<DllImport("shell32.dll", SetLastError:=True)> _
Private Shared Function SHGetPropertyStoreForWindow(handle As IntPtr, ByRef riid As Guid, ByRef propertyStore As IPropertyStore) As Integer
End Function
Public Shared Sub EnableEdgeGestures(ByVal hwnd As IntPtr, ByVal enable As Boolean)
Dim pPropStore As IPropertyStore = Nothing
Dim hr As Integer
hr = SHGetPropertyStoreForWindow(hwnd, IID_PROPERTY_STORE, pPropStore)
If hr = 0 Then
Dim propKey As New PropertyKey
propKey.fmtid = DISABLE_TOUCH_SCREEN
propKey.pid = 2
Dim var As New PropVariant
var.vt = VT_BOOL
var.boolVal = enable
pPropStore.SetValue(propKey, var)
Marshal.FinalReleaseComObject(pPropStore)
End If
End Sub
#End Region
End Class
Better and simplest way is to change 2 registry keys when start your application and restore them back when close it?
All you need is to create(if not existed) EdgeUI key under:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ImmersiveShell
and add new DWORD keys: DisableTLcorner & DisableCharmsHint
For disable:
DisableTLcorner = 1 DisableCharmsHint = 1
For Enable change to zero or delete them:
DisableTLcorner = 0 DisableCharmsHint = 0
All this can be easily done in programmatic using any language!!!!
Simple solution,not perfect, every time the charms bar is activate, your application is deactivate, so reactivate it immediately and the charms bar disappear. add this in your App.xaml.cs
private enum WindowShowStyle : uint
{ // find more info at http://stackoverflow.com/a/8210120/1245420
Hide = 0, ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
ShowNormalNoActvate = 4, Show = 5, Minimize = 6, ShowNoActivate = 8,
Restore = 9, ShowDefault = 10, ForceMinimized = 11
}
[DllImport("user32.dll", SetLastError = true)]
static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(System.IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(System.IntPtr hWnd, WindowShowStyle nCmdShow);
DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
public App()
{
this.Deactivated += App_Deactivated;
this.Activated += App_Activated;
timer.Tick += delegate
{
Application.Current.MainWindow.Activate();
System.IntPtr hWndCharmBar = FindWindowByCaption(System.IntPtr.Zero, "Charm Bar");
ShowWindow(hWndCharmBar, 0);
};
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
}
void App_Activated(object sender, EventArgs e)
{
timer.Stop();
}
void App_Deactivated(object sender, EventArgs e)
{
timer.Start();
}