How I Built a Working Online Poker Bot, Part 4: The Poker Botting Erector Set

Friday, June 27, 2008

Introduction

Today we're going to build a simple online poker bot—the simplest possible online poker bot—in just a few hundred lines of code.

But first: have you ever tinkered around with an erector set?

You remember. Colorful cardboard boxes with see-through fronts. Girders and gears, nuts and bolts, even the occasional electric motor.

A classic brand with over 90 years of building experience, Erector introduces an incredible 50-Model building set. It has a whopping 605 pieces for building 50 different models, including a helicopter, a construction truck with crane, a scooter and more. A powerful 6V motor brings life to your creations, and special mechanical functions create unique and exciting movements.

Set includes an assortment of construction materials and fasteners, plus detailed, step-by-step instructions with photographs -- no reading required. Requires 3 "AA" batteries, not included.

The thing that always appealed to me about Erector Sets? Ingredients. Dozens of components, each with a specific, and yet generic, purpose. Not to mention the recipes: instructions for assembling those components. With a little time and some careful screwdriver work, you could build just about anything. 

Charles Babbage's Difference Engine #2, Built by Tim Robinson

Charles Babbage, eat your heart out.

But what do erector sets have to do with poker, programming, and poker botting?

Well, a lot of people have written me or left comments to the effect of: show us the code, buddy. And for the past couple weeks I've struggled with how to do that.

  • First, there's a lot of code. We're talking a sizeable production code base, with various versions and branches, and a lot of half-finished one-off tools. Utility libraries, hand evaluators, history analyzers, DLL injectors, you name it.
  • Second, I don't have complete control over said code. Before too long, I'll introduce my poker buddy, botting partner, former client, and general co-conspirator, Mr. Anonymous. He has a stake in all this, too.
  • Third, the code is quite valuable on its face. Not because I'm such a superb programmer, or poker player—I've made more than my share of mistakes in both arenas—but because it's had the benefit of thousands of hours of work.

And as we start to publicize it, that value, that potential energy, depreciates.

So what to do?

Sell it? Open-source it? Discuss it?

And that's when a friend sent me a link to the above picture and I thought: aha!

Erector sets.

Self-Contained Experimental Botting Goodness

Programmers, like poker players, have a behavioral profile. They prefer a certain language, a certain methodology, a certain environment. My favorite languages are C# and C++. Yours might be Java and PHP.

When it comes to botting (or software automation in general) we're all hoping to take something different away from the table. Some come to learn. Some want to make money. Some have a product they'd like to build, or a built product they'd like to automate. Some are interested in poker, others spend their time on World of Warcraft, and still others invest their energy in the stock market. Some want specific code they can download, build, and use. Others just want the techniques.

So rather than posting isolated code samples without any context, and rather than posting a hundred thousand source lines of code without any focus, we'll take the middle ground: the software erector set. That's a self-contained package of ingredients—source code, tools, information—together with instructions for turning those ingredients into something real and, as we progress further into the series, something useful.

That means various flavors of bots:

  • FoldBots
  • TrashBots
  • Autofolders
  • ShortBots
  • DeepBots
  • General (non-poker) software automation tools

Various poker-related tools:

  • Simulators
  • Hand Evaluators
  • History Analyzers
  • Poker Calculators

And some other stuff as well.

Obviously this is a little much to handle in a single series—you'd end up with 400 articles, and I'd have to start saying things like: "In Part 267, I pointed out, for the fifty-sixth time, that DLL Injection isn't the only way to build a bot."

The poker botting series will continue indefinitely. But software automation and human emulation, poker-related and otherwise, will now be a primary content line on Coding the Wheel.

After all, that's kind of what the name means.

Or did you think I just woke up one day and decided to start blogging about poker bots?

The Simplest of All Possible Bots: Introducing the FoldBot

The first code sample/erector set I'd like to present is what happens when you start to apply iterative, incremental development to online poker botting. Before we tackle generic poker A.I., let's see if we can get a proof of concept up and running. Let's see if we can create a bot which:

  • Reads our hole cards
  • Clicks a button (it doesn't matter which)

This is what you might call the Ground Zero of software automation. It's where everything begins: software with the ability to see and touch. (And when I say "see" I don't mean image recognition, or at least, I don't mean image recognition exclusively. For a poker bot, "seeing" might be the ability to extract text from a window by snooping on window memory, sending a WM_GETTEXT, etc. But the end result is the same.)

Here's a picture of the FoldBot in action: 

Simple. The FoldBot is the simplest of all possible online poker bots. It doesn't understand, want to understand, or need to understand, poker. It doesn't understand online poker. It doesn't even know how to recognize a poker hand. It only knows how to do three things:

  • INPUT: Extract your hole cards and other table information from one or more online poker tables.
  • PROCESSING: Decide to fold every hand (yes, even pocket Aces).
  • OUTPUT: Click the Fold button when it's your turn to act.

That's the simplest possible Input stage, the simplest possible Processing stage, and the simplest possible Output stage.

And yet, with some additional work, the FoldBot can become a TrashBot (a bot intended to fold garbage hands) which in turn becomes an Autofolder (a bot intended to automate preflop play) which in turn becomes a ShortBot, which in turn becomes a DeepBot capable of playing deep-stacked poker on all rounds of betting, and so forth. We'll explore all of these at some point, and we'll follow the long-established practice of giving short, often cute or cryptic, names to our bots.

Which Online Poker Venues Does It Support?

The FoldBot is specialized for the PokerTime online poker venue (http://www.pokertime.com) so you'll need to download the (free) software and set up a (play-money) account in order to see it in action.

Why are we using PokerTime, instead of a more popular venue like PokerStars or Full Tilt?

Because it makes our Input stage very simple.

But don't worry: we'll add support for Poker Stars, Full Tilt, and other major poker venues, shortlyin case they should feel left out of the botting extravaganza. Extracting text from Poker Stars is slightly more difficult than PokerTime, but only slightly. Botting rules apply here:

Botting Rule #68: It's virtually impossible for a program to display human-readable text on the screen without exposing that text to other programs running on the machine. You might as well not even try.

This is especially the case when the text has to be easy to read, as it does in online poker, where 4x and 8x multi-tabling is commonplace. But even if we allow the text to be difficult to read, well, we're fast approaching the time where if a human can read it, software can too. As Jeff Atwood points out in CAPTCHA Is Dead, Long Live CAPTCHA!, even CAPTCHAs which were once thought to be unbreakable—Google, Yahoo, Hotmail—have since been broken.

2008 is shaping up to be a very bad year indeed for CAPTCHAs:

  • Jan 17: InformationWeek reports Yahoo CAPTCHA broken
  • Feb 6: Websense reports Hotmail CAPTCHA broken
  • Feb 22: Websense reports Google CAPTCHA broken

Which means I am now 0 for 3. Understand that I am no fan of CAPTCHA. I view them as a necessary and important evil, one of precious few things separating average internet users from a torrential deluge of email, comment, and forum spam.

In fact, I believe the text-only CAPTCHA is doomed to failure, as a long-term mechanism for separating the humans from the bots.

But I digress. On to the code.

About the Code

Download the FoldBot Source Code (C++/Windows 145KB).

The FoldBot code consists of two small projects, both implemented in C++.

The purpose of each project is straightforward:

  • XPokerBot.MfcView is the (simple and minimalistic) GUI. This is a standard Windows executable (.EXE).
  • XPokerBot.Hook is the payload containing the meat of the bot's "intelligence". This is a standard Windows dynamic-link library (.DLL).

In order to build them, you'll need:

  • Microsoft Visual Studio 2003, 2005, or 2008. VS2005 and VS2008 are both available as fully-functional, 90-day trial editions.
  • The C++ Boost libraries (for regular expression support).

This version of the bot has only been tested on Windows XP, so if you're running Vista or Windows 2000, you might have to do a little tweaking.

But far and away the most difficult part of building the source code is dealing with the Boost libraries. For step-by-step instructions, check out Boost: Getting Started on Windows. If you run into specific problems, you can try posting to the Boost Configuration forums on Nabble. And if all else fails, why, you can simply comment out the Boost code. It's only used in a couple places, for regular expression support. Rewriting that code to use plain vanilla string manipulation should be straightforward.

How It Works

The FoldBot, while simple, makes use of some interesting techniques:

  • Windows Hooks
  • DLL Injection
  • Window Subclassing
  • Rich Text Interception
  • Interprocess Communication (IPC)
  • Regular Expressions
  • Input Simulation

Here's what it does in a nutshell:

  1. Inject the botting DLL into the poker client's address space.
  2. Respond to (poker table) window create notifications via the CBT hook.
  3. Subclass the poker table's (RichEdit control) chat window.
  4. Intercept chat window text by listening for and parsing EM_STREAMIN messages.
  5. Parse the text using simple regular expressions.
  6. Simulate input (clicking the Fold button) at the right time, based on those messages.
  7. Transmit hole card and table information to the poker bot GUI via simple IPC.

Not too bad. No rocket science here. Now let's look at the individual steps.

Step 1: Inject the DLL

The first thing the bot does is inject a DLL (XPokerBot.Hook.dll) into the poker client process, using the same CBT Hook injection technique I mentioned in Part 1:

bool XPOKERBOTHOOK_API InstallHook()
{
    g_hHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) CBTProc, g_hInstance, 0);

    return g_hHook != NULL;
}

