您正在查看: 2017年3月

利用DIID_DWebBrowserEvents2接口接收WebBrowser事件

利用DIID_DWebBrowserEvents2接口接收WebBrowser事件

  1. 获取连接点容器的指针 (IConnectionPointContainer).

  2. 调用IconnectionPointContainer 的方法 FindConnectionPoint 找出你想接收的事件。对 Internet Explorer来讲, 你应当为DWebBrowserEvents2 连接点接口实现事件. (作为可选, 你可以调用 EnumConnectionPoints 以枚举服务器支持的全部连接点)

  3. 实现接入你想接收事件的连接点的通报(Advise)。 当实现通告时,传递一个事件接收槽的Iunknown接口的指针。 记住,事件接收槽必须实现 IDispatch 接口以接收来自WebBrowser的事件。 Advise 方法将返回一个cookie ,该Cookie在你调用Unadvise 方法的时候携带上。

  4. 实现 IDispatch::Invoke 以控制任何激发的事件

  5. 当你不再接受事件,调用Unadvise, 并且传递cookie.

h文件

#include <ExDispid.h>
#include <Exdisp.h>  //如果提示重复定义的话 把这个删掉

class WebBrwoser2EventListener : public IDispatch      //DWebBrowserEvents2
{
private:
    void _stdcall BeforeNavigate2(IDispatch *pDisp,
          OleVariant &URL, OleVariant &Flags, OleVariant &TargetFrameName,
          OleVariant &PostData, OleVariant &Headers, WordBool &Cancel);
    void _stdcall DocumentComplete(IDispatch *pDisp, OleVariant &URL);
    void _stdcall DownloadBegin();
    void _stdcall DownloadComplete();
    void _stdcall ProgressChange(int Progress, int ProgressMax);
    void _stdcall NewWindow3(VARIANT_BOOL* Cannel,BSTR url);

public: // IDispatch methods
    STDMETHOD(QueryInterface)( REFIID riid, void **ppvObject)
    {
        HRESULT hr = E_NOINTERFACE;
        if (riid == __uuidof(IDispatch))
        {
            *ppvObject = (IDispatch*)this;
            AddRef();
            hr = S_OK;
        }
        else if (riid == __uuidof(DWebBrowserEvents2))
        {
            *ppvObject = (IDispatch*)this;     // DWebBrowserEvents2*
            AddRef();
            hr = S_OK;
        }

        return hr;
    }
    STDMETHODIMP_(ULONG) AddRef(void)
    {
        return 1;
    };
    STDMETHODIMP_(ULONG) Release(void)
    {
        return 1;
    }
    STDMETHOD(GetTypeInfoCount)(UINT*)
    {
        return E_NOTIMPL;
    }
    STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**)
    {
        return E_NOTIMPL;
    }

    STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR *rgszNames, UINT, LCID, DISPID *rgDispId)
    {
        return E_NOTIMPL;
    }

    STDMETHOD(Invoke)(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult,EXCEPINFO FAR* pExcepInfo,unsigned int FAR* puArgErr)
    {
        HRESULT hr = S_OK;

        if (dispIdMember == DISPID_BEFORENAVIGATE2)
        {
            WordBool Cancel=false;
            IDispatch *pDisp=pDispParams->rgvarg[6].pdispVal;
            OleVariant URL=OleVariant(pDispParams->rgvarg[5].pvarVal->bstrVal);
            OleVariant Flags=OleVariant(pDispParams->rgvarg[4].pvarVal->bstrVal);
            OleVariant TargetFrameName=OleVariant(pDispParams->rgvarg[3].pvarVal->bstrVal);
            OleVariant PostData=OleVariant(pDispParams->rgvarg[2].pvarVal->bstrVal);
            OleVariant Headers=OleVariant(pDispParams->rgvarg[1].pvarVal->bstrVal);
            BeforeNavigate2(pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel);
            *pDispParams->rgvarg[0].pboolVal= Cancel? TRUE:FALSE;
        }
        if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
        {
            OleVariant URL=OleVariant(pDispParams->rgvarg[0].pvarVal->bstrVal);
            DocumentComplete(pDispParams->rgvarg[1].pdispVal, URL);
        }
        if (dispIdMember == DISPID_DOWNLOADBEGIN)
        {
            DownloadBegin();
        }
        if (dispIdMember == DISPID_DOWNLOADCOMPLETE)
        {
            DownloadComplete();
        }
        if (dispIdMember == DISPID_PROGRESSCHANGE)
        {
            ProgressChange(pDispParams->rgvarg[1].lVal,pDispParams->rgvarg[0].lVal);
        }
        if (dispIdMember == DISPID_NEWWINDOW3)
        {
           NewWindow3((pDispParams->rgvarg[3].pboolVal),(pDispParams->rgvarg->bstrVal));
        }

        return hr;
    }
};

cpp文件

void _stdcall WebBrwoser2EventListener::BeforeNavigate2(IDispatch *pDisp,
          OleVariant &URL, OleVariant &Flags, OleVariant &TargetFrameName,
          OleVariant &PostData, OleVariant &Headers, WordBool &Cancel)
{
    Form1->Memo1->Lines->Add(URL.bstrVal);
    //Form1->Memo1->Lines->Add(Headers.bstrVal);
    //Cancel=true;
}
//---------------------------------------------------------------------------
void _stdcall WebBrwoser2EventListener::DocumentComplete(IDispatch *pDisp, OleVariant &URL)
{
    IWebBrowser2 *po=(IWebBrowser2 *)pDisp;
    Form1->Memo1->Lines->Add(po->LocationName);
    Form1->Memo1->Lines->Add(URL.bstrVal);
}
//---------------------------------------------------------------------------
void _stdcall WebBrwoser2EventListener::DownloadBegin()
{
    //
}
//---------------------------------------------------------------------------
void _stdcall WebBrwoser2EventListener::DownloadComplete()
{
    //
}
//---------------------------------------------------------------------------
void _stdcall WebBrwoser2EventListener::ProgressChange(int Progress, int ProgressMax)
{
    //Form1->Memo1->Lines->Add((String)Progress+"  "+(String)ProgressMax);    //TEST
}
//---------------------------------------------------------------------------

