Avatar billede segato Nybegynder
15. maj 2006 - 15:16 Der er 8 kommentarer og
1 løsning

Finde et vindue med win32 API

Hej folkens!

Jeg vil gerne lave sådan en lille app der kan klikke på knapper og lave muse kliks osv osv. i andre applikationer.

Såvidt jeg er nået frem til skal man have fat i noget af det gamle Win32 API for at kunne gøre det. Man kan anvende EnumChildWindows, FindWindowEx og sikkert en masse andre. Ud fra dem kan lave en GetClassName GetWindowText så man har noget data så man kan finde vinduet igen.

Men jeg har ikke kunne finde en måde at indentificere et vindue unikt? Sætter man to TextBokse ind på en dialog har de samme classname så hvordan identificere jeg et vindue unikt?
Avatar billede nubi19 Nybegynder
15. maj 2006 - 15:40 #1
Du får handles serveret bl.a. ved findwindow og getwindow. Følgende kode finder alle synlige vinduer:
IntPtr hWnd = GetTopWindow(null);
System.Text.StringBuilder str = new System.Text.StringBuilder(200);
while (!hWnd.Equals(IntPtr.Zero))
{
if (IsWindowVisible(hWnd))
{
GetWindowText(hWnd, str, 200);
MessageBox.Show(str.ToString());
}
hWnd = GetWindow(hWnd, GW_HWNDNEXT); // Finder næste sibling
}

Koden viser blot deres navne, men da du jo har hWnd som er handlet og 100 % unikt, så har du vel hvad du skal bruge.
GetWindow, GetTopWindow, IsWindowVisible og GetWindowText er alle win32 API kald og GW_HWNDNEXT er en konstant defineret bl.a. i winuser.h fra C++ (der fandt jeg den anyway ;P)
Avatar billede segato Nybegynder
15. maj 2006 - 16:00 #2
Problemmet stikker en anelse dybere, den del du har vist der har jeg helt på plads. Men du skal tænke på jeg skal kunne lukke applikationen ned og starte den op igen og så på ny finde vinduet. Det kan du jo ikke gøre ud fra en handle da et vindue får et nt handle hver gang en applikation startes op. Så det man kan finde et vindue ud fra er et ClassName og en Text. Men hvis du tager en alm. .net dialog og smider to tekstbokse på vil du opdage at de begge har samme classname og samme text hvis de begge er tomme. Så jeg skal ud over classname have et eller andet unikt som adskiller de to.
Avatar billede sovsekoder Nybegynder
15. maj 2006 - 22:46 #3
brug, WM_GETCONTROLNAME, og kald dit vindue (textbox eller den control du vil ha' fat i). Søg google på WM_GETCONTROLNAME for at få en beskrivelse af dens virkemåde.

kode eksempel på en der bruger WM_GETCONTROLNAME:
https://beta.blogs.msdn.com/brianmcm/default.aspx

hvis denne kode skal returnere noget du skal bruge skal du nok rette '0' til '\0' i ByteArrayToString metoden.

Så:
hvis du skal finde en bestemt kontrol bliver du nok nødt til at løbe alle child kontroller igennem på formen og matche alle kontroller op imod det navn du søger. Men den del har du vist tjek på.. ;)
Avatar billede sovsekoder Nybegynder
15. maj 2006 - 22:47 #4
WM_GETCONTROLNAME returnerer .NET-navnet på kontrollen.
Avatar billede sovsekoder Nybegynder
15. maj 2006 - 22:49 #5
fuld source code:
using System;
using System.Text;
using System.ComponentModel;
using Microsoft.Win32;
using System.Runtime.InteropServices;

