Open Side Menu Go to the Top
Register
C# Code for PokerStars Data Mining C# Code for PokerStars Data Mining

11-28-2008 , 05:54 PM
Hello,

After two years of offering free results statistics of PokerStar's Sit&Gos, I have decided to bring StarTracker to an end so that I can pursue other interests. Below is a part of the code I used to capture the results. It is in C# and looks inside the pokerstars executable image to find names of players. It uses C# wrappers to MSC functions which allow programs to access the memory used by another process. You will need to be familiar with C# inorder to make any use of this.

The code below corrects player name spelling by finding the list of players in the pokerstars process which matches at least 1/3 of the names. The original list was created by scraping the tourney lobby and using Optical Character Recognition. The OCR code had problems with the non-English characters so I corrected them by looking in the PokerStars memory. I couldn't just look in the memory because the tournament number and the buyin/type/size info is not near the names (atleast I couldn't find it using ollydbg.)

StarTracker is on the approved list for PokerStars because we honored the rules PokerStars layed out for player databases. Specifically, we did not report bankroll amounts/ROI and we allowed players to block their stats.

Here is the class to read from PokerStars memory...
Code:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Diagnostics;

namespace ProcessMemoryReaderLib
{
    /// <summary>
    /// ProcessMemoryReader is a class that enables direct reading a process memory
    /// </summary>
    class ProcessMemoryReaderApi
    {
        // constants information can be found in <winnt.h>
        [Flags]
        public enum ProcessAccessType
        {
            PROCESS_TERMINATE = (0x0001),
            PROCESS_CREATE_THREAD = (0x0002),
            PROCESS_SET_SESSIONID = (0x0004),
            PROCESS_VM_OPERATION = (0x0008),
            PROCESS_VM_READ = (0x0010),
            PROCESS_VM_WRITE = (0x0020),
            PROCESS_DUP_HANDLE = (0x0040),
            PROCESS_CREATE_PROCESS = (0x0080),
            PROCESS_SET_QUOTA = (0x0100),
            PROCESS_SET_INFORMATION = (0x0200),
            PROCESS_QUERY_INFORMATION = (0x0400),
            PROCESS_ALL_ACCESS = (0x1F0FFF)
        }

        // function declarations are found in the MSDN and in <winbase.h> 
        [DllImport("kernel32.dll")]
        public static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);

        [StructLayout(LayoutKind.Sequential)]
        public struct SYSTEM_INFO
        {
            internal _PROCESSOR_INFO_UNION uProcessorInfo;
            public uint dwPageSize;
            public uint lpMinimumApplicationAddress;
            public uint lpMaximumApplicationAddress;
            public uint dwActiveProcessorMask;
            public uint dwNumberOfProcessors;
            public uint dwProcessorType;
            public uint dwAllocationGranularity;
            public uint dwProcessorLevel;
            public uint dwProcessorRevision;
        }
        [StructLayout(LayoutKind.Explicit)]
        public struct _PROCESSOR_INFO_UNION
        {
            [FieldOffset(0)]
            internal uint dwOemId;
            [FieldOffset(0)]
            internal ushort wProcessorArchitecture;
            [FieldOffset(2)]
            internal ushort wReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MEMORY_BASIC_INFORMATION
        {
            internal uint BaseAddress;
            internal uint AllocationBase;
            internal uint AllocationProtect;
            internal uint RegionSize;
            internal uint State;
            internal uint Protect;
            internal uint Type;
        }
        [DllImport("kernel32.dll")]
        public static extern uint VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress,
           out MEMORY_BASIC_INFORMATION lpBuffer, UIntPtr dwLength);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
           UIntPtr dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        public static unsafe extern bool VirtualFreeEx(
           IntPtr hProcess, byte* pAddress,
           UIntPtr size, AllocationType freeType);

        [Flags]
        public enum AllocationType
        {
            Commit = 0x1000,
            Reserve = 0x2000,
            Decommit = 0x4000,
            Release = 0x8000,
            Reset = 0x80000,
            Physical = 0x400000,
            TopDown = 0x100000
        }

        //		HANDLE OpenProcess(
        //			DWORD dwDesiredAccess,  // access flag
        //			BOOL bInheritHandle,    // handle inheritance option
        //			DWORD dwProcessId       // process identifier
        //			);
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

        //		BOOL CloseHandle(
        //			HANDLE hObject   // handle to object
        //			);
        [DllImport("kernel32.dll")]
        public static extern Int32 CloseHandle(IntPtr hObject);

