项目中有个场景,vb.net需要调用C++处理后的图片。
原来的处理方案是,C++处理图片后,保存图片到磁盘上,VB.NET再从磁盘上进行读取。
现在想对这个方案进行优化。内存映射文件的形式进行进程间通信是Windows下最高效的方法,因此打算用内存映射文件的形式来连接两个进程。.net端先新建内存映射文件,再调用C++对图片进行处理,处理后的结果写入.net端建好的内存映射文件中,然后.net端再读取内存映射文件。这样处理就可以不用在磁盘上进行读写操作了,节约了IO资源,也更加的高效。
内存映射文件包含虚拟内存中文件的内容。利用文件与内存之间的映射,应用程序(包括多个进程)可以通过直接在内存中进行读写来修改文件。
内存映射文件可以分为两种类型:
①持久内存映射文件:
持久文件是与磁盘上的源文件关联的内存映射文件。在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。这些内存映射文件适合用来处理非常大的源文件。
②非持久内存映射文件:
非持久文件是未与磁盘上的源文件关联的内存映射文件。当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。这些文件适用于为进程间通信(IPC)创建共享内存。
我仅仅是想进行进程之间的通信,所以选用了第二种,非持久化内存映射文件。
首先vb端先分配内存空间
Imports System.IO
Imports System.IO.Compression
Imports System.IO.MemoryMappedFiles
Imports System.Threading
Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateNew("img_1", 1024 * 1024)
然后调用c++编译好的命令行可执行文件
Dim p As New System.Diagnostics.Process
p.StartInfo.FileName = "D:\dev\workspaces\outputForMMF\answerSheet.exe"
p.StartInfo.Arguments = imgName & " " & xmlName
p.StartInfo.UseShellExecute = False
p.StartInfo.CreateNoWindow = True
p.Start()
p.WaitForExit()
c++端,对图片文件进行处理后,存入内存映射文件。
char *mmfName = "img_1";
//需要将char转为LPCWSTR
WCHAR wszClassName[20] = {0};
memset(wszClassName,0,sizeof(wszClassName));
MultiByteToWideChar(CP_ACP,0,mmfName,strlen(mmfName)+1,wszClassName,sizeof(wszClassName)/sizeof(wszClassName[0]));
//打开共享的文件对象。
HANDLE m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, wszClassName);
//获得映射视图
char* mmfm_base_address = (char*)MapViewOfFile(m_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, mmfSize);
DWORD error_code;
if(mmfm_base_address == NULL){
error_code = GetLastError();
if(error_code != SUCCESS){
cout<<"error code "<<error_code<<endl;
}
}else{
//由于C++中对图片的处理使用的是openCV,图片的格式是iplImage。需要将iplImage转为bmp,再存入内存映射文件,这样vb端才能解析出来。
//Iplimage *temp;存放的是处理好的图片结果,是全局变量
//以下的部分,是把iplimage转为bmp
//FILE *fpw;
//变量定义
BITMAPFILEHEADER strHead;
BITMAPINFOHEADER strInfo;
//初始化头文件。用0来填充内存区域
SecureZeroMemory(&strHead, sizeof(strHead));
SecureZeroMemory(&strInfo, sizeof(strInfo));
//初始化灰度图调色板
RGBQUAD *strPla = (RGBQUAD *)malloc(256*sizeof(RGBQUAD));//调色板的大小为1024字节
for (int i1 = 0; i1 < 256; i1++ ){
strPla[i1].rgbRed = strPla[i1].rgbGreen = strPla[i1].rgbBlue = i1;
strPla[i1].rgbReserved = 0;
}
//写bitmapFileHeader
strHead.bfType = 0x4d42;//写入字符"BM"
strHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 1024 + (temp->width + 3)/4*4 * temp->height;
strHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 1024;
//写bitmapInfoHeader
strInfo.biSize = sizeof(strInfo);
strInfo.biHeight = temp->height;
strInfo.biWidth = temp->width;
strInfo.biPlanes = 1;
strInfo.biBitCount = 8;
strInfo.biCompression = BI_RGB;
/*//保存bmp图片
if((fpw=fopen("M://abc.bmp","wb"))==NULL){
cout<<"create the bmp file error!"<<endl;
return NULL;
}
fwrite(&strHead,1,sizeof(strHead),fpw);
fwrite(&strInfo,1,sizeof(strInfo),fpw);
fwrite(strPla,1,1024,fpw);
BYTE *imagedata = NULL;
imagedata = (BYTE*)malloc((testBitmap->width + 3)/4*4 * testBitmap->height);
for (int k1 = 0; k1 < testBitmap->height; ++k1){
for (int k2 = 0; k2 < testBitmap->width; ++k2){
if (getPixelByTemp(testBitmap, k2 , k1)){
//黑
(*(imagedata + (testBitmap->height - k1 - 1) * ((testBitmap->width + 3)/4*4) + k2)) = 0;
}else{
//白
(*(imagedata + (testBitmap->height - k1 - 1) * ((testBitmap->width + 3)/4*4) + k2)) = 255;
}
}
}
fwrite(imagedata,1,(testBitmap->width + 3)/4*4 * testBitmap->height,fpw);
fclose(fpw); */
CopyMemory(mmfm_base_address, &strHead,sizeof(strHead));
char *imgData = temp->imageData;
char *data = NULL;
if (temp->nChannels == 3){
data = (char*)malloc((temp->width + 3)/4*4 * temp->height);
for(int i=0;i<temp->height;i++) for(int j=0;j<(temp->width + 3)/4*4;j++)
data[i * ((temp->width + 3)/4*4) + j] = temp->imageData[3*((temp->height - i - 1) * ((temp->width + 3)/4*4) + j)];
CopyMemory(mmfm_base_address + sizeof(strHead), &strInfo,sizeof(strInfo));
CopyMemory(mmfm_base_address + sizeof(strHead) + sizeof(strInfo), strPla, 1024);
CopyMemory(mmfm_base_address + sizeof(strHead) + sizeof(strInfo) + 1024, data, (temp->width + 3)/4*4 * temp->height);
}else if (temp->nChannels == 1){
strInfo.biHeight = -strInfo.biHeight;
CopyMemory(mmfm_base_address + sizeof(strHead), &strInfo,sizeof(strInfo));
CopyMemory(mmfm_base_address + sizeof(strHead) + sizeof(strInfo), strPla, 1024);
CopyMemory(mmfm_base_address + sizeof(strHead) + sizeof(strInfo) + 1024, imgData, (temp->width + 3)/4*4 * temp->height);
//for(int i=0;i<temp->height;i++) for(int j=0;j<(temp->width + 3)/4*4;j++)
//data[i * ((temp->width + 3)/4*4) + j] = temp->imageData[((temp->height - i - 1) * ((temp->width + 3)/4*4) + j)];
}else{
return false;
}
/*fwrite(data,1,(testBitmap->width + 3)/4*4 * testBitmap->height,fpw);
fclose(fpw);*/
/*FILE *fpw2;
if((fpw2=fopen("M://abc2.bmp","wb"))==NULL){
cout<<"create the bmp2 file error!"<<endl;
return NULL;
}
fwrite(mmfm_base_address,1,((testBitmap->width + 3)/4*4 * testBitmap->height + + sizeof(strHead) + sizeof(strInfo) + 1024),fpw2);
fclose(fpw2);*/
//卸载映射
UnmapViewOfFile(mmfm_base_address);
//关闭内存映射文件
CloseHandle(m_hMapFile);
}
完成了C++端的调用后,vb还需读出存入内存映射文件中的图片:
Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("img_1")
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
Dim reader As BinaryReader = New BinaryReader(Stream)
Dim bytes As Byte() = reader.ReadBytes(1024 * 1024)
'byte()转为bitmap
Dim newBitmap As Bitmap = Bitmap.FromStream(New MemoryStream(bytes))
newBitmap.Save("M:\" & mappingName & ".bmp")
End Using
End Using
mmf.Dispose()
这样就完成了VB与C++两个进程之间的通信。