ACTIVEX网页控件开发及测试

第1页:创建ActiveX控件

 

本文将首先介绍如何使用Visual Studio开发一个简单的ActiveX控件,然后介绍ActiveX控件开发相关的基础知识,比如方法,属性和事件等,最后介绍如何利用这些知识,实现ActiveX控件和网页页面之间的通信。


MyActiveX控件的效果图

 简介

  ActiveX是由Microsoft在90年代中期提出的一项技术。利用ActiveX技术,开发者可以创建像Java小程序applet一样的小型应 用程序。这些应用程序可以被下载然后在Microsoft的浏览器中运行。这篇文章是为那些想学习如何开发他们的第一个ActiveX控件的Visual C++开发者准备的。

 当我自己尝试学习这些技术的时候,我发现很多针对ActiveX技术的资料都已经不再适用了,都已经过时了,或者丢失了很多关键性的信息,这些都使得我在 为我的项目创建一个ActiveX控件的时候,变得困难重重。这篇文章将帮助你实现ActiveX网页控件开发的快速入门。本文将首先介绍如何使用 Visual Studio开发一个简单的ActiveX控件,然后介绍ActiveX控件开发相关的基础知识,比如方法,属性和事件等,最后介绍如何利用这些知识,实 现ActiveX控件和网页页面之间的通信。

 另外,你也将学习到如何在默认的Internet Explorer安全设置下取消掉控件加载过程中的警告或者错误提示,使你的控件成为一个“安全”的ActiveX控件。

 在本文中,我们将创建一个实际的ActiveX控件。在控件加载的过程中,它将显示一个动态的进度条,告诉用户控件正在加载并处理信息。同时,这个控件也 将展示如何在ActiveX控件和Web页面之间传递信息。本文将引导你一步步地利用Microsoft Visual Studio 2005完成ActiveX控件的开发工作。

 创建ActiveX控件

 要创建一个ActiveX控件,首先启动Visual Studio 2005,执行下面这些步骤:

  1.选择“文件”菜单,然后选择“新建”->“项目”。  
  2.在“新建项目”对话框中,如下图1所示,选择“Visual C++”分类下的“MFC”。然后选择“MFC ActiveX控件”作为项目模板。

 

 

第2页:添加对动态GIF动画的支持

 

3.将新项目命名 为“MyActiveX”,然后选择项目的保存目录,最后点击“确定”按钮,关闭“新项目”对话框,进入“MFC ActiveX控件”向导。 


图1 新项目对话框

  4.在“MFC ActiveX控件”向导中,如下图2所示,选择左侧的“控件设置”,进入控件参数设置页面。
  5.在“Create control based on”下拉列表中选择“STATIC”。这表示我们使用的是一个静态控件,因为我们仅仅是显示输出而不接收输入。 
  6.在下面的“Additional features”的所有选项中,我们确认选中“Activates when visible”和“Flicker-free activation”这两个选项,同时确认“Has an About box dialog”选项没有被选中。  


图2 MFC ActiveX 控件向导对话框

  7.点击“Finish”按钮关闭整个“MFC ActiveX控件向导”对话框,然后向导就会为我们创建整个项目的源代码。默认情况下,向导创建的项目是使用共享MFC DLL的,这里我们需要改变这一设置。因为如果目标机器上没有安装MFC DLL,那么ActiveX控件就不会运行。我们常常看到一些网页上的ActiveX控件被显示为红色的叉叉,很多都是这个原因造成的。
  为了解决这个问题,我们使用静态的MFC DLL。从Visual Studio的菜单中选择“项目”->“属性”,然后切换到“配置属性”->“普通”,在配置页面中的“Use of MFC ”下拉列表中,将“use MFC in a shared DLL”切换成“Use MFC in a Static Library”就可以了。

  8.向导创建了如下三个类: 
  CMyActiveXApp – 这是从COleControlModule派生的ActiveX控件应用程序类。通过这个类,我们可以得到一个OLE控件对象,这个对象包含了对控件进行 初始化(InitInstance)和对控件进行清理(ExitInstance)的成员函数。 
  CMyActiveXCtrl – 这个类是从COleControl派生的。我们将在这个类中实现控件的主要功能。 
  CMyActiveXPropPage – 这个类从COlePropertyPage派生的。它被用来管理控件的属性对话框。MFC ActiveX控件向导已经为我们创建了一个默认的属性对话框,我们只需要在其基础上进行修改就可以了。

 添加对动态GIF动画的支持

 为了实现使用ActiveX控件显示动态的进度条GIF动画,我们将使用Oleg Bykov在CodeProject的一篇文章中创建的类CPictureEx。具体的文章地址,请查阅本文最后的参考资料。

 首先,我们需要将源代码文件pictureex.cpp和pictureex.h添加到我们的项目中。选择Visual Studio 2005 IDE中的“解决方案浏览器”,在源代码窗口中点击“Header Files or Source Files”,然后点击右键,在弹出的上下文菜单中选择“Add”->“Existing Item”,在文件选择对话框中,选中我们需要的源代码文件添加到项目中。

 默认情况下,Visual Studio 2005本身是不支持GIF资源的。如果你尝试着添加一个GIF资源,它将弹出一个错误窗口报告说这个文件不是一个合法的GIF文件。为了添加一个动态的 GIF动画资源,我们需要做一些额外的工作。请按照下面的步骤添加GIF资源:

  1.复制GIF文件ProcessingProgressBar.gif到你的项目目录,然后将其扩展名修改为“gaf”。在资源视图中,右键单击 MyActiveX.rc,然后选择“Add Resource”。在弹出的添加资源对话框中,点击“Import”按钮,然后选择ProcessingProgressBar.gaf。

 在“自定义资源类型”对话框中,填入“GIF”作为资源类型。这样我们就可以将GIF图像文件导入到项目中作为资源了。你可以在资源视图的“GIF”目录 下中找到我们刚刚导入的GIF图像文件。选中这个GIF图像资源,然后将它的ID由默认的IDR_GIF1修改为IDR_PROGRESSBAR。

  2.在上一步中,我们使用“gaf”作为文件扩展名绕过了Visual Studio对GIF资源文件的检查。现在,我们需要做一些事情将这个“错误”修正过来,使得GIF图像文件能够正常工作。首先,在Visual Studio 2005中保存MyActiveX.rc。然后打开项目目录,用记事本直接打开资源文件MyActiveX.rc,然后找到 IDR_PROGRESSBAR的定义行。这里我们需要将文件名修改为“ProcessingProgressBar.gif”。同样的,将项目目录下的 GIF资源文件的文件名修改为“ProcessingProgressBar.gif”。

 pictureex.h源代码

//
// PictureEx.cpp: implementation of the CPictureEx class.
//
// Picture displaying control with support for the following formats:
// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR
//
// Written by Oleg Bykov (oleg_bykoff@rsdn.ru)
// Copyright (c) 2001
//
// To use CPictureEx, follow these steps:
//   - place a static control on your dialog (either a text or a bitmap)
//   - change its identifier to something else (e.g. IDC_MYPIC)
//   - associate a CStatic with it using ClassWizard
//   - in your dialog's header file replace CStatic with CPictureEx
//     (don't forget to #include "PictureEx.h" and add
//     PictureEx.h and PictureEx.cpp to your project)
//   - call one of the overloaded CPictureEx::Load() functions somewhere
//     (OnInitDialog is a good place to start)
//   - if the preceding Load() succeeded call Draw()
// 
// You can also add the control by defining a member variable of type
// CPictureEx, calling CPictureEx::Create (derived from CStatic), then
// CPictureEx::Load and CPictureEx::Draw.
//
// By default, the control initializes its background to COLOR_3DFACE
// (see CPictureEx::PrepareDC()). You can change the background by
// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().
//
// I decided to leave in the class the functions to write separate frames from
// animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING
// and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions
// won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,
// so you don't have to worry.
//
// Warning: this code hasn't been subject to a heavy testing, so
// use it on your own risk. The author accepts no liability for the
// possible damage caused by this code.
//
// Version 1.0  7 Aug 2001
//              Initial release
//
// Version 1.1  6 Sept 2001
//              ATL version of the class
//
// Version 1.2  14 Oct 2001
//              - Fixed a problem with loading GIFs from resources
//                in MFC-version of the class for multi-modules apps.
//                Thanks to Ruben Avila-Carretero for finding this out.
//
//              - Got rid of waitable timer in ThreadAnimation()
//                Now CPictureEx[Wnd] works in Win95 too.
//                Thanks to Alex Egiazarov and Wayne King for the idea.
//
//              - Fixed a visual glitch of using SetBkColor.
//                Thanks to Kwangjin Lee for finding this out.
//
// Version 1.3  10 Nov 2001
//              - Fixed a DC leak. One DC leaked per each UnLoad()
//                (forgot to put a ReleaseDC() in the end of
//                CPictureExWnd::PrepareDC() function).
//
//              - Now it is possible to set a clipping rectangle using
//                CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.
//                The LPRECT parameter tells the class what portion of
//                a picture should it display. If the clipping rect is
//                not set, the whole picture is shown.
//                Thanks to Fabrice Rodriguez for the idea.
//
//              - Added support for Stop/Draw. Now you can Stop() an
//                animated GIF, then Draw() it again, it will continue
//                animation from the frame it was stopped on. You can
//                also know if a GIF is currently playing with the
//                IsPlaying() function.
//            
//              - Got rid of math.h and made m_bExitThread volatile.
//                Thanks to Piotr Sawicki for the suggestion.
//
//

