VC中利用FLASH制作图声并茂的动画程序
作者:通信指挥学院三十一队 罗少波

前言:
FLASH
是一种功能强大的矢量动画,可以制作出各种华丽的电影效果,应用非常广泛!这也给予我们一个启迪:如果在VC程序中能够播放FLASH动画,将为程序增色不少,而且许多原本不易实现的功能,现在都可以轻松实现! 像金山词霸的安装程序主控界面就利用了FLASH,效果相当好。本文中笔者将制作一个完整的多媒体软件,将一些关键性技术介绍给大家,并提供全部代码供大家参考。


本文使用到的关键性技术:
1)利用VB制作MS AGENT播放模块。
2)将该播放模块、FLASH动画文件SWF与其它必要资源打包到程序中,并在运行时自动释放。
3)通过VCVB的通信实现多个不同模块的顺序执行。
4FLASH的播放,并实现避免右键菜单的弹出。

下面介绍具体的实现:
1)利用VB制作MS AGENT播放模块。
这里先插入MS AGENT模块。然后在Form_Load里进行Agent控件的初始化;

Private Sub Form_Load()

     Agent1.Characters.Load ("dot")

     下面就可进行动画操作了.

     Agent1.Characters.Character("dot").Show

     我们可用如下语句执行一些动作

     Agent1.Characters.Character("dot").Play "Congratulate"

     Agent1.Characters.Character("dot").Speak "你好!"

     Agent1.Characters.Character("dot").moveto  x,y ‘x,y 位屏幕坐标。

......

由于我们用的是dot.acs,我们不能假定别人的机子上有这个文件。所以我将这个文件复制到winnt(windows)\msagent\chars目录下, 用于复制的VC代码如下:

char *a = new char [255];

char *b = new char [255];

GetCurrentDirectory(255,a);

CString *str = new CString(a);

CString *sou = new CString;

////////////////////////////////////////

(*sou)=*str+CString("\\DOT.ACS");

GetWindowsDirectory (b,255);

CString  *des =new CString(b) ;

(*des)+=CString ("\\msagent\\chars");

CreateDirectory (des->GetBuffer (10),NULL);

*des+=CString ("\\DOT.ACS");

//AfxMessageBox (*des);

//AfxMessageBox (*sou);

CopyFile (sou->GetBuffer (10),des->GetBuffer (10),false);

//这个是为了避免在Load中用全路径。

2)实现了文件打包,将过多的模块整合到一起,避免文件的繁杂
首先我们将各个模块作为资源加入资源中。定义整数标识和类型。这些资源文件打包将在程序运行的时候解出。 以下是从资源释放到文件的函数:

int res2file(LPCTSTR lpName,LPCTSTR lpType,LPCTSTR filename)

{

//输入:lpName 为资源名,可用MAKEINTRESOURCE()宏将整型变为字符串。

//LpType  为串类型名

//Filename  为释放出的文件名。

//输出:成功1,失败0

HRSRC myres = FindResource (NULL,lpName,lpType);

HGLOBAL gl = LoadResource (NULL,myres);

LPVOID lp = LockResource(gl);//返回指向资源内存的地址的指针。

// CREATE_ALWAYS为不管文件存不存在都产生新文件。

HANDLE fp = CreateFile(filename ,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);

 

if (!fp)

        return false;

DWORD a;

//sizeofResource 得到资源文件的大小

if(!WriteFile (fp,lp,SizeofResource(NULL,myres),&a,NULL))

        return false;

CloseHandle(fp);

FreeResource(gl);

return true;

}

以下是调用的方法:

res2file (MAKEINTRESOURCE(IDR_ZF),"swf","zf.swf");

res2file (MAKEINTRESOURCE(IDR_ZG),"swf","zg.swf");

res2file (MAKEINTRESOURCE(IDR_DOT),"acs","DOT.ACS");

res2file (MAKEINTRESOURCE(IDR_SHARE),"dll","share.dll");

res2file (MAKEINTRESOURCE(IDR_TALK),"dll","talk.dll");