调用的时候 要先获取 IWebBrowser2* pWeb;

然后

IConnectionPointContainer* pCPC = NULL;
IConnectionPoint* pCP = NULL;
DWORD dwCookie = 0;

pWeb->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
pCPC->FindConnectionPoint( DIID_DWebBrowserEvents2,&pCP);
WebBrwoser2EventListener* pEventListener=new WebBrwoser2EventListener;
pCP->Advise( (IUnknown)(void)pEventListener,&dwCookie);

//到这里 就已经可以从 pWeb 捕获定义的几个事件了

//pCP->Unadvise(dwCookie); //使用完成后要执行这个

//delete pEventListener;

Maven常用命令

  1. 创建Maven的普通java项目:
    mvn archetype:create
    -DgroupId=packageName
    -DartifactId=projectName
  2. 创建Maven的Web项目:
    mvn archetype:create
    -DgroupId=packageName
    -DartifactId=webappName
    -DarchetypeArtifactId=maven-archetype-webapp
  3. 编译源代码: mvn compile
  4. 编译测试代码:mvn test-compile
  5. 运行测试:mvn test
  6. 产生site:mvn site
  7. 打包:mvn package
  8. 在本地Repository中安装jar:mvn install
  9. 清除产生的项目:mvn clean
  10. 生成eclipse项目:mvn eclipse:eclipse
  11. 生成idea项目:mvn idea:idea
  12. 组合使用goal命令,如只打包不测试:mvn -Dtest package
  13. 编译测试的内容:mvn test-compile
  14. 只打jar包: mvn jar:jar
  15. 只测试而不编译,也不测试编译:mvn test -skipping compile -skipping test-compile
    ( -skipping 的灵活运用,当然也可以用于其他组合命令)
  16. 清除eclipse的一些系统设置:mvn eclipse:clean

ps:

一般使用情况是这样,首先通过cvs或svn下载代码到本机,然后执行mvn eclipse:eclipse生成ecllipse项目文件,然后导入到eclipse就行了;修改代码后执行mvn compile或mvn test检验,也可以下载eclipse的maven插件。

mvn -version/-v 显示版本信息
mvn archetype:generate 创建mvn项目
mvn archetype:create -DgroupId=com.oreilly -DartifactId=my-app 创建mvn项目

mvn package 生成target目录,编译、测试代码,生成测试报告,生成jar/war文件
mvn jetty:run 运行项目于jetty上,
mvn compile 编译
mvn test 编译并测试
mvn clean 清空生成的文件
mvn site 生成项目相关信息的网站
mvn -Dwtpversion=1.0 eclipse:eclipse 生成Wtp插件的Web项目
mvn -Dwtpversion=1.0 eclipse:clean 清除Eclipse项目的配置信息(Web项目)
mvn eclipse:eclipse 将项目转化为Eclipse项目

在应用程序用使用多个存储库


Ibiblio
Ibiblio
http://www.ibiblio.org/maven/


PlanetMirror
Planet Mirror
http://public.planetmirror.com/pub/maven/

mvn deploy:deploy-file -DgroupId=com -DartifactId=client -Dversion=0.1.0 -Dpackaging=jar -Dfile=d:\client-0.1.0.jar -DrepositoryId=maven-repository-inner -Durl=ftp://xxxxxxx/opt/maven/repository/

发布第三方Jar到本地库中:

mvn install:install-file -DgroupId=com -DartifactId=client -Dversion=0.1.0 -Dpackaging=jar -Dfile=d:\client-0.1.0.jar

-DdownloadSources=true

-DdownloadJavadocs=true

mvn -e 显示详细错误 信息.
mvn validate 验证工程是否正确,所有需要的资源是否可用。
mvn test-compile 编译项目测试代码。 。
mvn integration-test 在集成测试可以运行的环境中处理和发布包。
mvn verify 运行任何检查,验证包是否有效且达到质量标准。
mvn generate-sources 产生应用需要的任何额外的源代码,如xdoclet。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lifxue/archive/2009/10/14/4662902.aspx

常用命令:
mvn -v 显示版本
mvn help:describe -Dplugin=help 使用 help 插件的 describe 目标来输出 Maven Help 插件的信息。
mvn help:describe -Dplugin=help -Dfull 使用Help 插件输出完整的带有参数的目标列
mvn help:describe -Dplugin=compiler -Dmojo=compile -Dfull 获取单个目标的信息,设置 mojo 参数和 plugin 参数。此命令列出了Compiler 插件的compile 目标的所有信息
mvn help:describe -Dplugin=exec -Dfull 列出所有 Maven Exec 插件可用的目标
mvn help:effective-pom 看这个“有效的 (effective)”POM,它暴露了 Maven的默认设置

mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 -DartifactId=simple -DpackageName=org.sonatype.mavenbook 创建Maven的普通java项目,在命令行使用Maven Archetype 插件
mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main Exec 插件让我们能够在不往 classpath 载入适当的依赖的情况下,运行这个程序
mvn dependency:resolve 打印出已解决依赖的列表
mvn dependency:tree 打印整个依赖树

mvn install -X 想要查看完整的依赖踪迹,包含那些因为冲突或者其它原因而被拒绝引入的构件,打开 Maven 的调试标记运行
mvn install -Dmaven.test.skip=true 给任何目标添加maven.test.skip 属性就能跳过测试
mvn install assembly:assembly 构建装配Maven Assembly 插件是一个用来创建你应用程序特有分发包的插件

mvn jetty:run 调用 Jetty 插件的 Run 目标在 Jetty servlet 容器中启动 web 应用
mvn compile 编译你的项目
mvn clean install 删除再编译

