Is there a way to remove the border/frame (the Aero bit) of another application's window (say notepad) from a VB.NET program?
views:
1142answers:
2You can do that using P-Invoke. Below is some code which uses SetWindowLong (in User32.dll) to change the border of the main window of notepad. (This code assumes that you have an instance of notepad running.) You can experiment with different window styles to achieve the result you want.
GWL_STYLE is for basic window styles. You can read about them here.
GWL_EXSTYLE is for extended window styles. You can read about them here.
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Module Module1
Sub Main()
Dim notepad As Process = Process.GetProcessesByName("notepad")(0)
Dim GWL_STYLE As Int32 = -16
Dim GWL_EXSTYLE As Int32 = -20
' MainWindowHandle happens to be the handle of the window you want for notepad.
' It may not be the handle you want if you try this on a different process.
Dim hWnd As IntPtr = notepad.MainWindowHandle
' You can examine the current styles using GetWindowLong.
Dim styles As WindowStyles = GetWindowLong(hWnd, GWL_STYLE)
Dim exStyles As WindowStyles = GetWindowLong(hWnd, GWL_EXSTYLE)
' WS_VISIBLE must be used for the window to be selectable.
Dim newStyles As WindowStyles = WindowStyles.WS_VISIBLE Or WindowStyles.WS_BORDER
SetWindowLong(hWnd, GWL_STYLE, newStyles)
' If you want to modify the extended styles, use GWL_EXSTYLE
SetWindowLong(hWnd, GWL_EXSTYLE, exStyles)
End Sub
_
Private Function GetWindowLong( _
ByVal hWnd As IntPtr, _
ByVal nIndex As Integer) As Integer
End Function
_
Private Function SetWindowLong( _
ByVal hWnd As IntPtr, _
ByVal nIndex As Integer, _
ByVal dwNewLong As IntPtr) As Integer
End Function
End Module
_
Public Enum WindowStyles As Long
WS_OVERLAPPED = 0
WS_POPUP = 2147483648
WS_CHILD = 1073741824
WS_MINIMIZE = 536870912
WS_VISIBLE = 268435456
WS_DISABLED = 134217728
WS_CLIPSIBLINGS = 67108864
WS_CLIPCHILDREN = 33554432
WS_MAXIMIZE = 16777216
WS_BORDER = 8388608
WS_DLGFRAME = 4194304
WS_VSCROLL = 2097152
WS_HSCROLL = 1048576
WS_SYSMENU = 524288
WS_THICKFRAME = 262144
WS_GROUP = 131072
WS_TABSTOP = 65536
WS_MINIMIZEBOX = 131072
WS_MAXIMIZEBOX = 65536
WS_CAPTION = WS_BORDER Or WS_DLGFRAME
WS_TILED = WS_OVERLAPPED
WS_ICONIC = WS_MINIMIZE
WS_SIZEBOX = WS_THICKFRAME
WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or _
WS_THICKFRAME Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX
WS_POPUPWINDOW = WS_POPUP Or WS_BORDER Or WS_SYSMENU
WS_CHILDWINDOW = WS_CHILD
WS_EX_DLGMODALFRAME = 1
WS_EX_NOPARENTNOTIFY = 4
WS_EX_TOPMOST = 8
WS_EX_ACCEPTFILES = 16
WS_EX_TRANSPARENT = 32
'#If (WINVER >= 400) Then
WS_EX_MDICHILD = 64
WS_EX_TOOLWINDOW = 128
WS_EX_WINDOWEDGE = 256
WS_EX_CLIENTEDGE = 512
WS_EX_CONTEXTHELP = 1024
WS_EX_RIGHT = 4096
WS_EX_LEFT = 0
WS_EX_RTLREADING = 8192
WS_EX_LTRREADING = 0
WS_EX_LEFTSCROLLBAR = 16384
WS_EX_RIGHTSCROLLBAR = 0
WS_EX_CONTROLPARENT = 65536
WS_EX_STATICEDGE = 131072
WS_EX_APPWINDOW = 262144
WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE Or WS_EX_CLIENTEDGE
WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE Or WS_EX_TOOLWINDOW Or WS_EX_TOPMOST
'#End If
'#If (WIN32WINNT >= 500) Then
WS_EX_LAYERED = 524288
'#End If
'#If (WINVER >= 500) Then
WS_EX_NOINHERITLAYOUT = 1048576 ' Disable inheritence of mirroring by children
WS_EX_LAYOUTRTL = 4194304 ' Right to left mirroring
'#End If
'#If (WIN32WINNT >= 500) Then
WS_EX_COMPOSITED = 33554432
WS_EX_NOACTIVATE = 67108864
'#End If
End Enum
Explanation of the code
I'm not sure how much experience you have in developing GUI applications, so I'll give a little background on how a window works. A window has a uniquely identifying number called a handle. Also associated with the window is a window procedure, which handles messages (integer numbers that identify events and commands) for that window. When a window is created, you specify what styles you want the window to have, etc. There's a lot more complexity to windows applications, but to avoid getting bogged down in the details we'll move on.
Thankfully, .NET Winforms isolates us from having to interact with the Windows API and handle messages directly (for the most part) and makes it very easy to create functional GUI applications. There is a lot more power under the hood in the Windows API that most .NET developers don't usually need to worry about.
Now with that background, the code should be a little easier to understand.
First off we need to get the first process named "notepad".
Dim notepad As Process = Process.GetProcessesByName("notepad")(0)
Then we define two integers GWL_STYLE
and GWL_EXSTYLE
. These two integers will have a specific meaning in context of the SetWindowLong
function. Their value (and the value of many other constants) can be found in Winuser.h and the rest of the header files in the Windows SDK.
Dim GWL_STYLE As Int32 = -16
Dim GWL_EXSTYLE As Int32 = -20
Next we get the handle of the notepad's main window.
Dim hWnd As IntPtr = notepad.MainWindowHandle
After that we encounter the GetWindowLong function. From MSDN:
The GetWindowLong function retrieves information about the specified window.
GetWindowLong
takes the window handle and a value indicating what information to retrieve and returns the specified information.
Dim styles As WindowStyles = GetWindowLong(hWnd, GWL_STYLE)
Dim exStyles As WindowStyles = GetWindowLong(hWnd, GWL_EXSTYLE)
These were included so you could see what styles were applied to the window, so you could determine which styles to leave out.
Next we define what we styles we want to apply to the window. You can read about the various styles and their meanings here.
Dim newStyles As WindowStyles = WindowStyles.WS_VISIBLE Or WindowStyles.WS_BORDER
Then we apply those styles to the window using SetWindowLong. From MSDN:
The SetWindowLong function changes an attribute of the specified window.
SetWindowLong
takes the window handle, a value indicating what attribute to change, and the new value of the attribute, and changes the attribute.
SetWindowLong(hWnd, GWL_STYLE, newStyles)
That's basically what the code does. To avoid repetition, I will not go over GWL_EXSTYLE
since it is used exactly the same way as GWL_STYLE
. The rest of the code is just logistics to allow us to use SetWindowLong
and GetWindowLong
.
This works beautifully, but when I hide the border of EVE it behaves poorly - The now borderless window resizes to fill the space where the border was and results in the mouse being offset(The actual mouse position is about an inch right and down from the cursor).
What I'm trying to achieve is making a fake fullscreen effect, so I can then display a form over it, like an overlay