我有2个申请。
VB应用程序是用.NET 3.5编写的。这是一个很大的应用程序。由于某些原因,我无法将其重写为C ++。我不确定这是否重要,但它是x86应用程序。
C ++应用程序是用.NET 4.0编写的。它是x64版本,不会有x86支持。现在 - 它是带有一些汇编代码的托管代码。当我更多地了解C ++时,我将在以后混合托管和非托管。它是x64版本,必须保持这样。
它应该扩展VB应用程序功能 - 从相机捕获帧,使用它们执行某些操作并将处理后的图像发送到VB应用程序。图像非常大(1920x1080x24bpp),我需要每秒处理30-60帧,所以它必须是有效的方式。
我的目标:
"发送"从C ++应用程序到VB应用程序的位图,VB应用程序应该在该位图出现时启动一些方法。
"发送"另一方面,从VB应用程序到C ++应用程序的一些信息。它应该从VB应用程序GUI更改C ++应用程序处理参数。
如果可能的话 - 只发送一个指针和位图大小,而不是将整个数据复制到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的数组。它并不重要。
我的问题:
有人可以解释一下,我该怎么做:
答案 0 :(得分: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;
}
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应用程序在被覆盖之前及时读取数据。
有用的链接:
编辑:我修改了源代码,以显示大致可以实现的每秒帧数。在我的机器上是190+ fps所以我希望它仍然高于所需的60 fps,考虑到在.Net app和c ++ dll之间传输数据/指针的开销。
上面的代码应该给你一个良好的开端,你需要将生产者和消费者共享内存代码重构为一个公共类并使其成为一个DLL。将c ++ dll导入.Net的方法很少。 How-to-Marshal-a-C-Class很好地解释了其中一些。
现在回答你的问题:
有人可以解释一下,我该怎么做:
发送一些对象数据(例如System.Drawing.Bitmap),或者从VB应用程序到C ++应用程序的更好的指向该数据的指针
您需要使用HBITMAP
方法从Bitmap
获取GetHbitmap()
并将其传递给c ++ dll。然后在c ++ dll中将像素数据和其他位图信息复制到共享内存中(指向数据的指针不起作用)。如何在.Net和c ++之间进行操作,请参阅c-get-raw-pixel-data-from-hbitmap。特别有用的是this answer。
在C ++应用程序中接收该数据
然后,要从共享内存中读取数据,您可能需要先在共享内存中创建相同大小的空Bitmap
,然后将其HBITMAP传递给C ++ dll以填充像素数据。
在接收/准备好数据时启动一些C ++函数(有一些事件吗?)
您只需要连续轮询共享内存以获取新数据,例如上面的代码。
答案 1 :(得分:1)
您应该将VB应用程序编译为x64架构。由于你的一个二进制文件是在x64中,所以你应该可以使用它。从您的描述中我了解到您使用的是托管C ++ .NET。你的C ++项目应该编译成一个dll,因为它除了扩展VB之外什么都不做。因此,您只需将.NET dll导入VB应用程序即可。现在,您可以在VB中使用托管C ++ .NET功能。 但是,如果您不使用托管C ++ .NET。你可以选择一个休息。 您可以为C ++创建本机C包装器。它可以是非常基本的,你可以创建一个函数女巫接受一些参数,并将函数指针作为回调(你想要一个回调)来获取位图。使用inter操作来探测这些函数的C ++ dll(在Runtime.Interop命名空间中找到inter运算符)。在VB应用程序中围绕这些函数制作一个VB包装器。您可以将VB中的方法转换为表示函数指针的委托,ant将这些委托作为回调传递。 或者,您可以围绕您的本机C ++方法制作托管C ++ .NET包装器。同样,它可以是基本的,您可以在托管C ++ .NET中创建基本类方法,只需将参数转发给本机代码。将所有内容编译为托管C ++ .NET DLL,并将该DLL包含在VB应用程序中并使用其功能。欢呼声。
答案 2 :(得分:1)
我会使用Named Pipe。它允许您在进程之间发送和接收数据。收到数据后,您可以随心所欲地执行任何操作。也就是说,您无法在进程之间共享对象,因此请勿尝试发送指针。好消息是,根据此page,.NET 3.5和.NET 4.0支持命名管道。如果你想要例子,网上有很多。
答案 3 :(得分:0)
您可以执行以下操作:
FileSystemWatcher
所以当第一个
application(c ++)自动将任何文件添加到common文件夹
第二个应用程序将读取它您可以使用此链接更多地了解有关FileSystemWatcher
的learn您可以让第一个应用程序以特定方式命名文件,例如p-dd-mm-yyyy-hh-mm-ss,第二个应用程序将其重命名为c-dd-mm-yyyy-hh-mm -ss wher p:pending,c:completed and dd-mm-yyyy-hh-mm-ss datetime value
根据您的评论,希望您能找到此处提到的解决方案How to do CreateFileMapping in a C++ DLL and access it in C#, VB and C++
答案 4 :(得分:-2)
您正在使用.Net。所以你可以使用interop lib。它包含一个IntPtr类,可用于封装对c ++指针的访问。位图类甚至有一个带IntPtr的构造函数。看看MSDN for Bitmap和IntPtr。