#if !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)
#define AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <vector>

//#define GIF_TRACING  // uncomment it if you want detailed TRACEs

class CPictureEx : public CStatic
{
public:

struct TFrame    // structure that keeps a single frame info
{
 IPicture *m_pPicture;  // pointer to the interface used for drawing
 SIZE     m_frameSize;
 SIZE     m_frameOffset;
 UINT     m_nDelay;     // delay (in 1/100s of a second)
 UINT     m_nDisposal;  // disposal method
};

#pragma pack(1)   // turn byte alignment on

enum GIFBlockTypes
{
 BLOCK_UNKNOWN,
 BLOCK_APPEXT,
 BLOCK_COMMEXT,
 BLOCK_CONTROLEXT,
 BLOCK_PLAINTEXT,
 BLOCK_IMAGE,
 BLOCK_TRAILER
};

enum ControlExtValues // graphic control extension packed field values
{
 GCX_PACKED_DISPOSAL,  // disposal method
 GCX_PACKED_USERINPUT,
 GCX_PACKED_TRANSPCOLOR
};

enum LSDPackedValues  // logical screen descriptor packed field values
{
 LSD_PACKED_GLOBALCT,
 LSD_PACKED_CRESOLUTION,
 LSD_PACKED_SORT,
 LSD_PACKED_GLOBALCTSIZE
};

enum IDPackedValues   // image descriptor packed field values
{
 ID_PACKED_LOCALCT,
 ID_PACKED_INTERLACE,
 ID_PACKED_SORT,
 ID_PACKED_LOCALCTSIZE
};

struct TGIFHeader       // GIF header 
{
 char m_cSignature[3]; // Signature - Identifies the GIF Data Stream
        // This field contains the fixed value 'GIF'
 char m_cVersion[3]; // Version number. May be one of the following:
      // "87a" or "89a"
};

struct TGIFLSDescriptor // Logical Screen Descriptor
{
 WORD m_wWidth; // 2 bytes. Logical screen width
 WORD m_wHeight; // 2 bytes. Logical screen height

 unsigned char m_cPacked;      // packed field 

 unsigned char m_cBkIndex;     // 1 byte. Background color index
 unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio
 inline int GetPackedValue(enum LSDPackedValues Value);
};

struct TGIFAppExtension // application extension block
{
 unsigned char m_cExtIntroducer; // extension introducer (0x21)
 unsigned char m_cExtLabel; // app. extension label (0xFF)
 unsigned char m_cBlockSize; // fixed value of 11
 char m_cAppIdentifier[8];   // application identifier
 char m_cAppAuth[3];  // application authentication code
};

struct TGIFControlExt // graphic control extension block
{
 unsigned char m_cExtIntroducer; // extension introducer (0x21)
 unsigned char m_cControlLabel;  // control extension label (0xF9)
 unsigned char m_cBlockSize; // fixed value of 4
 unsigned char m_cPacked;    // packed field
 WORD m_wDelayTime; // delay time
 unsigned char m_cTColorIndex; // transparent color index
 unsigned char m_cBlockTerm;   // block terminator (0x00)
public:
 inline int GetPackedValue(enum ControlExtValues Value);
};

struct TGIFCommentExt  // comment extension block
{
 unsigned char m_cExtIntroducer; // extension introducer (0x21)
 unsigned char m_cCommentLabel;  // comment extension label (0xFE)
};

struct TGIFPlainTextExt // plain text extension block
{
 unsigned char m_cExtIntroducer;  // extension introducer (0x21)
 unsigned char m_cPlainTextLabel; // text extension label (0x01)
 unsigned char m_cBlockSize; // fixed value of 12
 WORD m_wLeftPos;    // text grid left position
 WORD m_wTopPos;     // text grid top position
 WORD m_wGridWidth;  // text grid width
 WORD m_wGridHeight; // text grid height
 unsigned char m_cCellWidth;  // character cell width
 unsigned char m_cCellHeight; // character cell height
 unsigned char m_cFgColor; // text foreground color index
 unsigned char m_cBkColor; // text background color index
};

struct TGIFImageDescriptor // image descriptor block
{
 unsigned char m_cImageSeparator; // image separator byte (0x2C)
 WORD m_wLeftPos; // image left position
 WORD m_wTopPos;  // image top position
 WORD m_wWidth;   // image width
 WORD m_wHeight;  // image height
 unsigned char m_cPacked; // packed field
 inline int GetPackedValue(enum IDPackedValues Value);
};

#pragma pack() // turn byte alignment off

public:
 BOOL GetPaintRect(RECT *lpRect);
 BOOL SetPaintRect(const RECT *lpRect);
 CPictureEx();
 virtual ~CPictureEx();
 void Stop();   // stops animation
 void UnLoad(); // stops animation plus releases all resources

 BOOL IsGIF() const;
 BOOL IsPlaying() const;
 BOOL IsAnimatedGIF() const;
 SIZE GetSize() const;
 int GetFrameCount() const;
 COLORREF GetBkColor() const;
 void SetBkColor(COLORREF clr);

 // draws the picture (starts an animation thread if needed)
 // if an animation was previously stopped by Stop(),
 // continues it from the last displayed frame
 BOOL Draw();

 // loads a picture from a file
 // i.e. Load(_T("mypic.gif"));
 BOOL Load(LPCTSTR szFileName);

 // loads a picture from a global memory block (allocated by GlobalAlloc)
 // Warning: this function DOES NOT free the global memory, pointed to by hGlobal
 BOOL Load(HGLOBAL hGlobal, DWORD dwSize);

 // loads a picture from a program resource
 // i.e. Load(MAKEINTRESOURCE(IDR_MYPIC),_T("GIFTYPE"));
 BOOL Load(LPCTSTR szResourceName,LPCTSTR szResourceType);

protected:

#ifdef GIF_TRACING
 void EnumGIFBlocks();
 void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize);
#endif // GIF_TRACING

 RECT m_PaintRect;
 SIZE m_PictureSize;
 COLORREF m_clrBackground;
 UINT m_nCurrFrame;
 UINT m_nDataSize;
 UINT m_nCurrOffset;
 UINT m_nGlobalCTSize;
 BOOL m_bIsGIF;
 BOOL m_bIsPlaying;
 volatile BOOL m_bExitThread;
 BOOL m_bIsInitialized;
 HDC m_hMemDC;

 HDC m_hDispMemDC;
 HBITMAP m_hDispMemBM;
 HBITMAP m_hDispOldBM;

 HBITMAP m_hBitmap;
 HBITMAP m_hOldBitmap;
 HANDLE m_hThread;
 HANDLE m_hExitEvent;
 IPicture * m_pPicture;
 TGIFHeader * m_pGIFHeader;
 unsigned char * m_pRawData;
 TGIFLSDescriptor * m_pGIFLSDescriptor;
 std::vector<TFrame> m_arrFrames;

 void ThreadAnimation();
 static UINT WINAPI _ThreadAnimation(LPVOID pParam);

 int GetNextBlockLen() const;
 BOOL SkipNextBlock();
 BOOL SkipNextGraphicBlock();
 BOOL PrepareDC(int nWidth, int nHeight);
 void ResetDataPointer();
 enum GIFBlockTypes GetNextBlock() const;
 UINT GetSubBlocksLen(UINT nStartingOffset) const;
 HGLOBAL GetNextGraphicBlock(UINT *pBlockLen, UINT *pDelay,
  SIZE *pBlockSize, SIZE *pBlockOffset, UINT *pDisposal);

 // Generated message map functions
 //{{AFX_MSG(CPictureEx)
 afx_msg void OnDestroy();
 afx_msg void OnPaint();
 //}}AFX_MSG
 
 DECLARE_MESSAGE_MAP()
};

#endif // !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)


 

pictureex.cpp源代码

