### WebSocket Message Format Example (Plaintext) Source: https://context7.com/x64dbg/plugindevhelper/llms.txt Demonstrates the basic text-based message format for communicating actions and plugin filenames over WebSocket. This format is used to instruct x64dbg to unload or reload plugins. ```plaintext unload:MyPlugin.dp32 reload:MyPlugin.dp64 ``` -------------------------------- ### Start PluginDevServer WebSocket Server (C#) Source: https://context7.com/x64dbg/plugindevhelper/llms.txt Starts a WebSocket broadcast server on port 4649 using the WebSocketSharp library. It registers a broadcast service at the '/PluginDevHelper' endpoint to relay messages between the build tool and x64dbg instances. The server runs indefinitely until interrupted. ```csharp using WebSocketSharp.Server; class Program { static void Main(string[] args) { // Create server on port 4649 var server = new WebSocketServer(4649); // Register the broadcast service at /PluginDevHelper endpoint server.AddWebSocketService("/PluginDevHelper"); server.Start(); if (server.IsListening) { Console.WriteLine("Listening on port {0}", server.Port); // Output: Listening on port 4649 // Output: - /PluginDevHelper } // Keep running until Ctrl+C Thread.Sleep(Timeout.Infinite); server.Stop(); } } // BroadcastService.cs - Message broadcasting handler class BroadcastService : WebSocketBehavior { protected override void OnMessage(MessageEventArgs e) { // Broadcast received messages to all connected clients Sessions.Broadcast(e.RawData); } } ``` -------------------------------- ### C++ Plugin Initialization and WebSocket Connection Source: https://context7.com/x64dbg/plugindevhelper/llms.txt Initializes WinSock for WebSocket communication and starts a background thread to maintain a persistent connection to the PluginDevServer. Includes automatic retry logic for connection failures with a maximum of 50 attempts. This code is intended for the main plugin file (e.g., PluginDevHelper.cpp). ```cpp // PluginDevHelper/PluginDevHelper.cpp - Plugin lifecycle #define MAX_RETRIES 50 // Plugin initialization - called when x64dbg loads the plugin bool pluginInit(PLUG_INITSTRUCT* initStruct) { // Initialize WinSock for WebSocket communication WSADATA wsaData; INT rc = WSAStartup(MAKEWORD(2, 2), &wsaData); if (rc) { printf("WSAStartup Failed.\n"); return false; } // Get x64dbg plugins directory path GetModuleFileNameW(GetModuleHandleW(nullptr), pluginDir, _countof(pluginDir)); auto slash = wcsrchr(pluginDir, L'\\'); if (slash) slash[1] = L'\0'; wcscat_s(pluginDir, L"plugins\\"); // Start WebSocket connection thread hWebSocketThread = CreateThread(nullptr, 0, WebSocketThread, nullptr, 0, nullptr); return !!hWebSocketThread; } // WebSocket connection thread with retry logic static DWORD WINAPI WebSocketThread(LPVOID) { int curRetry = 0; while (curRetry < MAX_RETRIES) { std::this_thread::sleep_for(500ms); std::unique_ptr ws(WebSocket::from_url(WEBSOCKET_URL)); if (!ws) { dprintf("Failed to connect to %s\n", WEBSOCKET_URL); curRetry++; continue; } std::unordered_set unloadedPlugins; while (ws->getReadyState() != WebSocket::CLOSED) { HandleMessage(ws, unloadedPlugins); if (bStopWebSocketThread) { ws->close(); return 0; } } } return 0; } // Plugin shutdown - called when x64dbg unloads the plugin void pluginStop() { bStopWebSocketThread = true; WaitForSingleObject(hWebSocketThread, INFINITE); CloseHandle(hWebSocketThread); } ``` -------------------------------- ### WebSocket Message Protocol Source: https://context7.com/x64dbg/plugindevhelper/llms.txt Details the WebSocket endpoint and message format for controlling x64dbg plugins. ```APIDOC ## WebSocket Message Protocol ### Description This section describes the WebSocket endpoint and the message format used for communication with the PluginDevHelper server. All messages are broadcast to connected x64dbg instances. ### Method WebSocket ### Endpoint `ws://localhost:4649/PluginDevHelper` ### Parameters #### Message Format - **action** (string) - Required - The action to perform on the plugin (e.g., `unload`, `reload`). - **plugin_filename** (string) - Required - The filename of the plugin, including its extension (e.g., `MyPlugin.dp32`, `MyPlugin.dp64`). The plugin name is extracted by removing the extension. ### Actions - **unload**: Request plugin unload before build. - **reload**: Request plugin reload after build. ### Examples - `unload:MyPlugin.dp32` - `unload:MyPlugin.dp64` - `reload:MyPlugin.dp32` - `reload:MyPlugin.dp64` ### Message Flow 1. **Build Start**: PluginDevBuildTool sends `unload:plugin.dp32`. 2. **Broadcast**: PluginDevServer broadcasts the message to all connected x64dbg instances. 3. **Unload Plugin**: PluginDevHelper receives the message and executes `plugunload "plugin"`. 4. **File Handle Release**: x64dbg releases the file handle for the plugin. 5. **Build Proceed**: PluginDevBuildTool detects exclusive access is released, and the build proceeds. 6. **Build Complete**: PluginDevBuildTool sends `reload:plugin.dp32`. 7. **Reload Plugin**: PluginDevHelper receives the message and executes `plugload "plugin"`. 8. **Plugin Loaded**: The plugin is loaded with the new code. ``` -------------------------------- ### Visual Studio Build Events for Plugin Hot-Reloading Source: https://context7.com/x64dbg/plugindevhelper/llms.txt Configures Visual Studio project settings to automatically unload and reload plugins during the build process. The Pre-Link event command unloads the plugin before linking, and the Post-Build event command reloads it after a successful build, facilitating rapid development cycles. ```xml if exist "$(ProjectDir)PluginDevBuildTool.exe" ("$(ProjectDir)PluginDevBuildTool.exe" unload "$(TargetPath)") if exist "$(ProjectDir)PluginDevBuildTool.exe" ("$(ProjectDir)PluginDevBuildTool.exe" reload "$(TargetPath)") ``` -------------------------------- ### Handle Plugin Messages in x64dbg Plugin (C++) Source: https://context7.com/x64dbg/plugindevhelper/llms.txt The PluginDevHelper x64dbg plugin listens for WebSocket messages to manage plugin unload and reload actions. It maintains a set of unloaded plugins to ensure that reload commands are only processed for plugins that were intentionally unloaded by the system. Commands are executed using x64dbg's built-in script commands. ```cpp // PluginDevHelper/PluginDevHelper.cpp - WebSocket message handling #define WEBSOCKET_URL "ws://localhost:4649/PluginDevHelper" static void HandleMessage(std::unique_ptr &ws, std::unordered_set &unloadedPlugins) { ws->poll(20); ws->dispatch([&unloadedPlugins](const std::string& message) { // Message format: "action:pluginname.dp32" or "action:pluginname.dp64" // Example: "unload:MyPlugin.dp32" or "reload:MyPlugin.dp64" auto colonIdx = message.find(':'); auto plugin = message.substr(colonIdx + 1); // "MyPlugin.dp32" auto actionName = message.substr(0, colonIdx); // "unload" or "reload" auto pluginName = plugin.substr(0, plugin.rfind('.')); // "MyPlugin" auto performAction = [&](const char* commandStr) { char cmd[4096] = ""; _snprintf_s(cmd, _TRUNCATE, "%s \"%s\"", commandStr, pluginName.c_str()); dprintf("PluginDevHelper: %s\n", cmd); DbgCmdExec(cmd); // Execute x64dbg command asynchronously }; if (actionName == "unload") { unloadedPlugins.insert(plugin); // Track unloaded plugins performAction("plugunload"); // x64dbg output: [PluginDevHelper] PluginDevHelper: plugunload "MyPlugin" // [PLUGIN] MyPlugin.dp32 unloaded } else if (actionName == "reload") { auto unloadedItr = unloadedPlugins.find(plugin); if (unloadedItr != unloadedPlugins.end()) { unloadedPlugins.erase(unloadedItr); performAction("plugload"); // x64dbg output: [PluginDevHelper] PluginDevHelper: plugload "MyPlugin" // [PLUGIN] MyPlugin v1 Loaded! } } }); } ``` -------------------------------- ### Trigger Plugin Reload with PluginDevBuildTool (C#) Source: https://context7.com/x64dbg/plugindevhelper/llms.txt The PluginDevBuildTool's reload command signals x64dbg to reload a plugin. It checks for a '.unloaded' marker file to ensure the plugin was previously unloaded by the system. This is typically configured as a Visual Studio Post-Build Event. ```csharp // Usage: PluginDevBuildTool reload "C:\\Projects\\MyPlugin\\bin\\MyPlugin.dp32" // Visual Studio Post-Build Event Command Line: // if exist "$(ProjectDir)PluginDevBuildTool.exe" ("$(ProjectDir)PluginDevBuildTool.exe" reload "$(TargetPath)") // Implementation logic if (action == "reload") { var unloadedPath = pluginPath + ".unloaded"; // Only reload if plugin was previously unloaded by us if (File.Exists(unloadedPath)) { File.Delete(unloadedPath); // Clean up marker file using (var ws = new WebSocket("ws://127.0.0.1:4649/PluginDevHelper")) { ws.Connect(); ws.Send($"reload:{pluginName}"); // Send reload command } Console.WriteLine($"PluginDevBuildTool: Requested reload of {pluginName}"); } return 0; } // Expected console output: // PluginDevBuildTool: Requested reload of MyPlugin.dp32 ``` -------------------------------- ### PluginDevBuildTool Unload Command (C#) Source: https://context7.com/x64dbg/plugindevhelper/llms.txt A command-line tool that attempts to gain exclusive file access to a plugin DLL. If unsuccessful, it sends an 'unload' command via WebSocket to the PluginDevServer and polls for exclusive access for up to 5 seconds. This is intended to be used as a Pre-Link Event in Visual Studio build settings. ```csharp // Usage: PluginDevBuildTool unload "C:\Projects\MyPlugin\bin\MyPlugin.dp32" // Visual Studio Pre-Link Event Command Line: // if exist "$(ProjectDir)PluginDevBuildTool.exe" ("$(ProjectDir)PluginDevBuildTool.exe" unload "$(TargetPath)") // Implementation in PluginDevBuildTool/Program.cs static int Main(string[] args) { var action = args[0]; // "unload" var pluginPath = args[1]; // Full path to plugin DLL var pluginName = Path.GetFileName(pluginPath); // "MyPlugin.dp32" // Check if file can be opened exclusively bool CanOpenExclusively() { try { using (var fs = new FileInfo(pluginPath).Open( FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } // If this succeeds, we have exclusive access return true; } catch { return false; } // File is in use } // If already have exclusive access, nothing to do if (CanOpenExclusively()) { Console.WriteLine($"PluginDevBuildTool: Exclusive access to {pluginName} already obtained!"); return 0; } // Send unload request via WebSocket Console.WriteLine($"PluginDevBuildTool: Failed to obtain exclusive access, requesting to unload {pluginName}"); using (var ws = new WebSocket("ws://127.0.0.1:4649/PluginDevHelper")) { ws.Connect(); ws.Send($"unload:{pluginName}"); // Message format: "action:filename" } // Poll for up to 5 seconds waiting for exclusive access Thread.Sleep(200); // Initial small delay before polling for (var i = 0; i < 10; i++) // Poll 10 times with 500ms interval = 5 seconds total { if (CanOpenExclusively()) { File.WriteAllText(pluginPath + ".unloaded", "1"); // Mark as unloaded Console.WriteLine($"PluginDevBuildTool: Exclusive access to {pluginName} obtained!"); return 0; } Console.WriteLine($"PluginDevBuildTool: Waiting 500ms for exclusive access..."); Thread.Sleep(500); } return 1; // Failed to unload after waiting } // Expected console output during build: // PluginDevBuildTool: Failed to obtain exclusive access, requesting to unload MyPlugin.dp32 // PluginDevBuildTool: Exclusive access to MyPlugin.dp32 obtained! ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.