mvn hibernate3:hbm2ddl 使用 Hibernate3 插件构造数据库

直接在24位位图中访问得到象素

Directly Accessing Pixels in a 24-bit Bitmap

Directly Accessing Pixels in a 24-bit Bitmap

Environment: vc5/6, Win9x

This article describes how to display 24-bit (TrueColor) bitmaps which are created during run-time by directly accessing the bitmap's bit image.

The code in this article uses the CDib class, developed by David J. Kruglinski in his book Inside Visual C++, 4th edition. The CDib class is copyrighted by Microsoft, and can be used in your programs as long as you mention their name. The CDib class is included at the end of this document.

1. Create a CDib object, and allocate memory.

This will typically be done in CView::OnInitialUpdate, or some such. It's done in the following way:

// Create a compatible DC

CDC* pDC = GetDC();

ASSERT(pDC);

CDC dcCompat;

dcCompat.CreateCompatibleDC(pDC);

ReleaseDC(pDC);

// Allocate memory for the bitmap

m_pDib = new CDib(CSize(XSIZE,YSIZE), // size of the bitmap

  24);            // bits per pixel

VERIFY(m_pDib->CreateSection(&dcCompat));  // allocate memory for bitmap

2. Fill the bitmap data.

You now have a memory region, allocated by Windows, in which you can write the output information. This memory region is accessed from m_pDib->m_lpImage, and contains a series of three-byte (R,G,B) pixels. As an example, you can get a nice rainbow-color image using the following code:

int x,y;

BYTE* dibits = m_pDib->m_lpImage;

for(x=0; x<XSIZE;&NBSP;X++)< p>

for(y=0; y<YSIZE;&NBSP;Y++)&NBSP;{< p>

*(dibits++) = doc(x,y);               // red

*(dibits++) = doc(doc.XSize()-x-1,y); // green

*(dibits++) = doc(x,doc.YSize()-y-1); // blue

}

Where we used the function doc, defined as follows (intended for bitmaps of size 512x512 pixels, more or less):

BYTE doc(int x,int y) {

return (BYTE)(sqrt(x*x+y*y)/4);

}

3. Display the bitmap.

From within CView::OnDraw, use the following to display the bitmap:

m_pDib->Draw(pDC,m_rectDraw.TopLeft(),m_rectDraw.Size());

where m_rectDraw is a logical-coordinate rectangle defining the position in which we want to place the bitmap. The bitmap is shrunk or expanded using the StretchDIBits function so that it will fit into this rectangle.

4. Delete the DIB.

Don't forget to deallocate the CDib object when you're done. The constructor deallocates all memory associate with the DIB, including the memory of the actual image.

delete m_pDib;

File: CDib.h -- CDib class header file

// cdib.h declaration for Inside Visual C++ CDib class

#ifndef _INSIDE_VISUAL_CPP_CDIB

#define _INSIDE_VISUAL_CPP_CDIB

class CDib : public CObject

{

enum Alloc {noAlloc, crtAlloc, heapAlloc};

DECLARE_SERIAL(CDib)

public:

LPVOID m_lpvcolorTable;

HBITMAP m_hBitmap;

LPBYTE m_lpImage;  // starting address of DIB bits

LPBITMAPINFOHEADER m_lpBMIH; //  buffer containing the BITMAPINFOHEADER

private:

HGLOBAL m_hGlobal; // For external windows we need to free;

                   //  could be allocated by this class or allocated externally

Alloc m_nBmihAlloc;

Alloc m_nImageAlloc;

DWORD m_dwSizeImage; // of bits -- not BITMAPINFOHEADER or BITMAPFILEHEADER

int m_nColorTableEntries;

HANDLE m_hFile;

HANDLE m_hMap;

LPVOID m_lpvFile;

HPALETTE m_hPalette;

public:

CDib();

CDib(CSize size, int nBitCount); // builds BITMAPINFOHEADER

~CDib();

int GetSizeImage() {return m_dwSizeImage;}

int GetSizeHeader()

{return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * pBMIH->biClrImportant = m_nColorTableEntries;

ComputeMetrics();

memset(m_lpvColorTable, 0, sizeof(RGBQUAD) * m_nColorTableEntries);

m_lpImage = NULL;  // no data yet

}

#endif // _INSIDE_VISUAL_CPP_CDIB

File CDib.cpp -- CDib class definition file

// cdib.cpp

// new version for win32

#include "stdafx.h"

#include "cdib.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_SERIAL(CDib, CObject, 0);

CDib::CDib()

{

        m_hFile = NULL;

        m_hBitmap = NULL;

        m_hPalette = NULL;

        m_nBmihAlloc = m_nImageAlloc = noAlloc;

        Empty();

}

CDib::CDib(CSize size, int nBitCount)

{

        m_hFile = NULL;

        m_hBitmap = NULL;

        m_hPalette = NULL;

        m_nBmihAlloc = m_nImageAlloc = noAlloc;

        Empty();

        ComputePaletteSize(nBitCount);

        m_lpBMIH = (LPBITMAPINFOHEADER) new 

                char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries];

        m_nBmihAlloc = crtAlloc;

        m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);

        m_lpBMIH->biWidth = size.cx;

        m_lpBMIH->biHeight = size.cy;

        m_lpBMIH->biPlanes = 1;

        m_lpBMIH->biBitCount = nBitCount;

        m_lpBMIH->biCompression = BI_RGB;

        m_lpBMIH->biSizeImage = 0;

        m_lpBMIH->biXPelsPerMeter = 0;

        m_lpBMIH->biYPelsPerMeter = 0;

        m_lpBMIH->biClrUsed = m_nColorTableEntries;

        m_lpBMIH->biClrImportant = m_nColorTableEntries;

        ComputeMetrics();

        memset(m_lpvColorTable, 0, sizeof(RGBQUAD) * m_nColorTableEntries);

        m_lpImage = NULL;  // no data yet

}