That causes the DLL to be mapped into the address space of every process on the machine, including the poker client. Normally we might be concerned about overhead, but this DLL is fairly small. More to the point, it's almost completely inert when instantiated in an irrelevant (non-poker) process. As we start adding functionality to the DLL and it grows larger, we can switch to the two-stage injection approach:

But for now it's not necessary.

Step 2: Detect the opening and closing of poker windows

Another benefit of the CBT Hook approach is that it gives us a handy way to detect the opening and closing of poker table windows, and any other top-level windows of interest:

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);
    else if (!g_pClient)
        return 0;

    // Window was activated. We currently use this to detect window creation also.
    // Why not use HCBT_CREATE? Because often the window doesn't yet have a caption
    // when HCBT_CREATE is called, and our sample window-detection logic looks
    // at the window caption to determine the type.
    HWND hWnd = (HWND)wParam;
    if (!hWnd)
        return 0;

    if (nCode == HCBT_ACTIVATE)
    {
        if (!g_pClient->IsRegisteredWindow(hWnd))
            g_pClient->TryRegisterWindow(hWnd, NULL);
    }
    else if (nCode == HCBT_DESTROYWND)
    {
        if (g_pClient->IsRegisteredWindow(hWnd))
            g_pClient->UnregisterWindow(hWnd);
    }

    // Return 0 to allow window creation/destruction/activation to proceed as normal.
    return 0;
}

Here we can guess that that the HCBT_ACTIVATE message is sent when a given top-level window is activated, and that the HCBT_DESTROYWND message is sent when a given top-level window is closed. And we have some simple code to store or remove the window to or from the list of "managed" windows in each case.

The only thing to note here is that we can't really use HCBT_CREATEWND notification when we're answering the "is this a poker table window?" question by examining the window caption. That's because the window caption is usually empty at the time the HCBT_CREATEWND notification is sent. There are ways around this such as delayed creation but we don't need to go to such lengths, certainly not for this incarnation of the bot.

Step 3: Subclass the Chat Window

Now that we're able to detect when poker table windows are opened and closed, we need to extract text from the table chat window. The technique I'm describing here will only work on certain windows, such as the Windows rich edit control, so it may or may not be appropriate for a particular poker client or other gaming application.

Basically we want to subclass the chat window, listen for the EM_STREAMIN message, and replace the callback function provided by the poker client with one of our own. In this sample, I'm subclassing the window when it's first recruited into the poker botting runtime, but strictly speaking, the subclass doesn't have to be installed until the user/bot actually sits down, or even until the user issues a command saying, "okay, automate this table for me."

For each newly-created poker table window, the OnlinePokerClient::TryRegisterWindow method creates a corresponding OnlineTableWindow-derived object—in this case, a PokerTimeTableWindow object. During the construction of that object, we subclass the table's chat window, allowing us to intercept messages sent (by the PokerTime application) to this window:

PokerTimeTableWindow::PokerTimeTableWindow(HWND hWnd, PokerTimePokerClient* client) :
    OnlineTableWindow(hWnd, client)
{
    // Find the chat box...
    HWND hwndChat = ::FindWindowEx(hWnd, NULL, _T("RichEdit20W"), NULL);
    if (hwndChat)
    {
        // And subclass it!
        PokerTimeTableWindow::OldRichWndProc = (WNDPROC)::GetWindowLongPtr(hwndChat, GWL_WNDPROC);
        ::SetWindowLongPtr(hwndChat, GWL_WNDPROC, (LONG_PTR)PokerTimeTableWindow::MyRichWndProc);
    }
}

Step 4: Intercept chat window text

Technically speaking, the chat text window (on PokerTime) is a Rich Edit control. In order to fill this window with text, the Poker Time client sends it a standard EM_STREAMIN message, which our code intercepts:

LRESULT PokerTimeTableWindow::MyRichWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    EDITSTREAM* es = (EDITSTREAM*) lParam;

    if (msg == EM_STREAMIN)
    {
        // Store the original EDITSTREAMCALLBACK and replace it with our own..
        PokerTimeTableWindow::OldRichEditCB = es->pfnCallback;
        es->pfnCallback = PokerTimeTableWindow::MyEditStreamCallback;
        // Hack - keep track of current window
        PokerTimeTableWindow::CurrentChatWindow = hWnd;
    }

    // Call the original window procedure, with our new EDITSTREAMCALLBACK in place
    LRESULT lRet = ::CallWindowProc(PokerTimeTableWindow::OldRichWndProc, hWnd, msg, wParam, lParam);

    if (msg == EM_STREAMIN)
    {
        // Restore the original EDITSTREAMCALLBACK function
        es->pfnCallback = PokerTimeTableWindow::OldRichEditCB;
    }

    return lRet;
}