//
// PictureEx.cpp: implementation of the CPictureEx class.
//
// Picture displaying control with support for the following formats:
// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR
//
// Written by Oleg Bykov (oleg_bykoff@rsdn.ru)
// Copyright (c) 2001
//
// To use CPictureEx, follow these steps:
//   - place a static control on your dialog (either a text or a bitmap)
//   - change its identifier to something else (e.g. IDC_MYPIC)
//   - associate a CStatic with it using ClassWizard
//   - in your dialog's header file replace CStatic with CPictureEx
//     (don't forget to #include "PictureEx.h" and add
//     PictureEx.h and PictureEx.cpp to your project)
//   - call one of the overloaded CPictureEx::Load() functions somewhere
//     (OnInitDialog is a good place to start)
//   - if the preceding Load() succeeded call Draw()
// 
// You can also add the control by defining a member variable of type
// CPictureEx, calling CPictureEx::Create (derived from CStatic), then
// CPictureEx::Load and CPictureEx::Draw.
//
// By default, the control initializes its background to COLOR_3DFACE
// (see CPictureEx::PrepareDC()). You can change the background by
// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().
//
// I decided to leave in the class the functions to write separate frames from
// animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING
// and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions
// won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,
// so you don't have to worry.
//
// Warning: this code hasn't been subject to a heavy testing, so
// use it on your own risk. The author accepts no liability for the
// possible damage caused by this code.
//
// Version 1.0  7 Aug 2001
//              Initial release
//
// Version 1.1  6 Sept 2001
//              ATL version of the class
//
// Version 1.2  14 Oct 2001
//              - Fixed a problem with loading GIFs from resources
//                in MFC-version of the class for multi-modules apps.
//                Thanks to Ruben Avila-Carretero for finding this out.
//
//              - Got rid of waitable timer in ThreadAnimation()
//                Now CPictureEx[Wnd] works in Win95 too.
//                Thanks to Alex Egiazarov and Wayne King for the idea.
//
//              - Fixed a visual glitch of using SetBkColor.
//                Thanks to Kwangjin Lee for finding this out.
//
// Version 1.3  10 Nov 2001
//              - Fixed a DC leak. One DC leaked per each UnLoad()
//                (forgot to put a ReleaseDC() in the end of
//                CPictureExWnd::PrepareDC() function).
//
//              - Now it is possible to set a clipping rectangle using
//                CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.
//                The LPRECT parameter tells the class what portion of
//                a picture should it display. If the clipping rect is
//                not set, the whole picture is shown.
//                Thanks to Fabrice Rodriguez for the idea.
//
//              - Added support for Stop/Draw. Now you can Stop() an
//                animated GIF, then Draw() it again, it will continue
//                animation from the frame it was stopped on. You can
//                also know if a GIF is currently playing with the
//                IsPlaying() function.
//            
//              - Got rid of math.h and made m_bExitThread volatile.
//                Thanks to Piotr Sawicki for the suggestion.
//
//

#include "stdafx.h"
#include "PictureEx.h"
#include <process.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//
// Nested structures member functions
//

inline int CPictureEx::TGIFControlExt::GetPackedValue(enum ControlExtValues Value)
{
 int nRet = (int)m_cPacked;
 switch (Value)
 {
 case GCX_PACKED_DISPOSAL:
  nRet = (nRet & 28) >> 2;
  break;

 case GCX_PACKED_USERINPUT:
  nRet = (nRet & 2) >> 1;
  break;

 case GCX_PACKED_TRANSPCOLOR:
  nRet &= 1;
  break;
 };

 return nRet;
}

inline int CPictureEx::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value)
{
 int nRet = (int)m_cPacked;

 switch (Value)
 {
 case LSD_PACKED_GLOBALCT:
  nRet = nRet >> 7;
  break;

 case LSD_PACKED_CRESOLUTION:
  nRet = ((nRet & 0x70) >> 4) + 1;
  break;

 case LSD_PACKED_SORT:
  nRet = (nRet & 8) >> 3;
  break;

 case LSD_PACKED_GLOBALCTSIZE:
  nRet &= 7;
  break;
 };

 return nRet;
}

inline int CPictureEx::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value)
{
 int nRet = (int)m_cPacked;

 switch (Value)
 {
 case ID_PACKED_LOCALCT:
  nRet >>= 7;
  break;

 case ID_PACKED_INTERLACE:
  nRet = ((nRet & 0x40) >> 6);
  break;

 case ID_PACKED_SORT:
  nRet = (nRet & 0x20) >> 5;
  break;

 case ID_PACKED_LOCALCTSIZE:
  nRet &= 7;
  break;
 };

 return nRet;
}


//
// Construction/Destruction
//

CPictureEx::CPictureEx()
{
 // check structures size
 ASSERT(sizeof(TGIFImageDescriptor) == 10);
 ASSERT(sizeof(TGIFAppExtension)    == 14);
 ASSERT(sizeof(TGIFPlainTextExt)    == 15);
 ASSERT(sizeof(TGIFLSDescriptor)    ==  7);
 ASSERT(sizeof(TGIFControlExt)    ==  8);
 ASSERT(sizeof(TGIFCommentExt)    ==  2);
 ASSERT(sizeof(TGIFHeader)     ==  6);

 m_pGIFLSDescriptor = NULL;
 m_pGIFHeader    = NULL;
 m_pPicture     = NULL;
 m_pRawData     = NULL;
 m_hThread     = NULL;
 m_hBitmap          = NULL;
 m_hMemDC     = NULL;

 m_hDispMemDC       = NULL;
 m_hDispMemBM       = NULL;
 m_hDispOldBM       = NULL;

 m_bIsInitialized   = FALSE;
 m_bExitThread    = FALSE;
 m_bIsPlaying       = FALSE;
 m_bIsGIF     = FALSE;
 m_clrBackground    = RGB(255,255,255); // white by default
 m_nGlobalCTSize    = 0;
 m_nCurrOffset    = 0;
 m_nCurrFrame    = 0;
 m_nDataSize     = 0;
 m_PictureSize.cx = m_PictureSize.cy = 0;
 SetRect(&m_PaintRect,0,0,0,0);

 m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
}

CPictureEx::~CPictureEx()
{
 UnLoad();
 CloseHandle(m_hExitEvent);
}

BEGIN_MESSAGE_MAP(CPictureEx, CStatic)
 //{{AFX_MSG_MAP(CPictureEx)
 ON_WM_DESTROY()
 ON_WM_PAINT()
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CPictureEx::Load(HGLOBAL hGlobal, DWORD dwSize)
{
 IStream *pStream = NULL;
 UnLoad();

 if (!(m_pRawData = reinterpret_cast<unsigned char*> (GlobalLock(hGlobal))) )
 {
  TRACE(_T("Load: Error locking memory\n"));
  return FALSE;
 };

 m_nDataSize = dwSize;
 m_pGIFHeader = reinterpret_cast<TGIFHeader *> (m_pRawData);

 if ((memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) &&
  ((memcmp(&m_pGIFHeader->m_cVersion,"87a",3) != 0) ||
   (memcmp(&m_pGIFHeader->m_cVersion,"89a",3) != 0)) )
 {
 // it's neither GIF87a nor GIF89a
 // do the default processing

  // clear GIF variables
  m_pRawData = NULL;
  GlobalUnlock(hGlobal);

  // don't delete memory on object's release
  if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
   return FALSE;

  if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
   reinterpret_cast<LPVOID *>(&m_pPicture)) != S_OK)
  {
   pStream->Release();
   return FALSE;
  };
  pStream->Release();

  // store picture's size

  long hmWidth;
  long hmHeight;
  m_pPicture->get_Width(&hmWidth);
  m_pPicture->get_Height(&hmHeight);

  HDC hDC = ::GetDC(m_hWnd);
  m_PictureSize.cx = MulDiv(hmWidth, GetDeviceCaps(hDC,LOGPIXELSX), 2540);
  m_PictureSize.cy = MulDiv(hmHeight, GetDeviceCaps(hDC,LOGPIXELSY), 2540);
  ::ReleaseDC(m_hWnd,hDC);
 }
 else
 {
  // it's a GIF
  m_bIsGIF = TRUE;
  m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *>
   (m_pRawData + sizeof(TGIFHeader));
  if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1)
  {
   // calculate the globat color table size
   m_nGlobalCTSize = static_cast<int>
    (3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1)));
   // get the background color if GCT is present
   unsigned char *pBkClr = m_pRawData + sizeof(TGIFHeader) +
    sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex;
   m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]);
  };

  // store the picture's size
  m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth;
  m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;

  // determine frame count for this picture
  UINT nFrameCount=0;
  ResetDataPointer();
  while (SkipNextGraphicBlock())
   nFrameCount++;

#ifdef GIF_TRACING
  TRACE(
   _T(" -= GIF encountered\n"
      "Logical Screen dimensions = %dx%d\n"
      "Global color table = %d\n"
      "Color depth = %d\n"
      "Sort flag = %d\n"
      "Size of Global Color Table = %d\n"
      "Background color index = %d\n"
      "Pixel aspect ratio = %d\n"
      "Frame count = %d\n"
      "Background color = %06Xh\n\n"
     ),
   m_pGIFLSDescriptor->m_wWidth,
   m_pGIFLSDescriptor->m_wHeight,
   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT),
   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION),
   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT),
   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE),
   m_pGIFLSDescriptor->m_cBkIndex,
   m_pGIFLSDescriptor->m_cPixelAspect,
   nFrameCount,
   m_clrBackground
   );
  EnumGIFBlocks();