CDib::~CDib()

{

        Empty();

}

CSize CDib::GetDimensions()

{

if(m_lpBMIH == NULL) return CSize(0, 0);

return CSize((int) m_lpBMIH->biWidth, (int) m_lpBMIH->biHeight);

}

BOOL CDib::AttachMapFile(const char* strPathname, BOOL bShare) // for reading

{

// if we open the same file twice, Windows treats it as 2 separate files

// doesn't work with rare BMP files where # palette entries > biClrUsed

HANDLE hFile = ::CreateFile(strPathname, GENERIC_WRITE | GENERIC_READ,

bShare ? FILE_SHARE_READ : 0,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

ASSERT(hFile != INVALID_HANDLE_VALUE);

DWORD dwFileSize = ::GetFileSize(hFile, NULL);

HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);

DWORD dwErr = ::GetLastError();

if(hMap == NULL) {

AfxMessageBox("Empty bitmap file");

return FALSE;

}

LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); // map whole file

ASSERT(lpvFile != NULL);

if(((LPBITMAPFILEHEADER) lpvFile)->bfType != 0x4d42) {

AfxMessageBox("Invalid bitmap file");

DetachMapFile();

return FALSE;

}

AttachMemory((LPBYTE) lpvFile + sizeof(BITMAPFILEHEADER));

m_lpvFile = lpvFile;

m_hFile = hFile;

m_hMap = hMap;

return TRUE;

}

BOOL CDib::CopyToMapFile(const char* strPathname)

{

// copies DIB to a new file, releases prior pointers

// if you previously used CreateSection, the HBITMAP will be NULL (and unusable)

BITMAPFILEHEADER bmfh;

bmfh.bfType = 0x4d42;  // 'BM'

bmfh.bfSize = m_dwSizeImage + sizeof(BITMAPINFOHEADER) +

sizeof(RGBQUAD) * m_nColorTableEntries + sizeof(BITMAPFILEHEADER);

// meaning of bfSize open to interpretation

bmfh.bfReserved1 = bmfh.bfReserved2 = 0;

bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +

sizeof(RGBQUAD) * m_nColorTableEntries;

HANDLE hFile = ::CreateFile(strPathname, GENERIC_WRITE | GENERIC_READ, 0, NULL,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

ASSERT(hFile != INVALID_HANDLE_VALUE);

int nSize =  sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +

sizeof(RGBQUAD) * m_nColorTableEntries +  m_dwSizeImage;

HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, nSize, NULL);

DWORD dwErr = ::GetLastError();

ASSERT(hMap != NULL);

LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); // map whole file

ASSERT(lpvFile != NULL);

LPBYTE lpbCurrent = (LPBYTE) lpvFile;

memcpy(lpbCurrent, &bmfh, sizeof(BITMAPFILEHEADER)); // file header

lpbCurrent += sizeof(BITMAPFILEHEADER);

LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) lpbCurrent;

memcpy(lpbCurrent, m_lpBMIH,

sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries); // info

lpbCurrent += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;

memcpy(lpbCurrent, m_lpImage, m_dwSizeImage); // bit image

DWORD dwSizeImage = m_dwSizeImage;

Empty();

m_dwSizeImage = dwSizeImage;

m_nBmihAlloc = m_nImageAlloc = noAlloc;

m_lpBMIH = lpBMIH;

m_lpImage = lpbCurrent;

m_hFile = hFile;

m_hMap = hMap;

m_lpvFile = lpvFile;

ComputePaletteSize(m_lpBMIH->biBitCount);

ComputeMetrics();

MakePalette();

return TRUE;

}

BOOL CDib::AttachMemory(LPVOID lpvMem, BOOL bMustDelete, HGLOBAL hGlobal)

{

// assumes contiguous BITMAPINFOHEADER, color table, image

// color table could be zero length

Empty();

m_hGlobal = hGlobal;

if(bMustDelete == FALSE) {

m_nBmihAlloc = noAlloc;

}

else {

m_nBmihAlloc = ((hGlobal == NULL) ? crtAlloc : heapAlloc);

}

try {

m_lpBMIH = (LPBITMAPINFOHEADER) lpvMem;

ComputeMetrics();

ComputePaletteSize(m_lpBMIH->biBitCount);

m_lpImage = (LPBYTE) m_lpvColorTable + sizeof(RGBQUAD) * m_nColorTableEntries;

MakePalette();

}

catch(CException* pe) {

AfxMessageBox("AttachMemory error");

pe->Delete();

return FALSE;

}

return TRUE;

}

UINT CDib::UsePalette(CDC* pDC, BOOL bBackground /* = FALSE */)

{

if(m_hPalette == NULL) return 0;

HDC hdc = pDC->GetSafeHdc();

::SelectPalette(hdc, m_hPalette, bBackground);

return ::RealizePalette(hdc);

}

BOOL CDib::Draw(CDC* pDC, CPoint origin, CSize size)

{

if(m_lpBMIH == NULL) return FALSE;

if(m_hPalette != NULL) {

::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);

}

pDC->SetStretchBltMode(COLORONCOLOR);

::StretchDIBits(pDC->GetSafeHdc(), origin.x, origin.y, size.cx, size.cy,

0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight,

m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);

return TRUE;

}

HBITMAP CDib::CreateSection(CDC* pDC /* = NULL */)

{

if(m_lpBMIH == NULL) return NULL;

if(m_lpImage != NULL) return NULL; // can only do this if image doesn't exist

m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,

DIB_RGB_COLORS, (LPVOID*) &m_lpImage, NULL, 0);

ASSERT(m_lpImage != NULL);

return m_hBitmap;

}

BOOL CDib::MakePalette()