If you've ever worked with EM_STREAMIN before, you know that you have to provide it with a callback function—specifically, an EDITSTREAMCALLBACK function. That's just what the PokerTime client is doing. What we want to do is somehow replace that callback function (which we'll call A) with our own (which we'll call B). We want Windows to call our callback (B), which will in turn call the PokerTime callback (A) to retrieve the text. That's the purpose of these two lines of code:

// Store the original EDITSTREAMCALLBACK and replace it with our own..
PokerTimeTableWindow::OldRichEditCB = es->pfnCallback;
es->pfnCallback = PokerTimeTableWindow::MyEditStreamCallback;

We're actually overwriting the EDITSTREAMCALLBACK member of the EDITSTREAM structure passed in by the client. So as far as Windows is concerned, it's processing a perfectly normal EM_STREAMIN message. And as far as the PokerTime client is concerned, it's sending a perfectly normal EM_STREAMIN message which it expects to be handled by Windows.

Step 5: Parse the Chat Text

In the meantime, we get a chance to parse the text generated by the poker client using a couple simple regular expressions:

DWORD CALLBACK PokerTimeTableWindow::MyEditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG numberOfBytes, LONG* actualBytes)
{
    // Call the original (PokerTime-provided) callback to get the text..
    DWORD dwRet = PokerTimeTableWindow::OldRichEditCB(dwCookie, pbBuff, numberOfBytes, actualBytes);
    if (0 == dwRet && actualBytes && *actualBytes > 0)
    {
        // 
        // do some busy work...
        // 

        // We're looking for the line (in the PokerTime text window) that looks like this:
        //> Dealing Hole Cards(8d Kc )
        boost::smatch what;
        if( boost::regex_match(line, what, regHoleCards, boost::match_default|boost::match_single_line) && what.size() == 3)
        {
            string sCard1 = what[1];
            string sCard2 = what[2];

            ApplicationProxy::TransmitHoleCards((sCard1 + sCard2).c_str(), hPokerTable);
        }
        //> JohnDoe, you have 10 seconds to respond
        //> JohnDoe, you have 5 seconds to respond
        else if (boost::regex_match(line, what, regWakeUp, boost::match_default|boost::match_single_line) && what.size() == 3)
        {
            string theActor = what[1];
            if (theActor == g_pClient->LoggedInAs)
                OnlinePokerExecutor::PerformAction(hPokerTable);
        }

Specifically, we're looking for two pieces of text:

  • "Dealing Hole Cards(Ah Ad)"
  • "JohnDoe, you have 10 seconds to respond"

The equivalent regular expressions, stripped of the redundant C++ backslash escapes (really makes you long for the world of verbatim @ strings such as we enjoy in C#, doesn't it?), look like this:

  • ^\s*>\s*Dealing\ Hole\ Cards(?:\((.{2,3})\ (.{2,3})\ \))?\s*$
  • ^\s*>\s*(.*),\ you\ have\ (\d+)\ seconds\ to\ respond\s*$

There are better regular expressions out there, but these are just an example. Ultimately we'll want to create a generic regular expression engine which is powerful enough to analyze text from multiple poker venues, using named capture groups. But that's a topic for another post.

Step 6: Simulate User Input

The "JohnDoe, you have 10 seconds to respond" message is our v1.0 method of detecting when it's our turn to act. We're obviously going to have to improve on that before we turn the bot loose, but for now, it's an easy way to queue the logic to click the Fold button. Once we know it's our turn to act, we leverage our bot's impressive A.I...

Fold all hands without exception.

...and invoke our not-too-imaginatively named OnlinePokerExecutor object to perform the actual click of the Fold button.

//> JohnDoe, you have 10 seconds to respond
//> JohnDoe, you have 5 seconds to respond
else if (boost::regex_match(line, what, regWakeUp, boost::match_default|boost::match_single_line) && what.size() == 3)
{
    string theActor = what[1];
    if (theActor == g_pClient->LoggedInAs)
        OnlinePokerExecutor::PerformAction(hPokerTable);
}

The implementation of that, unfortunately, is a little messy. No matter; we can abstract the details of input simulation away into an easy-to-use class. For now, here's a hard-coded, somewhat kludgy attempt:

void OnlinePokerExecutor::PerformAction(HWND hPokerTable)
{
    // Coordinates of our click spot (relative to poker table client area)
    POINT ptCoords;
    ptCoords.x = 290;
    ptCoords.y = 513;
    //ptCoords.x = 450;
    //ptCoords.y = 354;

    // Convert client coords to screen
    ::ClientToScreen(hPokerTable, &ptCoords);

    // Get the screen resolution
    HDC hdc = ::GetDC(NULL);
    int screenWidth = ::GetDeviceCaps(hdc, HORZRES);
    int screenHeight= ::GetDeviceCaps(hdc, VERTRES);
    ::ReleaseDC(NULL, hdc);

    // Convert our screen coordinates to world coordinates
    double temp1 = 65535 * ptCoords.x;
    double dX = temp1 / screenWidth;
    temp1 = 65535 * ptCoords.y;
    double dY = temp1 / screenHeight;

    // Now lets create our mouse inputs..
    INPUT input[4];
    MOUSEINPUT mouseInput;

    // Move the mouse to the button...
    input[0].type=INPUT_MOUSE;
    mouseInput.dx = (int)dX;
    mouseInput.dy = (int)dY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0;
    mouseInput.dwExtraInfo = 1001;
    input[0].mi = mouseInput;

    // Left-click the mouse down
    input[1].type=INPUT_MOUSE;
    mouseInput.dx = (int)dX;
    mouseInput.dy = (int)dY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0;
    mouseInput.dwExtraInfo = 1001;
    input[1].mi = mouseInput;

    // ..and release it
    input[2].type=INPUT_MOUSE;
    mouseInput.dx = (int)dX;
    mouseInput.dy = (int)dY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_LEFTUP|MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0;
    mouseInput.dwExtraInfo = 1001;
    input[2].mi = mouseInput;

    // Move the mouse to where it was before
    input[3].type=INPUT_MOUSE;
    mouseInput.dx = (int)dX;
    mouseInput.dy = (int)dY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0;
    mouseInput.dwExtraInfo = 1001;
    //input[3].mi = mouseInput;

    int numberOfInputs = 3;

    // Remember which window has the focus, so we can restore it to that window.
    GUITHREADINFO info;
    ::ZeroMemory(&info, sizeof(info));
    info.cbSize = sizeof(info);
    ::GetGUIThreadInfo(NULL, &info);

    // Also get the current mouse position, so we can restore that.
    POINT ptCursor;
    if (::GetCursorPos(&ptCursor))
    {
        numberOfInputs++;

        temp1 = 65535 * ptCursor.x;
        mouseInput.dx = (int)(temp1 / screenWidth);
        temp1 = 65535 * ptCursor.y;
        mouseInput.dy = (int)(temp1 / screenHeight);
        input[3].mi = mouseInput;
    }

    // Make sure this window isn't obscured behind some other window
    ::BringWindowToTop(hPokerTable);

    // Now actually send the mouse input
    ::SendInput(numberOfInputs, input, sizeof(INPUT));

    // Reset the focus to the previous window
    if (info.hwndFocus)
        ::SetFocus(info.hwndFocus);
}

Another problem with the above input simulation is that the mouse coordinates (button locations) are hard-coded. This is really the type of data you want to "file and forget" in an external XML or other settings file—which is just what we'll do, in future versions of the bot. We'll also want to add random (or normal distribution) input timing along with a few other features. And of course, SendInput is just one way of several ways to simulate user input.

Step 7: Update the GUI

The last piece is to somehow get this data (our hole cards and open tables) to display in the bot's UI. The problem is that the bot user interface lives in one process, and our hook DLL lives in another. So whatever we do is going to involve some sort of inter-process communication or IPC. (Note, however, that you can create a full botting GUI, living entirely in the poker client's process, without a separate .EXE at all.)

For now, let's throw some "poor man's IPC" at the problem by manhandling the data into a WM_COPYDATA message. We'll look at cleaner and more robust approaches as needed.

void ApplicationProxy::TransmitHoleCards(LPCSTR cards, HWND hPokerTable)
{
    HWND hWnd = ::FindWindow(NULL, _T("FoldBot v1.0a"));
    if (hWnd)
    {
        COPYDATASTRUCT cds;
        ::ZeroMemory(&cds, sizeof(COPYDATASTRUCT));
        cds.dwData = (DWORD)hPokerTable;
        cds.lpData = (PVOID)cards;
        cds.cbData = strlen(cards) + 1;
        ::SendMessage(hWnd, WM_COPYDATA, (WPARAM)hPokerTable, (LPARAM)&cds);
    }
}

Meanwhile, the code sitting on the other side of that message can implement a WM_COPYDATA handler, extract the data, and display it appropriately.

Revisiting the Choice of Language

In Part One of this series, I suggested that the ideal language for bot-building was C++. A lot of people took that as a sort of language elitism. So I'd would now like to qualify that statement.

Build the bot in whatever language you're most comfortable with. When it comes to DLL injection, API hooking, log file snooping, stealth, and other "close to the metal" techniques, you'll find these are easier in C and C++ than in other languages (the greatest weakness of C/C++ also happens to be its greatest strength). It saves you the considerable trouble of accomplishing these things through P/Invoke or Interop or other virtualization layers. But the bot's domain modelling and A.I. aspects can and should be built in whatever langage you prefer. It depends on your level of comfort with your current language, with C/C++, and with the Windows API.

I used C++ (and later, C#) exclusively in the creation of the bot. That's C++ for the close-to-the-metal components, and C# for domain modelling and A.I. So the code samples I publish, today and in the future, will more or less reflect that. If you'd rather see these techniques in another language: what are you waiting for? The code isn't going to walk out of the compiler and migrate itself.

Disclaimer

Like many programmers, I have an almost superhuman ability to produce ugly, buggy, downright evil code. While the "production" botting source code has had the benefit of constant use and refactoring, the same can't be said for all of the samples I'll be publishing. In order to highlight particular techniques without burying innocent bystanders under an avalanche of sloc, I've had to manhandle the source in various ways:

  • By removing error handling.
  • By collapsing objects or object hierarchies into global variables or removing them entirely.
  • By occasionally introducing classes, methods, etc., which we'll end up throwing away as we put together more powerful abstractions.
  • By relying on quick hacks and naive solutions when necessary to keep the code samples relevant.
  • By severing important dependencies.
  • By ignoring bugs when they're irrelevant to the topic at hand.

And so forth. So it should go without saying that the code is intended to illustrate practical techniques, rather than proper coding style.

Using the FoldBot

The instructions for use are bundled with the software, but here are the basic steps.

  1. Build the FoldBot.
  2. Download the PokerTime client. Create a play-money account.
  3. Launch the FoldBot.
  4. Launch the PokerTime client.
  5. Open one or more poker table windows.
  6. The table name(s) should appear in the Fold Bot GUI.
  7. Sit down at one or more tables and wait for a hand.
  8. Your hole cards should appear in the FoldBot GUI.
  9. When it's your turn to act, don't do anything. Wait.
  10. Continue waiting until the PokerTime client prompts you with the message "so-and-so, you have 10 seconds to respond".
  11. Your hand should now be folded.
  12. Leave the table(s).
  13. The table(s) should disappear from the FoldBot GUI.

The acid test for whether or not the FoldBot is actually working, is whether it displays PokerTime tables as you open them.

Look Ma! No Hands!

Even if you shut the FoldBot application (.EXE) down, it will continue folding your hands on any table which you opened up when the FoldBot was running. Any tables opened after you close the FoldBot GUI won't be folded.

This has to do with the fact that, although we "pin" the Hook DLL in the poker client's address space via a redundant call to LoadLibrary, the Windows CBT hook itself dies when the FoldBot GUI shuts down. So the Hook DLL is still mapped, but you no longer receive CBT notifications, which means new windows don't get subclassed. This behavior is pretty easy to change but it's good to see an EXE-less, GUI-less bot in practice.

Conclusion

Believe it or not, we covered a lot of ground today. Although this code isn't going to be too useful for established botters, except perhaps as a way to compare notes, it's illustrative. We'll take these techniques and add them to our arsenal for the next incarnation of the bot: the TrashBot, a.k.a., the first "useful" bot.

We'll also reintroduce Detours, with a twist. We'll talk about what poker has to do with the Fastest Gun in the West. And I might even tell you about how the Twelve of Spades changed my life, though I have a hard and fast rule to never, ever tell a bad beat story over the web. And at some point we'll have to take a stroll through the underbelly of poker, before too long. Unscrupulous business practices; routinely poor quality of service; and the fact that yes, sometimes your opponents really CAN see your hole cards.

Until then: thanks for reading, as always, and good luck to you in your poker and programming struggles.

Tags: DLL Injection, poker bot, Windows API, Win32 Hooks, online poker, poker

236 comment(s)

Finally! I fired up google reader and saw your entry and I was so happy! Just in time for the weekend. I got a hold of Windows Via C/C++ and I'm gonna try to extend this before your next article for learning/hacking purposes. Thanks man.

James- Just ran it over here. Haven't spent too much time with it but looks good. I for one would like to see some C# as well... any of that in the pipeline?

How whould you read the hole cards, in case the client doesnt display them in the chat.

Thanks for the continuation of this most interesting series! I know people told you before, you do have a clear writing style even when you're writing about code.

Excellent series of articles. I have been following your analysis since the first article and I am looking forward for the next ones. I became nostalgic of my student days and the late night talks with friends after reading 'Bringing Down the House' ;)

Thanks for another well-written and captivating article. Hope to see them coming :)

One question: What do you think about VS 2008 Express Edition as the coding platform of choice? Is it functional enough?

Man!! you really rock!!!, dont show me the code, show me the money!!

Really good work, i am very glad to read your blog!

Eugenio from Argentina

A nice set of articles and easy to read. I'm not a C++ programmer, although i understand most code and regularly port C++ into my language of choice: VB6. I have developed several bots in VB6 and they've been quite successful. The evaluation code is probably slow by today's standards due to VB6's Rnd() function, however i'm happy with the ~80,000 Monte Carlo simulations per sec. 10,000 simulations has proven to be plenty fast enough to get an accurate result.

I'm now getting deeper into C++ as i hate the whole framework/managed code thing and ms have killed off VB6. My own bots used BitBlt() to take screen captures, however that was a kludgy fix and i'm now researching deeper methods, with the intention of hooking into the SSL packet decryption. Finding it hard work but it'll be worth it.

Keep up the good work, it's sites like this that inspire "hobby" coders like me...

As always excellent article. I look forward to seeing how you implement this for PokerStars. What I would like to do is create the DLL in C++ and do all of my logic and interface in C#. Can't wait for the next article!

great article!!! Finally some code

Thanks for interesting read and for showing what can be done to noobs in programming world like me. I like to learn through "live" examples and this one is perfect. Thanks!

Dude! PokerBots so totally ROCK! www.FireMe.To/udi

I have to say "You Rock!!". Thank you for sharing your hard earned knowledge. You are bringing together a considerable amount of valuable and difficult to find information. This Blog has the potential of becoming a cornerstone for extending existing applications of all kinds! I am now going to google "botting rule #68" to find the rest!

Please quit your day job and continue the series full time. I can't wait another couple weeks for part 5.

Great post James. I look forward to more!

Hi James. Another great post you have there but can I ask what client you're using in the FoldBot picture? I've never seen it before.

Thanks

^^^^^ If you actually READ the articles, he tells you!!

I can't help but wonder: Is it legal to use a bot to earn you money in online poker rooms?

And by legal i mean, legal in the sense that you will not get thrown to jail - not whether the poker room allows it or not. And of course laws can differ a great deal depending on where you are, but i'd be interested in hearing what you think is the case in europe and the US. I am personally from Denmark, but any european country where you have knowledge of the laws will be of interest. (I asked this on reddit as well, so sorry for being a bit spammy :))

Great articles. Thank you for taking the time writing these :)

