Posts Tagged ‘windows

05
May
09

[VB.Net] FindWindowsIndexOf – .Netish Version

A more .net style function for getting a window using an indexOf:

    ''API Imports
    <Runtime.InteropServices.DllImport("user32.dll", CharSet:=Runtime.InteropServices.CharSet.Auto)> Private Shared Sub GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer)
    End Sub
    <Runtime.InteropServices.DllImport("user32.dll", CharSet:=Runtime.InteropServices.CharSet.Auto)> Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function

    ''FoundWindow stricture
    Public Structure FoundWindow
        Dim strWindowName As String
        Dim strClassName As String
        Dim hWnd As IntPtr
    End Structure

    ''' <summary>
    ''' This function is used to loop through all running processes, checking their main windows to see which ones contain a specific string
    ''' </summary>
    ''' <param name="strIndexOf">The string that you wish to check for in the window captions</param>
    ''' <param name="boolCase">Whether the function is should check for case</param>
    ''' <returns>A list of FoundWindow's containing the window name, class and handle</returns>
    ''' <remarks>As this function only loops through running processes main windows, it may miss some windows.</remarks>
    Public Function FindWindowsIndexOf(ByVal strIndexOf As String, ByVal boolCase As Boolean) As List(Of FoundWindow)
        Dim foundWindows As New List(Of FoundWindow)
        If boolCase = False Then
            strIndexOf = strIndexOf.ToLower
        End If
        For Each p As Process In Process.GetProcesses
            Dim windowCaption As String = p.MainWindowTitle
            If boolCase = False Then
                windowCaption = windowCaption.ToLower
            End If
            If windowCaption.IndexOf(strIndexOf) > -1 Then
                Dim foundWindow As New FoundWindow
                foundWindow.hWnd = FindWindow(vbNullString, p.MainWindowTitle)
                foundWindow.strWindowName = p.MainWindowTitle
                Dim sbClassName As New System.Text.StringBuilder("", 256)
                GetClassName(foundWindow.hWnd, sbClassName, 256)
                foundWindow.strClassName = sbClassName.ToString
                foundWindows.Add(foundWindow)
            End If
        Next
        Return foundWindows
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For Each fWnd As FoundWindow In FindWindowsIndexOf("TeST", False)
            Debug.WriteLine(fWnd.strWindowName & " : " & fWnd.hWnd.ToString)
        Next
    End Sub

Same as the last function, you can choose whether or not the function is case sensitive.

Advertisements
05
May
09

[VB.Net] FindWindowsIndexOf – API Version

Somebody on VBForums was asking how to do something similar to this, so after answering his question, I created this fairly useful function – for windows that names change each time you load them up or something…

    ''API Declarations
    Private Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As EnumWindowProc, ByVal lParam As IntPtr) As Integer
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer
    Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Integer) As Integer
    <Runtime.InteropServices.DllImport("user32.dll", CharSet:=Runtime.InteropServices.CharSet.Auto)> Private Shared Sub GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer)
    End Sub

    ''EnumWindow Callback Delegate Function
    Private Delegate Function EnumWindowProc(ByVal hwnd As Integer, ByVal lParam As IntPtr) As Boolean

    ''Found Window Structures
    Public Structure FoundWindows
        Dim boolCase As Boolean
        Dim strFindWindow As String
        Dim lstFoundWindows As List(Of FoundWindow)
    End Structure
    Public Structure FoundWindow
        Dim strWindowName As String
        Dim strClassName As String
        Dim hWnd As IntPtr
    End Structure

    ''' <summary>
    ''' ''EnumWindow Callback Function
    ''' </summary>
    ''' <param name="hwnd">Current windows handle</param>
    ''' <param name="lParam">Contains the FoundWindows structure</param>
    ''' <returns>The function passes the currently found windows in the lParam using a GCHandle to reform the data</returns>
    ''' <remarks>This function is called for each of the windows found, and is where the indexOf is called</remarks>
    Public Function EnumWindowsProc(ByVal hwnd As Integer, ByVal lParam As IntPtr) As Boolean
        Dim foundWindows As FoundWindows = CType(System.Runtime.InteropServices.GCHandle.FromIntPtr(lParam).Target, FoundWindows)
        Dim windowTitle As String = foundWindows.strFindWindow
        Dim wndTxtLen As Integer = GetWindowTextLength(hwnd)
        If Not wndTxtLen = 0 Then
            Dim sb As New System.Text.StringBuilder("", wndTxtLen + 1)
            GetWindowText(hwnd, sb, sb.Capacity)
            Dim windowCaption As String = sb.ToString
            If foundWindows.boolCase = False Then
                windowCaption = windowCaption.ToLower
                windowTitle = windowTitle.ToLower
            End If
            If windowCaption.IndexOf(windowTitle) > -1 Then
                Dim foundWindow As New FoundWindow
                foundWindow.hWnd = CType(hwnd, IntPtr)
                Dim sbClassName As New System.Text.StringBuilder("", 256)
                GetClassName(foundWindow.hWnd, sbClassName, 256)
                foundWindow.strClassName = sbClassName.ToString
                foundWindow.strWindowName = sb.ToString
                foundWindows.lstFoundWindows.Add(foundWindow)
            End If
        End If
        Return True
    End Function

    ''' <summary>
    ''' This function is used to loop through all open windows and check which ones contain a specific string
    ''' </summary>
    ''' <param name="strIndexOf">The string that you wish to check for in the window captions</param>
    ''' <param name="boolCase">Whether the function is should check for case</param>
    ''' <returns>A FoundWindows structure containing a list of FoundWindow</returns>
    ''' <remarks>The FoundWindows structure also contains the strIndexOf that was used to search the windows caption</remarks>
    Public Function FindWindowsIndexOf(ByVal strIndexOf As String, ByVal boolCase As Boolean) As FoundWindows
        Dim foundWindows As New FoundWindows
        foundWindows.boolCase = boolCase
        foundWindows.strFindWindow = strIndexOf
        foundWindows.lstFoundWindows = New List(Of FoundWindow)
        Dim ListHandle As System.Runtime.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(foundWindows)
        Try
            EnumWindows(AddressOf EnumWindowsProc, System.Runtime.InteropServices.GCHandle.ToIntPtr(ListHandle))
        Finally
            If ListHandle.IsAllocated Then ListHandle.Free()
        End Try
        Return foundWindows
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For Each fWnd As FoundWindow In FindWindowsIndexOf("Test", True).lstFoundWindows
            Debug.WriteLine(fWnd.strWindowName & " : " & fWnd.hWnd.ToString)
        Next
    End Sub

The function can check for casing as well. For example if you are running two windows one named “test” and another named “Test” if you use the function like this:

FindWindowsIndexOf("Test", True)

It will only return one window, however if you set the boolCase to false:

FindWindowsIndexOf("Test", False)

It will return both windows

29
Mar
09

[C++] Creating a Window from a DLL

Currently I’ve been working on a project that requires me to inject a DLL to an external program, and create a window under that process.
Looking on the web, there are really very few resources explaining how to do this.

I don’t know if this is the ‘correct’ way to do this, but it works.
The basic idea behind it:

  • On inject
    • Create new thread
  • On new thread
    • Register window class
    • CreateWindowEx + ShowWindow
    • GetMessage and send them to a WndProc

Right so, starting off.
Create a new Windows DLL and add windows.h as an include
Now on the DLLMain, the entrypoint for the DLL, you want to add the code to create a new thread:

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call,LPVOID lpReserved)
{
	if(ul_reason_for_call==DLL_PROCESS_ATTACH) {
		inj_hModule = hModule;
		CreateThread(0, NULL, ThreadProc, (LPVOID)L"Window Title", NULL, NULL);
	}
	return TRUE;
}

Im passing our new windows title as the lParam for the tread, this is not necessary – and in this situation, silly.
Now we have a new thread, which has its start routine at ThreadProc
You will also need to create a new variable where we can store our modules handle:

HINSTANCE  inj_hModule;          //Injected Modules Handle

So now, lets set up our window.
We are going to create a function to register our window:

BOOL RegisterDLLWindowClass(wchar_t szClassName[])
{
    WNDCLASSEX wc;
    wc.hInstance =  inj_hModule;
    wc.lpszClassName = (LPCWSTR)szClassName;
    wc.lpfnWndProc = DLLWindowProc;
    wc.style = CS_DBLCLKS;
    wc.cbSize = sizeof (WNDCLASSEX);
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.lpszMenuName = NULL;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    if (!RegisterClassEx (&wc))
		return 0;
}

This function will register whatever class name is passed to it, again, in this situation it is unnecessary, you could just write:

wc.lpszClassName = (LPCWSTR)L"InjectedDLLWindowClass";

Now, we are going to make a function to create a menu for our window, to check that the messages are being received
Firstly, define two IDs for our menu

#define MYMENU_EXIT         (WM_APP + 101)
#define MYMENU_MESSAGEBOX   (WM_APP + 102)

Now the code for creating menus is fairly simple, we will just create 2 basic popup menus with two test buttons on, one to close the window, and one to display a messagebox.

HMENU CreateDLLWindowMenu()
{
	HMENU hMenu;
	hMenu = CreateMenu();
	HMENU hMenuPopup;
	if(hMenu==NULL)
           return FALSE;
	hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, TEXT("Exit"));
	AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("File")); 

	hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING,MYMENU_MESSAGEBOX, TEXT("MessageBox")); 
	AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("Test")); 
	return hMenu;
}

That returns a handle to our new menu, which we can pass to the CreateWindowEx function.
So, now lets deal with out ThreadProc, the start routine for the thread.
To begin with, I cast the lParam from a LPVOID – which contains our window name – back to a wchar_t
I then use the CreateDLLWindowMenu function and RegisterDLLWindowClass to be used in CreateWindowEx
Then just create the window, show it and handle the messages:

DWORD WINAPI ThreadProc( LPVOID lpParam )
{
	MSG messages;
	wchar_t *pString = reinterpret_cast<wchar_t * > (lpParam);
	HMENU hMenu = CreateDLLWindowMenu();
	RegisterDLLWindowClass(L"InjectedDLLWindowClass");
	prnt_hWnd = FindWindow(L"Window Injected Into ClassName", L"Window Injected Into Caption");
	HWND hwnd = CreateWindowEx (0, L"InjectedDLLWindowClass", pString, WS_EX_PALETTEWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, prnt_hWnd, hMenu,inj_hModule, NULL );
	ShowWindow (hwnd, SW_SHOWNORMAL);
	while (GetMessage (&messages, NULL, 0, 0))
	{
		TranslateMessage(&messages);
        	DispatchMessage(&messages);
	}
	return 1;
}

Since I wanted this window to have a parent, I used FindWindow to locate the handle, and passed that to the CreateWindowEx, you would need to add another HWND to your globals:

HWND       prnt_hWnd;            //Parent Window Handle

Now we need to set up our WndProc for the new window, this is stated in the RegisterDLLWindowClass:

wc.lpfnWndProc = DLLWindowProc;

So we create the WndProc and handle the messages that we will receive from the Menu (Exit and MessageBox)

LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
		case WM_COMMAND:
               switch(wParam)
               {
                    case MYMENU_EXIT:
						SendMessage(hwnd, WM_CLOSE, 0, 0);
                        break;
                    case MYMENU_MESSAGEBOX:
						MessageBox(hwnd, L"Test", L"MessageBox",MB_OK);
                        break;
               }
               break;
		case WM_DESTROY:
			PostQuitMessage (0);
			break;
		default:
			return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}

Now if you were to inject this DLL into another process, you would get something that looked like this:
injectedwindow

Below is the complete code listing.
Continue reading ‘[C++] Creating a Window from a DLL’

28
Mar
09

[VB.Net] GetWindowPos

I wrote this function a while back, but its often quite useful, so I thought that I would post it back up here.
Its basically the equivalent of the SetWindowPos API, which is used for setting various properties of a window.
This function is slightly edited from the one previously posted, which had an error in the size calculation and this also uses the .net Point and Rectangle structures

    Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As IntPtr, ByVal wFlag As Integer) As IntPtr
    Private Declare Function GetWindowPlacement Lib "user32" (ByVal hwnd As IntPtr, ByRef lpwndpl As WINDOWPLACEMENT) As Integer

    Private Structure WINDOWPLACEMENT
        Public Length As Integer
        Public flags As Integer
        Public showCmd As Integer
        Public ptMinPosition As Point
        Public ptMaxPosition As Point
        Public rcNormalPosition As Rectangle
    End Structure

    Public Enum WNDSTATE
        SW_HIDE = 0
        SW_SHOWNORMAL = 1
        SW_NORMAL = 1
        SW_SHOWMINIMIZED = 2
        SW_MAXIMIZE = 3
        SW_SHOWNOACTIVATE = 4
        SW_SHOW = 5
        SW_MINIMIZE = 6
        SW_SHOWMINNOACTIVE = 7
        SW_SHOWNA = 8
        SW_RESTORE = 9
        SW_SHOWDEFAULT = 10
        SW_MAX = 10
    End Enum

    Private Const GW_HWNDNEXT = 2
    Private Const GW_HWNDPREV = 3

    ''' <summary >
    ''' Return values from equivalent to those in the SetWindowPos API
    ''' </summary >
    ''' <param name="hwnd" > In - The handle of the window you wish to get the information from</param >
    ''' <param name="ptrPhwnd" > Out - Returns the handle of the previous window in the Z order</param >
    ''' <param name="ptrNhwnd" > Out - Returns the handle of the next window in the Z order</param >
    ''' <param name="ptPoint" > Out - Returns the location of the specified window on the screen</param >
    ''' <param name="szSize" > Out - Returns the size of the specified window</param >
    ''' <param name="intShowCmd" > Out - Returns the state of the window, returns a WNDSTATE value</param >
    ''' <remarks > If you use the function on a control, it will return the handles for the buttons next in the forms Z order.
    ''' If the handle passed for hwnd is invalid, the function will return 0 for all values</remarks >
    Public Sub GetWindowPos(ByVal hwnd As Integer, ByRef ptrPhwnd As Integer, ByRef ptrNhwnd As Integer, ByRef ptPoint As Point, ByRef szSize As Size, ByRef intShowCmd As WNDSTATE)
        Dim wInf As WINDOWPLACEMENT
        wInf.Length = System.Runtime.InteropServices.Marshal.SizeOf(wInf)
        GetWindowPlacement(hwnd, wInf)
        szSize = New Size(wInf.rcNormalPosition.Right - (wInf.rcNormalPosition.Left * 2), wInf.rcNormalPosition.Bottom - (wInf.rcNormalPosition.Top * 2))
        ptPoint = New Point(wInf.rcNormalPosition.Left, wInf.rcNormalPosition.Top)
        ptrPhwnd = GetNextWindow(hwnd, GW_HWNDPREV)
        ptrNhwnd = GetNextWindow(hwnd, GW_HWNDNEXT)
        intShowCmd = wInf.showCmd
    End Sub