{

// makes a logical palette (m_hPalette) from the DIB's color table

// this palette will be selected and realized prior to drawing the DIB

if(m_nColorTableEntries == 0) return FALSE;

if(m_hPalette != NULL) ::DeleteObject(m_hPalette);

TRACE("CDib::MakePalette -- m_nColorTableEntries = %d\n", m_nColorTableEntries);

LPLOGPALETTE pLogPal = (LPLOGPALETTE) new char[2 * sizeof(WORD) +

m_nColorTableEntries * sizeof(PALETTEENTRY)];

pLogPal->palVersion = 0x300;

pLogPal->palNumEntries = m_nColorTableEntries;

LPRGBQUAD pDibQuad = (LPRGBQUAD) m_lpvColorTable;

for(int i = 0; i < m_nColorTableEntries; i++) {

pLogPal->palPalEntry[i].peRed = pDibQuad->rgbRed;

pLogPal->palPalEntry[i].peGreen = pDibQuad->rgbGreen;

pLogPal->palPalEntry[i].peBlue = pDibQuad->rgbBlue;

pLogPal->palPalEntry[i].peFlags = 0;

pDibQuad++;

}

m_hPalette = ::CreatePalette(pLogPal);

delete pLogPal;

return TRUE;

}

BOOL CDib::SetSystemPalette(CDC* pDC)

{

// if the DIB doesn't have a color table, we can use the system's halftone palette

if(m_nColorTableEntries != 0) return FALSE;

m_hPalette = ::CreateHalftonePalette(pDC->GetSafeHdc());

return TRUE;

}

HBITMAP CDib::CreateBitmap(CDC* pDC)

{

    if (m_dwSizeImage == 0) return NULL;

    HBITMAP hBitmap = ::CreateDIBitmap(pDC->GetSafeHdc(), m_lpBMIH,

            CBM_INIT, m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS);

    ASSERT(hBitmap != NULL);

    return hBitmap;

}

BOOL CDib::Compress(CDC* pDC, BOOL bCompress /* = TRUE */)

{

// 1. makes GDI bitmap from existing DIB

// 2. makes a new DIB from GDI bitmap with compression

// 3. cleans up the original DIB

// 4. puts the new DIB in the object

if((m_lpBMIH->biBitCount != 4) && (m_lpBMIH->biBitCount != 8)) return FALSE;

// compression supported only for 4 bpp and 8 bpp DIBs

if(m_hBitmap) return FALSE; // can't compress a DIB Section!

TRACE("Compress: original palette size = %d\n", m_nColorTableEntries); 

HDC hdc = pDC->GetSafeHdc();

HPALETTE hOldPalette = ::SelectPalette(hdc, m_hPalette, FALSE);

HBITMAP hBitmap;  // temporary

if((hBitmap = CreateBitmap(pDC)) == NULL) return FALSE;

int nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;

LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];

memcpy(lpBMIH, m_lpBMIH, nSize);  // new header

if(bCompress) {

switch (lpBMIH->biBitCount) {

case 4:

lpBMIH->biCompression = BI_RLE4;

break;

case 8:

lpBMIH->biCompression = BI_RLE8;

break;

default:

ASSERT(FALSE);

}

// calls GetDIBits with null data pointer to get size of compressed DIB

if(!::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,

NULL, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS)) {

AfxMessageBox("Unable to compress this DIB");

// probably a problem with the color table

  ::DeleteObject(hBitmap);

delete [] lpBMIH;

::SelectPalette(hdc, hOldPalette, FALSE);

return FALSE; 

}

if (lpBMIH->biSizeImage == 0) {

AfxMessageBox("Driver can't do compression");

  ::DeleteObject(hBitmap);

delete [] lpBMIH;

::SelectPalette(hdc, hOldPalette, FALSE);

return FALSE; 

}

else {

m_dwSizeImage = lpBMIH->biSizeImage;

}

}

else {

lpBMIH->biCompression = BI_RGB; // decompress

// figure the image size from the bitmap width and height

DWORD dwBytes = ((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) / 32;

if(((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) % 32) {

dwBytes++;

}

dwBytes *= 4;

m_dwSizeImage = dwBytes * lpBMIH->biHeight; // no compression

lpBMIH->biSizeImage = m_dwSizeImage;

// second GetDIBits call to make DIB

LPBYTE lpImage = (LPBYTE) new char[m_dwSizeImage];

VERIFY(::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,

     lpImage, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS));

    TRACE("dib successfully created - height = %d\n", lpBMIH->biHeight);

::DeleteObject(hBitmap);

Empty();

m_nBmihAlloc = m_nImageAlloc = crtAlloc;

m_lpBMIH = lpBMIH;

m_lpImage = lpImage;

ComputeMetrics();

ComputePaletteSize(m_lpBMIH->biBitCount);

MakePalette();

::SelectPalette(hdc, hOldPalette, FALSE);

TRACE("Compress: new palette size = %d\n", m_nColorTableEntries); 

return TRUE;

}

BOOL CDib::Read(CFile* pFile)

{

// 1. read file header to get size of info hdr + color table

// 2. read info hdr (to get image size) and color table

// 3. read image

// can't use bfSize in file header

Empty();

int nCount, nSize;

BITMAPFILEHEADER bmfh;

try {

nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));

if(nCount != sizeof(BITMAPFILEHEADER)) {

throw new CException;

}

if(bmfh.bfType != 0x4d42) {

throw new CException;

}

nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);

m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];

m_nBmihAlloc = m_nImageAlloc = crtAlloc;

nCount = pFile->Read(m_lpBMIH, nSize); // info hdr & color table

ComputeMetrics();

ComputePaletteSize(m_lpBMIH->biBitCount);

MakePalette();

m_lpImage = (LPBYTE) new char[m_dwSizeImage];

nCount = pFile->Read(m_lpImage, m_dwSizeImage); // image only

}

catch(CException* pe) {

AfxMessageBox("Read error");

pe->Delete();

return FALSE;

}

return TRUE;

}

BOOL CDib::ReadSection(CFile* pFile, CDC* pDC /* = NULL */)

