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

Kam*_*mil 14 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 
Run Code Online (Sandbox Code Playgroud)

C++方面:

void sendBitmapToVb(BMP_POINTER?, BMP_SIZE_X?, BMP_SIZE_Y?, BMP_BPP?)
{
    int bitmapsendingresult = ?????
}
Run Code Online (Sandbox Code Playgroud)

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


我的问题:

有人可以解释一下,我该怎么办:

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

doq*_*tor 5

使用共享内存循环缓冲区在进程之间交换数据.这可以使用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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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

有用的链接:

促进interpocess

简单的例子

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


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

现在回答你的问题:

有人可以解释一下,我该怎么办:

发送一些对象数据(例如System.Drawing.Bitmap),或者从VB应用程序到C++应用程序的更好的指向该数据的指针

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

在C++应用程序中接收该数据

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

收到/准备好数据时,启动一些C++函数(有一些事件吗?)

您只需要连续轮询共享内存以获取新数据,如上面的代码所示.


小智 -2

您正在使用.Net。所以你可以使用互操作库。它包含一个 IntPtr 类,可用于封装对 C++ 指针的访问。位图类甚至有一个带有 IntPtr 的构造函数。查看 MSDN 中的 Bitmap 和 IntPtr。

  • @darkfirewave - 我否决了你的答案。问题中的详细信息指出,这是两个可执行应用程序,它们具有不同的目标(x64 - x86)。由于多种原因,您的互操作解决方案在这种情况下将不起作用。 (2认同)