程序功能:对某些参数进行计算,并生成一列JPG图,如1.jpg,2.jpg,3.jpg。然后利用GDI+,将1.jpg读入进来并显示在视图上,点击下一张按钮,依次读入后面的JPG图。
出现的问题:第一次计算时,能够正确生成所要的JPG图,并能将1.jpg读入进来。如果第二次对同样的参数进行计算,理论上程序会生成同样的三张JPG图,并且覆盖原来已有的JPG图。但实际上程序计算完毕,在保存第一张JPG图时出错退出。
问题的原因:因为GDI+读入第一次生成的1.jpg时,该文件被独占,导致第二次保存1.jpg文件时无法替换原有的文件。现在,我的问题是:如何在每一次计算前,清空视图,也就是取消程序对JPG文件的独占访问?

解决方案 »

  1.   

    独占JPG的问题已经解决了,原来加载图片调用的是Image::FromFile,现在改用Image::FromStream,从内存中加载JPG,从而避免了JPG被独占的问题。但现在有另一个问题:随着JPG图片的不断载入,程序所消耗的内存会逐步递增。首先描述一下我这个程序的设计思路:这个程序通过计算,首先生成一列JPG图片并保存在某个硬盘路径中,然后程序会读取第一个JPG文件并将它显示在视图里,工具栏有个按钮,点击一下,程序会读取下一个JPG文件,显示在视图中。每次需要显示一个JPG时,会调用名为DrawPicture的函数,这是一个文档类的函数,它通过向视图发送消息,传递JPG图片的路径,并响应视图类的OnUpdate函数,JPG的载入和绘图都是在OnUpdate函数中具体执行的,由于涉及到图片的放大缩小和漫游,因此我用的是双缓冲方法来显示图片。相关代码如下:首先在试图的OnInitialUpdate中初始化
    void CMy2008BAC35B02View::OnInitialUpdate()
    {
    // CScrollView::OnInitialUpdate(); // 内存画布的初始化设置
    m_memDC.CreateCompatibleDC(NULL);  // 准备一个内存DC
    CDC * pDC = GetDC();
    m_memBitmap.CreateCompatibleBitmap(pDC, 5000, 4000);  // 准备一个5000*4000的位图画布
    ReleaseDC(pDC); // 滚动条初始化设置
    SetScrollSizes();
    }
    然后自定义一个消息函数,用来接收菜单传入的图片的地址
    // 自定义消息函数,当文档类向本视图发送WM_NOTIFY_DRAW消息时,执行该函数,接收来自对话框的
    // 图件完整路径信息,并绘图
    LRESULT CMy2008BAC35B02View::OnNotifyDraw(WPARAM wParam, LPARAM lParam)
    {
    CString *p_strGetPicPathName;
    // 将lParam参数转换为指向CString的指针,这个指针指向的CString对象携带了图件完整路径信息
    p_strGetPicPathName = (CString*)lParam;
    m_strPicPathName = *p_strGetPicPathName;  // 完整路径赋值给View的成员变量 CMy2008BAC35B02Doc *pDoc = GetDocument();
    pDoc->UpdateAllViews(NULL);  // 更新视图,调用视图类的OnUpdate函数
    SetScrollSizes();  // 设置滚动条
    return 1;
    }
    其中定义了成员变量
    CString p_strGetPicPathName;
    然后再定义几个成员变量
    Image *image;
    IStream *pStream;
    HGLOBAL hMem;
    并在视图类的构造函数中初始化为NULL。
    在OnUpdate中执行具体的绘图
    void CMy2008BAC35B02View::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
    {
    CFile file;
    if (!file.Open(m_strPicPathName, CFile::modeRead))
    {
    MessageBox("无法打开结果图件。", "错误", MB_OK|MB_ICONSTOP);
    return;
    } DWORD m_nFileLen;
    m_nFileLen = file.GetLength(); // 每执行一次OnUpdate,都先清空内存
    if (!hMem)
    {
    ::GlobalFree(hMem);
    hMem = NULL;
    }
    // 动态分配内存
    // 分配的是固定内存,不是可移动的(GMEM_MOVEABLE),故不必锁定内存
    hMem = ::GlobalAlloc(GMEM_FIXED, m_nFileLen);
    if (hMem==NULL)
    {
    MessageBox("内存分配失败。", "错误", MB_OK|MB_ICONSTOP);
    return;
    }
    if (file.Read(hMem, m_nFileLen)!=m_nFileLen)
    {
    MessageBox("读结果图件失败", "错误", MB_OK|MB_ICONSTOP);
    ::GlobalFree(hMem);
    return;
    }
    file.Close();
    pStream = NULL;
    if (CreateStreamOnHGlobal(hMem, FALSE, &pStream)!=S_OK)
    {
    MessageBox("创建IStream对象失败。", "错误", MB_OK|MB_ICONSTOP);
    ::GlobalFree(hMem);
    return;
    } if (!image)
    {
    delete image;
    image = NULL;
    }
    image = Image::FromStream(pStream);
    pStream->Release(); // 得到图像的宽和高
    m_nPicWidth = image->GetWidth();
    m_nPicHeight = image->GetHeight(); // 得到当前视图的宽和高
    CRect rcClient;
    GetClientRect(&rcClient);
    m_nClientWidth = rcClient.Width();
    m_nClientHeight = rcClient.Height(); // 得到图像缩放后的宽和高
    m_nPicZoomedWidth = (int)(m_nPicWidth*m_nScale);
    m_nPicZoomedHeight = (int)(m_nPicHeight*m_nScale); // 求视图中显示图片的原点
    m_nClientOriginX = (m_nClientWidth-m_nPicZoomedWidth)/2;
    m_nClientOriginY = (m_nClientHeight-m_nPicZoomedHeight)/2;
    if (m_nClientOriginX<0)
    m_nClientOriginX = 0;
    if (m_nClientOriginY<0)
    m_nClientOriginY = 0; // 在内存画布上显示图形
    // 将准备的位图画布选入内存DC中成为内存画布
    m_pOldBitmap = m_memDC.SelectObject(&m_memBitmap);
    m_memDC.FillSolidRect(0, 0, 5000, 4000, RGB(255,255,255));  // 用白色填充整个内存画布
    Graphics graphics(m_memDC.GetSafeHdc());  // 内存DC和GDI+的Graphics对象关联
    // 设置插值模式改善图像缩放质量
    graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
    Rect destinationRc(0, 0, m_nPicZoomedWidth, m_nPicZoomedHeight);
    // 在内存画布上显示图像
    graphics.DrawImage(image, destinationRc, 0, 0, m_nPicWidth, m_nPicHeight, UnitPixel);
    m_memDC.SelectObject(m_pOldBitmap);
    Invalidate();  // 提示OnDraw重绘
    }最后在OnDraw中显示图像
    void CMy2008BAC35B02View::OnDraw(CDC* pDC)
    {
    CMy2008BAC35B02Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
    // 直接绘制内存画布上的位图(双缓冲)
    pDC->BitBlt(m_nClientOriginX, m_nClientOriginY, m_nPicZoomedWidth, m_nPicZoomedHeight,
    &m_memDC, 0, 0, SRCCOPY);
    }
    如果按照上面的代码,分配的内存应该都是及时清空了的,因为每一次显示JPG时,都会先清空上一次分配的内存。但实际情况是随着JPG的不断显示,程序耗费的内存越来越多。不过,如果程序最小化了,貌似内存又被瞬间清空了,不知是怎么回事?