#endif

  if (nFrameCount == 0) // it's an empty GIF!
  {
   m_pRawData = NULL;
   GlobalUnlock(hGlobal);
   return FALSE;
  };

  // now check the frame count
  // if there's only one frame, no need to animate this GIF
  // therefore, treat it like any other pic

  if (nFrameCount == 1)
  {
   // clear GIF variables
   m_pRawData = NULL;
   GlobalUnlock(hGlobal);

   // don't delete memory on object's release
   if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
    return FALSE;

   if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
    (LPVOID *)&m_pPicture) != S_OK)
   {
    pStream->Release();
    return FALSE;
   };

   pStream->Release();
  }
  else
  {
  // if, on the contrary, there are several frames
  // then store separate frames in an array

   TFrame frame;
   UINT nBlockLen;
   HGLOBAL hFrameData;
   UINT nCurFrame = 0;

   ResetDataPointer();
   while (hFrameData = GetNextGraphicBlock(&nBlockLen,
    &frame.m_nDelay, &frame.m_frameSize,
    &frame.m_frameOffset, &frame.m_nDisposal) )
   {
    #ifdef GIF_TRACING
    //
    // uncomment the following strings if you want
    // to write separate frames on disk
    //
    // CString szName;
    // szName.Format(_T("%.4d.gif"),nCurFrame);
    // WriteDataOnDisk(szName,hFrameData,nBlockLen);
    // nCurFrame++;
    #endif // GIF_TRACING

    IStream *pStream = NULL;

    // delete memory on object's release
    if (CreateStreamOnHGlobal(hFrameData,TRUE,&pStream) != S_OK)
    {
     GlobalFree(hFrameData);
     continue;
    };

    if (OleLoadPicture(pStream,nBlockLen,FALSE,
     IID_IPicture,
     reinterpret_cast<LPVOID *>(&frame.m_pPicture)) != S_OK)
    {
     pStream->Release();
     continue;
    };
    pStream->Release();
   
    // everything went well, add this frame
    m_arrFrames.push_back(frame);
   };

   // clean after ourselves
   m_pRawData = NULL;
   GlobalUnlock(hGlobal);

   if (m_arrFrames.empty()) // couldn't load any frames
    return FALSE;
  };
 }; // if (!IsGIF...

 return PrepareDC(m_PictureSize.cx,m_PictureSize.cy);
}

void CPictureEx::UnLoad()
{
 Stop();
 if (m_pPicture)
 {
  m_pPicture->Release();
  m_pPicture = NULL;
 };
 
 std::vector<TFrame>::iterator it;
 for (it=m_arrFrames.begin();it<m_arrFrames.end();it++)
  (*it).m_pPicture->Release();
 m_arrFrames.clear();

 if (m_hMemDC)
 {
  SelectObject(m_hMemDC,m_hOldBitmap);
  ::DeleteDC(m_hMemDC);
  ::DeleteObject(m_hBitmap);
  m_hMemDC  = NULL;
  m_hBitmap = NULL;
 };

 if (m_hDispMemDC)
 {
  SelectObject(m_hDispMemDC,m_hDispOldBM);
  ::DeleteDC(m_hDispMemDC);
  ::DeleteObject(m_hDispMemBM);
  m_hDispMemDC  = NULL;
  m_hDispMemBM = NULL;
 };

 SetRect(&m_PaintRect,0,0,0,0);
 m_pGIFLSDescriptor = NULL;
 m_pGIFHeader    = NULL;
 m_pRawData     = NULL;
 m_hThread     = NULL;
 m_bIsInitialized   = FALSE;
 m_bExitThread    = FALSE;
 m_bIsGIF     = FALSE;
 m_clrBackground    = RGB(255,255,255); // white by default
 m_nGlobalCTSize    = 0;
 m_nCurrOffset    = 0;
 m_nCurrFrame    = 0;
 m_nDataSize     = 0;
}

BOOL CPictureEx::Draw()
{
 if (!m_bIsInitialized)
 {
  TRACE(_T("Call one of the CPictureEx::Load() member functions before calling Draw()\n"));
  return FALSE;
 };

 if (IsAnimatedGIF())
 {
 // the picture needs animation
 // we'll start the thread that will handle it for us
 
  unsigned int nDummy;
  m_hThread = (HANDLE) _beginthreadex(NULL,0,_ThreadAnimation,this,
   CREATE_SUSPENDED,&nDummy);
  if (!m_hThread)
  {
   TRACE(_T("Draw: Couldn't start a GIF animation thread\n"));
   return FALSE;
  }
  else
   ResumeThread(m_hThread);
 }
 else
 {
  if (m_pPicture)
  {
   long hmWidth;
   long hmHeight;
   m_pPicture->get_Width(&hmWidth);
   m_pPicture->get_Height(&hmHeight);
   if (m_pPicture->Render(m_hMemDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy,
    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
   {
    Invalidate(FALSE);
    return TRUE;
   };
  };
 };

 return FALSE; 
}

SIZE CPictureEx::GetSize() const
{
 return m_PictureSize;
}

BOOL CPictureEx::Load(LPCTSTR szFileName)
{
 ASSERT(szFileName);
 
 CFile file;
 HGLOBAL hGlobal;
 DWORD dwSize;

 if (!file.Open(szFileName,
    CFile::modeRead |
    CFile::shareDenyWrite) )
 {
  TRACE(_T("Load (file): Error opening file %s\n"),szFileName);
  return FALSE;
 };

 dwSize = file.GetLength();
 hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
 if (!hGlobal)
 {
  TRACE(_T("Load (file): Error allocating memory\n"));
  return FALSE;
 };
 
 char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
 if (!pData)
 {
  TRACE(_T("Load (file): Error locking memory\n"));
  GlobalFree(hGlobal);
  return FALSE;
 };

 TRY
 {
  file.Read(pData,dwSize);
 }
 CATCH(CFileException, e);                                         
 {
  TRACE(_T("Load (file): An exception occured while reading the file %s\n"),
   szFileName);
  GlobalFree(hGlobal);
  e->Delete();
  file.Close();
  return FALSE;
 }
 END_CATCH
 GlobalUnlock(hGlobal);
 file.Close();

 BOOL bRetValue = Load(hGlobal,dwSize);
 GlobalFree(hGlobal);
 return bRetValue;
}

BOOL CPictureEx::Load(LPCTSTR szResourceName, LPCTSTR szResourceType)
{
 ASSERT(szResourceName);
 ASSERT(szResourceType);

 HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType);
 HGLOBAL hResData;
 if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture)))
 {
  TRACE(_T("Load (resource): Error loading resource %s\n"),szResourceName);
  return FALSE;
 };
 DWORD dwSize = SizeofResource(AfxGetResourceHandle(),hPicture);

 // hResData is not the real HGLOBAL (we can't lock it)
 // let's make it real

 HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
 if (!hGlobal)
 {
  TRACE(_T("Load (resource): Error allocating memory\n"));
  FreeResource(hResData);
  return FALSE;
 };
 
 char *pDest = reinterpret_cast<char *> (GlobalLock(hGlobal));
 char *pSrc = reinterpret_cast<char *> (LockResource(hResData));
 if (!pSrc || !pDest)
 {
  TRACE(_T("Load (resource): Error locking memory\n"));
  GlobalFree(hGlobal);
  FreeResource(hResData);
  return FALSE;
 };
 CopyMemory(pDest,pSrc,dwSize);
 FreeResource(hResData);
 GlobalUnlock(hGlobal);

 BOOL bRetValue = Load(hGlobal,dwSize);
 GlobalFree(hGlobal);
 return bRetValue;
}

void CPictureEx::ResetDataPointer()
{
 // skip header and logical screen descriptor
 m_nCurrOffset =
  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
}

BOOL CPictureEx::SkipNextGraphicBlock()
{
 if (!m_pRawData) return FALSE;

 // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data

 enum GIFBlockTypes nBlock;

 nBlock = GetNextBlock();

 while ((nBlock != BLOCK_CONTROLEXT) &&
     (nBlock != BLOCK_IMAGE) &&
     (nBlock != BLOCK_PLAINTEXT) &&
     (nBlock != BLOCK_UNKNOWN) &&
     (nBlock != BLOCK_TRAILER) )
 {
  if (!SkipNextBlock()) return NULL;
  nBlock = GetNextBlock();
 };

 if ((nBlock == BLOCK_UNKNOWN) ||
  (nBlock == BLOCK_TRAILER))
  return FALSE;

 // it's either a control ext.block, an image or a plain text

 if (GetNextBlockLen() <= 0) return FALSE;

 if (nBlock == BLOCK_CONTROLEXT)
 {
  if (!SkipNextBlock()) return FALSE;
  nBlock = GetNextBlock();

  // skip everything until we meet an image block or a plain-text block
  while ((nBlock != BLOCK_IMAGE) &&
      (nBlock != BLOCK_PLAINTEXT) &&
      (nBlock != BLOCK_UNKNOWN) &&
      (nBlock != BLOCK_TRAILER) )
  {
   if (!SkipNextBlock()) return NULL;
   nBlock = GetNextBlock();
  };

  if ((nBlock == BLOCK_UNKNOWN) ||
   (nBlock == BLOCK_TRAILER))
   return FALSE;
 };

 // skip the found data block (image or plain-text)
 if (!SkipNextBlock()) return FALSE;

 return TRUE;
}

UINT CPictureEx::GetSubBlocksLen(UINT nStartingOffset) const
{
 UINT nRet = 0;
 UINT nCurOffset = nStartingOffset;
 
 while (m_pRawData[nCurOffset] != 0)
 {
  nRet += m_pRawData[nCurOffset]+1;
  nCurOffset += m_pRawData[nCurOffset]+1;
 };

 return nRet+1;
}

enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
{
 switch(m_pRawData[m_nCurrOffset])
 {
 case 0x21:
 // extension block
  switch(m_pRawData[m_nCurrOffset+1])
  {
  case 0x01:
  // plain text extension
   return BLOCK_PLAINTEXT;
   break;

  case 0xF9:
  // graphic control extension
   return BLOCK_CONTROLEXT;
   break;

  case 0xFE:
  // comment extension
   return BLOCK_COMMEXT;
   break;

  case 0xFF:
  // application extension
   return BLOCK_APPEXT;
   break;
  };
  break;
 
 case 0x3B:
 // trailer
  return BLOCK_TRAILER;
  break;

 case 0x2C:
 // image data
  return BLOCK_IMAGE;
  break;
 };

 return BLOCK_UNKNOWN;
}

BOOL CPictureEx::SkipNextBlock()
{
 if (!m_pRawData) return FALSE;

 int nLen = GetNextBlockLen();
 if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize))
  return FALSE;

 m_nCurrOffset += nLen;
 return TRUE;
}

int CPictureEx::GetNextBlockLen() const
{
 GIFBlockTypes nBlock = GetNextBlock();

 int nTmp;

 switch(nBlock)
 {
 case BLOCK_UNKNOWN:
  return -1;
  break;

 case BLOCK_TRAILER:
  return 1;
  break;

 case BLOCK_APPEXT:
  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension));
  if (nTmp > 0)
   return sizeof(TGIFAppExtension)+nTmp;
  break;

 case BLOCK_COMMEXT:
  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt));
  if (nTmp > 0)
   return sizeof(TGIFCommentExt)+nTmp;
  break;

 case BLOCK_CONTROLEXT:
  return sizeof(TGIFControlExt);
  break;

 case BLOCK_PLAINTEXT:
  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt));
  if (nTmp > 0)
   return sizeof(TGIFPlainTextExt)+nTmp;
  break;

 case BLOCK_IMAGE:
  TGIFImageDescriptor *pIDescr =
   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
  int nLCTSize = (int)
   (pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3*
   (1 << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1)));

  int nTmp = GetSubBlocksLen(m_nCurrOffset+
   sizeof(TGIFImageDescriptor) + nLCTSize + 1);
  if (nTmp > 0)
   return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp;
  break;
 };

 return 0;
}

UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
{
 ASSERT(pParam);
 CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);

 pPic->m_bIsPlaying = TRUE;
 pPic->ThreadAnimation();
 pPic->m_bIsPlaying = FALSE;

 // this thread has finished its work so we close the handle
 CloseHandle(pPic->m_hThread);
 // and init the handle to zero (so that Stop() doesn't Wait on it)
 pPic->m_hThread = 0;
 return 0;
}

void CPictureEx::ThreadAnimation()
{
 // first, restore background (for stop/draw support)
 // disposal method #2
 if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
 {
  HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
  if (hBrush)
  {
   RECT rect = {
    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
   FillRect(m_hMemDC,&rect,hBrush);
   DeleteObject(hBrush);
  };
 }
 else
  // disposal method #3
  if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
  {
   // put it back
   BitBlt(m_hMemDC,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
    m_arrFrames[m_nCurrFrame].m_frameSize.cx,
    m_arrFrames[m_nCurrFrame].m_frameSize.cy,
    m_hDispMemDC,0,0, SRCCOPY);
   // init variables
   SelectObject(m_hDispMemDC,m_hDispOldBM);
   DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
   DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
  };

 while (!m_bExitThread)
 {
  if (m_arrFrames[m_nCurrFrame].m_pPicture)
  {
  ///
  // Before rendering a frame we should take care of what's
  // behind that frame. TFrame::m_nDisposal will be our guide:
  //   0 - no disposal specified (do nothing)
  //   1 - do not dispose (again, do nothing)
  //   2 - restore to background color (m_clrBackground)
  //   3 - restore to previous

    disposal method #3
   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 3)
   {
    // prepare a memory DC and store the background in it
    m_hDispMemDC = CreateCompatibleDC(m_hMemDC);
    m_hDispMemBM = CreateCompatibleBitmap(m_hMemDC,
       m_arrFrames[m_nCurrFrame].m_frameSize.cx,
       m_arrFrames[m_nCurrFrame].m_frameSize.cy);
    
    if (m_hDispMemDC && m_hDispMemBM)
    {
     m_hDispOldBM = reinterpret_cast<HBITMAP> (SelectObject(m_hDispMemDC,m_hDispMemBM));
     BitBlt(m_hDispMemDC,0,0,
      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
      m_hMemDC,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
      SRCCOPY);
    };
   };
   ///

   long hmWidth;
   long hmHeight;
   m_arrFrames[m_nCurrFrame].m_pPicture->get_Width(&hmWidth);
   m_arrFrames[m_nCurrFrame].m_pPicture->get_Height(&hmHeight);

   if (m_arrFrames[m_nCurrFrame].m_pPicture->Render(m_hMemDC,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
    m_arrFrames[m_nCurrFrame].m_frameSize.cx,
    m_arrFrames[m_nCurrFrame].m_frameSize.cy,
    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
   {
    Invalidate(FALSE);
   };
   
   if (m_bExitThread) break;

   // if the delay time is too short (like in old GIFs), wait for 100ms
   if (m_arrFrames[m_nCurrFrame].m_nDelay < 5)
    WaitForSingleObject(m_hExitEvent, 100);
   else
    WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[m_nCurrFrame].m_nDelay);

   if (m_bExitThread) break;

   // disposal method #2
   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
   {
    HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
    if (hBrush)
    {
     RECT rect = {
      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
     FillRect(m_hMemDC,&rect,hBrush);
     DeleteObject(hBrush);
    };
   }
   else
    if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
    {
     // put it back
     BitBlt(m_hMemDC,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
      m_hDispMemDC,0,0, SRCCOPY);
     // init variables
     SelectObject(m_hDispMemDC,m_hDispOldBM);
     DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
     DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
    };
  };
  m_nCurrFrame++;
  if (m_nCurrFrame == m_arrFrames.size())
  {
   m_nCurrFrame
    = 0;
  // init the screen for the first frame,
   HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
   if (hBrush)
   {
    RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
    FillRect(m_hMemDC,&rect,hBrush);
    DeleteObject(hBrush);
   };
  };
 };
}

void CPictureEx::Stop()
{
 m_bIsPlaying = FALSE;
 m_bExitThread = TRUE;
 SetEvent(m_hExitEvent);
 if (m_hThread)
 {
  // we'll wait for 5 seconds then continue execution
  WaitForSingleObject(m_hThread,5000);
  CloseHandle(m_hThread);
  m_hThread = NULL;
 }

 // make it possible to Draw() again
 ResetEvent(m_hExitEvent);
 m_bExitThread = FALSE;
}

HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen,
 UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset,
 UINT *pDisposal)
{
 if (!m_pRawData) return NULL;

 // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data

 *pDisposal = 0;
 enum GIFBlockTypes nBlock;
 nBlock = GetNextBlock();

 while (
  (nBlock != BLOCK_CONTROLEXT) &&
  (nBlock != BLOCK_IMAGE) &&
  (nBlock != BLOCK_PLAINTEXT) &&
  (nBlock != BLOCK_UNKNOWN) &&
  (nBlock != BLOCK_TRAILER)
  )
 {
  if (!SkipNextBlock()) return NULL;
  nBlock = GetNextBlock();
 };

 if ((nBlock == BLOCK_UNKNOWN) ||
  (nBlock == BLOCK_TRAILER))
  return NULL;

 // it's either a control ext.block, an image or a plain text

 int nStart = m_nCurrOffset;
 int nBlockLen = GetNextBlockLen();

 if (nBlockLen <= 0) return NULL;

 if (nBlock == BLOCK_CONTROLEXT)
 {
  // get the following data
  TGIFControlExt *pControl =
   reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
  // store delay time
  *pDelay = pControl->m_wDelayTime;
  // store disposal method
  *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);

  if (!SkipNextBlock()) return NULL;
  nBlock = GetNextBlock();
  
  // skip everything until we find data to display
  // (image block or plain-text block)
  
  while (
   (nBlock != BLOCK_IMAGE) &&
   (nBlock != BLOCK_PLAINTEXT) &&
   (nBlock != BLOCK_UNKNOWN) &&
   (nBlock != BLOCK_TRAILER)
   )
  {
   if (!SkipNextBlock()) return NULL;
   nBlock = GetNextBlock();
   nBlockLen += GetNextBlockLen();
  };

  if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
   return NULL;
  nBlockLen += GetNextBlockLen();
 }
 else
  *pDelay = -1; // to indicate that there was no delay value

 if (nBlock == BLOCK_IMAGE)
 {
  // store size and offsets
  TGIFImageDescriptor *pImage =
   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
  pBlockSize->cx = pImage->m_wWidth;
  pBlockSize->cy = pImage->m_wHeight;
  pBlockOffset->cx = pImage->m_wLeftPos;
  pBlockOffset->cy = pImage->m_wTopPos;
 };

 if (!SkipNextBlock()) return NULL;

 HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
  sizeof(TGIFHeader) +
  sizeof(TGIFLSDescriptor) +
  m_nGlobalCTSize +
  nBlockLen +
  1);  // for the trailer

 if (!hGlobal) return NULL;

 int nOffset = 0;

 // GMEM_FIXED means we get a pointer
 unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);

 CopyMemory(pGlobal,m_pRawData,
  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize);
 nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;

 CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
 nOffset += nBlockLen;

 pGlobal[nOffset] = 0x3B; // trailer
 nOffset++;

 *pBlockLen = nOffset;

 return hGlobal;
}