        //		BOOL ReadProcessMemory(
        //			HANDLE hProcess,              // handle to the process
        //			LPCVOID lpBaseAddress,        // base of memory area
        //			LPVOID lpBuffer,              // data buffer
        //			SIZE_T nSize,                 // number of bytes to read
        //			SIZE_T * lpNumberOfBytesRead  // number of bytes read
        //			);
        [DllImport("kernel32.dll")]
        public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);

        //		BOOL WriteProcessMemory(
        //			HANDLE hProcess,                // handle to process
        //			LPVOID lpBaseAddress,           // base of memory area
        //			LPCVOID lpBuffer,               // data buffer
        //			SIZE_T nSize,                   // count of bytes to write
        //			SIZE_T * lpNumberOfBytesWritten // count of bytes written
        //			);
        [DllImport("kernel32.dll")]
        public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesWritten);


    }

    class AdvAPI
    {
        public static bool MakeProcessDebuggable(IntPtr process)
        {
            IntPtr hToken;

            TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES();
            TOKEN_PRIVILEGES oldtp = new TOKEN_PRIVILEGES();
            LUID luid;
          
            if (!AdvAPI.OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
            {
                return false;
            }

            if (!LookupPrivilegeValue("", "SeDebugPrivilege", out luid))
            {
                ProcessMemoryReaderApi.CloseHandle(hToken);
                return false;
            }
      
            tp.PrivilegeCount = 1;
            tp.Luid = luid;
            tp.Attributes = 0x2;

            /* Adjust Token Privileges */
            UInt32 dwSize;
            if (!AdvAPI.AdjustTokenPrivileges(hToken, false, ref tp, 100, ref oldtp, out dwSize))
            {
                ProcessMemoryReaderApi.CloseHandle(hToken);
                return false;
            }
            // close handles
            ProcessMemoryReaderApi.CloseHandle(hToken);
            return true;
        }

        //struct TOKEN_PRIVILEGES
        //{
        //    public int PrivilegeCount;
        //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        //    public LUID_AND_ATTRIBUTES[] Privileges;
        //}
        //[StructLayout(LayoutKind.Sequential)]
        //struct LUID_AND_ATTRIBUTES
        //{
        //    public LUID Luid;
        //    public int Attributes;
        //}
        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_PRIVILEGES
        {
            public int PrivilegeCount;
            public LUID Luid;
            public int Attributes;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct LUID
        {
            public uint LowPart;
            public uint HighPart;
        }
        [DllImport("advapi32.dll", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
           [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
           ref TOKEN_PRIVILEGES NewState,
           UInt32 BufferLength,
           ref TOKEN_PRIVILEGES PreviousState,
           out UInt32 ReturnLength);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool LookupPrivilegeValue(string lpSystemName, string lpName,
           out LUID lpLuid);

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
        private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
        private static uint STANDARD_RIGHTS_READ = 0x00020000;
        private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;
        private static uint TOKEN_DUPLICATE = 0x0002;
        private static uint TOKEN_IMPERSONATE = 0x0004;
        private static uint TOKEN_QUERY = 0x0008;
        private static uint TOKEN_QUERY_SOURCE = 0x0010;
        private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
        private static uint TOKEN_ADJUST_GROUPS = 0x0040;
        private static uint TOKEN_ADJUST_DEFAULT = 0x0080;
        private static uint TOKEN_ADJUST_SESSIONID = 0x0100;
        private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
        private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
            TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
            TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
            TOKEN_ADJUST_SESSIONID);
    }
    public class ProcessMemoryReader
    {

        public ProcessMemoryReader()
        {
        }

        /// <summary>	
        /// Process from which to read		
        /// </summary>
        public Process ReadProcess
        {
            get
            {
                return m_ReadProcess;
            }
            set
            {
                m_ReadProcess = value;
            }
        }

        private Process m_ReadProcess = null;

        private IntPtr m_hProcess = IntPtr.Zero;

        public void OpenProcess()
        {
            //			m_hProcess = ProcessMemoryReaderApi.OpenProcess(ProcessMemoryReaderApi.PROCESS_VM_READ, 1, (uint)m_ReadProcess.Id);
            ProcessMemoryReaderApi.ProcessAccessType access;
            //access = ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_READ
            //    | ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_WRITE
            //    | ProcessMemoryReaderApi.ProcessAccessType.PROCESS_VM_OPERATION;
            access = ProcessMemoryReaderApi.ProcessAccessType.PROCESS_ALL_ACCESS;
            AdvAPI.MakeProcessDebuggable(m_ReadProcess.Handle);
            m_hProcess = ProcessMemoryReaderApi.OpenProcess((uint)access, 1, (uint)m_ReadProcess.Id);
        }

        public void CloseHandle()
        {
            int iRetValue;
            iRetValue = ProcessMemoryReaderApi.CloseHandle(m_hProcess);
            if (iRetValue == 0)
                throw new Exception("CloseHandle failed");
        }

        public byte[] ReadProcessMemory(IntPtr MemoryAddress, uint bytesToRead, out int bytesRead)
        {
            byte[] buffer = new byte[bytesToRead];

            IntPtr ptrBytesRead;
            ProcessMemoryReaderApi.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, bytesToRead, out ptrBytesRead);

            bytesRead = ptrBytesRead.ToInt32();

            return buffer;
        }

        public void WriteProcessMemory(IntPtr MemoryAddress, byte[] bytesToWrite, out int bytesWritten)
        {
            IntPtr ptrBytesWritten;
            ProcessMemoryReaderApi.WriteProcessMemory(m_hProcess, MemoryAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);

            bytesWritten = ptrBytesWritten.ToInt32();
        }
    }
}
Here is the code which fixes a List of names and finish positions (fix_scanned_names) by looking into the actual PokerStars process. It basically finds an array of a structure in PokerStars memory which holds the name, location and finish position for each player in the tourney. This array is filled in when the tourney lobby is shown in PokerStars client.

Code:
        static int BYTESBEFORENAME = 30; 
        static int BYTESPASTLOCATION = 23;
        static ProcessMemoryReaderApi.MEMORY_BASIC_INFORMATION mbi = new ProcessMemoryReaderApi.MEMORY_BASIC_INFORMATION();
        static unsafe byte* lpMem = null;
      
        private bool get_name_from_mem(byte[] buffer, int buffsize, int t, out string name, out int pos)
        {
            name = "";
            pos = -1;
            if (buffer[t] != 0x00)
            {
                return false;
            }
            //byte[] temp_buffer = new byte[100]; int ik = 0;
            //for (int ij = t; ij < t + BYTESPASTLOCATION + 30 + BYTESBEFORENAME; ij++)
            //{
            //    temp_buffer[ik++] = buffer[ij];
            //}  
            int i = t + BYTESBEFORENAME;

            if ((i > buffsize) || (buffer[i - 5] != 0xFF))
            {
                return false;
            }
            StringBuilder sb2 = new StringBuilder();
            while ((i < buffsize - 1) && (buffer[i] != 0x00))
            {
                sb2 = sb2.Append((char)buffer[i++]);
            }
            name = sb2.ToString();

            // Skip over location
            i++;
            while ((i < buffsize - 1) && (buffer[i] != 0x00))
            {
                i++;
            }
            i++;
            if (i >= buffsize) return false;
            pos = (int)buffer[i + 3];
 
            return true;
        }
        private bool next_player(byte[] buffptr, int buffsize, ref int t)
        {
            int i = t + BYTESBEFORENAME;

            while ((i < buffsize) && (buffptr[i] != 0x00))
            {
                i++;
            }
            i++;
            while ((i < buffsize) && (buffptr[i] != 0x00))
            {
                i++;
            }
            i++;
            i += BYTESPASTLOCATION;
  
            t = i;
            return true;
        }
        private bool previous_player(byte[] buffptr, int buffsize, ref int t)
        {
            int previoust = t;
            int i = t;  
            
            if ((i < 6) || (buffptr[i-6] != 0xFF)) return false;

            i -= BYTESPASTLOCATION; // back up to \0 after location.

            i--; // before \0
            while ((i >= 0) && (buffptr[i] != 0x00))
            {
                i--;
            }
            i--; // before \0
            while ((i >= 5) && (buffptr[i-5] != 0xFF))
            {
                i--;
            }
            
            i -= BYTESBEFORENAME;
            if ((i < 0) || (buffptr[i] != 0x00)) return false;
            t = i;

            //byte[] temp_buffer = new byte[previoust-t+1]; int ik = 0;
            //for (int ij = t; ij <= previoust; ij++)
            //{
            //    temp_buffer[ik++] = buffptr[ij];
            //}  
            return true;
        }
        private int find_start_name_range(string scanned, ref byte[] buffptr, ref int buffsize, ref int i)
        {
            char[] sbuff = scanned.Trim(new char[] { '_' }).ToCharArray();
            int sbuffsize = sbuff.GetLength(0);
            bool found = false;
            int found_at = -1;

            try
            { 
                while ((i < buffsize) && (!found))
                {
                    int j = 0;

                    int k = i;

                    while ((j < sbuffsize) && (k < buffsize) && (!found))
                    {
                        // Skip the wildcards.
                        //while ((j < sbuffsize) && (sbuff[j] == '_'))
                        //{
                        //    j++;
                        //}
                        if (j < sbuffsize)
                        {
                            if (sbuff[j] == (char)buffptr[k])
                            {
                                j++;
                                k++;
                                if (j == sbuffsize)
                                {
                                    found = true;
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    if (found)
                    {
                        int namestart = i - BYTESBEFORENAME;
                        while (previous_player(buffptr, buffsize, ref namestart))
                        {
  //                          System.Console.WriteLine(namestart);
                        }
                        i += 50;
                        found_at = namestart;
                        break;
                    }
                    else
                    {
                        i++;
                    }
                }
            }
            catch (Exception ie)
            {
                MyWebMethods.MessageLog("Exception: " + ie.Message);
            }
            return found_at;
        }
        // Get names in memory location
        private int get_memory_names(byte[] buffptr, int buffsize, int start_of_names, ref List<string> memory_names, ref List<int> position)
        {
            int pos = -1;
            string name = "";
            int last_start = start_of_names;
       
            while (get_name_from_mem(buffptr, buffsize, last_start, out name, out pos))
            {
                memory_names.Add(name);
                position.Add(pos); 
                next_player(buffptr, buffsize, ref last_start);
            }
            return memory_names.Count;
        }

        private unsafe bool fix_names_in_mbi( ProcessMemoryReaderApi.MEMORY_BASIC_INFORMATION mbi, 
                                       byte* lpMem,
                                       ProcessMemoryReaderLib.ProcessMemoryReader pReader,
                                       string scanned, 
                                       ref List<string> scanned_list, 
                                       ref List<int> finish_pos )
        {
            bool foundit = false;
            System.DateTime dt = System.DateTime.Now;
            int start_of_names = -1;

            int buffsize = 0;
            byte[] buffptr = pReader.ReadProcessMemory((IntPtr)(void*)lpMem, mbi.RegionSize, out buffsize);
            int i = 0;
            while ((i < buffsize) && (!foundit))
            {
                // If the name is found, save the buffer address so we don't 
                // have to look for the area again.
                start_of_names = find_start_name_range(scanned, ref buffptr, ref buffsize, ref i);
 
                if ((start_of_names >= 0))
                {
                    // MyWebMethods.MessageLog("find_start end " + System.DateTime.Now.ToString());                   
                    List<string> memory_names = new List<string>();
                    List<int> mem_finish_pos = new List<int>();
                    // MyWebMethods.MessageLog("get_memory_names beg " + System.DateTime.Now.ToString());
                    int n_mem = get_memory_names(buffptr, buffsize, start_of_names, ref memory_names, ref mem_finish_pos);
                    // MyWebMethods.MessageLog("get_memeory_names end " + System.DateTime.Now.ToString());
                    if (n_mem == scanned_list.Count)
                    {
                        // See how many match. 
                        int[] match = new int[scanned_list.Count];
                        for (int ii = 0; ii < scanned_list.Count; ii++)
                        {
                            match[ii] = memory_names.IndexOf(scanned_list[ii]);
                        }
                        // MyWebMethods.MessageLog("indexes found " + System.DateTime.Now.ToString());
                        int nmatch = 0;
                        for (int ii = 0; ii < scanned_list.Count; ii++)
                        {
                            if (match[ii] >= 0) nmatch++;
                        }

                        // MyWebMethods.MessageLog("matches = " + nmatch + "/" + scanned_list.Count);
                        if (nmatch == scanned_list.Count)
                        {
                            // All matched.
                            // System.Console.WriteLine("OK...mbi = " + mbi.Protect + " " + mbi.State.ToString() + " " + mbi.Type.ToString() + " " + mbi.BaseAddress + ":" + mbi.RegionSize);
                            foundit = true;
                            break;
                        }
                        else if ((float)nmatch / (float)scanned_list.Count > 0.33)
                        {
                            // We have the block of memory with some which don't match.
                            if (memory_names.Count == scanned_list.Count)
                            {
                                for (int ii = 0; ii < mem_finish_pos.Count; ii++)
                                {
                                    if (scanned_list.IndexOf(memory_names[ii]) >= 0)
                                    {
                                        if (mem_finish_pos[ii] != (scanned_list.IndexOf(memory_names[ii]) + 1))
                                        {
                                            mem_finish_pos[ii] = (scanned_list.IndexOf(memory_names[ii]) + 1);
                                        }
                                    }
                                    if (mem_finish_pos[ii] > mem_finish_pos.Count)
                                    {
                                        // MyWebMethods.MessageLog("Error: Bad position in memory.");
                                        foundit = false;
                                        break;
                                    }
                                }
                                scanned_list.Clear();
                                finish_pos.Clear();
                                // MyWebMethods.MessageLog("fixed " + memory_names[0] + " : " + start_of_names.ToString());
                                for (int ii = 0; ii < memory_names.Count; ii++)
                                {
                                    scanned_list.Add(memory_names[ii]);
                                    finish_pos.Add(mem_finish_pos[ii]);
                                }

                                // This one is used.
                                // founded.Add(start_of_names);

                                // MyWebMethods.MessageLog("added " + System.DateTime.Now.ToString());
                                // System.Console.WriteLine("Fixed..mbi = " + mbi.Protect + " " + mbi.State.ToString() + " " + mbi.Type.ToString() + " " + mbi.BaseAddress + ":" + mbi.RegionSize);

                                foundit = true;
                                break;
                            }
                            else
                            {
                               // MyWebMethods.MessageLog("Number of players didn't match:" + memory_names.Count.ToString() + "(mem) != " + scanned_list.Count.ToString() + "(scan)");
                            }
                        }
                        else
                        {
                          //  MyWebMethods.MessageLog("Not 33 percent in this: " + nmatch + " matched of " + scanned_list.Count.ToString());
                        }
                    }
                    else
                    {
                      //  MyWebMethods.MessageLog("n_mem (" + n_mem + ") != scanned (" + scanned_list.Count.ToString() + ")");
                    }
                }
                else
                {
                   // MyWebMethods.MessageLog("Not found..mbi = " + mbi.Protect + " " + mbi.State.ToString() + " " + mbi.Type.ToString());
                }           
            }
            return foundit;
        }
    
        // Correct scanned names list based on actual memory contents. 
        private bool fix_scanned_names(ref List<string> scanned_list, ref List<int> finish_pos )
        {
            bool foundit = false;

            // Find a buffer with the name in it.
            Process[] pArray = Process.GetProcessesByName("pokerstars");
            if (pArray.Length == 0)
            {
                return false;
            }

            ProcessMemoryReaderApi.SYSTEM_INFO pSI = new ProcessMemoryReaderApi.SYSTEM_INFO();
            ProcessMemoryReaderApi.GetSystemInfo(ref pSI);

            // Create memory reader
            ProcessMemoryReaderLib.ProcessMemoryReader pReader =
              new ProcessMemoryReaderLib.ProcessMemoryReader();

            // Take the first process found
            pReader.ReadProcess = pArray[0];
            pArray[0].PriorityClass = ProcessPriorityClass.High;

            pReader.OpenProcess();
            unsafe
            {

                // If we already have some 
                if (lpMem != null)
                {
                    // MyWebMethods.MessageLog("n_look loop start " + System.DateTime.Now.ToString());
                    int nlook = 4; if (scanned_list.Count < nlook) nlook = scanned_list.Count;

                    for (int i = 0; i < nlook; i++)
                    {
                        if (fix_names_in_mbi(mbi, lpMem, pReader, scanned_list[i], ref scanned_list, ref finish_pos))
                        {
                            foundit = true;
                            break;
                        }
                    }
                }
                // MyWebMethods.MessageLog("n_look loop " + foundit.ToString() + " " + System.DateTime.Now.ToString());
                if (foundit) return foundit;

                int max_look = 5; int lookat = 0;

                // couldn't find it in last mbi, look in all mbi's
                // MyWebMethods.MessageLog("scanned loop  " + System.DateTime.Now.ToString());

                foreach (string scanned in scanned_list)
                {
                    if (lookat >= max_look) break;
                    lookat++;

                    lpMem = null;
                
                    while (lpMem < (byte*)(void*)pSI.lpMaximumApplicationAddress)
                    {
                        ProcessMemoryReaderApi.VirtualQueryEx(pArray[0].Handle,
                                        (IntPtr)(void*)lpMem,
                                        out mbi,
                                        (System.UIntPtr)sizeof(ProcessMemoryReaderApi.MEMORY_BASIC_INFORMATION));
                        if ((mbi.Protect == 4) && (mbi.State == 4096) && (mbi.Type == 131072))
                        {
                            if (fix_names_in_mbi(mbi, lpMem, pReader, scanned, ref scanned_list, ref finish_pos))
                            {
                                foundit = true;
                                break;
                            }
                        }

                        /* increment lpMem to next region of memory */
                        lpMem = (byte*)(void*)mbi.BaseAddress;
                        lpMem += mbi.RegionSize;
                    }
                    if (foundit) break;
               }
               // MyWebMethods.MessageLog("scanned loop  end " + foundit.ToString() + " " + System.DateTime.Now.ToString());

            }
            return foundit;
        }
Hopefully someone else can use the information here to write their own add-on for PokerStars.

Regards,

-Bill
C# Code for PokerStars Data Mining Quote
11-28-2008 , 06:25 PM
Thank you for sharing, I might use this
C# Code for PokerStars Data Mining Quote
11-28-2008 , 10:57 PM
Pretty sure this is going to get locked up.
C# Code for PokerStars Data Mining Quote
11-29-2008 , 10:27 AM
Quote:
Pretty sure this is going to get locked up.
There is nothing in that code which violates any security holes in PokerStars. The hole card info on other players is never in the stars client until showdown. IP addesses of other players does not exist either.

The PokerStars client is very tight and any info you get from the client is
probably OK with PokerStars as they have done lots to encrypt things they don't want you to see.

The key was using ollydbg to browse through the memory. I then used the debugging hooks in MSC (via pinvoke wrappers) to get to it in my own C# app.

Yes, they do have policies against data mining, and you may have to deal with that, however, not enough people know how to carry this through to a real datamining app to cause any burden on their system. Also, all data captures were done without logging into PokerStars.
C# Code for PokerStars Data Mining Quote
11-29-2008 , 08:26 PM
Quote:
Originally Posted by StarTracker
... Also, all data captures were done without logging into PokerStars.
This is the part that surprises me. Stars makes an effort to make the dealer text hard (but not impossible) to capture, but then makes it possible to mine the HH (and data mine) without even logging in. So the opportunity to know which accounts are doing the industrial strength data mining is lost.

Hint:
They are the ones that are observing 24/7 without ever playing.
C# Code for PokerStars Data Mining Quote
11-29-2008 , 11:26 PM
Even if it does get locked, Thank you for posting this. As a fellow programmer I enjoy seeing how others code. I'm just starting to learn C# and I've already learned some scrolling through your code.
C# Code for PokerStars Data Mining Quote
11-29-2008 , 11:50 PM
Quote:
Originally Posted by Amerzel
Even if it does get locked, Thank you for posting this. As a fellow programmer I enjoy seeing how others code. I'm just starting to learn C# and I've already learned some scrolling through your code.

link for you
C# Code for PokerStars Data Mining Quote
11-30-2008 , 12:35 AM
I'm guessing you are trying some sarcastic level and not actually trying to be helpful or productive?

The website you linked is focused on C and C++ and has almost nothing to do with C#.

I was really just trying to thank the poster for posting his code.
C# Code for PokerStars Data Mining Quote
11-30-2008 , 12:41 AM
there is no reason 2p2 (thereby me) should lock this. we are not imo here to enforce sites T&C. We are here to increase collective knowledge. It does not stop me from saying a warning to users however, that using such may well put you in breach of T&C therefore forfeiting your account and other such dangers.

PokerStars datamining status quo is untenable and the sooner they do *something* about it the better, be that allow datamining or make it more complicated than windows notepad + copy/paste.

If PokerStars do not want this executed, they should make it clear to all and if so desired punish those using it - or alter their client so it is no longer effective.

information wants to be free etc. and should be. ty for sharing

Last edited by _dave_; 12-01-2008 at 06:55 PM.
C# Code for PokerStars Data Mining Quote
12-01-2008 , 12:44 PM
Thanks _dave_!

Someone asked for this in a PM so I thought I would post it here.

Once you can see into PokerStars memory, you can see the observed hand histories if you have "Save My Hand History" checked in the Hand History Options dialog (you still don't need to be logged in for this).

I wrote a program which saves the hand histories of an observed player to a disk file for future use. The PokerStars client will only save "My" hand histories so I wrote this...

http://arcatum.net/startracker/Software/NotMyHand2.zip

Download and unzip the file and then open the NotMyHand.sln solution file.
You can get a free version of C# Visual Studio Express by googling it.

Build the solution and run it. You will see the following dialog come up.



Fill in the name of a player at a table you are observing and press "Start". It will start saving the hand history in a sub-folder with the name of the player.

You can see in the code how it searches for hands in the PokerStars memory and saves them. A good enhancement would be to modify it to save all hands observed instead of just those played by the named player.

Enjoy,

-Bill
C# Code for PokerStars Data Mining Quote
12-01-2008 , 01:42 PM
Excellent work! Thank you!
C# Code for PokerStars Data Mining Quote
12-01-2008 , 06:34 PM
Thanks StarTracker.

Has anyone managed to get this working (except you StarTracker, you don't count ?

The loop
while (lpMem < (byte*)(void*)pSI.lpMaximumApplicationAddress)
{..}

never gets entered for me.

My process:
1) Open stars
2) Open a SnG table (also tried cash table & MTT table)
3) Ran NotMyHand
4) Entered username of player at the table
5) Pressed Start
6) Posted on 2+2


This is for Tournies only, right? SnG and MTTs?
Would x64 XP cause any problems?

Thanks again StarTracker, good fun to play with!
C# Code for PokerStars Data Mining Quote
12-01-2008 , 07:10 PM
I got it working on a 32 bit machine. pSI.lpMaximumApplicationAddress always evaluated to 0 on the x64 computer. Anyone know what's up there?
C# Code for PokerStars Data Mining Quote
12-01-2008 , 09:49 PM
David B pointed me to the bug, I think.

The SYSTEM_INFO structure (which you see defined as uint in the first window of code above) is not correct...

http://www.pinvoke.net/default.aspx/....GetSystemInfo

After looking at that page...it should be...

....
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
....

I couldn't test it, but it is worth a try.
C# Code for PokerStars Data Mining Quote
12-01-2008 , 10:45 PM
I think some of this code about PokerStars could also be found at codingthewheel. StartTracker, did you or some other people took the time to look into other clients' memory to see what we could find?

By the way I think the best way to work with native calls to the win32 API is to create a C++ managed DLL that you later uses in a C# project (kinda like a facade design pattern)
All those nasty DLLIMPORT and C# versions of win32 structures => gone.
C# Code for PokerStars Data Mining Quote
12-01-2008 , 11:30 PM
I haven't looked into any other poker clients. Only PokerStars. Ollydbg is really good for that. Use the memory search feature. Search on names is fruitful as they are very unique.

The C++ managed DLL is the professional way to go as it is very robust, however when you are hacking around the stuff at pinvoke.net is very easy to roll in for a quick test.

I also used the DLLIMPORT for sending button clicks and scraping the 'Stars graphics in my results data miner.
C# Code for PokerStars Data Mining Quote
12-08-2008 , 02:31 AM
Just in case anyone is confused by this situation. Using programs like Ollydbg against the Poker stars client is also against their ToS. Clearly if you are writing a data mining app then you don't care about Stars ToS. However I figured some people may want to just poke around for fun and not realize they are in fact breaking the rules.

From the Stars ToS:

Quote:
5.1. SOFTWARE MODIFICATIONS. User may not attempt to modify, decompile, reverse-engineer or disassemble the Software in any way.
C# Code for PokerStars Data Mining Quote
07-11-2015 , 05:23 PM
Hello i know this post is very old, but i need to contact StarTracker because of a personall project with Pokerstars and this dude knows a lot about low level programming. Anyone can help me?
C# Code for PokerStars Data Mining Quote
07-12-2015 , 04:03 AM
Quote:
Pretty sure this is going to get locked up.
Hopefully not. It's one way to force them to do something about it, specifically about this:

Quote:
Also, all data captures were done without logging into PokerStars.
OP, really great move to show that code, thanks!
Can I ask where did you learn to do such things? Scanning the process during execution to look for things sounds like black magic to me

Last edited by punter11235; 07-12-2015 at 04:09 AM.
C# Code for PokerStars Data Mining Quote
07-14-2015 , 01:11 PM
Quote:
Originally Posted by punter11235
Hopefully not. It's one way to force them to do something about it, specifically about this:



OP, really great move to show that code, thanks!
Can I ask where did you learn to do such things? Scanning the process during execution to look for things sounds like black magic to me
I can't speak for the OP, but I wrote some programs years ago(stopped soon after black Friday) that pulled data from poker clients in C#. My reasoning approaching it in a similar fashion is that I much prefer managed code and C# over doing all of this in C++. In .NET, as the OP mentions, you can hook into the unmanaged system DLL's to get information and then do what you need to in managed code. You can use these unmanaged hooks to certain pieces of information about the windows and elements on the screen. Other pieces you will need to use OCR, which isn't 100% accurate.

Learning how to do this, for me at least, came through my existing knowledge of some of this, lots of research and experimentation. A tool I used a lot is Microsoft Spy++, if you have Visual Studio installed then this is also installed, it provides lots of information about windows and applications on your machine and can help point you in the direction of what you need to hook into.

Also check out http://pinvoke.net/ as the op mentioned, it provides a lot of information on how to use these unmanaged hooks.

I still have some source code laying around so if you need some help, samples or have questions let me know, I would be willing to share most of it.
C# Code for PokerStars Data Mining Quote
05-04-2016 , 05:20 AM
Quote:
Originally Posted by rubixxcube
I still have some source code laying around so if you need some help, samples or have questions let me know, I would be willing to share most of it.
Hello!
Is it still available to contact you with questions on codind of PS add-on software? I'm trying to create my own stacks>>bb display converter but cant fully understand the hooks technology.. (or maybe there is another method for my task?) It would be very nice if you and your code can guide me to the right direction..
C# Code for PokerStars Data Mining Quote
05-04-2016 , 05:41 PM
Quote:
Originally Posted by tsrp
Hello!
Is it still available to contact you with questions on codind of PS add-on software? I'm trying to create my own stacks>>bb display converter but cant fully understand the hooks technology.. (or maybe there is another method for my task?) It would be very nice if you and your code can guide me to the right direction..
I'll see what i can dig up this weekend. I actually had another poster contact me a while back over PM and I was lazy and never got around to it, i will package some stuff up for you and him in the next few days and help as best i can. Keep in mind the code i wrote is several years old so some of it may not work still if they have changed their client much.
C# Code for PokerStars Data Mining Quote
05-05-2016 , 12:16 PM
Quote:
Originally Posted by rubixxcube
I'll see what i can dig up this weekend. I actually had another poster contact me a while back over PM and I was lazy and never got around to it, i will package some stuff up for you and him in the next few days and help as best i can. Keep in mind the code i wrote is several years old so some of it may not work still if they have changed their client much.
Aw, that will be very kind of you! Waiting for the package!
C# Code for PokerStars Data Mining Quote
05-07-2016 , 05:22 PM
Below is a Windows utility class i wrote and used that put a C# wrapper around many native window functions, getting open windows, moving tables around to get information i needed about clients, windows, open tables etc...

Code:
namespace Windows.Utility
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;
    public class WindowFunctions
    {
        #region "Windows Dll Extern imports"

        [DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr windowHandle,
            IntPtr insertAfterHandle,
            int x,
            int y,
            int cx,
            int cy,
            UInt32 flags);

        [DllImport("user32.dll")]
        private static extern bool ClientToScreen(IntPtr windowHandle, out POINT point);

        [DllImport("user32.dll")]
        private static extern bool EnumDesktopWindows(IntPtr desktop, EnumWindowsProc windowsProc, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumWindows(EnumWindowsProc windowsProc, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(IntPtr windowHandle);

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr windowHandle, out RECT rect);

        [DllImport("user32.dll")]
        private static extern int GetWindowThreadProcessId(IntPtr windowHandle, out int processId);

        [DllImport("user32.dll")]
        private static extern IntPtr GetParent(IntPtr childHandle);


        [DllImport("user32.dll")]
        private static extern int GetClassName(IntPtr windowHandle, StringBuilder text, int maxCount);

        [DllImport("user32.dll")]
        private static extern bool IsWindow(IntPtr windowHandle);

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string windowClass, string windowTitle);

        [DllImport("user32.dll")]
        private static extern int GetWindowText(IntPtr windowHandle, StringBuilder text, int maxCount);

        [DllImport("user32.dll")]
        private static extern bool GetClientRect(IntPtr windowHandle, out RECT rect);

        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr windowHandle, UInt32 cmd);

        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr windowHandle, int index, int newLong);

        [DllImport("user32.dll")]
        private static extern int GetWindowLong(IntPtr windowHandle, int index);

        [DllImport("user32.dll")]
        private static extern bool InvalidateRect(IntPtr windowHandle, ref RECT rect, bool erase);

        [DllImport("user32.dll")]
        private static extern bool ScreenToClient(IntPtr windowHandle, out POINT point);

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr windowHandle);

        [DllImport("user32.dll")]
        private static extern IntPtr ReleaseDC(IntPtr windowHandle, IntPtr hdc);


        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int width, int height);

        [DllImport("gdi32.dll")]
        private static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);

        [DllImport("gdi32.dll")]
        private static extern bool DeleteObject(IntPtr obj);

        [DllImport("gdi32.dll")]
        private static extern bool BitBlt(IntPtr hdcDestination, int xDestination, int yDestination, int width, int heigth, IntPtr hdcSource, int xSource, int ySource, UInt32 mode);

        [DllImport("gdi32.dll")]
        private static extern bool TextOut(IntPtr hdc, int nXStart, int nYStart, string lpString, int cbString);

        [DllImport("gdi32.dll")]
        private static extern uint SetBkColor(IntPtr hdc, int crColor);

        [DllImport("gdi32.dll")]
        private static extern uint SetTextColor(IntPtr hdc, int crColor);

        [DllImport("gdi32.dll")]
        private static extern IntPtr GetStockObject(int fnObject);

        #endregion


        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        private const int SWP_NOSIZE = 0x0001;
        private const int SWP_NOMOVE = 0x0002;

        private const int SWP_NOACTIVATE = 0x0010;

        private const int WS_EX_TRANSPARENT = 0x0020;

        private const int GWL_EXSTYLE = -20;

        public const int GW_HWNDNEXT = 2;
        public const int GW_HWNDPREV = 3;

        private const int SWP_SHOWWINDOW = 0x0040;
        private const int SWP_HIDEWINDOW = 0x0080;
        private const int SWP_NOOWNERZORDER = 0x0200;

        private const UInt32 SRCCOPY = 0x00CC0020;

        private const int SYSTEM_FONT = 13;

        private List<IntPtr> TopWindowPointers { get; set; }

        /// <summary>
        /// Delegate EnumWindowsProc
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <param name="lParam">The l parameter.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);


        /// <summary>
        /// Gets the top level windows.
        /// </summary>
        /// <returns>The window handle list.</returns>
        public List<IntPtr> GetTopWindowList()
        {
            this.TopWindowPointers = new List<IntPtr>();
            EnumWindows(new EnumWindowsProc(this.EnumWindowsHandler), IntPtr.Zero);

            return this.TopWindowPointers;
        }

        /// <summary>
        /// Gets the top window handle by process.
        /// </summary>
        /// <param name="processName">The window process name (without '.exe').</param>
        /// <returns>The window handle.</returns>
        public IntPtr GetWindowByProcess(string processName)
        {
            List<IntPtr> windowHandleList = this.GetTopWindowList();
            foreach (IntPtr windowHandle in windowHandleList)
            {
                if (!IsWindowVisible(windowHandle)
                    || GetWindowOuterBounds(windowHandle).Size == Size.Empty)
                {
                    continue;
                }

                if (String.Compare(GetWindowProcessName(windowHandle), processName, true) == 0)
                {
                    return windowHandle;
                }
            }

            return IntPtr.Zero;
        }

        /// <summary>
        /// Gets the window process name.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <returns>The window process name (without '.exe').</returns>
        public string GetWindowProcessName(IntPtr windowHandle)
        {
            Process process = GetWindowProcess(windowHandle);
            if (process == null)
            {
                return String.Empty;
            }
            else
            {
                return process.ProcessName;
            }
        }

        /// <summary>
        /// Gets the window process.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <returns>The window process.</returns>
        public Process GetWindowProcess(IntPtr windowHandle)
        {
            int processId = 0;
            GetWindowThreadProcessId(windowHandle, out processId);
            if (processId == 0)
            {
                return null;
            }

            return Process.GetProcessById(processId);
        }

        /// <summary>
        /// Gets the parent window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The parent window handle</returns>
        public IntPtr GetParentWindow(IntPtr windowHandle)
        {
            return GetParent(windowHandle);
        }


        /// <summary>
        /// Gets the window screen outer bounds rectangle.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The window screen area bouns rectangle.</returns>
        public Rectangle GetWindowOuterBounds(IntPtr windowHandle)
        {
            if (windowHandle == IntPtr.Zero)
            {
                return Rectangle.Empty;
            }

            RECT rect = new RECT();
            GetWindowRect(windowHandle, out rect);

            return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
        }

        /// <summary>
        /// Gets the topmost parent window of any child window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The top level window handle</returns>
        public IntPtr GetTopMostWindow(IntPtr windowHandle)
        {
            IntPtr parentHandle = windowHandle;

            while (GetParent(parentHandle) != IntPtr.Zero)
            {
                parentHandle = GetParent(parentHandle);
            }
            return parentHandle;
        }

        /// <summary>
        /// Gets the window class name.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The window class name.</returns>
        public string GetWindowClass(IntPtr windowHandle)
        {
            if (!IsValidWindow(windowHandle))
            {
                return String.Empty;
            }
            StringBuilder text = new StringBuilder(1000);
            GetClassName(windowHandle, text, 1000);
            return text.ToString();

        }
        /// <summary>
        /// Determinates is the specified handle identifies an existing window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>True if window exists, else false.</returns>
        public bool IsValidWindow(IntPtr windowHandle)
        {
            if (windowHandle == IntPtr.Zero)
            {
                return false;
            }

            return IsWindow(windowHandle);

        }

        /// <summary>
        /// Gets the window handle by class and title.
        /// </summary>
        /// <param name="windowClass">The window class.</param>
        /// <param name="windowTitle">The window title.</param>
        /// <returns>The window handle.</returns>
        public IntPtr GetWindowFromClassAndName(string windowClass, string windowTitle)
        {
            return FindWindow(windowClass, windowTitle);
        }


        /// <summary>
        /// Gets the window title.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The window title.</returns>
        public string GetWindowTitle(IntPtr windowHandle)
        {
            if (!IsValidWindow(windowHandle))
            {
                return String.Empty;
            }

            StringBuilder text = new StringBuilder(1000);
            GetWindowText(windowHandle, text, 1000);
            return text.ToString();

        }

        /// <summary>
        /// Gets the window screen inner bounds rectangle.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The window client area bounds rectangle.</returns>
        public Rectangle GetWindowInnerBounds(IntPtr windowHandle)
        {
            if (windowHandle == IntPtr.Zero)
            {
                return Rectangle.Empty;
            }

            RECT rect = new RECT();
            GetClientRect(windowHandle, out rect);

            POINT point1 = new POINT();
            point1.x = rect.left;
            point1.y = rect.top;
            ClientToScreen(windowHandle, out point1);

            POINT point2 = new POINT();
            point2.x = rect.right;
            point2.y = rect.bottom;
            ClientToScreen(windowHandle, out point2);

            return new Rectangle(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y);
        }

        /// <summary>
        /// Gets the window client bounds rectangle.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <returns>The window client area bounds rectangle.</returns>
        public  Rectangle GetWindowClientBounds(IntPtr windowHandle)
        {
            if (windowHandle == IntPtr.Zero)
            {
                return Rectangle.Empty;
            }

            RECT rect = new RECT();
            GetClientRect(windowHandle, out rect);

            return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
        }

        /// <summary>
        /// Sets the focus to the specified window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <param name="topHandle">The top window handle.</param>
        public  void SetWindowFocus(IntPtr windowHandle, IntPtr topHandle)
        {
            SetWindowPos(windowHandle, topHandle, 0, 0, 0, 0,
                SWP_NOSIZE | SWP_NOMOVE);
        }

        /// <summary>
        /// Sets the new window Z-order above the specified window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <param name="bottomHandle">The low level window handle.</param>
        public  void SetWindowOrder(IntPtr windowHandle, IntPtr bottomHandle)
        {
            IntPtr HWND_TOP = new IntPtr(0);
        IntPtr topHandle = GetWindow(bottomHandle, GW_HWNDPREV);

            if (topHandle == IntPtr.Zero)
            {
                SetWindowPos(windowHandle, HWND_TOP, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
            }
            else if (topHandle != windowHandle)
            {
                SetWindowPos(windowHandle, topHandle, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
            }
        }

        /// <summary>
        /// Sets window WS_EX_TRANSPARENT style.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        public void SetWindowTransparent(IntPtr windowHandle)
        {
            int oldLong = GetWindowLong(windowHandle, GWL_EXSTYLE);
            SetWindowLong(windowHandle, GWL_EXSTYLE, oldLong | WS_EX_TRANSPARENT);
        }

        /// <summary>
        /// Shows the window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        public void ShowWindow(IntPtr windowHandle)
        {
            IntPtr HWND_TOP = new IntPtr(0);
            if (!IsWindowVisible(windowHandle))
            {
                SetWindowPos(windowHandle, HWND_TOP, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);
            }
        }

        /// <summary>
        /// Redraw the specified window.
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        public void RedrawWindow(IntPtr windowHandle)
        {
            if (IsWindowVisible(windowHandle))
            {
                RECT rect = new RECT();
                GetClientRect(windowHandle, out rect);
                InvalidateRect(windowHandle, ref rect, true);
            }
        }

        /// <summary>
        /// Converts client point to screen point.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <param name="clientPoint">Client point.</param>
        /// <returns>Screen point.</returns>
        public Point PointToScreen(IntPtr windowHandle, Point clientPoint)
        {
            POINT point = new POINT();
            point.x = clientPoint.X;
            point.y = clientPoint.Y;
            ClientToScreen(windowHandle, out point);

            return new Point(point.x, point.y);
        }

        /// <summary>
        /// Converts screen point to client point.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <param name="screenPoint">Screen point.</param>
        /// <returns>Client point.</returns>
        public  Point PointToClient(IntPtr windowHandle, Point screenPoint)
        {
            POINT point = new POINT();
            point.x = screenPoint.X;
            point.y = screenPoint.Y;
            ScreenToClient(windowHandle, out point);

            return new Point(point.x, point.y);
        }

        /// <summary>
        /// Gets the bitmap from window.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <param name="bounds">The bounds.</param>
        /// <returns>The window client area bitmap.</returns>
        public  Bitmap GetWindowBitmap(IntPtr windowHandle, Rectangle bounds)
        {
            IntPtr hdcSource = GetDC(windowHandle);
            IntPtr hdcDestination = CreateCompatibleDC(hdcSource);

            IntPtr hBitmap = CreateCompatibleBitmap(hdcSource, bounds.Width, bounds.Height);

            SelectObject(hdcDestination, hBitmap);

            BitBlt(hdcDestination, 0, 0, bounds.Width, bounds.Height, hdcSource, bounds.X, bounds.Y, SRCCOPY);

            Bitmap bitmap = Bitmap.FromHbitmap(hBitmap);

            DeleteObject(hdcDestination);
            DeleteObject(hBitmap);

            ReleaseDC(windowHandle, hdcSource);

            return bitmap;
        }
        /// <summary>
        /// Gets the bitmap from window.
        /// </summary>
        /// <param name="windowHandle">Window handle.</param>
        /// <returns>The window client area bitmap.</returns>
        public Bitmap GetWindowBitmap(IntPtr windowHandle)
        {
            Rectangle bounds = GetWindowInnerBounds(windowHandle);
            if (windowHandle == IntPtr.Zero)
            {
                bounds = Screen.PrimaryScreen.Bounds;
            }

            return GetWindowBitmap(windowHandle, bounds);
        }

        /// <summary>
        /// Draws the text string with system font.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        /// <param name="text">Text string.</param>
        /// <param name="textColor">Text color.</param>
        /// <param name="backColor">Background color.</param>
        public void DrawText(Graphics graphics, string text, Color textColor, Color backColor)
        {
            IntPtr dc = graphics.GetHdc();

            SetTextColor(dc, ColorTranslator.ToWin32(textColor));
            SetBkColor(dc, ColorTranslator.ToWin32(backColor));

            IntPtr newFont = GetStockObject(SYSTEM_FONT);
            SelectObject(dc, newFont);
            TextOut(dc, 0, 0, text, text.Length);
            DeleteObject(newFont);

            graphics.ReleaseHdc(dc);
        }

        /// <summary>
        /// Handler/Callback function for EnumWindows Call
        /// </summary>
        /// <param name="windowHandle">The window handle.</param>
        /// <param name="lParam">The l parameter.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private bool EnumWindowsHandler(IntPtr windowHandle, IntPtr lParam)
        {
            this.TopWindowPointers.Add(windowHandle);
            return true;
        }
    }
}
C# Code for PokerStars Data Mining Quote
05-14-2016 , 07:52 PM
Hi, shows it cards, stack sizes, and BB size?

And could PS know, that i m using this script?

Or dont you know if is pokerstars AutoIt friendly?
C# Code for PokerStars Data Mining Quote

      
m