{

// new function reads BMP from disk and creates a DIB section

//    allows modification of bitmaps from disk

// 1. read file header to get size of info hdr + color table

// 2. read info hdr (to get image size) and color table

// 3. create DIB section based on header parms

// 4. read image into memory that CreateDibSection allocates

Empty();

int nCount, nSize;

BITMAPFILEHEADER bmfh;

try {

nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));

if(nCount != sizeof(BITMAPFILEHEADER)) {

throw new CException;

}

if(bmfh.bfType != 0x4d42) {

throw new CException;

}

nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);

m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];

m_nBmihAlloc = crtAlloc;

m_nImageAlloc = noAlloc;

nCount = pFile->Read(m_lpBMIH, nSize); // info hdr & color table

if(m_lpBMIH->biCompression != BI_RGB) {

throw new CException;

}

ComputeMetrics();

ComputePaletteSize(m_lpBMIH->biBitCount);

MakePalette();

UsePalette(pDC);

m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,

DIB_RGB_COLORS, (LPVOID*) &m_lpImage, NULL, 0);

ASSERT(m_lpImage != NULL);

nCount = pFile->Read(m_lpImage, m_dwSizeImage); // image only

}

catch(CException* pe) {

AfxMessageBox("ReadSection error");

pe->Delete();

return FALSE;

}

return TRUE;

}

BOOL CDib::Write(CFile* pFile)

{

BITMAPFILEHEADER bmfh;

bmfh.bfType = 0x4d42;  // 'BM'

int nSizeHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;

bmfh.bfSize = 0;

// bmfh.bfSize = sizeof(BITMAPFILEHEADER) + nSizeHdr + m_dwSizeImage;

// meaning of bfSize open to interpretation (bytes, words, dwords?) -- we won't use it

bmfh.bfReserved1 = bmfh.bfReserved2 = 0;

bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +

sizeof(RGBQUAD) * m_nColorTableEntries;

try {

pFile->Write((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));

pFile->Write((LPVOID) m_lpBMIH,  nSizeHdr);

pFile->Write((LPVOID) m_lpImage, m_dwSizeImage);

}

catch(CException* pe) {

pe->Delete();

AfxMessageBox("write error");

return FALSE;

}

return TRUE;

}

void CDib::Serialize(CArchive& ar)

{

DWORD dwPos;

dwPos = ar.GetFile()->GetPosition();

TRACE("CDib::Serialize -- pos = %d\n", dwPos);

ar.Flush();

dwPos = ar.GetFile()->GetPosition();

TRACE("CDib::Serialize -- pos = %d\n", dwPos);

if(ar.IsStoring()) {

Write(ar.GetFile());

}

else {

Read(ar.GetFile());

}

}

// helper functions

void CDib::ComputePaletteSize(int nBitCount)

{

if((m_lpBMIH == NULL) || (m_lpBMIH->biClrUsed == 0)) {

switch(nBitCount) {

case 1:

m_nColorTableEntries = 2;

break;

case 4:

m_nColorTableEntries = 16;

break;

case 8:

m_nColorTableEntries = 256;

break;

case 16:

case 24:

case 32:

m_nColorTableEntries = 0;

break;

default:

ASSERT(FALSE);

}

}

else {

m_nColorTableEntries = m_lpBMIH->biClrUsed;

}

ASSERT((m_nColorTableEntries >= 0) && (m_nColorTableEntries <= 256)); 

}

void CDib::ComputeMetrics()