BOOL CPictureEx::IsGIF() const
{
 return m_bIsGIF;
}

BOOL CPictureEx::IsAnimatedGIF() const
{
 return (m_bIsGIF && (m_arrFrames.size() > 1));
}

BOOL CPictureEx::IsPlaying() const
{
 return m_bIsPlaying;
}

int CPictureEx::GetFrameCount() const
{
 if (!IsAnimatedGIF())
  return 0;

 return m_arrFrames.size();
}

COLORREF CPictureEx::GetBkColor() const
{
 return m_clrBackground;
}

void CPictureEx::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 LONG nPaintWidth = m_PaintRect.right-m_PaintRect.left;

 if (nPaintWidth > 0)
 {
  LONG nPaintHeight = m_PaintRect.bottom - m_PaintRect.top;
  ::BitBlt(dc.m_hDC, 0, 0, nPaintWidth, nPaintHeight, 
   m_hMemDC, m_PaintRect.left, m_PaintRect.top, SRCCOPY);
 }
 else
 {
  ::BitBlt(dc.m_hDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy,
   m_hMemDC, 0, 0, SRCCOPY);
 };
}

BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
{
 SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);

 HDC hWinDC = ::GetDC(m_hWnd);
 if (!hWinDC) return FALSE;
 
 m_hMemDC = CreateCompatibleDC(hWinDC);
 if (!m_hMemDC)
 {
  ::ReleaseDC(m_hWnd,hWinDC);
  return FALSE;
 };

 m_hBitmap  = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
 if (!m_hBitmap)
 {
  ::ReleaseDC(m_hWnd,hWinDC);
  ::DeleteDC(m_hMemDC);
  return FALSE;
 };

 m_hOldBitmap = reinterpret_cast<HBITMAP>
      (SelectObject(m_hMemDC,m_hBitmap));
 
 // fill the background
 m_clrBackground = GetSysColor(COLOR_3DFACE);
 RECT rect = {0,0,nWidth,nHeight};
 FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));

 ::ReleaseDC(m_hWnd,hWinDC);
 m_bIsInitialized = TRUE;
 return TRUE;
}

void CPictureEx::OnDestroy()
{
 Stop(); 
 CStatic::OnDestroy();
}

void CPictureEx::SetBkColor(COLORREF clr)
{
 if (!m_bIsInitialized) return;

 m_clrBackground = clr;

 HBRUSH hBrush = CreateSolidBrush(clr);
 if (hBrush)
 {
  RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
  FillRect(m_hMemDC,&rect,hBrush);
  DeleteObject(hBrush);
 };
}

#ifdef GIF_TRACING
void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize)
{
 CFile file;

 if (!file.Open(szFileName,
   CFile::modeCreate |
   CFile::modeWrite |
   CFile::shareDenyNone))
 {
  TRACE(_T("WriteData: Error creating file %s\n"),szFileName);
  return;
 };

 char *pData = reinterpret_cast<char *> (GlobalLock(hData));
 if (!pData)
 {
  TRACE(_T("WriteData: Error locking memory\n"));
  return;
 };

 TRY
 {
  file.Write(pData,dwSize);
 }
 CATCH(CFileException, e);                                         
 {
  TRACE(_T("WriteData: An exception occured while writing to the file %s\n"),
   szFileName);
  e->Delete();
  GlobalUnlock(hData);
  file.Close();
  return;
 }
 END_CATCH
 
 GlobalUnlock(hData);
 file.Close();
}

