c++ - 如何将对象(或指针)从 C++ .NET 应用程序发送到 VB 应用程序

标签 c++ vb.net interprocess

我有 2 个应用程序。

VB 应用程序 是用 .NET 3.5 编写的。这是一个相当大的应用程序。由于几个原因,我无法将其重写为 C++。我不确定这是否重要,但它是 x86 应用程序。

C++ 应用程序 是用 .NET 4.0 编写的。它是 x64 版本,将不支持 x86。现在 - 它是带有一些汇编代码的托管代码。稍后当我了解有关 C++ 的更多信息时,我将混合托管和非托管。它是 x64 版本,必须保持这种状态。

它应该扩展 VB 应用程序的功能 - 从相机捕获帧,对它们进行处理并将处理后的图像发送到 VB 应用程序。图像非常大(1920x1080x24bpp),我需要每秒处理 30-60 帧,所以它必须是有效的方式。

我的目标:

  1. 将位图从 C++ 应用程序“发送”到 VB 应用程序,当该位图到达时,VB 应用程序应该启动一些方法。

  2. 以另一种方式“发送”一些信息,从 VB 应用程序到 C++ 应用程序。它应该从 VB 应用程序 GUI 更改 C++ 应用程序处理参数。

  3. 如果可能 - 只发送一个指针和位图大小,而不是在 RAM 中复制整个数据。


可以说,我想要这样的东西:

VB端:

Function receivedBitmapFromCpp(BMP_POINTER?, BMP_SIZE_X?, BMP_SIZE_Y?, BMP_BPP?) As Integer Handles ????

End Function 

C++ 方面:

void sendBitmapToVb(BMP_POINTER?, BMP_SIZE_X?, BMP_SIZE_Y?, BMP_BPP?)
{
    int bitmapsendingresult = ?????
}

它可能是 System.Drawing.Bitmap,或者只是一些我将在 VB 应用程序中转换为 System.Drawing.Bitmap 的数组。没关系。


我的问题:

谁能解释一下,我该怎么做:

  • 发送一些对象数据(例如 System.Drawing.Bitmap),或从 VB 应用程序到 C++ 应用程序的更好的指向该数据的指针
  • 在 C++ 应用程序中接收该数据
  • 在接收/准备好数据时启动一些 C++ 函数(带有一些事件?)

最佳答案

使用共享内存循环缓冲区在进程之间交换数据。这可以使用 boost interprocess 作为 C++ dll 来实现,然后可以将该 dll 导入到您的 .Net 应用程序中。请注意,您将需要构建 32 位和 64 位版本的 boost 和您的共享内存 dll。我准备了一个 32 位和 64 位应用程序的示例,您可以运行它们,看看它有多快。我认为它应该足够快,但如果不是那么仍然可以使用多线程。

64 位生产者:

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <chrono>

struct shmem_info
{
    boost::interprocess::interprocess_mutex mutex;
    uint64_t pos;
    bool run;
};

int main(int argc, char *argv[])
{
    using namespace boost::interprocess;

    struct shm_remove
    {
        shm_remove() { shared_memory_object::remove("MySharedMemory"); shared_memory_object::remove("MySharedMemoryInfo");}
        //~shm_remove() { shared_memory_object::remove("MySharedMemory"); shared_memory_object::remove("MySharedMemoryInfo");}
    } remover;
    const size_t width = 1920;
    const size_t height = 1080;
    const size_t bytes_per_pixel = 3;
    const size_t frame_size = width*height*bytes_per_pixel;
    const size_t frames = 60;
    const size_t shmem_frames = 3 * frames;
    const size_t shmem_size = width * height * bytes_per_pixel * shmem_frames;

    std::cout << "Generating data ..." << std::endl;
    std::vector<uint8_t> frame(frame_size);
    
    // generate frame data
    for (size_t x = 0; x < width*height; ++x)
        for (size_t y = 0; y < bytes_per_pixel; ++y)
            frame[x*bytes_per_pixel + y] = (x%252) + y;

    std::cout << "Creating shared memory files ..." << std::endl;
    shared_memory_object shm(create_only, "MySharedMemory", read_write);
    shared_memory_object shm_info(create_only, "MySharedMemoryInfo", read_write);

    //Set size
    shm.truncate(shmem_size);
    shm_info.truncate(sizeof(shmem_info));

    //Map the whole shared memory in this process
    mapped_region region(shm, read_write);
    mapped_region region_info(shm_info, read_write);

    shmem_info *info = new (region_info.get_address()) shmem_info;
    {
        scoped_lock<interprocess_mutex> lock(info->mutex);
        info->pos = 0;
        info->run = true;
    }

    char c;
    std::cout << "Ready. Now start client application and wait for it to be ready." << std::endl;
    std::cout << "Then press a key and enter to start" << std::endl;
    std::cin >> c;
    std::cout << "Running ..." << std::endl;

    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    size_t times = 10;
    for (size_t t = 0; t < times; ++t)
    {
        for (size_t f = 0; f < shmem_frames; ++f)
        {
            // get pointer to the beginning of shared memory
            uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
            // move pointer to the next frame
            ptr += f*frame_size;

            // modify first data point for testing purposes
            frame[0] = f;
            frame[1] = f + 1;
            frame[2] = f + 2;

            // copy data to shared memory
            memcpy(ptr, &frame[0], frame_size);

            // update the position each "frames" number, doing that too frequently kills the performance
            if (f % frames == 0)
            {
                // this will lock access to the pos for the time of updating the pos only
                scoped_lock<interprocess_mutex> lock(info->mutex);
                info->pos += frames;
                std::cout << "write pos = " << info->pos << std::endl;
            }
        }
    }

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    size_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    std::cout << (double(times*shmem_frames*1000) / double(ms)) << " fps." << std::endl;

    winapi::sleep(2000);

    // stop run
    {
        scoped_lock<interprocess_mutex> lock(info->mutex);
        info->run = false;
    }

    return 0;
}