So I built your FoldBot and it can detect holecars from the tables i join, but it doesn't seem to fold them. Do i need to setup the client in a specific way to make it work?

The hard part is programming a winning (or break even) poker bot... was that achieved ? That's the question ;) Still a great tutorial about advanced stuff. Thanks.

I'm an experienced developer (although in the very different world of web development), and I have to say this is a fantastic series. I can't wait to read more.

I enjoyed this article very much. I built it (you're right, the boost was the trickiest part, but not very) and it works well except the table names aren't erased from the list when I leave the table. Speaking of boost, vb.NET has a regular expression library. I haven't used C++ in a long (like 10 years) time and it's not as easy for me to follow as other languages. What are the disadvantages to writing it all in vb.NET? Also, I'm trying to follow it in the debugger and it doesn't work when I set breakpoints in the dll section. Any tips? Now I'm trying to add Stars to the equation. I figure I'll learn more trying to do it first and when you show how you do it, I can absorb it even more quickly. Thanks again.

"Give a man a fish and he'll eat for a day, teach a man to fish and you have fed him for a lifetime"

Perhaps instead of providing bulk code you should instead providing us with the logic behind developing that code. How you decided on what messages to intercept, how you figured out what you needed to do. That would be far more useful!

The regular expression only seems to match the Hold'em game table so the bot will only work with the Hold'em game. The other problem is when you leave a table it is not removed from the "open tables" list.

I do so love WM_COPYDATA.... ;) So if that's your "just do it approach" which IPC mechanisms do you actually use? Not that there's much difference for this purpose... even with many tables running that's a relatively small amount of data floating across process boundaries.