res2file (MAKEINTRESOURCE(IDR_SWFLASH),"ocx","swflash.ocx");


3)通过VCVB的通信实现多个不同模块的顺序执行
在程序中我们需要调用VB模块并等待它执行完毕再继续我们的VC程序,这就涉及到VCVB之间的通讯问题,在这里我使用事件来解决这个问题,请看如下代码:

CreateEvent(NULL,false,false,"lsbeven");

然后,调用ms agent 动画

PROCESS_INFORMATION pi;

STARTUPINFO si={0};

si.cb=sizeof(si);

char *a =new char [255];

GetCurrentDirectory (255,a);

CString *str=new CString(a);

//AfxMessageBox (*des);

//AfxMessageBox (*sou);

*str+=CString("\\talk.e\0");

BOOL fRet=CreateProcess(NULL,

      str->GetBuffer (5),

      NULL,

      NULL,

      FALSE,

      0,

      NULL,

      NULL,

      &si,

      &pi);

最后,用WaitForSingleObject (heven,40000)等待事件被触发。(40s超时)

接下来的问题便是如何在VB中完成执行后触发这个事件。由于在vb中使用API很复杂,为了提供简单起见我用vc做了share.dll实现setvalue,然后在VB中进行调用来激活事件。 setvalue中调用如下两个函数来激活事件。

OpenEvent(DWORD dwAccess,//存取方式

BOOL bInheritHandle,//是否能被继承

LPCTSTR lpName)//名字

SetEvent (HANDLE hEvent);

Vb中用变量锁方法实现在未出来提示点我呀!前对左键点击的无效,在显示完提示后,当用户点击就调用setvalue来激活事件。

Agent1_Click(ByVal CharacterID As String, ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Integer, ByVal y As Integer)

If  b ==ture

     Then  setvalue

Endif

4FLASH的播放,并实现避免右键菜单的弹出
1. 控键注册:

HMODULE   hmod=LoadLibrary ("swflash.ocx");

FARPROC   p=GetProcAddress (hmod,"DllRegisterServer");

(*p)();

2.在工程中插入 flash 控件。由于在不规则窗口中在进行flash控件的支持会使程序过于不清晰。于是另开独立的窗口来显示flash

void TransparentWnd::OnTimer(UINT nIDEvent)

{

  switch (nIDEvent)

  {

        case 1:

        {

               KillTimer (nIDEvent);

               DoChange(MAKEINTRESOURCE(IDB_MAIN2));

               //创建视,作为默认视

               if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,

                               CRect(260, 30,630, 370), this, AFX_IDW_PANE_FIRST, NULL))

                      TRACE0("Failed to create view window\n");

        CenterWindow ();

        ShowWindow (SW_SHOW);

        }

        break;

        default: break;

  }

}

 

BOOL TransparentWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

{

        if(m_wndView.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))

               return TRUE;//如果是command消息则视类先处理。

        return CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

 

void TransparentWnd::OnSetFocus(CWnd* pOldWnd)

{

        //如果有视类则视类获得焦点。

        if(m_wndView.m_hWnd !=NULL)

               m_wndView.SetFocus();

}

 

下面在CchildView中定义

 

CShockwaveFlash  myflash;

 

///改变窗口特征

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)

{

   if (!CWnd::PreCreateWindow(cs))

       return FALSE;

   cs.dwExStyle |= WS_EX_CLIENTEDGE;

   cs.style &= ~WS_BORDER;

   cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,

       ::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOWTEXT), NULL);

   return TRUE;

}

 

 

 

BOOL CChildView::OnEraseBkgnd(CDC* pDC)

{

   return TRUE;//不让window自动刷屏

}

 

//下面进入控制部分

int CChildView ::OnCreate (LPCREATESTRUCT lpCreateStruct)

{

    if(CWnd::OnCreate(lpCreateStruct)==-1)

        return -1;

    myflash.Create(NULL,WS_CHILD|WS_VISIBLE,CRect(0,0,0,0), this, 1024);

    SetTimer (1,0,NULL);

    SetTimer (2,12000,NULL);

    CRect m_rect;

    GetClientRect (&m_rect );

    myflash.MoveWindow (&m_rect);//使flash 控件占满整个视区域

    return 0;

}

 