32 位消费者:

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <chrono>

struct shmem_info
{
    boost::interprocess::interprocess_mutex mutex;
    uint64_t pos;
    bool run;
};

int main(int argc, char *argv[])
{
    using namespace boost::interprocess;

    const size_t width = 1920;
    const size_t height = 1080;
    const size_t bytes_per_pixel = 3;
    const size_t frame_size = width*height*bytes_per_pixel;
    const size_t frames = 60;
    const size_t shmem_frames = 3 * frames;
    const size_t shmem_size = width * height * bytes_per_pixel * shmem_frames;

    std::vector<uint8_t> frame(frame_size);

    std::cout << "Opening shared memory files ..." << std::endl;

    //Open already created shared memory object.
    shared_memory_object shm(open_only, "MySharedMemory", read_write);
    shared_memory_object shm_info(open_only, "MySharedMemoryInfo", read_write);

    //Map the whole shared memory in this process
    mapped_region region(shm, read_only);
    mapped_region region_info(shm_info, read_write);

    shmem_info *info = static_cast<shmem_info*>(region_info.get_address());

    std::cout << "Ready." << std::endl;

    bool run = true;

    // first wait for processing to be started
    while (true)
    {
        {
            scoped_lock<interprocess_mutex> lock(info->mutex);
            if (info->run)
                break;
        }
        winapi::Sleep(1);
    }

    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    uint64_t pos = 0;
    uint64_t shm_pos = 0;
    while(run)
    {
        // wait for a new data
        {
            scoped_lock<interprocess_mutex> lock(info->mutex);
            run = info->run;
            if (info->pos == pos)
            {
                winapi::Sleep(1);
                continue;
            }
            // we've got the new data
            shm_pos = info->pos;
        }

        while (pos < shm_pos)
        {
            // get pointer to the beginning of shared memory
            uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
            // calculate the frame position in circular buffer and move pointer to that frame 
            ptr += (pos%shmem_frames)*frame_size;

            // copy data from shared memory
            memcpy(&frame[0], ptr, frame_size);

            //winapi::Sleep(1);
            ++pos;
            if (pos % frames == 0)
                std::cout << "read pos: " << pos << std::endl;
        }
    }

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    size_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    ms -= 2000; // producer waits 2 seconds before sets run=false
    std::cout << (double(pos*1000) / double(ms)) << " fps." << std::endl;

    return 0;
}

我使用 boost 1.58,第一个循环总是很慢,您可能需要在开始使用共享内存之前运行一个热身循环。需要将数据复制到共享内存中,但是为了读取指向帧的 shmem 指针,可以将其传递给 .Net 应用程序。然后,您需要确保您的 .Net 应用在数据被覆盖之前按时读取数据。

有用的链接:

boost interpocess

Simple example

编辑:我修改了源代码以显示大致可以达到的每秒帧数。在我的机器上是 190+ fps,所以考虑到在 .Net 应用程序和 c++ dll 之间传输数据/指针的开销,我希望它仍然高于所需的 60 fps。


上面的代码应该给你一个好的开始,你需要将生产者和消费者共享内存代码重构为一个公共(public)类并使其成为一个dll。将 c++ dll 导入 .Net 的方法很少。 How-to-Marshal-a-C-Class其中一些解释得很好。

现在回答你的问题:

Can someone explain, how can I:

send some object data (like System.Drawing.Bitmap for example), or better pointer to that data from VB application to C++ application

您需要使用 GetHbitmap() 方法从 Bitmap 获取 HBITMAP 并将其传递给 c++ dll。然后在 c++ dll 中,如果需要,将像素数据和其他位图信息复制到共享内存中(指向数据的指针不起作用)。如何在 .Net 和 c++ 之间执行此操作,请参阅 c-get-raw-pixel-data-from-hbitmap . this answer 特别有用.

receive that data in C++ application

然后要从共享内存中读取数据,您可能需要首先创建与共享内存中大小相同的空 Bitmap,并将其 HBITMAP 传递给 C++ dll 以填充像素数据。

start some C++ function (with some event?) when data is received/ready

您只需要像上面的代码一样不断地轮询共享内存以获取新数据。

关于c++ - 如何将对象(或指针)从 C++ .NET 应用程序发送到 VB 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31449582/

相关文章:

c++ - 实现提取和插入运算符 C++

c++ - Qt QOpenGLWidget wheelEvent 奇怪的行为

c++ - 这段代码不是线程安全的,不是吗?

mysql - 从 mysql 数据库返回 VB.Net 中当前插入行的自动递增 ID 号

Windows 的 C++ 互斥量

c++ - boost的managed_external_buffer在哪里定义的?

c++ - 使用c++从事件窗口获取选定的文本

vb.net - Solidworks EPDM API IEdmEnumeratorVariable5::SetVar 未按预期工作

vb.net - 如何将复选框添加到动态创建的标签页,然后将组框与每个复选框链接(VB.NET)?

java - 在 Java 中使用 IO 流