@ Casper, James pokerbots are not illegal, you'll not go to jail or will be prosecuted. But on every better known pokersite like Full Tilt,UB, Party,Stars, with opening an account you agree not to collude,cheat and use software thats blacklisted or gives you an advantage to other players (Pokertracker and other stat software are allowed because you have the information that other players have too). Botting is not allowed on these sites and if they catch you they will freeze your account with the money on it. That's ok if you're botting for sport or small amounts. Not much money to take. Besides colluding, thew sites put much of they resources in bot-searching. Party has sometimes a window to pt some numbers in it, they scan the memory and so on. Often people tell them suspicious players and they check the history. Conclusion: Not illegal by law but you're risking your bankroll. So don't try with an account with much money on it

After compiling in VS 2008 (if it helps any other readers. you can get a full copy if you have a .edu email) in Vista, when I run it and open a window, the window does not show in the list. I am assuming that this is due to the increased security in Vista that apparently disables DLL injections.

Fold bots are evil! However, it's amazing how some players can simply "sit out" to let their hand fold, and still OFTEN make it into the payout in play money sit & go tournaments. There are usually 2 or 3 players auto-sitting out at all of those tables, amassing play money for unknown reasons...

I enjoyed the article and forward to reading future parts of the series! Keep it up.

May I suggest you show HOW you find out what processes or objects you need to 'hook into' in Poker application to get the info you need. What tools, etc.? Thanks, it's a great series.

I hope you don't plan on including any .NET :( I like the old school way of doing things as it shows more actual how-to than all the shortcuts you can take within that massive framework; and I don't need to mention the footprint...

great article though. I've been toying around since the first article and I got to a stopping point with PStars so was hoping you could give a little insight. I used detours to grab all text being passed (with drawtext) and actually am seeing everything but I can't figure out which window it is being pointed to since all I get is a HDC. any help here? or some more detail on subclassing and figuring out WHAT messages are being passed in? as 1esproc said, more "how" you are figuring stuff out would be great. I don't think releasing functional bots is helping as much...

thanks

Someone ... Anyone...

I have Winspector and Detours Express but have not figured out how to grab dealer text messages from PStars. I would welcome any specific advice or example.

hello, james i really appreciate your good work and looking forward to new interesting topics.