namespace WindowsApplication1
{
    /// <summary>
    /// Summary description for NativeMethods.
    /// </summary>
    public class NativeMethods
    {

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle,
            uint dwProcessId);
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
            UIntPtr dwSize, uint flAllocationType, PageProtection flProtect);
        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [DllImport("kernel32.dll")]
        public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
            UIntPtr dwSize, uint dwFreeType);
        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll")]
        public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
            dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
            UIntPtr dwNumberOfBytesToMap);
        [DllImport("kernel32.dll")]
        public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateFileMapping(IntPtr hFile,
            IntPtr lpFileMappingAttributes, PageProtection flProtect, int dwMaximumSizeHigh,
            int dwMaximumSizeLow, string lpName);
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
            [Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static extern void MoveMemoryFromByte(IntPtr dest, ref byte src, int size);
        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static extern void MoveMemoryToByte(ref byte dest, IntPtr src, int size);
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int RegisterWindowMessage(string lpString);



        //=========== Win95/98/ME Shared memory staff===============
        public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        public const short SECTION_QUERY = 0x1;
        public const short SECTION_MAP_WRITE = 0x2;
        public const short SECTION_MAP_READ = 0x4;
        public const short SECTION_MAP_EXECUTE = 0x8;
        public const short SECTION_EXTEND_SIZE = 0x10;
        public const int SECTION_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE;
        public const int FILE_MAP_ALL_ACCESS = SECTION_ALL_ACCESS;

        //============NT Shared memory constant======================
        public const short PROCESS_VM_OPERATION = 0x8;
        public const short PROCESS_VM_READ = 0x10;
        public const short PROCESS_VM_WRITE = 0x20;
        public const long PROCESS_ALL_ACCESS = 0x1F0FFF;
        public const short MEM_COMMIT = 0x1000;
        public const short MEM_RESERVE = 0x2000;
        public const short MEM_DECOMMIT = 0x4000;
        public const int MEM_RELEASE = 0x8000;
        public const int MEM_FREE = 0x10000;
        public const int MEM_PRIVATE = 0x20000;
        public const int MEM_MAPPED = 0x40000;
        public const int MEM_TOP_DOWN = 0x100000;


        public const int INVALID_HANDLE_VALUE = -1;



    }

    [Flags]
    public enum PageProtection : uint
    {
        NoAccess = 0x01,
        Readonly = 0x02,
        ReadWrite = 0x04,
        WriteCopy = 0x08,
        Execute = 0x10,
        ExecuteRead = 0x20,
        ExecuteReadWrite = 0x40,
        ExecuteWriteCopy = 0x80,
        Guard = 0x100,
        NoCache = 0x200,
        WriteCombine = 0x400,
    }

    /// <summary>
    /// Summary description for WinFormsUtilities.
    /// </summary>
    public class WinFormsUtilities
    {
        private static int GetControlNameMessage = 0;

        static WinFormsUtilities()
        {
            GetControlNameMessage = NativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME");
        }

        public static string GetWinFormsId(IntPtr hWnd)
        {
            return XProcGetControlName(hWnd, GetControlNameMessage);
        }

        protected static string XProcGetControlName(IntPtr hwnd, int msg)
        {
            //define the buffer that will eventually contain the desired window's WinFormsId

            byte[] bytearray = new byte[65535];

            //allocate space in the target process for the buffer as shared memory
            IntPtr bufferMem = IntPtr.Zero; //base address of the allocated region for the buffer
            IntPtr written = IntPtr.Zero;  //number of bytes written to memory
            IntPtr retHandle = IntPtr.Zero;
            bool retVal;


            //creating and reading from a shared memory region is done differently in Win9x then in newer OSs
            IntPtr processHandle = IntPtr.Zero;
            IntPtr fileHandle = IntPtr.Zero;

            if (!(Environment.OSVersion.Platform == PlatformID.Win32Windows))
            {
                try
                {
                    uint size; //the amount of memory to be allocated
                    size = 65536;

                    processHandle = NativeMethods.OpenProcess(NativeMethods.PROCESS_VM_OPERATION | NativeMethods.PROCESS_VM_READ | NativeMethods.PROCESS_VM_WRITE, false, GetProcessIdFromHWnd(hwnd));

                    if (processHandle.ToInt64() == 0)
                    {
                        throw new Win32Exception();
                    }

                    bufferMem = NativeMethods.VirtualAllocEx(processHandle, IntPtr.Zero, new UIntPtr(size), NativeMethods.MEM_RESERVE | NativeMethods.MEM_COMMIT, PageProtection.ReadWrite);

                    if (bufferMem.ToInt64() == 0)
                    {
                        throw new Win32Exception();
                    }

                    //send message to the control's hWnd for getting the specified control name
                    retHandle = NativeMethods.SendMessage(hwnd, msg, new IntPtr(size), bufferMem);

                    //now read the TVITEM's info from the shared memory location
                    retVal = NativeMethods.ReadProcessMemory(processHandle, bufferMem, bytearray, new UIntPtr(size), written);
                    if (!retVal)
                    {
                        throw new Win32Exception();
                    }
                }
                finally
                {
                    //free the memory that was allocated
                    retVal = NativeMethods.VirtualFreeEx(processHandle, bufferMem, new UIntPtr(0), NativeMethods.MEM_RELEASE);
                    if (!retVal)
                    {
                        throw new Win32Exception();
                    }
                    NativeMethods.CloseHandle(processHandle);
                }
            }
            else
            {
                try
                {
                    int size2; //the amount of memory to be allocated
                    size2 = 65536;

                    fileHandle = NativeMethods.CreateFileMapping(new IntPtr(NativeMethods.INVALID_HANDLE_VALUE), IntPtr.Zero, PageProtection.ReadWrite, 0, size2, null);
                    if (fileHandle.ToInt64() == 0)
                    {
                        throw new Win32Exception();
                    }
                    bufferMem = NativeMethods.MapViewOfFile(fileHandle, NativeMethods.FILE_MAP_ALL_ACCESS, 0, 0, new UIntPtr(0));
                    if (bufferMem.ToInt64() == 0)
                    {
                        throw new Win32Exception();
                    }
                    NativeMethods.MoveMemoryFromByte(bufferMem, ref bytearray[0], size2);

                    retHandle = NativeMethods.SendMessage(hwnd, msg, new IntPtr(size2), bufferMem);

                    //read the control's name from the specific shared memory for the buffer
                    NativeMethods.MoveMemoryToByte(ref bytearray[0], bufferMem, 1024);

                }
                finally
                {
                    //unmap and close the file
                    NativeMethods.UnmapViewOfFile(bufferMem);
                    NativeMethods.CloseHandle(fileHandle);
                }
            }

            //get the string value for the Control name
            return ByteArrayToString(bytearray);

        }

        private static uint GetProcessIdFromHWnd(IntPtr hwnd)
        {
            uint pid;

            NativeMethods.GetWindowThreadProcessId(hwnd, out pid);

            return pid;
        }

        private static string ByteArrayToString(byte[] bytes)
        {
            if (Environment.OSVersion.Platform == PlatformID.Win32Windows)
            {
                // Use the Ansii encoder
                return Encoding.Default.GetString(bytes).TrimEnd('\0');
            }
            else
            {
                // use Unicode
                return Encoding.Unicode.GetString(bytes).TrimEnd('\0');
            }
        }
    }
}
Avatar billede sovsekoder Nybegynder
15. maj 2006 - 22:49 #6
eksempel på kald:
private void button1_Click(object sender, EventArgs e)
        {
            IntPtr hWnd = new IntPtr(0x0001080A);
            string name = WinFormsUtilities.GetWinFormsId(hWnd);
            int k = 8;

        }
Avatar billede segato Nybegynder
16. maj 2006 - 09:16 #7
Du er en skat mester. Var på ved ud i noget EnumChildWindows og så finde ud af hvis der var flere ved samme navn ville jeg bruge index på kontrolen. Men den der løsning er bedre da den tager mindre cpu. Smid et svar.
Avatar billede sovsekoder Nybegynder
16. maj 2006 - 22:31 #8
svar :-)
Avatar billede sovsekoder Nybegynder
16. maj 2006 - 22:32 #9
og nu et rigtigt svar :o
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester