开发者

Interprocess communication between C# and C++

开发者 https://www.devze.com 2022-12-16 04:06 出处:网络
I\'m writing开发者_如何学Python a bot for a game, which has a C++ API interface (ie. methods in a Cpp dll get called by the game when events occur, the dll can call back methods in the game to trigger

I'm writing开发者_如何学Python a bot for a game, which has a C++ API interface (ie. methods in a Cpp dll get called by the game when events occur, the dll can call back methods in the game to trigger actions).

I don't really want to write my bot in C++, I'm a fairly experienced C# programmer but I have no C++ experience at all. So, the obvious solution is to use ipc to send event to a C# program, and send actions back to the C++ one, that way all I need to write in C++ is a basic framework for calling methods and sending events.

What would be the best way to do this? Sample code would be greatly appreciated as I no have particular desire to learn C++ at this point!


There are lots of different ways of doing IPC in Windows. For C# to C++, I'd be tempted to use Sockets as the API under both C++ (WinSock is OK once you get your head around it) and C# is pretty easy.

Named Pipes might be better though if you don't want to use sockets, and were designed specifically for IPC. The API under C++ seems pretty simple, example here.


One solution is to create a managed C++ class library with regular __declspec(dllexport) functions which call managed methods in a referenced C# class library.

Example - C++ code file in managed C++ project:

#include "stdafx.h"

__declspec(dllexport) int Foo(int bar) 
{
    csharpmodule::CSharpModule mod;
    return mod.Foo(bar);
}

C# Module (separate project in solution):

namespace csharpmodule
{
    public class CSharpModule
    {
        public int Foo(int bar)
        {
            MessageBox.Show("Foo(" + bar + ")");
            return bar;
        }
    }
}

Note that I am demonstrating that this is an actual .NET call by using a System.Windows.Forms.MessageBox.Show call.

Sample basic (non-CLR) Win32 console application:

__declspec(dllimport) int Foo(int bar);

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << Foo(5) << std::endl;
    return 0;
}

Remember to link the Win32 console application with the .lib file resulting from the build of the managed C++ project.


In such occasion, I would like to see a C++/CLI party and a C# one using the .NET Framework's named pipes.


I've already written a blocking version using pipes. But none of the methods the game calls are allowed to block

This issue is solved by running the blocking pipe functions in a separate thread. I found this issue looking for a better solution, but here is the basic idea of what I currently have:

#include <mutex>
// #include ...

HANDLE pipe;
char* pipeBuffer;
int pipeSize;
bool pipeHasData = false;
std::mutex m;
std::condition_variable cv;

void named_pipe()
{
    // optional
    int pid = _getpid();
    std::wstring pipe_name = L"\\\\.\\pipe\\Game-" + std::to_wstring(pid);
    
    // (very) shortened loop to show mutex lock
    // add your own error checking and retrying
    hPipe = init_named_pipe(pipe_name);
    while (true)
    {
        if (PeekNamedPipe(hPipe, NULL, 0, NULL, &bytesAvailable, NULL) == FALSE)
        {
            break;
        }

        pipeBuffer = buffer;
        pipeSize = dwRead;

        pipeHasData = true;
        {
            std::unique_lock<std::mutex> lock(m);
            cv.wait(lock);
        }
    }
}

void (__fastcall* oGame_Tick)(float deltaTime);
void __fastcall hkGame_Tick(float deltaTime)
{
    oGame_Tick(deltaTime);
    
    if (pipeHasData)
    {
        // parse the packet received in pipeBuffer and call your game actions
        // respond by writing back to pipe
        ProcessPipe(pipe, pipeBuffer, pipeSize);

        {
            m.lock();
            pipeHasData = false;
            pipeBuffer = nullptr;
            pipeSize = NULL;
            m.unlock();
            cv.notify_all();
        }
    }
}

// called once on dll attach
void __cdecl init()
{
    // hook gameloop tick function here
    
    // start loop in new thread
    std::thread t1(named_pipe);
    t1.detach();
}

Things to note:

  • This code will only process 1 packet per frame and will block until a response is sent. Instead you can use a Queue system where packets sent through the pipe are queued up from your pipe loop, and then processed in the main game thread (this is exactly how the games themselves do it!)
  • Limited by frame rate
  • Some game actions are only meant to be called once per frame and can otherwise crash the client. I've had this issue with Unreal Engine games (specifically when toggling UI related things twice in a single tick)
0

精彩评论

暂无评论...
验证码 换一张
取 消