void CPictureEx::EnumGIFBlocks()
{
 enum GIFBlockTypes nBlock;

 ResetDataPointer();
 while(m_nCurrOffset < m_nDataSize)
 {
  nBlock = GetNextBlock();
  switch(nBlock)
  {
  case BLOCK_UNKNOWN:
   TRACE(_T("- Unknown block\n"));
   return;
   break;

  case BLOCK_TRAILER:
   TRACE(_T("- Trailer block\n"));
   break;

  case BLOCK_APPEXT:
   TRACE(_T("- Application extension block\n"));
   break;

  case BLOCK_COMMEXT:
   TRACE(_T("- Comment extension block\n"));
   break;

  case BLOCK_CONTROLEXT:
   {
   TGIFControlExt *pControl =
    reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
   TRACE(_T("- Graphic control extension block (delay %d, disposal %d)\n"),
     pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
   };
   break;

  case BLOCK_PLAINTEXT:
   TRACE(_T("- Plain text extension block\n"));
   break;

  case BLOCK_IMAGE:
   TGIFImageDescriptor *pIDescr =
    reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
   TRACE(_T("- Image data block (%dx%d  %d,%d)\n"),
     pIDescr->m_wWidth,
     pIDescr->m_wHeight,
     pIDescr->m_wLeftPos,
     pIDescr->m_wTopPos);
   break;
  };

  SkipNextBlock(); 
 };

 TRACE(_T("\n"));
}
#endif // GIF_TRACING

BOOL CPictureEx::SetPaintRect(const RECT *lpRect)
{
 return CopyRect(&m_PaintRect, lpRect);
}

BOOL CPictureEx::GetPaintRect(RECT *lpRect)
{
 return CopyRect(lpRect, &m_PaintRect);
}


 

第3页:添加代码实现进度条的显示

 

接着,保存记事本中的MyActiveX.rc。这时,Visual Studio将报告说MyActiveX.rc被外部程序修改,点击“Yes”重载这个资源文件。最后,选中“解决方案浏览器”中的 “ProcessingProgressBar.gaf”,在它的属性对话框中,将“Relative Path”修改为“ProcessingProgressBar.gif”
添加对话框显示进度条

 现在,我们将添加一个对话框用以显示进度条:
  1.在资源视图中,右键点击对话框节点,在上下文菜单中选择“Insert Dialog”创建一个新的默认对话框。
  2.删除对话框模板上我们不需要的“OK”和“Cancel”按钮,然后将对话框的尺寸调整为230 x 40。
  3.然后,我们对对话框的属性做一些必要的修改,使它更适合于用来显示进度条:Border – None, Style – Child, System Menu – False, Visible – True。
  4.将对话框的ID修改为IDD_MAINDIALOG。
  5.在对话框上添加一个图像控件(Picture Control)。将图像控件的尺寸调整为200 x 20,然后将其ID修改为IDC_PROGRESSBAR。同时,将其颜色属性修改为白色(White)。
  6.最后,我们需要为对话框资源创建一个新的类。在对话框资源上右键单击,在弹出的菜单中选择“Add Class”,在接着出现的“MFC类向导”对话框中,我们将新的类命名为CMainDialog,基类设置为CDialog,点击“Finish”完成类的创建。


图3 创建新的对话框类CMainDialog

 现在,我们来为这个新创建的对话框类添加成员变量。成员变量m_MainDialog对应于类CMainDialog,而m_ProgressBar对应于我们刚刚添加的进度条显示控件。
  1.为类CMyActiveXCtrl添加成员变量m_MainDialog。选择类视图,在类CMyActiveXCtrl的节点上点击右键,在上下文 菜单中选择“Add”->“ Add Variable”,然后输入CMainDialog作为变量类型,输入m_MainDialog作为变量名。
  2.跟上面的步骤一样,我们为类CMainDialog添加成员变量m_ProgressBar。输入CPictureEx作为变量类 型,m_ProgressBar作为变量名。同时,选中“Control variable”复选框,使其成为一个控件变量。在“Control ID”下拉列表中,确认控件的ID为“IDC_PROGRESSBAR”。具体设置如下图4所示:


图4 添加成员变量m_ProgressBar

 添加代码实现进度条的显示

 现在,我们将添加代码实现主对话框和进度条控件的显示。

  1.在类视图中,选中类CMyActiveXCtrl。然后在其属性页中选中消息(Messages)图标,在消息列表中,选择“WM_CREATE”消 息,然后选择“<Add> OnCreate”为WM_CREATE消息创建一个消息响应函数OnCreate。这样,向导就会为我们在类CMyActiveXCtrl中创建一个消 息响应函数OnCreate。
  2.编辑MyActiveXCtrl.cpp文件,将下列代码添加到我们刚刚创建的OnCreate函数中。这段代码将负责创建主对话框:
m_MainDialog.Create(IDD_MAINDIALOG, this);
然后,将下列代码添加到绘制函数OnDraw中,以实现对主对话框大小尺寸和背景的修改:
m_MainDialog.MoveWindow(rcBounds, TRUE);
CBrush brBackGnd(TranslateColor(AmbientBackColor()));
pdc->FillRect(rcBounds, &brBackGnd);
  3.同样的,我们为主对话框类CMainDialog添加创建消息WM_CREATE的消息响应函数OnCreate。
  4.编辑MainDialog.cpp文件,将下列代码添加到函数OnCreate中,以实现进度条GIF图像的加载和绘制: 
if(m_ProgressBar.Load(MAKEINTRESOURCE(IDR_PROGRESSBAR),_T("GIF")))
    m_ProgressBar.Draw();

 最后,确认我们的配置文件是Release版本,构建生成MyActiveX整个解决方案。这样,我们就完成了整个ActiveX控件的创建。

 

第4页:为ActiveX控件创建网页

 

为ActiveX控件创建网页

 光完成了ActiveX控件还不行,因为ActiveX控件无法单独运行,所以我们还需要为ActiveX控件创建相应的测试页面。如果想为我们的 ActiveX控件创建一个默认的测试页面,最佳的工具就是Microsoft ActiveX Control Pad。这是一个免费的工具,我们可以从微软的网站上下载得到。

 我们需要下载ActiveX Control Pad并将它安装在我们的开发机器上。同时为了简化测试工作,我们还需要在同一台机器上安装微软的IIS服务器作为Web服务器

 当我们第一次运行ActiveX Control Pad的时候,它将创建一个默认的HTML页面。接下来,我们需要将ActiveX控件插入到这个页面中。在<BODY>标签内的某个位置右 键单击,选择“Insert ActiveX Control”,在弹出的“Insert ActiveX Control”对话框中,选中我们刚刚创建的ActiveX控件MyActiveX,然后点击“OK”关闭对话框,将ActiveX控件插入到页面中。

 


图5 插入ActiveX控件

  ActiveX Control Pad提供了两个对话框,供我们对ActiveX控件的各个属性进行修改编辑。属性对话框用于修改ActiveX控件的各项属性。而“编辑ActiveX 控件(Edit ActiveX Control)”对话框用于手动地对ActiveX控件进行修改。当然,我们也可以不使用对话框,直接编辑HTML代码达到相同的效果。在代码中找到 OBJECT ID标签,如下图6所示,修改它的尺寸参数为“WIDTH=350”和“HEIGHT=50”。完成代码的修改后,将这个文件保存为 myactivex.htm,放置到IIS服务器的网站根目录wwwroot下。

 


图6 在ActiveX Control Pad中编辑页面

 现在,我们就可以使用IE打开http://localhost/myactivex.htm对 ActiveX控件进行测试了。在ActiveX控件加载的过程中,如果你得到一些警告消息,点击“OK”略过就可以了。正常情况下,你将看到页面中显示 的一个进度条GIF动画。如果你没有看到动画,或者是仅仅在ActiveX控件的位置显示一个红色的叉叉,这可能是你的浏览器的安全设置,阻止了ActiveX控件的加载和运行。要解决这个问题,请修改你的浏览器的安全设置,使其所有关于ActiveX的选项都在“允许(enabled)”状态。


图7 浏览器中的MyActiveX控件

 接下来,我们将让我们的控件成为一个“签名的”或者是“安全的”控件,以此来解决在ActiveX控件加载过程中所产生的那些警告信息。
构 建一个“签名的”ActiveX控件。要想创建一个“签名的”ActiveX控件,你必须从一些认证机构,例如Thawte,Verisign和 GeoTrust,获得代码签名证书(Code Signing Certificate)。通过这项服务,他们将对你进行认证同时为你提供用于对ActiveX控件进行签名的证书文件。这些用于应用程序签名的证书文件 包括mycert.spc和mykey.pvk.两个文件。

 为了对ActiveX应用程序进行签名,我们需要将这个程序的所有组件打包成CAB文件。这个CAB文件将被从网站上下载到目标机器上,然后 ActiveX控件会通过下载的CAB文件被安装到目标机器上。要启用这项功能,我们必须在ActiveX控件的版本信息VERSIONINFO结构体中 定义OLESelfRegister变量。在VS 2003及其之前的Visual Studio之中,这一变量是被自动添加的。但是在Visual Studio 2005中,默认情况下VERSIONINFO结构体中并不包含这一变量,我们需要按照下面的示例手动添加OLESelfRegister这一变量:

VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904e4"
        BEGIN
            VALUE "CompanyName", "TODO: <Company name>"
            VALUE "FileDescription", "TODO: <File description>"
            VALUE "FileVersion", "1.0.0.1"
            VALUE "InternalName", "MyActiveX.ocx"
            VALUE "LegalCopyright", 
                  "TODO: (c) <Company name>. All rights reserved."
            VALUE "OLESelfRegister", "\0"
            VALUE "OriginalFilename", "MyActiveX.ocx"
            VALUE "ProductName", "TODO: <Product name>"
            VALUE "ProductVersion", "1.0.0.1"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1252
    END
END

 在签名ActiveX控件之前,ActiveX控件应该被打包成CAB文件。这个CAB文件同样还包含一个INF文件,它将用于安装你的ActiveX控 件。要创建CAB文件,你需要Microsoft Cabinet Software Development Kit中的cabarc.exe。下面的INF文件演示了如何将我们的MyActiveX控件打包到CAB文件中。对于其中的CLSID,你需要将其修改 为跟之前我们使用ActiveX Control Pad创建的HTML页面中OBJECT ID相同的值。

[Add.Code]
myactivex.ocx=myactivex.ocx
myactivex.inf=myactivex.inf
[myactivex.ocx]
file=thiscab
clsid={36299202-09EF-4ABF-ADB9-47C599DBE778}
RegisterServer=yes
FileVersion=1,0,0,0
[myactivex.inf]
file=thiscab

 我们可以通过下面的DOS命令创建CAB文件。这里需要注意的是:OCX文件和INF文件必须跟你所执行的cabarc.exe在同一个目录,否则CAB文件在被下载后将无法正确的解压缩。这也是一个导致ActiveX控件被显示为红色叉叉的重要原因。
cabarc -s 6144 N myactivex.cab myactivex.ocx myactivex.inf

 要对我们刚刚创建的CAB文件进行签名,我们需要Microsoft MSDN为我们提供的工具signcode.exe 。(请查阅本文末尾与“Signing and Checking with Authenticode”相关的参考资料) 。通过这个工具,我们就可以用我们从认证机构获得的代码签名证书对CAB文件进行签名了。下面是一个使用signcode对myactivex.cab进 行签名的例子:

signcode -n "myactivex" -i 
http://www.myactivex.com -spc mycert.spc -v mykey.pvk -t http://timestamp.verisign.com/scripts/timstamp.dll myactivex.cab

  在上面的例子中,http://www.myactivex.com应该被替换成你的ActiveX控件的说明页面,通过这个页面,你可以为用户提供更多的关于这个控件的信息。
  要使用签名过后的CAB文件,首先我们需要将myactivex.cab复制到web服务器的一个合适的目录下,然后我们修改Web页面中的OBJECT ID标签,添加一个参数CODEBASE指向CAB文件。具体代码如下图8所示。这样,当你再使用IE打开这个页面时,CAB文件会被下载,然后 ActiveX控件会被正确的安装到你的机器上。在安装的过程中,再也不会弹出提示未签名ActiveX控件的警告信息了。


图8 添加CODEBASE参数

 

第5页:创建安全的ActiveX控件

 

创建安全的ActiveX控件

 要想创建一个能够在IE中成功加载而没有“不安全”的警告或者错误提示信息的ActiveX控件,我们必须实现安全的初始化和脚本。关于安全初始化的更加 详细的信息,请参考本文结尾的参考资料“Safe Initialization and Scripting for ActiveX Controls”。基本上,所有要做的工作都是在DllRegisterServer 和DllUnregisterServer这两个函数中完成的。下面,我们就来一步步地将我们的ActiveX控件变成一个“安全的”控件。

  1.编辑MyActiveX.cpp并添加如下的代码。其中,CLSID_SafeItem的值应该跟MyActiveXCtrl.cpp中的 IMPLEMENT_OLECREATE_EX一致,这就等同于你的ActiveX控件。同样,它也应该跟你的HTML页面中的OBJECT ID标签中的CLSID一致。

#include "comcat.h"
#include "strsafe.h"
#include "objsafe.h"
// CLSID_SafeItem – 安全的ActiveX控件所必须的内容
// ID的值跟xxxCtrl.cpp文件中的IMPLEMENT_OLECREATE_EX一致
const CATID CLSID_SafeItem =
{ 0x36299202, 0x9ef, 0x4abf,{ 0xad, 0xb9, 0x47, 0xc5, 0x99, 0xdb, 0xe7, 0x78}};
// HRESULT CreateComponentCategory – 用于注册安全的ActiveX控件
HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription)
{
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (FAILED(hr))
        return hr;
    // 确认HKCR\Component Categories\{..catid...}键值被注册
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english
    size_t len;
    // 确认描述不是太长。
// 只复制开始的127个字符。
// StringCchLength的第二个参数表示被读入catDescription的最大字符数。
    // 第三个参数表示字符串的长度
    hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, &len);
    if (SUCCEEDED(hr))
        {
        if (len>127)
          {
            len = 127;
          }
        }   
    else
        {
          // TODO: Write an error handler;
        }
    hr = StringCchCopy(catinfo.szDescription, len + 1, catDescription);
    // 添加字符串结束符.
    catinfo.szDescription[len + 1] = '\0';
    hr = pcr->RegisterCategories(1, &catinfo);
    pcr->Release();
    return hr;
}
// HRESULT RegisterCLSIDInCategory -
//      注册组件分类信息
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// 注册组件分类信息
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
       CATID rgcatid[1] ;
       rgcatid[0] = catid;
       hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
    }
    if (pcr != NULL)
        pcr->Release();
            
    return hr;
}
// HRESULT UnRegisterCLSIDInCategory
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
       CATID rgcatid[1] ;
       rgcatid[0] = catid;
       hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
    }
    if (pcr != NULL)
        pcr->Release();
    return hr;
}

  2.修改 DllRegisterServer函数: 

