大多算法都有自己特定的数据流程,给调试带来麻烦。
如果在几个关键节点,能够把当前数据发送给外部的显示程序实时显示,很多问题都能一目了然。
在进程之间传递数据的最好手段,应该是共享内存了。当然发送者和接收者之间还要约定好消息ID,代表特定的数据格式,接收者得到数据之后才能理解显示。
下面的演示代码我已经用过很长时间,比较稳定了,与大家分享:
class CShareMem//通过名字标识的共享内存
{
protected:
TCHAR m_szShareName[64];
HANDLE m_hShareMem; //共享内存句柄
BYTE * m_pBuffer; //映射地址
bool m_bFirst; //是否第一次分配
public:
CShareMem():m_hShareMem(0),m_pBuffer(0),m_bFirst(false){
m_szShareName[0]= 0;
};
virtual ~CShareMem(){
free();
};
bool isinited() const{
return (m_pBuffer!=0);
};
//根据指定的名字分配一个共享内存或取得一个共享内存
//如果是分配的m_bFirst为TRUE,如果是取得的m_bFirst为FALSE
//不管分配还是取得的,如果成功就返回TRUE
bool init(LPCTSTR lpShareName, DWORD size=0);
//必要是重新分配内存
bool realloc(DWORD mem);
//释放共享内存
void free();
//设置共享内存中的数据
bool setbuf(const void* pBuf, DWORD buflen);
//发送数据
bool sendata(const void* pBuf, DWORD buflen,
DWORD msgid, DWORD wpara, DWORD lpara,
HWND& hwnd, LPCTSTR szExeTitle);
public:
//取当前放在共享内存的数据长度
DWORD getdatasize() const{
if(m_pBuffer==0)
return 0;
DWORD datasize= *(DWORD*)(m_pBuffer+sizeof(DWORD));
return datasize;
};
DWORD getcapacity() const{
if(m_pBuffer==0)
return 0;
DWORD mem= *(DWORD*)(m_pBuffer);
return mem;
};
//取得分配的共享内存地址
BYTE* getbuf(){
if(m_pBuffer==0)
return 0;
else
return (m_pBuffer+sizeof(DWORD)*2);
};
//是分配还是取得,分配为TRUE,取得为FALSE
bool isfirst() const{
return m_bFirst;
};
//取得共享内存句柄
HANDLE gethandle(){
return m_hShareMem;
};
};
inline bool CShareMem::init(LPCTSTR lpShareName, DWORD size)
{
lstrcpy(m_szShareName, lpShareName);
if(m_hShareMem==0)
{
m_hShareMem = OpenFileMapping(FILE_MAP_WRITE, FALSE, lpShareName);
if(m_hShareMem)
{//共享内存已经存在
m_bFirst = false;
}
else if(size>0)
{//共享内存不存在
return realloc(size);
}
if(m_hShareMem==0)
return false;
m_pBuffer= (BYTE*)MapViewOfFile(m_hShareMem, FILE_MAP_WRITE, 0, 0, 0);
if(m_pBuffer==0)
return false;
assert(!m_bFirst);
}
return true;
}
inline bool CShareMem::realloc(DWORD size)
{
free();
m_bFirst = true;
//对齐系统内存分页地址
SYSTEM_INFO info;
GetSystemInfo(&info);
DWORD dwAllocUnit= info.dwAllocationGranularity;
size= (size/dwAllocUnit + 1)*dwAllocUnit;
m_hShareMem = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size, m_szShareName);
if(m_hShareMem==0)
return false;
DWORD capacity= size;
m_pBuffer= (BYTE*)MapViewOfFile(m_hShareMem, FILE_MAP_WRITE, 0, 0, 0);
//第一次成功分配内存后用0初始化该内存
ZeroMemory(m_pBuffer, min(64,size/64));
*(DWORD*)m_pBuffer= capacity;
return true;
}
inline void CShareMem::free()
{
if(m_pBuffer)
{
UnmapViewOfFile(m_pBuffer);
m_pBuffer= 0;
}
if(m_hShareMem)
{
CloseHandle(m_hShareMem);
m_hShareMem= 0;
m_bFirst= false;
}
}
//设置共享内存中的数据
inline bool CShareMem::setbuf(const void* pBuf, DWORD buflen)
{
DWORD capacity= getcapacity();
if(buflen+sizeof(DWORD)*4>capacity){
if(!realloc(buflen+sizeof(DWORD)*4))
return false;
}
*(DWORD*)(m_pBuffer+sizeof(DWORD))= buflen; //设置datasize!
memcpy(m_pBuffer+sizeof(DWORD)*2, pBuf, buflen);
return true;
}
//通过共享内存发送数据
inline bool CShareMem::sendata( const void* pBuf, DWORD buflen,
DWORD msgid, DWORD wpara, DWORD lpara,
HWND& hwnd, LPCTSTR szExeTitle)
{
assert(isinited());
if(hwnd){
if(!IsWindow(hwnd))//maybe closed!
hwnd= 0;
}
if(hwnd==0){
hwnd= ::FindWindow(0, szExeTitle);
#ifdef _DEBUG
if(hwnd==0){
// #ifdef _UNICODE
// TRACE(_T("run %S.exe, please!\n"), szExeTitle);
// #else
TRACE(_T("run %s.exe, please!\n"), szExeTitle);
// #endif
}
#endif
}
if(hwnd==0){
// AfxMessageBox(_T("run StchViewer.exe, please!\n"));
return false;
}
if(!setbuf(pBuf, buflen))
return false;
// ::ShowWindow(hwnd, SW_SHOWNORMAL);
::PostMessage(hwnd, msgid, wpara, lpara);
return true;
}
/
#define WM_XBUFFER_DATA WM_USER+110
inline bool SendBmpBufData(const BYTE* pBuf,int width,int height, LPCTSTR appname = _T("Observer"))
{
bool sucd= false;
static HWND hwnd= 0;
static CShareMem sharem;
DWORD buflen= (DWORD)(width*height);
if(!sharem.isinited()){
DWORD H(1024),W(4096);
sharem.init(_T("sharem_xbuffer"), max(W*H, buflen+1024));
}
if(sharem.isinited()){
ASSERT((width&3)==0); //width若不为4的倍数时,接收到的图像可能错位!
sucd= sharem.sendata(pBuf,buflen, WM_XBUFFER_DATA, width,height, hwnd, appname );
}
return sucd;
}
上面的 SendBmpBufData 就能够把灰度图像数据发给我的 Image Observer,我就能立即看到图像,获知当前程序的处理状态是否正常,如果出错又错在哪里,给算法调试带来了非常大的方便。我还利用这种方法,把多边形曲线轮廓数据发送给相应的数据显示浏览程序,这给算法的跟踪调试带来的好处是显而易见的。