{

if(m_lpBMIH->biSize != sizeof(BITMAPINFOHEADER)) {

TRACE("Not a valid Windows bitmap -- probably an OS/2 bitmap\n");

throw new CException;

}

m_dwSizeImage = m_lpBMIH->biSizeImage;

if(m_dwSizeImage == 0) {

DWORD dwBytes = ((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) / 32;

if(((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) % 32) {

dwBytes++;

}

dwBytes *= 4;

m_dwSizeImage = dwBytes * m_lpBMIH->biHeight; // no compression

}

m_lpvColorTable = (LPBYTE) m_lpBMIH + sizeof(BITMAPINFOHEADER);

}

void CDib::Empty()

{

// this is supposed to clean up whatever is in the DIB

DetachMapFile();

if(m_nBmihAlloc == crtAlloc) {

delete [] m_lpBMIH;

}

else if(m_nBmihAlloc == heapAlloc) {

::GlobalUnlock(m_hGlobal);

::GlobalFree(m_hGlobal);

}

if(m_nImageAlloc == crtAlloc) delete [] m_lpImage;

if(m_hPalette != NULL) ::DeleteObject(m_hPalette);

if(m_hBitmap != NULL) ::DeleteObject(m_hBitmap);

m_nBmihAlloc = m_nImageAlloc = noAlloc;

m_hGlobal = NULL;

m_lpBMIH = NULL;

m_lpImage = NULL;

m_lpvColorTable = NULL;

m_nColorTableEntries = 0;

m_dwSizeImage = 0;

m_lpvFile = NULL;

m_hMap = NULL;

m_hFile = NULL;

m_hBitmap = NULL;

m_hPalette = NULL;

}

void CDib::DetachMapFile()

{

if(m_hFile == NULL) return;

::UnmapViewOfFile(m_lpvFile);

::CloseHandle(m_hMap);

::CloseHandle(m_hFile);

m_hFile = NULL;

}

 

在SQL SERVER中保存和输出图片

在SQL Server中保存和输出图片

在SQL Server中保存和输出图片

 

有时候我们需要保存一些binary data进数据库。SQL Server提供一个叫做image的特殊数据类型供我们保存binary data。Binary data可以是图片、文档等。在这篇文章中我们将看到如何在SQL Server中保存和输出图片。

 

建表

  为了试验这个例子你需要一个含有数据的table(你可以在现在的库中创建它,也可以创建一个新的数据库),下面是它的结构:

Column Name
Datatype
Purpose

ID
Integer
identity column Primary key

IMGTITLE
Varchar(50)
Stores some user friendly title to identity the image

IMGTYPE
Varchar(50)
Stores image content type. This will be same as recognized content types of ASP.NET

IMGDATA
Image
Stores actual image or binary data.

 

保存images进SQL Server数据库

  为了保存图片到table你首先得从客户端上传它们到你的web服务器。你可以创建一个web form,用TextBox得到图片的标题,用html File Server Control得到图片文件。确信你设定了Form的encType属性为multipart/form-data。

 

Stream imgdatastream = File1.PostedFile.InputStream;

int imgdatalen = File1.PostedFile.ContentLength;

string imgtype = File1.PostedFile.ContentType;

string imgtitle = TextBox1.Text;

byte[] imgdata = new byte[imgdatalen];

int n = imgdatastream.Read(imgdata,0,imgdatalen);

string connstr=

((NameValueCollection)Context.GetConfig

("appSettings"))["connstr"];

SqlConnection connection = new SqlConnection(connstr);

SqlCommand command = new SqlCommand

("INSERT INTO ImageStore(imgtitle,imgtype,imgdata)

VALUES ( @imgtitle, @imgtype,@imgdata )", connection );

SqlParameter paramTitle = new SqlParameter

("@imgtitle", SqlDbType.VarChar,50 );

paramTitle.Value = imgtitle;

command.Parameters.Add( paramTitle);

SqlParameter paramData = new SqlParameter

( "@imgdata", SqlDbType.Image );

paramData.Value = imgdata;

command.Parameters.Add( paramData );

SqlParameter paramType = new SqlParameter

( "@imgtype", SqlDbType.VarChar,50 );

paramType.Value = imgtype;

command.Parameters.Add( paramType );

connection.Open();

int numRowsAffected = command.ExecuteNonQuery();

connection.Close();

从数据库中输出图片

  现在让我们从数据库中取出我们刚刚保存的图片,在这儿,我们将直接将图片输出至浏览器。你也可以将它保存为一个文件或做任何你想做的。

private void Page_Load(object sender, System.EventArgs e)

{

string imgid =Request.QueryString["imgid"];

string connstr=((NameValueCollection)

Context.GetConfig("appSettings"))["connstr"];

string sql="SELECT imgdata, imgtype FROM ImageStore WHERE id = "

+ imgid;

SqlConnection connection = new SqlConnection(connstr);

SqlCommand command = new SqlCommand(sql, connection);

connection.Open();

SqlDataReader dr = command.ExecuteReader();

if(dr.Read())

{

Response.ContentType = dr["imgtype"].ToString();

Response.BinaryWrite( (byte[]) dr["imgdata"] );

}

connection.Close();

}

在上面的代码中我们使用了一个已经打开的数据库,通过datareader选择images。接着用Response.BinaryWrite代替Response.Write来显示image文件。

在MFC程序中显示JPGGIF图像

mfc程序中显示jpg/gif图像

mfc程序中显示jpg/gif图像

编译:Northtibet


下载源代码


    如果你是一个使用VB编程的程序员,要在程序中显示JPG或者GIF图像简直易如反掌,将图像控件拖到Form中,分分钟即可搞掂。但是C++程序员要显示同样的图形却没有那么轻松,那么是不是要自己编写JPG解压缩代码呢?当然不用那么复杂啦!本文将针对这个问题讨论如何在MFC中显示JPG或者GIF图像。
    用VB写图像显示程序之所以如此轻松,完全是利用了琳琅满目的图像处理控件,把你想要做的事情都一一搞掂。而C++程序员为了实现相同的功能必须忙乎半天。其实,C/C++程序员也能使用那些VB程序员所用的(或者说几乎一样的)图像控件。VB用的图像控件实际上都基于一个系统级COM类——IPicture。下面是有关 IPicture 的方法描述:

方法

描述

get_Handle 

返回图像对象的Windows GDI句柄 

get_Hpal 

返回图像对象当前使用的调色板拷贝

get_Type

返回当前图像对象的的图像类型

get_Width 

返回当前图像对象的图像宽度

get_Height 

返回当前图像对象的图像高度

Render 

在指定的位置、指定的设备上下文上绘制指定的图像部分

set_Hpal 

设置当前图像的调色板

get_CurDC 

返回当前选中这个图像的设备上下文

SelectPicture 

将一个位图图像选入给定的设备上下文,返回选中图像的设备上下文和图像的GDI句柄

get_KeepOriginalForma 

返回图像对象KeepOriginalFormat 属性的当前值

put_KeepOriginalFormat 

设置图像对象的KeepOriginalFormat 属性

PictureChanged 

通知图像对象它的图像资源改变了

SaveAsFile 

将图像数据存储到流中,格式与存成文件格式相同

get_Attributes 

返回图像位属性当前的设置

    从上面这个表可以看出,IPicture操纵着图像对象及其属性。图像对象提供对位图的抽象,而Windows负责BMP、JPG和GIF位图的标准实现。程序员要做的只是实例化IPicture,然后调用其Render函数。与通常使用接口的方式不同,这里实例的创建我们不用CoCreateInstance函数,而是用一个专门的函数OleLoadPicture。

IStream* pstm = // 需要一个流(stream)

IPicture* pIPicture;

hr = OleLoadPicture(pstm, 0, FALSE, IID_IPicture, (void**)&pIPicture);     

OleLoadPicture从流中加载图像并创建一个可用来显示图像的新IPicture对象。

rc = // 显示图像的矩形

// 将rc 转换为 HIMETRIC

spIPicture->Render(pDC, rc);     

    IPicture 负责处理所有琐事,以便确定图形之格式,如 Windows 位图、JPEG或者GIF文件——甚至是图标和元文件(metafiles)。当然啦,所有这些的实现细节是需要技巧的,为此我写了一个Demo程序Myimgapp(如图二)来示范这些IPicture的使用方法。


图一 Myimgapp的运行画面

    Myimgapp是个典型的MFC文档/视图程序,在编写这个程序之前,我首先对 IPicture COM接口进行封装,之所以要这么做,主要是考虑到并不是每一个程序员都能熟练运用COM接口进行编程,另外将IPicture的主要功能封装在C++类中可以使我们的问题更容易解决,我封装的这个C++类名字叫做CPicture。它的定义和实现细节请参考本文提供的源代码。
    我在这个类中将复杂而陌生的COM风格的参数映射成MFC程序员更为熟悉的类型。例如,CPicture可以让你直接从文件名加载一幅图像,CFile或者CArchive,而不用去处理流,CPicture::Render替你完成了IPicture中所有令人讨厌的但又是必须的HIMETRIC平滑转换工作。CPicture甚至具备了一个Load函数,它可以从资源数据中加载图像,所以你只要用下面的代码就可以显示资源中的图像:

   CPicture pic(ID_MYPIC); // 加载图像

   CRect rc(0,0,0,0);      // 使用缺省的rc

   pic.Render(pDC, rc);    // 显示图像     

CPicture::Render提供一个显示图片的矩形。IPicture 对图像进行延伸处理。如果传递一个空矩形,则CPicture用图像本身的大小--不进行延伸处理。对于图像本身而言,CPicture查找"IMAGE"类型的资源,所以在资源文件中你必须要加入下面的代码:

   IDR_MYPIC IMAGE MOVEABLE PURE "res\\MyPic.jpg"     

    CPicture是个很棒的傻瓜类,它具备一个 ATL 智能指针CComQIPtr指向IPicture接口,通过调用OleLoadPicture来初始化不同的Load函数。CPicture提供了常用的打包函数来调用底层的IPicture。CPicture只封装了那些在Demo例子程序中要用到的方法。如果你需要调用IPicture::get_Handle或其它一些很少用到的IPicture方法,你可以自己尝试编写相应的打包代码。 另外,在编写完CPicture之后,我发现了一个现成的MFC类——CPictureHolder,这个类的功能几乎与CPicture完全一样,你可以在afxctl.h文件中找到它的定义。 前面说过,Demo例子是个典型的MFC文档/视图应用程序,因此它肯定少不了与文档和视图类相对应的CPictureDoc 和CPictureView:
CPictureDoc类没有什么特别的处理代码,它用CPicture对象存储图像:

class CPictureDoc : public CDocument {

protected:

  CPicture m_pict; // the picture

};     

并且CPictureDoc::Serialize 调用CPicture::Load 从MFC存档的数据中读取图像。

void CPictureDoc::Serialize(CArchive& ar)

{

  if (ar.IsLoading()) {

    m_pict.Load(ar);

  }

}     

为了使Myimgapp程序更实用,CPictureDoc::OnNewDocument从程序资源数据加载了一幅图像。为了显示这幅图像,CPictureView::OnDraw要调用CPicture::Render。这样程序一启动便会显示一幅默认的图像。

void CPictureView::OnDraw(CDC* pDC)

{

  CPictureDoc* pDoc = GetDocument();

  CPicture* ppic = pDoc->GetPicture();

  CRect rc;

  GetImageRect(rc);

  ppic->Render(pDC,rc);

}

    GetImageRect是CPictureView类的一个成员函数,作用是根据当前Myimgapp的缩放比率(可用25%、33%、50%、75%、100%或自适应方式)获取图像矩形。GetImageRect调用CPicture::GetImageSize来获得真正的图像大小,然后根据比率显示。 CPictureView其余的部分完全和CScrollView的做法差不多,初始化视图并设置滚动大小,处理命令等等。唯一让人操心的是IPicture::Render中HIMETRIC的处理问题,因为标准的MFC应用程序都使用MM_TEXT映射模型。不用担心,CPicture::Render和CPicture::GetImageSize会将这一切转换过来,所以你不必为这些事情伤神。 CPictureView有一个消息处理器值得一提:它就是OnEraseBkgnd,当要显示的图像比客户区小的时候,这个函数必须绘制空白区域,如图二,OnEraseBkgnd创建一个与图像大小相等的切边(clip)矩形,然后将客户区填成黑色。之所以要创建切边矩形,主要是避免当改变窗口大小时出现的抖动——FillRect不绘制切边矩形内的区域,此乃Windows图形处理的常识。


图二 OnEraseBkgnd 填充修剪的图像

    IPicture/CPicture简化了图像的显示。它甚至可以实现调色板的识别这样复杂的处理。你完全可以抛开老式DIB 图像绘制方法,如加载调色板、BitBlts、StretchBlts等等——这一切IPicture全都可以搞掂。如果你未曾用IPicture显示过图像,那么现在试试吧。 CPictureView完成图像浏览的任务看来不是什么难事了。但是如果要把一幅图像添加到一个对话框或者其它的什么窗口中怎么办呢?为此我创建了另外一个类——CPictureCtrl。
CPictureCtrl 使你可以在任何对话框或窗口中把图像作为子窗口显示。例如:

class CAboutDialog : public CDialog {

protected:

  CPictureCtrl m_wndPict;

  virtual BOOL OnInitDialog();

};

BOOL CAboutDialog::OnInitDialog()

{

  m_wndPict.SubclassDlgitem(IDC_MYIMAGE,this);

  return CDialog::OnInitDialog();

}     

    假设你的对话框中有一个静态控制,它的ID=IDC_IMAGE,并且有一幅IMAGE资源的ID与之相同。则从CStaticLink派生出的CPictureCtrl还可以指定一个URL超链接(或者创建一个ID与此控制或图像的ID相同的串资源)。如果你指定了一个URL,则在图像上单击鼠标将启动默认浏览器访问URL。真是酷呆了。CPicture控制着CPicture对象并改写WM_PAINT消息处理例程,调用CPicture::Render代替通常的静态控制处理例程。处理细节请参见代码。打开Myimgapp程序的“关于”对话框就知道了。