Usage:

        Dim hwnd As Integer = Process.GetProcessesByName("calc")(0).MainWindowHandle
        Dim ptrPhwnd, ptrNhwnd As Integer
        Dim ptPoint As Point
        Dim szSize As Size
        Dim intShowCmd As WNDSTATE
        GetWindowPos(hwnd, ptrPhwnd, ptrNhwnd, ptPoint, szSize, intShowCmd)
        Debug.WriteLine("Window Handle: " & hwnd)
        Debug.WriteLine("Previous Window Handle: " & ptrPhwnd)
        Debug.WriteLine("Next Window Handle: " & ptrNhwnd)
        Debug.WriteLine("Window Location: (" & ptPoint.X & ", " & ptPoint.Y & ")")
        Debug.WriteLine("Window Size - Width: " & szSize.Width & " Height: " & szSize.Height)
        Debug.WriteLine("Window State: " & intShowCmd.ToString)

Would output:

Window Handle: 4065694
Previous Window Handle: 8194392
Next Window Handle: 263910
Window Location: (2934, 89)
Window Size – Width: 480 Height: 310
Window State: SW_NORMAL

27
Mar
09

[VB.Net] Q: Sending Mouseclicks/Keystrokes into minimized window

The question asked “What i want is how do i make a program to be able to send inputs (key clicks) into the application when it is minimized”