F. Bueller: take a look at the Detours examples, especially at traceapi and simple. if you compiled the detours tidily, you will find a withdll.exe in the bin directory. this dll is injecting a giving dll into a proess and starts it (e.g. withdll.exe /d:traceapi.dll "C:\Program Files\PokerStars\PokerStars.exe"). traceapi will output the calls into a logging client(syelogd.exe). if you dont want to trace all api calls included in the traceapi dll, you can simplfy write your own using the source code of the traceapi.dll(especially in the _win32.cpp file). i copied some code which writes all TextOutW calls in C:\a.txt: [url=http://rafb.net/p/rMgHd916.html]click[/url]. compile this as a dll an run: withdll.exe /d:mydll.dll "C:\Program Files\PokerStars". i hope i could help you and if you have problems with compiling detours with vs2008, take a look at http://forums.msdn.microsoft.com/en-US/vcgeneral/thread/fcb5a809-5404-481f-82c0-40ffa6f58c9b/. greets

oh i'm sorry for the typing and grammar mistakes, but i can't edit my previous post. if anyone compile the code above, then please concern that withdll expects an export function. i declared the NullExport function, but it's important to add "/export:NullExport" into the Additional Options at the Linker Properties of your project.

thanks Anon!

I will give it a try.

Bueller

Great article. I am also trying to get started. Also downloaded Detour, but am having trouble running the initial "nmake" command.

Read the readme.txt which says "To build the libraries and the sample applications, type "nmake"." I assume this is from a command prompt (is that correct?) Open Command Prompt in Windows Accessories cd to Program Files\Microsoft Research\Detours Express 2.1type nmake (enter) and got "'rc' is not recognized as an internal or external command, program or batch file" NMAKE Fatal Error U1077 return code '0x1' and '0x2'

I admit to being weak in programming, but would like to at least be able to complete this simple step.

Bot Envy... when you're in the cmd window, you've got to run a file called vsvars32.bat to set your environment variables before you run nmake. That file is located in c:\program files\microsoft visual studio 8\common7\tools on my machine.

@Bot Envy I have not tried it yet, but found this at http://www.devsource.com/c/a/Using-VS/Working-at-the-Visual-Studio-Command-Line/

"You might think that accessing the command line is a simple matter of clicking the Start\Programs\Accessories\Command Prompt shortcut. That shortcut opens a command prompt, but it's not the right one. This command prompt lacks the required environment variables, especially the path information to Visual Studio.

Instead, open a development command line by clicking the \Start\Programs\Microsoft Visual Studio\Visual Studio Tools\2005 Visual Studio 2005 Command Prompt shortcut."

Hope that helps

Excellent series and really educational. I am reaaaaly looking forward to knowing exactly how you detect the whole cards of PT and FT. I am a professional programmer, but I know next to nothing about graphics and I have a hard time imaging how to do screen scraping in a timely manner.

Michael, you wrote "I used detours to grab all text being passed (with drawtext) and actually am seeing everything". What does seeing everything means? Does it include the whole cards which isn't written to the status window???

HI. In my computer, the FoldBot perfectly works with Windows vista, without making any change.

In fact, the most tricky part, is building the boost library, but any programmer can do it :)

Thanks for sharing all this stuff, but I have a question for you.

Do you think tha amount of BOTs will dramatically increase because of this blog?? What will be the result of this? I think the poker rooms will increase their security. Will it be possible to do all this stuff in the future?

Thanks for your work

Thank you to Pro Bot and EnviroMan. Following your advice, I was able to do the nmake properly, and now have withdll.exe, simple, traceapi, etc.

Will attempt to run some detours examples, including the one provided by Anonymous.

Interesting stuff.

Where can I download detours? MS-download-link seems to be broken. reg. popo

Detours is at http://research.microsoft.com/sn/detours/

Cool. We have evolved from just waiting for the next article to actually helping each other understand the tools and concepts better.

Thx James. I don't know the first thing about codes or programming, so for me this seems like a crazy hard task, but I am determined to build a fold bot, and keep up with the series. I would love to ask some questions, but I'm going to exhaust the research first. Peace and elbow grease.

The link to the mydll.dll code by Anonymous above has stopped working, but here is the excellent code that he provided as an example:

include <windows.h>

include <detours.h>

include <stdio.h>

BOOL (_stdcall * RealTextOutW)(HDC a0, int a1, int a2, LPCWSTR a3, int a4) = TextOutW;

BOOL _stdcall MineTextOutW(HDC a0, int a1, int a2, LPCWSTR a3, int a4) { FILE *file; file = fopen("C:\a.txt","a+"); fprintf(file, "TextOutW(%p,%p,%p,%ls,%p)\n", a0, a1, a2, a3, a4);

BOOL rv = 0;
__try {
    rv = Real_TextOutW(a0, a1, a2, a3, a4);
} __finally {
    fprintf(file, "TextOutW(,,,,) -> %p\n", rv);
};
fclose(file);
return rv;

}

VOID NullExport() { }

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) { if (dwReason == DLLPROCESSATTACH) { printf("simple.dll: Starting.\n"); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)RealTextOutW, MineTextOutW); DetourTransactionCommit(); } else if (dwReason == DLLPROCESSDETACH) { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)RealTextOutW, MineTextOutW); DetourTransactionCommit(); } return TRUE; }

Anonymous wrote: oh i'm sorry for the typing and grammar mistakes, but i can't edit my previous post. if anyone compile the code above, then please concern that withdll expects an export function. i declared the NullExport function, but it's important to add "/export:NullExport" into the Additional Options at the Linker Properties of your project.

Thank you for the clarification, but I have just started using VS (following James Devlin's suggestion) and am not sure where to add the "/export:NullExport". Is it in the code you supplied, near the

VOID NullExport() { }

or is it in the Mydll Property Pages, Configuration Properties, General, Output File?

Thank you for your patience.

almost

It is found in mydll Properties, Configuration Properties, Linker, General, Output File (type /export:NullExport here)

Don't give up.

1esproc said

""Give a man a fish and he'll eat for a day, teach a man to fish and you have fed him for a lifetime"

Perhaps instead of providing bulk code you should instead providing us with the logic behind developing that code. How you decided on what messages to intercept, how you figured out what you needed to do. That would be far more useful!"

i also would love to hear about how you know which ones to intercept. ive been working on a bot like this but sometimes figureing out which to intercept are hard. anyhelp on that would be appreicated.

Any commandline compiler for the foldbot out there? Currently not enough diskspace for VS ;-).

Anybody tried the foldbot on stars without issues/anoying e-mail-notifications?

Hope we stay on C++ not NET :-).

Thank you Bueller, the detours-download-link on MS ist ftp, therefore I had to prepair my browser.

Thanks for the effort James, it is being a really instructional trip for me :)

In the line of those requesting more information on how to collect information on how the poker clients work, I have tried to build a tool for BossMedia that maps keystrokes to mouse actions like folding and raising for faster interaction while multitabling and ran into some weird stuff. The sendInput command works because I have tried it in other clients, but on BossMedia it only moves the mouse but doesn't perfoms the MOUSEEVENTFLEFTDOWN and MOUSEEVENTFLEFTUP events.

Any guidelines on how to follow up in my research of whats happening?

Great!. Need the next article. I'm stopped now. I need catch text from textout or drawtext. The window only shows 'wmpaint' event. 'Wmpaint' is generated for everything, not useful for my program. I need another way.

I am trying to compile the dll example posted by Code Warrier above, but am getting the following errors during linking:

1>Linking... 1>detourcode.obj : error LNK2019: unresolved external symbol _DetourDetach@8 referenced in function _DllMain@12 1>detourcode.obj : error LNK2019: unresolved external symbol _DetourTransactionCommit@0 referenced in function _DllMain@12 1>detourcode.obj : error LNK2019: unresolved external symbol _DetourAttach@8 referenced in function _DllMain@12 1>detourcode.obj : error LNK2019: unresolved external symbol _DetourUpdateThread@4 referenced in function _DllMain@12 1>detourcode.obj : error LNK2019: unresolved external symbol _DetourTransactionBegin@0 referenced in function _DllMain@12 1>/export:NullExport : fatal error LNK1120: 5 unresolved externals

Would appreciate any advice on how to resolve these.

I just installed the Boost Regex library and added it to VS 2008, when I compile the solution I get some warnings about strcpy being unsafe and that I should use a strcpy_c, and also I get a warning about a command line option Wp64 being deprecated. Then at the end I get this:

Linking... LINK : C:\Users\kasra\Desktop\XPokerBot\XPokerBot\bin\XPokerBot.MfcView.exe not found or not built by the last incremental link; performing full link Embedding manifest... Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0 Copyright (C) Microsoft Corporation. All rights reserved. Build log was saved at "file://c:\Users\kasra\Desktop\XPokerBot\XPokerBot\XPokerBot.MfcView\Debug\BuildLog.htm" XPokerBot.MfcView - 0 error(s), 4 warning(s) ========== Rebuild All: 2 succeeded, 0 failed, 0 skipped ==========

When I run the exe that this creates, and then run the PokerTime app and join a table, the table does not show up in the list at all and nothing happens.

I successfully build detours, now I am trying to compile the dll example posted by Code Warrier above too!

I physicaly copied the detours.h into my project-dir and added it to the header tree on the left side of the VS IDE.

In PHP it would include it as long as the include-file exists but I m not an include professional on VS...what ist wrong, any idea?

It says:

------ Build started: Project: mydll, Configuration: Debug Win32 ------ Compiling... mydll.cpp c:\dokumente und einstellungen\popop\eigene dateien\visual studio 2008\projects\mydll\mydll\mydll.cpp(2) : fatal error C1083: Cannot open include file: 'detours.h': No such file or directory Build log was saved at "file://c:\Dokumente und Einstellungen\popo\Eigene Dateien\Visual Studio 2008\Projects\mydll\mydll\Debug\BuildLog.htm" mydll - 1 error(s), 0 warning(s) ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Anonymous:

The strcpy_s functions are version of strcpy with [url=http://msdn.microsoft.com/en-us/library/8ef0s5kh(VS.80).aspx]security enhancements[/url]. You can ignore the warning for this purpose. in your open poker table windows, make sure you're using Normal or Large table views and that the chat window is visible and set to Full Detail. Sometimes you have to click an up-arrow to get it to appear.

Hope that helps...

Juan, should wie link something, thought only build/compile mydll.dll? What is the exact procedure? And what is linking ;-)?

Anonymouse wrote "oh i'm sorry for the typing and grammar mistakes, but i can't edit my previous post. if anyone compile the code above, then please concern that withdll expects an export function. i declared the NullExport function, but it's important to add "/export:NullExport" into the Additional Options at the Linker Properties of your project."

I think I was doing this correct, but may be not.

Juan, the problem is that the linking of your detours lib isnt working. you probably forgot to add the detours libs to the linker options. if you add the detours include dir to the VC++ include dir, it's also important to tell the linker that he should link detours.lib and detoured.lib to your project. for that, add "detours.lib detoured.lib" to die Additional Dependencies at the Linker -> Input configuration properties of your project (VS2008).

========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

Thank you Anonymous. That was it.

I added the two .libs to my Input because I was having the same problem Juan was having, but one of the errors I got before adding them did not go away; mydll.obj : error LNK2005: _DllMain@12 already defined in dllmain.obj >mydll.dll/export:NullExport : fatal error LNK1169: one or more multiply defined symbols found

Juan says; "Thank you Anonymous. That was it."

Another Anonymous later: "I added the two .libs to my Input because..."

Could someone tell more? Or better give a small summery how to get mydll.dll working. May a step by step tut. how to include or what to link to get it.

Im using VC Express and when I go to project>mydll property pages>linker>input there are several fields which could have user-text-input.

Would be nice to get a short perfect formulated advice (all menu-items and input-fields) for vc newbies.

So we all can benefit from the great mydll.dll - script.

I m not using VC Express. I m using VS Express sorry ;-).

OK, did some research... we have to tell vc the detours include path by going to tools>options>vc++directories...then choose pulldown show directories for include path...and then add a new path for example: c:\Programme\Microsoft Research\Detours Express 2.1\include we can do the same for the library files...so linking is no longer a problem...and remeber not to type any backslash for output file like "/export:NullExport", seems to be "export:NullExport"

  1. create an emty dll-project
  2. copy paste the code
  3. set the include and library pathes for detours as metioned above
  4. build and compile

mydll - 0 error(s), 0 warning(s) ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Thanks Popo (and others). That helped me.

I found three files in C:\Documents and Settings\Terry\My Documents\Visual Studio 2008\Projects\mydll they are: mydll (VC+++ Intellisense Database) mydll (Microsoft Visual Studio Solution) mydll (Visual Studio User Options)

Is the second one the desired dll?

The example says to run: withdll.exe /d:mydll.dll "C:\Program Files\PokerStars".

is that done from the Start\Programs\Accessories\Command Prompt or from withine VS?

and when i rebuild:

LINK : fatal error LNK1104: cannot open file '/export:NullExport'

what the heck we are doing here?

popo: When compiled, the files go to /bin/debug in the folder where your project lies. Anonymous before popo: You first compile the solution (or recompile if you already have and encountered errors), then go to /bin/debug in your Projects/mydll folder to find your .dll

everyone breathe normally!

thank you Anonymous, I will look there

oh thx i did not noticed that i have two debug folders into my project-dir what did you typed in the linker/general/Output File - Field "/export:NullExport" or "export:NullExport" ?

Still get the error: LINK : fatal error LNK1104: cannot open file '/export:NullExport'

i have two mydll folders and two debug folders!

using export:NullExport

looks like the second one is created if you select Build, Rebuild Solution

after rebuild i get

mydll : error PRJ0021 : Tool 'Linker', Property 'Output File' contains invalid file name.

and you do the same without any error-messages? i use vs 2008 express, and you?

i still don't have a mydll.dll file

not exactly intuitive, but making some progress

may someone is kidding us.

i am using micro VS pro edition

========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

just can't find mydll.dll !

it is possible

but i think it should compile into a dll

how about this?

if anyone has compiled the above code into a dll, please post the file size here

the location of mydll.dll varies but it depends on using the export:NullExport hint or not. i get a mydll.dll when i m not using the export:NullExport. But then i get a prompt message: mydll.dll does not export function with ordinal #1

after hours of research...its so disappointing...was someone successfull? pleas make a detailed post.

compare settings and code:

in Solution Explorer i have 1 header file: header.h 0 resource files 1 source file: detourcode.cpp (or whatever you want to call it)

what do you have in your source file?