char *a = new char [512];

GetCurrentDirectory (100,a);

CString  *s=new CString(a);

myflash.LoadMovie (0,*s+CString("\\zf.swf"));//这里一定要为全路径

myflash.Play();//播放FLASH动画

5)两个高级话题
5.1
VC中用COM接口对ms agent 进行操作

#include "stdafx.h"

#include

#include

#include

#include

 

const LPWSTR kpwszCharacter =L"dot.acs";

int APIENTRY WinMain(HINSTANCE hInstance,

                     HINSTANCE hPrevInstance,

                     LPSTR     lpCmdLine,

                     int       nCmdShow)

{

IAgent *pAgent;

if (FAILED(OleInitialize(NULL)))

     return -1;

HRESULT hRes=CoCreateInstance (CLSID_AgentServer,

                     NULL,

                     CLSCTX_LOCAL_SERVER,

                     IID_IAgent,

                     (LPVOID*)&pAgent);

//下面的代码调用IAgent::Load()方法来装入一个动画人物的数据

//由于Agent服务器在自己的内存空间中运行,所以传送的字符串变量需要用SysAllocString()来分配内存

 

VARIANTARG  vPath;

VariantInit(&vPath);

 

//初始化OLE变量

vPath.vt = VT_BSTR;

 

//指明变量类型为Unicode的字符串

vPath.bstrVal=SysAllocString(lpCharacter);

 

//lpCharacter 为动画人物数据的存放路径

 

long __RPC_FAR   lCharID,lRequestID;

hRes=pAgent->Load(vPath,&lCharID,&lRequestID);//装入数据人物ID在lCharID中返回

 

IDispatch  *pdCharacter;

hRes=pAgent->GetCharacter(lCharID,&pdCharacter);

//获取 lCharID的IDispatch接口指针调用IDispatch::QueryInterface()方法

 

//可以得到IAgentCharacter的接口指针:

IAgentCharacter  *pCharacter;

hRes=pdCharacter->QueryInterface(IID_IAgentCharacter,

                             (LPVOID*)&pCharacter);

 

pdCharacter->Release();

 

//释放IDispath 通过IAgentCharacter接口就可以调用动画人物支持的各种方法了:

 

hRes = pCharacter->Show(FALSE,&lRequestID); //显示动画人物

hRes = pCharacter->MoveTo(320,240,100,&lRequestID);//移动动画人物到屏幕中央

BSTR bszSpeak = SysAllocString(L"Hello World!");//分配字符串

hRes = pCharacter->Speak(bszSpeak,NULL,&lRequestID);//让动画人物说话

 

SysFreeString(bszSpeak);//释放字符串所占内存

 

// 程序在退出之前需要把创建的Agent对象释放:

 

if(pCharacter){

    pCharacter->Release();//释放IAgentCharacter接口

    pAgent->Unload(lCharID); //卸载动画人物数据

}

pAgent->Release(); //释放Agent对象

VariantClear(&vPath); //清除OLE变量
OleUnInitialize();

}

下载MS AGENT演示源代码(VC11.1K

5.2
同步问题还可用信号灯来实现
vc 中进行启动时第一步就创建信号量,值为0。在启动vb创建的程序,然后vc中进行p操作. vb创建的程序中 在退出时使用v操作。实现同步。

P (s):

S - -

 If s<0

Then 挂起当前进程

对应:win32 API :WaitForSingleObject 

V(s):

    S++

If s<=0

Then 在s的等待队列中唤醒一进程

对应:win32 API :ReleaseSemaphore 


6)后记
本程序写于6月份,当时是一个朋友的生日,它是作为一个礼物送给她的。开始的时候,只提供了简单的窗口界面后来,在看了几位高手的佳作后,将它改进了一下。并希望和大家一起讨论,将它更加完美。

作者罗少波信息:
QQ:40871850
EMail: pathfinder@sohu.com
地址:通信指挥学院三十一队