The solution:

Imports System.Runtime.InteropServices
Public Class Form1
    ''--------------------
    ''Minimise the window
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto) >  _
    Private Shared Function ShowWindow(ByVal hwnd As IntPtr, _
                          ByVal nCmdShow As Integer) As Integer
    End Function
    Private Const SW_SHOWMINIMIZED = 2
    ''--------------------
    ''Finding the Control
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto) >  _
    Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
                          ByVal childAfter As IntPtr, _
                          ByVal lclassName As String, _
                          ByVal windowTitle As String) As IntPtr
    End Function
    ''--------------------
    ''Sending the click
    Private Declare Auto Function SendMessage Lib "user32" (ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
    Private Const BM_CLICK = &HF5

    Private Const WM_LBUTTONDOWN = &H201
    Private Const WM_LBUTTONUP = &H202
    Private Const BM_SETSTATE = &HF3
    ''--------------------
    ''Read the edit control
    Private Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As System.Text.StringBuilder) As IntPtr
    Private Const WM_GETTEXT = &HD
    Private Const WM_GETTEXTLENGTH As Integer = &HE
    ''--------------------
    ''Keyboard Input
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto) >  _
    Private Shared Function PostMessage( _
                          ByVal hWnd As IntPtr, _
                          ByVal Msg As UInteger, _
                          ByVal wParam As IntPtr, _
                          ByVal lParam As IntPtr) As Boolean
    End Function
    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Private Const WM_SYSKEYDOWN = &H104
    Private Const WM_SYSKEYUP = &H105

    Private Function GetAnswer(ByVal hWnd As Integer) As String
        ''Get the controls handle
        Dim calc_textHandle As Integer = FindWindowEx(hWnd, IntPtr.Zero, "Edit", vbNullChar)
        ''Get length of the text
        Dim calc_textLen As IntPtr = SendMessage(calc_textHandle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)
        ''Create string builder
        Dim calc_textSb As New System.Text.StringBuilder(calc_textLen.ToInt32 + 2)
        ''Get text
        SendMessage(calc_textHandle, WM_GETTEXT, calc_textLen.ToInt32 + 2, calc_textSb)
        ''Return
        If Not calc_textSb.ToString = "" Then
            Return ("Result: " & calc_textSb.ToString)
        Else
            Return ("Error Reading Answer")
        End If
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim ProcessFileName As String = "calc"
        Dim p As Process = Process.GetProcessesByName(ProcessFileName)(0)
        '   Instead of using Process.GetProcessByName, you could use:
        '        Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String,_
        '                                    ByVal lpWindowName As String) As IntPtr
        ' FindWindow('SciCalc', 'Calculator')
        '    or FindWindow('SciCalc', vbNullChar)
        '       or FindWindow(vbNullChar, 'Calculator')
        'To return the handle of the window

        ''Minimise Window
        ShowWindow(p.MainWindowHandle, SW_SHOWMINIMIZED)

        ''Now to do a basic sum on the calculator
        ''Firstly reset the calculator, and then do 1+1="

        Dim calc_CButton As Integer = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", "C")
        ''Clear
        SendMessage(calc_CButton, BM_CLICK, 0, 0)
        Dim calc_1Button As Integer = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", "1")
        ''1
        SendMessage(calc_1Button, BM_CLICK, 0, 0)
        Dim calc_plusButton As Integer = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", "+")
        ''+
        SendMessage(calc_plusButton, BM_CLICK, 0, 0)
        ''1, instead of using BM_CLICK using Mouse events [BM_CLICK works fine]
        SendMessage(calc_1Button, WM_LBUTTONDOWN, 0, 0)
        SendMessage(calc_1Button, BM_SETSTATE, 1, 0)
        SendMessage(calc_1Button, WM_LBUTTONUP, 0, 0)
        Dim calc_equalButton As Integer = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", "=")
        ''=
        SendMessage(calc_equalButton, BM_CLICK, 0, 0)

        ''Now read the results
        Debug.WriteLine(GetAnswer(p.MainWindowHandle))

        ''Now using Keyboard input
        ''Escape Key to clear textbox
        PostMessage(p.MainWindowHandle, WM_KEYDOWN, Keys.Escape, 0)
        PostMessage(p.MainWindowHandle, WM_KEYUP, Keys.Escape, 0)
        ''5
        PostMessage(p.MainWindowHandle, WM_KEYUP, Keys.D5, 0)
        ''*
        PostMessage(p.MainWindowHandle, WM_KEYUP, Keys.Multiply, 0)
        ''10
        PostMessage(p.MainWindowHandle, WM_KEYUP, Keys.D1, 0)
        PostMessage(p.MainWindowHandle, WM_KEYUP, Keys.D0, 0)
        ''=
        PostMessage(p.MainWindowHandle, WM_KEYUP, &HBB, 0)
        Debug.WriteLine(GetAnswer(p.MainWindowHandle))
    End Sub
End Class

This is an old project demonstrating how to click buttons and send keys to a minimised window.
Use FindWindow/FindWindowEx/EnumChildWindows/etc to get the handle of the button
Then I would use the BM_CLICK message as the Msg param however you could also use WM_LBUTTONDOWN+BM_SETSTATE(1)+WM_LBUTTONUP

The above is an example using Windows Calculator. It clicks the buttons on the form, sends key presses, and gets the answer.