STDAPI DllRegisterServer(void)
{
    HRESULT hr;    // HResult used by Safety Functions 
    AFX_MANAGE_STATE(_afxModuleAddrThis); 
    if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
      return ResultFromScode(SELFREG_E_TYPELIB); 
    if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
      return ResultFromScode(SELFREG_E_CLASS); 
    // 为初始化标记控件为安全控件                                       
    hr = CreateComponentCategory(CATID_SafeForInitializing, 
         L"Controls safely initializable from persistent data!");
    if (FAILED(hr))
      return hr; 
    hr = RegisterCLSIDInCategory(CLSID_SafeItem, 
         CATID_SafeForInitializing);
    if (FAILED(hr))
        return hr;
   // 为脚本标记控件为安全控件                                        
    hr = CreateComponentCategory(CATID_SafeForScripting,                                 L"Controls safely  scriptable!");
    if (FAILED(hr))
        return hr; 
    hr = RegisterCLSIDInCategory(CLSID_SafeItem,                         CATID_SafeForScripting);
    if (FAILED(hr))
        return hr;
    return NOERROR;
}

  3.修改 DllUnregisterServer函数: 

STDAPI DllUnregisterServer(void)
{
    HRESULT hr;    // HResult used by Safety Functions 
    AFX_MANAGE_STATE(_afxModuleAddrThis); 
    if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
      return ResultFromScode(SELFREG_E_TYPELIB); 
    if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
      return ResultFromScode(SELFREG_E_CLASS); 
    hr=UnRegisterCLSIDInCategory(CLSID_SafeItem,                      CATID_SafeForInitializing);
    if (FAILED(hr))
      return hr; 
    hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, 
                        CATID_SafeForScripting);
    if (FAILED(hr))
      return hr; 
    return NOERROR;
}

第6页:ActiveX控件的属性,方法和事件

 

ActiveX控件的属性,方法和事件

  ActiveX控件和Web页面之间的通信是 通过ActiveX控件的属性,方法和事件来完成的。为了演示这些内容,我们需要创建一个带有输入框的页面。当页面上的“提交”按钮被点击后,输入的文本 将通过一个输入参数自定义属性传递给ActiveX控件。然后,控件的一个函数将被调用,复制这些文本到一个输出参数自定义属性。接着,将触发一个事件, 将这些文本显示到页面上。我们将通过下面这些步骤来实现这一ActiveX控件和Web页面之间的通信过程:

  1.首先,我们将为ActiveX控件创建自定义属性,用于发送和接收文本。在类视图中,展开MyActiveXLib,然后选择 _DMyActiveX。右键单击_DMyActiveX,在上下文菜单中选择“Add”->“Add Property”。在”添加属性向导”对话框中,选择BSTR作为属性类型,然后输入“InputParameter”作为属性的名称。至于对话框中的 其它内容,向导将自动为我们生成。最后,点击“Finish”按钮关闭对话框,向导将为我们创建相应的代码。同样地,添加另外一个BSTR类型的属性 OutputParameter。

 


图9 添加属性向导

  2.接下来,我们将创建一个方法,使Web页面能够通知控件将文本从输入参数传递到输出参数。在类视图中,展开MyActiveXLib,然后选择 _DMyActiveX。右键单击_DMyActiveX,在上下文菜单中选择“Add”->“ Add Method”。在“添加方法向导”对话框中,选择void作为方法的返回类型,输入“LoadParameter”作为方法名,向导会自动地填入 “LoadParameter”作为外部名字。点击“Finish”按钮关闭对话框,向导将为我们创建相应的方法代码。  

 


图10添加方法向导

  3.现在,我们将创建一个事件,让ActiveX控件通知Web页面已经完成了文本从输入参数到输出参数的传送。Web页面中的代码将对这个事件做出响 应,将输出参数中的文本显示出来以证明传送确实发生了。在类视图中,右键点击CMyActiveXCtrl,然后选择“Add”->“Add Event”。在“添加事件向导”对话框中,输入“ParameterLoaded”作为事件名称,然后将外部名称修改为 “FireParameterLoaded”。点击“Finish”按钮关闭对话框,向导将为我们创建相应的代码。

 


图11 添加事件向导

 通过上面这些步骤,向导已经为我们创建了代码的大致框架。我们只需要添加几行代码,完成文本的复制然后通过事件通知Web页面就可以了。编辑文件MyActiveXCtrl.cpp,在函数LoadParameter中添加如下的代码:

// 将文本从输入参数复制到输出参数
m_OutputParameter = m_InputParameter;
// 触发一个事件通知Web页面
FireParameterLoaded();
  为了测试ActiveX控件和Web页面之间的通信,使用ActiveX Control Pad创建如下的HTML代码:
<HTML>
<HEAD>
<TITLE>MyActiveX - Methods, Properties, and Events</TITLE> 
<SCRIPT LANGUAGE="JavaScript">
function PassParameter()
{
    if (StringInput.value != " ")
    {
        MyActiveX1.InputParameter = StringInput.value;
        MyActiveX1.LoadParameter();
    }
}
</SCRIPT>
</HEAD>
<BODY>
<center>
MyActiveX - Methods, Properties, and Events Example
<p></p> 
<OBJECT ID="MyActiveX1" WIDTH=350 HEIGHT=50
CLASSID="CLSID:36299202-09EF-4ABF-ADB9-47C599DBE778">
    <PARAM NAME="_Version" VALUE="65536">
    <PARAM NAME="_ExtentX" VALUE="2646">
    <PARAM NAME="_ExtentY" VALUE="1323">
    <PARAM NAME="_StockProps" VALUE="0">
</OBJECT>
<p></p> 
Input Parameter: <INPUT TYPE ="text" NAME="StringInput" VALUE=" ">
<p></p>
<INPUT TYPE="button" NAME="Submit" 
       VALUE="Submit" ONCLICK=PassParameter()>
<SCRIPT FOR=MyActiveX1 EVENT=ParameterLoaded()>
<!-- {
   window.document.write("The parameter you entered is:<br> " + MyActiveX1.OutputParameter + "  ")
-->
</SCRIPT>
</center>
</BODY>

 将上面的页面保存到Web服务器上,然后在IE中打开这个页面。你将看到一个用于输入文本的编辑框和“提交”按钮。在编辑框中输入文本,然后点击“提交”按钮,你将得到一个新的页面,其中显示了你刚刚输入的文本内容。下面,我们来解释一下整个Web页面的工作过程。

 当你点击“提交”按钮之后,JavaScript函数PassParameter将被调用。这个函数将文本从编辑框StringInput复制到 ActiveX控件的InputParameter属性。然后,它将调用控件的函数LoadParameter,将文本从InputParameter复 制到OutputParameter,接着调用FireParameterLoaded产生一个ActiveX控件事件。下面的HTML代码将负责对这个 事件进行处理,通过ActiveX控件的OutputParameter属性访问文本内容,将输入的文本显示到新窗口中。如此,就完成了ActiveX控 件和Web页面之间的通信。

<SCRIPT FOR=MyActiveX1 EVENT=ParameterLoaded()>
<!-- {
  window.document.write("The parameter you entered is:<br> " +                         MyActiveX1.OutputParameter + "  ")
-->
</SCRIPT>

microsoft activeX control pad程序下载:https://msdn.microsoft.com/en-us/library/ms968493.aspx

win7中安装microsoft activeX control pad过程中主要遇到了两个问题: 

1. 一双击setuppad就弹出“„„应用程序无法正常启动0xc0000142„„”的对话框;  解决方法:右键程序 属性—>兼容性,选中以兼容模式运行这个程序,在下拉框中选择windowXP,就可以安装了

 2. 在点击“安装”后,又出现了“„„16位应用程序„„管理员权限„„”之类的对话框。  解决方法:运行gpedit.msc,打开组策略,依次展开:本地计算机策略→用户配置→管理模板→Windows组件→应用程序兼容性,在右侧你就能看到“防止访问16位应用程序”,很有可能此处已被设置为“已启用”,你应该更改为“已禁用”才能运行16位Windows程序。

 

  • 0
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

jasonfqw

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值