如何用C++无窗口打开浏览器并模拟表单提交与按钮点击
Hey there! Let's tackle your problem of automating browser actions (like filling forms and clicking buttons) without showing a window or simulating mouse/keyboard inputs.
First, let's address why your existing code doesn't work:
bool open_browser(const char* url, HWND parent = NULL) { HINSTANCE result = ShellExecuteA(parent, NULL, url, NULL, NULL, SW_SHOWNORMAL); return ((int)result > 32); }
This uses ShellExecuteA, which explicitly launches the browser in a visible window (SW_SHOWNORMAL). Even if you swapped that for SW_HIDE, you'd still be running a browser with a hidden window—not the windowless, programmatic control you're after.
The Best Solution: Headless Chrome + Chrome DevTools Protocol (CDP)
Since you prefer Chrome, its Headless mode is exactly what you need. It runs the full Chrome browser engine without any visible UI, and you can control it using the Chrome DevTools Protocol (CDP)—a set of APIs that let you simulate user actions, manipulate the DOM, execute JavaScript, and more, all via HTTP/WebSocket.
How to Implement This in C++
Launch Headless Chrome Programmatically
UseCreateProcessto start Chrome in headless mode with remote debugging enabled. This lets your C++ app connect to Chrome's debug endpoint:#include <windows.h> bool launch_headless_chrome() { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; // Command line: start Chrome in headless mode, listen on port 9222 for debug commands LPCSTR cmd_line = "chrome.exe --headless=new --remote-debugging-port=9222 --disable-gpu"; if (!CreateProcessA(NULL, (LPSTR)cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { return false; } // Clean up handles (optional, but good practice) CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return true; }Control Chrome via CDP
CDP uses JSON-RPC over HTTP or WebSocket. For C++, you can use:libcurlto send HTTP requests (for initial setup and simple commands)- A WebSocket library like
libwebsocketsorwebsocketppfor real-time interactions (better for ongoing automation)
Example workflow to fill a text box and click a button:
- Send a
Page.navigatecommand to load your target URL. - Wait for the page to load using
Page.loadEventFired. - Execute JavaScript to fill the text box:
{"id":1,"method":"Runtime.evaluate","params":{"expression":"document.getElementById('username').value = 'your_username';"}} - Execute JavaScript to click the button:
{"id":2,"method":"Runtime.evaluate","params":{"expression":"document.getElementById('submit-button').click();"}}
You'd send these JSON commands to Chrome's debug endpoint (e.g.,
http://localhost:9222/json/newto create a new tab, then use the returned WebSocket URL for further commands).
Alternative: libcurl (For Simple, Non-JS Pages)
If your target page has no complex JavaScript (e.g., a basic HTML form without client-side validation or dynamic behavior), libcurl can work. But note:
- You can't "click a button" directly—instead, you need to manually construct the POST request that the button would trigger.
- You'll need to inspect the form's HTML to get field names, CSRF tokens (if any), and the form's action URL.
Example with libcurl to submit a form:
#include <curl/curl.h> bool submit_form_with_curl() { CURL* curl = curl_easy_init(); if (!curl) return false; // Form data (match the form's input names) const char* post_data = "username=your_username&password=your_password"; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/login"); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); // Execute the request CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); return (res == CURLE_OK); }
Why CGI/Socket Isn't the Right Fit
- CGI: This is a server-side technology for handling web requests—it has nothing to do with client-side browser automation.
- Raw Sockets: You could implement HTTP requests manually with sockets, but this is just reinventing what
libcurlalready does well. It still can't handle JavaScript-driven interactions.
Key Takeaways
- Use Headless Chrome + CDP if you need to simulate real user actions (including handling JavaScript, dynamic pages, and button clicks) without a window. This is the most robust solution.
- Use libcurl only for simple, static forms where you don't need to interact with client-side logic.
内容的提问来源于stack exchange,提问作者lucky simon

