C# 和 C++ 之间的进程间通信

发布于 2024-08-17 15:55:10 字数 277 浏览 3 评论 0原文

我正在为游戏编写一个机器人,它具有 C++ API 接口(即,当事件发生时,游戏会调用 Cpp dll 中的方法,该 dll 可以回调游戏中的方法来触发操作)。

我真的不想用 C++ 编写我的机器人,我是一个相当有经验的 C# 程序员,但我根本没有 C++ 经验。因此,显而易见的解决方案是使用 ipc 将事件发送到 C# 程序,并将操作发送回 C++ 程序,这样我需要用 C++ 编写一个用于调用方法和发送事件的基本框架。

最好的方法是什么?示例代码将不胜感激,因为我此时没有特别想要学习 C++ 的愿望!

I'm writing 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦回旧景 2024-08-24 15:55:10

在 Windows 中执行 IPC 的不同方法。对于 C# 到 C++,我很想使用 Sockets 作为 C++ 下的 API(一旦你了解了 WinSock 就可以了),而 C# 则非常简单。

命名管道可能会更好,如果你不这样做的话不想使用套接字,并且是专门为IPC设计的。 C++下的API看起来很简单,例如这里

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.

桃气十足 2024-08-24 15:55:10

一种解决方案是使用常规 __declspec(dllexport) 函数创建托管 C++ 类库,这些函数调用引用的 C# 类库中的托管方法。

示例 - 托管 C++ 项目中的 C++ 代码文件:

#include "stdafx.h"

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

C# 模块(解决方案中的单独项目):

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

请注意,我正在使用 System.Windows.Forms.MessageBox.Show 演示这是一个实际的 .NET 调用> 打电话。

基本(非 CLR)Win32 控制台应用程序示例:

__declspec(dllimport) int Foo(int bar);

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

请记住将 Win32 控制台应用程序与生成托管 C++ 项目所生成的 .lib 文件链接起来。

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.

无声无音无过去 2024-08-24 15:55:10

在这种情况下,我希望看到一个 C++/CLI 聚会和一个使用 .NET Framework 命名管道的 C# 聚会。

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

窗影残 2024-08-24 15:55:10

我已经使用管道编写了一个阻塞版本。但游戏调用的任何方法都不允许被阻塞

这个问题可以通过在单独的线程中运行阻塞管道函数来解决。
我发现这个问题正在寻找更好的解决方案,但这是我目前的基本想法:

#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();
}

需要注意的事项:

  • 此代码每帧只会处理 1 个数据包,并且会阻塞,直到发送响应为止。
    相反,您可以使用队列系统,其中通过管道发送的数据包从管道循环中排队,然后在主游戏线程中进行处理(这正是游戏本身的做法!)
  • 受帧速率限制
  • 某些游戏操作仅意味着每帧调用一次,否则可能会使客户端崩溃。我在使用虚幻引擎游戏时遇到了这个问题(特别是在一次切换 UI 相关内容两次时)

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文