上面博客C#和python通过socket方法进行通信_jiugeshao的专栏-CSDN博客提到接下来讲下如何通过c#共享内存方式来读写数据及图像,以给c#和python之间通过共享内存方式读写图片埋下伏笔。
所实验的界面长如下样子(右边是panel控件里放入了一个picturebox控件):
成员按变量有如下:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32", EntryPoint = "GetLastError")]
public static extern int GetLastError();
const int INVALID_HANDLE_VALUE = -1;
const int ERROR_ALREADY_EXISTS = 183;
const int FILE_MAP_WRITE = 0x0002;
int m_MemSize = 0;
IntPtr m_hSharedMemoryFile = IntPtr.Zero;
IntPtr m_pwData = IntPtr.Zero;
const int PAGE_READWRITE = 0x04;
string sharememoryName = "img";
1.c#往共享内存里写入字符串
对应界面上Write按钮的回调函数:
try
{
long t = 1 << 10 << 10;
var mmf = MemoryMappedFile.CreateOrOpen("test1", t, MemoryMappedFileAccess.ReadWrite);
var viewAccessor = mmf.CreateViewAccessor(0, t);
string s = textBox1.Text; ;
viewAccessor.Write(0, s.Length);
viewAccessor.WriteArray<char>(4, s.ToArray(), 0, s.Length);
MessageBox.Show("write ok");
}
catch(System.Exception s)
{
MessageBox.Show(s.Message);
}
2.c#读取共享内存里的字符串
对应界面上Read按钮的回调函数
long capacity = 8000;
var mmf = MemoryMappedFile.OpenExisting("test1");
MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, capacity);
int strLength = viewAccessor.ReadInt32(0);
char[] charsInMMf = new char[strLength];
viewAccessor.ReadArray<char>(4, charsInMMf, 0, strLength);
textBox2.Text = new string(charsInMMf);
MessageBox.Show("ok");
运行程序可check下功能
选中如上Write按钮,往共享内存里写入了hello world
点击Read按钮,会从共享内存里读取数据并显示到textbox中,可看到可以正确读取
3.初始化一个图像共享内存
对应界面上的Init ImageShareMemory按钮的回调函数
// 获取图片大小从而知道创建多大的共享内存
byte[] vs = GetPictureData(@"D:\2.jpg");
int lngSize = vs.Length;
m_MemSize = lngSize;
if (lngSize <= 0 || lngSize > 100000000)
{
MessageBox.Show("error,check the size!");
return;
}
//创建内存共享体(INVALID_HANDLE_VALUE)
m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)lngSize, sharememoryName);
if (m_hSharedMemoryFile == IntPtr.Zero)
{
MessageBox.Show("创建失败!");
return;
}
else
{
if (GetLastError() == ERROR_ALREADY_EXISTS) //已经创建
{
MessageBox.Show("error, 已经创建过!");
return;
}
else //新创建
{
MessageBox.Show("新创建OK");
}
}
m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
if (m_pwData == IntPtr.Zero)
{
CloseHandle(m_hSharedMemoryFile);
MessageBox.Show("create fail"); //创建内存映射失败
}
else
{
MessageBox.Show("create ok");
}
GetPictureData函数内容如下:
public byte[] GetPictureData(string imagepath)
{
FileStream fs = new FileStream(imagepath, FileMode.Open);
byte[] byData = new byte[fs.Length];
fs.Read(byData, 0, byData.Length);
fs.Close();
return byData;
}
4.关闭一个图像共享内存
对应界面上Close shareMemory按钮的回调函数
if (m_hSharedMemoryFile != IntPtr.Zero)
{
UnmapViewOfFile(m_pwData);
CloseHandle(m_hSharedMemoryFile);
m_hSharedMemoryFile = IntPtr.Zero;
m_pwData = IntPtr.Zero;
}
MessageBox.Show("close成功");
5.往共享内存里写入图片
对应界面上write Img按钮的回调函数
//把图片转换为byte数组
byte[] vs = GetPictureData(@"D:\5.jpg");
int lngAddr = 0;
int lngSize = vs.Length;
if (lngAddr + lngSize > m_MemSize)
MessageBox.Show("超出数据区");
DateTime SingleStartTime;
TimeSpan SingleTimeSpan;
string SingleCT;
SingleStartTime = DateTime.Now;
Marshal.Copy(vs, lngAddr, m_pwData, lngSize);
SingleTimeSpan = DateTime.Now - SingleStartTime;
SingleCT = (SingleTimeSpan.TotalMilliseconds / 1000).ToString("0.000");
Console.WriteLine("写耗时" + SingleCT + "ms");
MessageBox.Show("write img success");
6. 从共享内存里读取图片
对应界面上read Img按钮的回调函数
byte[] tempbyte = new byte[m_MemSize];
MemoryMappedFile file = MemoryMappedFile.OpenExisting(sharememoryName);
MemoryMappedViewStream stream = file.CreateViewStream();
stream.Read(tempbyte, 0, m_MemSize);
System.IO.MemoryStream ms = new System.IO.MemoryStream(tempbyte);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
img.Save("D:\\sharemempry.bmp");
pictureBox1.Width = img.Size.Width;
pictureBox1.Height = img.Size.Height;
pictureBox1.Image = img;
运行程序可以验证下是否能够正常往共享内存里存入图片和读取图片
点击Init ImageShareMemory按钮后,再点击write Img按钮,再点击read Img按钮
图像便在右侧显示出来了
程序中所用的2.jpg和5.jpg附上
链接:https://pan.baidu.com/s/18iSTzpEDUC4GcZ0I4yU2nA
提取码:0ip5
至此,c#通过共享内存方式读取信息演示完毕
接下来介绍如何用python去获得c#往共享内存里写入的图片,重点讲解如何使用swig封装c++来做python的扩展库,
补充:由于上面演示的是c#往共享内存里写入图片,同时也是用c#从共享内存里读,所以是以文件流数据形式存入共享内存里的,但后python进行交互时,应该是指把图像的数据放入到共享内存里,下面就说明下如何操作:
A. 修改GetPictureData函数中代码如下:
public byte[] GetPictureData(string imagepath)
{
//FileStream fs = new FileStream(imagepath, FileMode.Open);
//byte[] byData = new byte[fs.Length];
//fs.Read(byData, 0, byData.Length);
//fs.Close();
Bitmap srcBmp = new Bitmap(imagepath);
BitmapData bmdata = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
IntPtr pSrc = bmdata.Scan0;
int iBytes = srcBmp.Width * srcBmp.Height * 3;
byte[] s = new byte[iBytes];
System.Runtime.InteropServices.Marshal.Copy(pSrc, s, 0, iBytes);
return s;
}
同时增加下如下引用
using System.Drawing.Imaging;
B.上面代码即可以实现获得图像的数据区域,接下来修改read Img中的代码,如下:
private void button6_Click(object sender, EventArgs e)
{
byte[] tempbyte = new byte[m_MemSize];
MemoryMappedFile file = MemoryMappedFile.OpenExisting(sharememoryName);
MemoryMappedViewStream stream = file.CreateViewStream();
stream.Read(tempbyte, 0, m_MemSize);
//System.IO.MemoryStream ms = new System.IO.MemoryStream(tempbyte);
//System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
//img.Save("D:\\sharemempry.bmp");
Bitmap bmp = new Bitmap(500, 375);
BitmapData bData = bmp.LockBits(new Rectangle(0, 0, 500, 375),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
Marshal.Copy(tempbyte, 0, bData.Scan0, tempbyte.Length);
bmp.UnlockBits(bData);
pictureBox1.Width = 500;
pictureBox1.Height = 375;
pictureBox1.Image = bmp;
}
测试图片的大小是500*375的彩色图像,代码中大小有写死,各位可以拿我的测试图片实验,也可以拿自己的来实验,重新运行代码,按顺序按照上面方法点击按钮后出现如下界面:
也可以正确往共享内存里写和从共享内存里读