this is all "extra credit" anyway

as a further hint, once youve gotten detours running like James describes, open up the TRACEAPI.DLL source code and start commenting out the functions you don't want. you can selected from the top and scroll down to the bottom, comment them all using edit->advanced->comment Selection, and then go in and turn on the individual funcs you want to trace.

Thank you folks, unfortunately I get this strange message on my command

prompt wenn triggering withdll:

LoadLibraryEx(TRACEAPI.DLL) failed with error 126

TRACEAPI.DLL does not export function with ordinal #1

I m trying to rebuild XPokerBot and get the following output:

------ Rebuild All started: Project: XPokerBot.Hook, Configuration: Debug Win32 ------ Deleting intermediate and output files for project 'XPokerBot.Hook', configuration 'Debug|Win32' Compiling... cl : Command line warning D9035 : option 'Wp64' has been deprecated and will be removed in a future release ApplicationProxy.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(91) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(92) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\applicationproxy.cpp(132) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' OnlinePokerClient.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(91) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(92) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' OnlinePokerExecutor.cpp OnlineTableWindow.cpp PokerTimeCaptionDecoder.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(91) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(92) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\pokertimecaptiondecoder.cpp(78) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\pokertimecaptiondecoder.cpp(86) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' PokerTimePokerClient.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(91) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(92) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' PokerTimeTableWindow.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(91) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.hook\tablesummary.h(92) : warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpys instead. To disable deprecation, use CRTSECURENOWARNINGS. See online help for details. c:\programme\microsoft visual studio 9.0\vc\include\string.h(74) : see declaration of 'strcpy' stdafx.cpp XPokerBot.Hook.cpp Generating Code... Compiling manifest to resources... Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0 Copyright (C) Microsoft Corporation. All rights reserved. Linking... LINK : fatal error LNK1104: cannot open file 'libboostregex-vc90-mt-gd-135.lib' Build log was saved at "file://c:\00000pokeraibauen\XPokerBot\XPokerBot\XPokerBot.Hook\Debug\BuildLog.htm" XPokerBot.Hook - 1 error(s), 14 warning(s) ------ Rebuild All started: Project: XPokerBot.MfcView, Configuration: Debug Win32 ------ Deleting intermediate and output files for project 'XPokerBot.MfcView', configuration 'Debug|Win32' Compiling... cl : Command line warning D9035 : option 'Wp64' has been deprecated and will be removed in a future release stdafx.cpp c:\00000pokeraibauen\xpokerbot\xpokerbot\xpokerbot.mfcview\stdafx.h(57) : fatal error C1083: Cannot open include file: 'afxwin.h': No such file or directory Build log was saved at "file://c:\00000pokerai_bauen\XPokerBot\XPokerBot\XPokerBot.MfcView\Debug\BuildLog.htm" XPokerBot.MfcView - 1 error(s), 1 warning(s) ========== Rebuild All: 0 succeeded, 2 failed, 0 skipped =========

Think the main errors are include-erros, but i pointed VS to the boost-dir.

Could someone help, please?

Environment: WinXP, VS Express, Boost 1.35.0

Hm, yes I would do so...but right now I have to less diskspace ;-)...have to wait to buy some DVD's to free some space...but since several days I play break even by hand, may I will earn some money to get it ;-).

Thanks for your advise james.

I figured out that there is an installer for boost (was realtivly hidden on the page you mentioned), fo first I got the source-distribution ;-). The Link is: http://www.boostpro.com/products/free

exciting stuff. this site is the tip of the spear

@james: you > me

Could someone bring up an example how to point Visual Studio correctly to boost?

to avoid such things like this: c:\00000pokerai_bauen\xpokerbot\xpokerbot\xpokerbot.hook\stdafx.h(100) : fatal error C1083: Cannot open include file: 'regex.hpp': No such file or directory OnlinePokerClient.cpp

my boost is located in: C:\Programme\boost

[...] This directory (XPokerBot/boost) contains Debug and Release versions of the Boost regex library. You can link to these binaries assuming you've installed the Boost header files, even if you haven't gone to the trouble of building Boost yourself. [...] ...and which header-files should I put where...any idea? I used the boost installer.

popo, you need to add the following two directories to your IDE options (Tools->Options):

-C:\Programme\Boost\boost1331-C:\Programme\Boost\boost1331\boost

In VS2008, go to Tools->Options. Select 'Projects and Solutions' followed by 'VC++ Directories'. Then change the dropdown to say 'Show Directories for: INCLUDE files'. And add the above two lines to the list. Not sure how to get at this setting in VC Express but thats in VS2008.

You can also if worst comes to worst add the full path to the include:

include "c:\programs...etc\regex.hpp"

Kind of a hack. Oh and if you've installed some other version of Boost you'll need to tweak the above directories approp.

Thank you Jeremyx, I forgot that I solved this problem befor playing with the dir-locations. You rembered me the right notation.

I know VC Express is not the right IDE but can someone tell me where this files normaly resist when using VS Pro.?

LINK : fatal error LNK1104: cannot open file 'libboostregex-vc90-mt-gd-135.lib'

c:\00000pokerai_bauen\xpokerbot\xpokerbot\xpokerbot.mfcview\stdafx.h(57) : fatal error C1083: Cannot open include file: 'afxwin.h': No such file or directory

XPokerBot.MfcView - 1 error(s), 1 warning(s)

For all VS Express user, I found this comment which underlines James idea:

You're missing MFC to get afxwin.h and the Platform SDK for <windows.h>. They don't come included with the Express edition. MFC is a no-go but you can get the Platform SDK; search this forum for "SDK".

when trying WITHDLL /D:TRACEAPI.DLL C:\PROGRAM FILES\POKERSTARS\POKERSTARS.EXE I got the same message as Popo.

withdll.exe: LoadLibraryEx(TRACEAPI.DLL) failed with error 126. withdll.exe: Error: TRACEAPI.DLL does not export funtion with ordinal #1.

also, does it matter if it is? WITHDLL /D:TRACEAPI.DLL C:\PROGRAM FILES\POKERSTARS\POKERSTARS.EXE or WITHDLL /D:TRACEAPI.DLL "C:\PROGRAM FILES\POKERSTARS\POKERSTARS.EXE"

Hello James, what is the syntax to nmake a traceapi?

Use the form below to leave a comment.






Coding the Wheel has appeared on the New York Time's Freakonomics blog, Jeff Atwood's Coding Horror, and the front page of Reddit, Slashdot, Digg.

On Twitter

Thanks for reading!

If you enjoyed this post, consider subscribing to Coding the Wheel by RSS or email. You can also follow us on Twitter and Facebook. And even if you didn't enjoy this post, better subscribe anyway. Keep an eye on us.

Question? Ask us.

About

Poker

Coding the Wheel =
Code, poker, technology, games, design, geekery.


Hire

You've read our technical articles, you've tolerated our rants and raves. Now you can hire us anytime, day or night, for any project large or small.

Learn more

We Like

Speculation, by Edmund Jorgensen.