VC5-高级音频函数

 

  • 2005-09-07

    高级音频函数 - [VC专栏]

    使用多媒体API,源文件中需要包含头文件 Mmsystem.h,在Project->Settings->Link->Object/libray module中加入库 Winmm.lib。

    1、MessageBeep

      MessageBeep函数主要用来播放系统报警声音。系统报警声音是由用户在控制面板中的声音(Sounds)程序中定义的,或者在WIN.INI的[sounds]段中指定。

      该函数的声明为:  BOOL MessageBeep(UINT uType);
      参数uType说明了告警级:
       -1         从机器的扬声器中发出蜂鸣声。
       MB_ICONASTERISK  播放由SystemAsterisk定义的声音。
       MB_ICONEXCLAMATION 播放由SystemExclamation定义的声音。
       MB_ICONHAND    播放由SystemHand定义的声音。
       MB_ICONQUESTION  播放由SystemQuestion定义的声音。
       MB_OK       播放由SystemDefault定义的声音。

      要使用该函数,只须在程序中直接调用该函数即可。例如:
      void CTttView::OnMyMenu()
      {
        ......
        MessageBeep(MB_OK);
        ......
      );

    2、sndPlaySound

      MessageBeep只能用来播放少数定义的声音,如果程序需要播放数字音频文件(*.WAV文件),可以使用sndPlaySound函数。

      函数的声明为:
        BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound);
      参数lpszSound是指定了要播放的WAV文件名,如果该参数为NULL则停止正在播放的声音。
      参数fdwSound是播放标志:
        SND_SYNC  同步播放声音,在播放完后PlaySound函数才返回。
        SND_ASYNC  用异步方式播放声音,PlaySound函数在开始播放后立即返回。
        SND_LOOP  重复播放声音,必须与SND_ASYNC标志一块使用。

      要使用该函数,只须在程序中直接调用该函数即可。例如:
      void CTttView::OnMyMenu()
      {
        ......
        sndPlaySound("MYSOUND.WAV",SND_ASYNC);
        ......
      );

    3、PlaySound

      如果程序需要播放数字音频文件(*.WAV文件)或音频资源,可以使用PlaySound函数。

      函数的声明为:
        BOOL PlaySound(LPCSTR lpszSound, HMODULE hmod, UINT fuSound);
      参数lpszSound是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAV资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音。如果该参数为NULL则停止正在播放的声音。
      参数hmod是应用程序的实例句柄,当播放WAV资源时要用到该参数,否则它必须为NULL。
      参数fdwSound是播放标志的组合:
        SND_SYNC  同步播放声音,在播放完后PlaySound函数才返回。
        SND_ASYNC  用异步方式播放声音,PlaySound函数在开始播放后立即返回。
        SND_LOOP  重复播放声音,必须与SND_ASYNC标志一块使用。
        SND_FILENAME lpszSound参数指定了WAVE文件名。
        SND_RESOURCE lpszSound参数是WAVE资源的标识符,这时要用到hmod参数。
        SND_MEMORY  播放载入到内存中的声音,此时lpszSound是指向声音数据的指针。
        SND_ALIAS   lpszSound参数指定了注册表或WIN.INI中的系统事件的别名。
        SND_ALIAS_ID lpszSound参数指定了预定义的声音标识符。

    (1)直接播出声音文件。  可在程序中直接调用该函数:
        PlaySound("c://windows//media//The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC);

    (2)可将声音文件加入到资源中,然后从资源中播放声音。
      Visual C++支持WAVE型资源,用户在资源视图中单击鼠标右键并选择Import命令,然后在文件选择对话框中选择The Microsoft Sound.wav文件,则该文件就会被加入到WAVE资源中。
      假定声音资源的ID为IDR_STARTWIN,则可:
        PlaySound((LPCTSTR)IDR_STARTWIN, AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC);

    (2)可用PlaySound播放系统声音。  Windows启动的声音是由SystemStart定义的系统声音,因此可:
        PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC);

  • 2005-09-07

    MCI编程 - [VC专栏]


      使用MCI API,源文件中需要包含头文件 Mmsystem.h,在Project->Settings->Link->Object/libray module中加入库 Winmm.lib。

    1、MCI简介

      MCI(Media Control Interface,媒体控制接口)向Windows程序提供了在高层次上控制媒体设备接口的能力。程序不必关心具体设备,就可以对激光唱机(CD)、视盘机、波形音频设备、视频播放设备和MIDI设备等媒体设备进行控制。
      对于程序员来说,可以把MCI理解为设备面板上的一排按键,通过选择不同的按键(发送不同的MCI命令)可以让设备完成各种功能,而不必关心设备内部实现。
      比如,对于play,视盘机和CD机有不同的反应(一个是播放视频,一个播放音频),而对用户来说却只需要按同一按钮。

      应用程序通过向MCI发送命令来控制媒体设备。MCI命令接口分命令字符串和命令消息两种,两者具有相同的功能。命令字符串具有使用简单的特点,但是它的执行效率不如命令消息。

      所有的MCI命令字符串都是通过多媒体API函数mciSendString传递给MCI的,该函数的声明为:
        MCIERROR mciSendString(
          LPCTSTR lpszCommand,    //MCI命令字符串
          LPTSTR lpszReturnString, //存放反馈信息的缓冲区
          UINT  cchReturn,     //缓冲区的长度
          HANDLE hwndCallback    //回调窗口的句柄,一般为NULL
        ); //若成功则返回0,否则返回错误码。
      该函数返回的错误码可以用mciGetErrorString函数进行分析,该函数的声明为:
        BOOL mciGetErrorString(
          DWORD fdwError,   //函数mciSendString返回的错误码
          LPTSTR lpszErrorText, //接收描述错误的字符串的缓冲区
          UINT  cchErrorText  //缓冲区的长度
        );

      下面是使用mciSendString函数的一个简单例子:
        char buf[50];
        MCIERROR mciError;
        mciError=mciSendString("open cdaudio",buf,strlen(buf),NULL);
        if(mciError)
        {
          mciGetErrorString(mciError,buf,strlen(buf));
          AfxMessageBox(buf);
          return;
        }
      open cdaudio命令打开CD播放器,如果出错(如驱动器内没有CD)则返回错误码,此时可以用mciGetErrorString函数取得错误信息字符串。

    2、MCI设备

      open是MCI打开设备的命令,cdaudio是MCI设备名。MCI的设备类型如下:
        animation  动画设备
        cdaudio   CD播放器
        dat     数字音频磁带机
        digitalvideo 某一窗口中的数字视频(不基于GDI)
        other    未定义的MCI设备
        overlay   重叠设备(窗口中的模拟视频)
        scanner   图象扫描仪
        sequencer  MIDI序列器
        videodisc  视盘机
        waveaudio  播放数字波形文件的音频设备

      设备名是在注册表或SYSTEM.INI的[mci]部分定义的,典型的[mci]段如下:
        [mci]
        cdaudio=mcicda.drv
        sequencer=mciseq.drv
        waveaudio=mciwave.drv
        avivideo=mciavi.drv
        videodisc=mcipionr.drv
      等号的左边是设备名,右边是对应的MCI驱动程序。当安装了新的MCI驱动程序时,系统要用不同的设备名来区分。

    3、MCI命令

      使用MCI设备一般包括打开、使用和关闭三个过程,常用的MCI命令有:
        open    打开设备
        close    关闭设备
        play    开始设备播放
        stop    停止设备的播放或记录
        record   开始记录
        save    保存设备内容
        pause    暂停设备的播放或记录
        resume   恢复暂停播放或记录的设备
        seek    改变媒体的当前位置
        capacility 查询设备能力
        info    查询设备的信息
        status   查询设备状态信息
      MCI的大部分命令可以控制不同的媒体设备,但其中record和save命令并不是所有MCI设备都可以使用。
      MCI命令的使用是很随意的,只要先打开,最后关闭,中间可以随意调用各种命令。

    (1) open 打开设备

      MCI设备使用前必须先打开,当然,使用后也必须要关闭,以免影响他人的使用。

      open device_name type device_type alias device_alias
        device_name     要使用的设备名,通常是文件名。
        type device_type  设备类型,例如waveaudio或sequencer,可省略。
        alias device_alias 设备别名,指定后可在其他命令中代替设备名。

    (2) play 开始设备播放

      MCI设备打开后即可以播放,可使用设备名或别名。

      play device_alias from pos1 to pos2 wait repeat
        若省略from则从当前磁道开始播放,若省略to则播放到结束。
        若指明wait则等到播放完毕命令才返回。
        若指明rep
  • 2005-09-07

    MCIWnd的使用 - [VC专栏]

    Windows中提供了一个MCIWnd窗口类,可以更加方便的在一个VC窗口中操作多媒体。
      使用MCIWnd,源文件中需要包含头文件 vfw.h,在Project->Settings->Link->Object/libray module中加入库 vfw32.lib。

    1、MCIWnd的创建

      MCIWnd子窗口的创建可使用MCIWndCreate函数:
        HWND MCIWndCreate(
          HWND hwndParent,   //父窗口句柄
          HINSTANCE hInstance, //应用程序的实例句柄
          DWORD dwStyle,    //显示风格
          LPSTR szFile     //多媒体文件名
        );
      返回的HWND可以保存下来,以供以后使用,也可不保存。
      该函数会在其父窗口上创建一个子窗口,类似于创建一个控间如按扭或列表框等。
      该子窗口会占据父窗口一定空间,可带有播放按钮、进度条、菜单按钮等。

    图3.3.1 MCIWnd子窗口
    示例:
     (1)建一个多文档的MFC应用程序。
     (2)在View类的头文件中加入变量:
        HWND m_mciWnd;
     (3)在View类中用ClassWizard重载OnInitialUpdate函数。
     (4)在此函数中加入代码:
         m_mciWnd=MCIWndCreate(m_hWnd, AfxGetInstanceHandle(), MCIWNDF_SHOWALL | MCIWNDF_RECORD, GetDocument()->GetPathName());
       这里,m_hWnd为此View窗口的HWND,
          AfxGetInstanceHandle()可取得本应用程序的实例句柄,
          MCIWNDF_flag们决定了子窗口中是否要加入播放按钮、录音按钮、菜单按钮、进度条等控件,
          GetDocument()->GetPathName()则可获得通过打开文件对话框取得的文件名。
     (5)编译运行。
     (6)在运行的程序中已经可以任意打开一个多媒体文件进行播放。例如 .wav、.avi、.mid文件。

    2、MCIWnd的使用

      如果在MCIWnd子窗口中有播放按钮、录音按钮、菜单按钮、进度条等控件,可以通过它们操作多媒体。
      如果象上例一样保存了MCIWndCreate函数返回的HWND,则不管子窗口中是否有控件,都可通过MCIWndxxxx函数操作多媒体。

     (7)在上面的程序中加入ID为 ID_NEW、ID_OPEN、ID_PLAY、ID_PLAYREVERSE、ID_RECORD、ID_SAVE、ID_STOP、ID_CLOSE 的菜单项或Toolbar按钮。
     (8)在 ID_NEW 的消息响应函数中加入:
         MCIWndNew(m_mciWnd,"waveaudio");
       MCIWnd子窗口可以建立一个新的音频多媒体文件。
     (9)在 ID_OPEN 的消息响应函数中加入:
         MCIWndOpen(m_mciWnd,"c://MyWav.wav",0);
       MCIWnd子窗口可以打开一个已存在的多媒体文件。
     (10)在 ID_PLAY 的消息响应函数中加入:
         MCIWndPlay(m_mciWnd);
       MCIWnd子窗口可以播放多媒体文件。
     (11)在 ID_PLAYREVERSE 的消息响应函数中加入:
         MCIWndPlayReverse(m_mciWnd);
       MCIWnd子窗口可以倒着播放视频多媒体文件。
     (12)在 ID_RECORD 的消息响应函数中加入:
         MCIWndRecord(m_mciWnd);
       MCIWnd子窗口可以录制音频多媒体文件。
     (13)在 ID_SAVE 的消息响应函数中加入:
         MCIWndSave(m_mciWnd,"c://MyWav.wav");
       MCIWnd子窗口可以保存已录制的音频多媒体文件。
     (14)在 ID_STOP 的消息响应函数中加入:
         MCIWndStop(m_mciWnd);
       MCIWnd子窗口可以停止正在播放或录制的多媒体文件。
     (15)在 ID_CLOSE 的消息响应函数中加入:
         MCIWndClose(m_mciWnd);
       MCIWnd子窗口可以关闭当前的多媒体文件,若要再使用,必须重新打开。
     (16)编译运行。
  • 作者:分享吧收集整理 发布日期:2005年3月23日 人气:2 打印

    作者:耿海增

    下载本文示例工程

    示例代码运行效果图如下:


    实现程序只运行一次的方法很多,但是原理都是一样的,就是运行第一次的时候设置一个标记,每次运行的时候检查该标记,如果有就说明已经运行了。

    具体实现:

    1、在程序初始化的时候 (InitInstance()) 枚举所有的窗口,查找本程序的实例是否存在
    2、在主窗口初始化的时候在本窗口的属性列表中添加一个标记,以便程序查找.

    部分关键代码

    1、在App的InitInstance()中枚举所有窗口,查找本程序实例

    HWND oldHWnd = NULL;
    EnumWindows(EnumWndProc,(LPARAM)&oldHWnd); //枚举所有运行的窗口
    if(oldHWnd != NULL)
    {
    AfxMessageBox("本程序已经在运行了");
    ::ShowWindow(oldHWnd,SW_SHOWNORMAL); //激活找到的前一个程序
    ::SetForegroundWindow(oldHWnd); //把它设为前景窗口
    return false; //退出本次运行
    }
    2、添加EnumWndProc窗口过程函数:
    //添加的标识只运行一次的属性名
    CString g_szPropName = "Your Prop Name"; //自己定义一个属性名
    HANDLE g_hValue = (HANDLE)1; //自己定义一个属性值

    BOOL CALLBACK EnumWndProc(HWND hwnd,LPARAM lParam)
    {
    HANDLE h = GetProp(hwnd,g_szPropName);
    if( h == g_hValue)
    {
    *(HWND*)lParam = hwnd;
    return false;
    }
    return true;
    }
    3、在主窗口的 OnInitDialog()中添加属性
    //设置窗口属性
    SetProp(m_hWnd,g_szPropName,g_hValue);

    (全文完)


  • 2005-09-07

    ListBox - [VC专栏]

    ListBox窗口用来列出一系列的文本,每条文本占一行。创建一个列表窗口可以使用成员函数:
    BOOL CListBox::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );
    其中dwStyle将指明该窗口的风格,除了子窗口常用的风格WS_CHILD,WS_VISIBLE外,你可以针对列表 控件 指明专门的风格。

    LBS_MULTIPLESEL 指明列表框可以同时选择多行
    LBS_EXTENDEDSEL 可以通过按下Shift/Ctrl键选择多行
    LBS_SORT 所有的行按照字母顺序进行排序

    在列表框生成后需要向其中加入或是删除行,可以利用:
    int AddString( LPCTSTR lpszItem )添加行,
    int DeleteString( UINT nIndex )删除指定行,
    int InsertString( int nIndex, LPCTSTR lpszItem )将行插入到指定位置。
    void ResetContent( )可以删除列表框中所有行。
    通过调用int GetCount( )得到当前列表框中行的数量。

    如果需要得到/设置当前被选中的行,可以调用int GetCurSel( )/int SetCurSel(int iIndex)。如果你指明了选择多行的风格,你就需要先调用int GetSelCount( )得到被选中的行的数量,然后int GetSelItems( int nMaxItems, LPINT rgIndex )得到所有选中的行,参数rgIndex为存放被选中行的数组。通过调用int GetLBText( int nIndex, LPTSTR lpszText )得到列表框内指定行的字符串。

    此外通过调用int FindString( int nStartAfter, LPCTSTR lpszItem )可以在当前所有行中查找指定的字符传的位置,nStartAfter指明从那一行开始进行查找。
    int SelectString( int nStartAfter, LPCTSTR lpszItem )可以选中包含指定字符串的行。

    在MFC 4.2版本中添加了CCheckListBox类,该类是由CListBox派生并拥有CListBox的所有功能,不同的是可以在每行前加上一个检查框。必须注意的是在创建时必须指明LBS_OWNERDRAWFIXED或LBS_OWNERDRAWVARIABLE风格。

    通过void SetCheckStyle( UINT nStyle )/UINT GetCheckStyle( )可以设置/得到检查框的风格,关于检查框风格可以参考4.1 Button中介绍。通过void SetCheck( int nIndex, int nCheck )/int GetCheck( int nIndex )可以设置和得到某行的检查状态,关于检查框状态可以参考4.1 Button中介绍。

    最后介绍一下列表框几种常用的消息映射宏:

    ON_LBN_DBLCLK 鼠标双击
    ON_EN_ERRSPACE 输入框无法分配 内存 时产生
    ON_EN_KILLFOCUS / ON_EN_SETFOCUS 在输入框失去/得到输入焦点时产生
    ON_LBN_SELCHANGE 选择的行发生改变
    使用以上几种消息映射的方法为定义原型如:afx_msg void memberFxn( );的函数,并且定义形式如ON_Notification( id, memberFxn )的消息映射。如果在对话框中使用列表框,Class Wizard会自动列出相关的消息,并能自动产生消息映射代码。
  • 系统提供了四个标准的对话框:AudioFormat, VideoFormat, VideoSource, Video Compression,但有时程序希望通过函数控制它们,而不是使用系统提供的那个单一的对话框,此时就应该使用函数调用的方法:

    AudioFormat对话框
    可以通过使用capSetAudioFormat来实现,此时要使用WAVEFORMATEX结构。
    例如:改成PCM格式,立体声,16声道,12.05kHz,则:
    WAVEFORMATEX audioFormat;
    // 确定宽度
    acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT,&dwSize);
    dwSize = max (dwSize, capGetAudioFormatSize (m_hwCapCapturing));
    // 设置参数
    audioFormat.wFormatTag = WAVE_FORMAT_PCM;
    audioFormat.nChannels = 2;
    audioFormat.nSamplesPerSec = 120500;
    audioFormat.wBitsPerSample =16;
    audioFormat.nBlockAlign = nBitsPerSample * nChannels / 8;
    audioFormat.nAvgBytesPerSec =
    audioFormat.nBlockAlign * nSamplesPerSec;
    // 更新
    capSetAudioFormat(ghCapWnd,&audioFormat,dwSize);
    VideoFormat对话框
    可以通过使用capSetVideoFormat来实现,此时要使用BITMAPINFOHEADER结构。
    例如:设置图片大小为RGB24位岁,大小为230X160
    BITMAPINFOHEADER bi;
    DWORD dwSize,dw;
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = 320; // 起作用
    bi.biHeight = 160; // 起作用
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 176;
    bi.biYPelsPerMeter = 144;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;
    dwSize = bi.biSize + ((bi.biBitCount > 8 || bi.biClrUsed) ? (bi.biClrUsed * sizeof(PALETTEENTRY)) : (2 ^ bi.biBitCount * sizeof(PALETTEENTRY)));
    dw = capSetVideoFormat(m_hwCapCapturing, &bi, dwSize);
    VideoSource对话框
    没有找到现成的方法,但视频捕获卡提供的CD里面有一个动态链接库可以实现。
    Video Compression对话框
    可以通过使用ICOpen,ICInfo等函数联合起来,得到当前系统里面的视频压缩驱动的列表,并可选择其一,MSDN里面有一个程序示范了此用户,程序名叫:ICWalk。

    注:此文绝大部分是我译自MSDN的资料。
  • 2005-09-05

    使用视频捕获 - [VC专栏]

    1.创建捕获窗口(Creating a Capture Window)
    hWndC = capCreateCaptureWindow (
    (LPSTR) "My Capture Window", // window name if pop-up
    WS_CHILD | WS_VISIBLE, // window style
    0, 0, 160, 120, // window position and dimensions
    (HWND) hwndParent,
    (int) nID /* child ID */);

    2.连接到捕获驱动(Connecting to a Capture Driver)
    下面的例子是将MSVIDEO驱动连接到句柄为hWndC的捕获窗口, 然后调用capDriverDisconnect宏来断接.
    fOK = SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L);
    //
    // Or, use the macro to connect to the MSVIDEO driver:
    // fOK = capDriverConnect(hWndC, 0);
    //
    // Place code to set up and capture video here.
    //
    capDriverDisconnect (hWndC);

    3.列举所有已安装的捕获驱动(Enumerating Installed Capture Drivers)
    下面的例子使用capGetDriverDescription函数得到已安装的捕获驱动的名称及版本:
    char szDeviceName[80];
    char szDeviceVersion[80];

    for (wIndex = 0; wIndex < 10; wIndex++)
    {
    if (capGetDriverDescription (wIndex, szDeviceName,
    sizeof (szDeviceName), szDeviceVersion,
    sizeof (szDeviceVersion))
    {
    // Append name to list of installed capture drivers
    // and then let the user select a driver to use.
    }
    }

    4.得到捕获驱动的性能(Obtaining the Capabilities of a Capture Driver)
    发送WM_CAP_DRIVER_GET_CAPS消息可以得到捕获驱动的性能,并保存入一个CAPDRIVERCAPS结构.每当程序连接一个新的捕获驱动到一个捕获窗口时, 就应该更新CAPDRIVERCAPS结构. 下面的程序举例说明了如何使用capDriverGetCaps宏来得到捕获驱动的性能:

    CAPDRIVERCAPS CapDrvCaps;
    SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS,
    sizeof (CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps);
    // Or, use the macro to retrieve the driver capabilities.
    // capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));

    5.得到捕获窗口的状态(Obtaining the Status of a Capture Window)
    下面的例子使用SetWindowPos函数使捕获窗口与进来的视频流尺寸保持一致, 视频流的基本信息是使用capGetStatus宏得到的, 保存在CAPSTATUS结构中.

    CAPSTATUS CapStatus;
    capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
    SetWindowPos(hWndC, NULL, 0, 0, CapStatus.uiImageWidth,
    CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);

    6.显示对话框设置视频特征(Displaying Dialog Boxes to Set Video Characteristics)
    每个视频捕获卡一般能提供三个不同的对话框用于控制视频捕获及数字化处理. 下面的例子说明如何显示这些对话框, 在显示这些对话框之前,使用了capDriverGetCaps宏来检查CAPDRIVERCAPS结构, 以检测该卡是否有显示这些对话框:

    CAPDRIVERCAPS CapDrvCaps;
    capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));

    // Video source dialog box.
    if (CapDriverCaps.fHasDlgVideoSource)
    capDlgVideoSource(hWndC);

    // Video format dialog box.
    if (CapDriverCaps.fHasDlgVideoFormat)
    {
    capDlgVideoFormat(hWndC);
    // Are there new image dimensions?
    capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
    // If so, notify the parent of a size change.
    }

    // Video display dialog box.
    if (CapDriverCaps.fHasDlgVideoDisplay)
    capDlgVideoDisplay(hWndC);

    7.得到和设置视频格式(Obtaining and Setting the Video Format)
    BITMAPINFO结构的长度既适应于标准的也适应于压缩的数据格式, 所有程序必须总是询问此结构的尺寸以便在得到当前的视频格式之前分配内存. 下面的例子就是使用capGetVideoFormatSize宏来得到缓冲区尺寸并调用capGetVideoFormat宏来得到当前的视频格式.

    LPBITMAPINFO lpbi;
    DWORD dwSize;
    dwSize = capGetVideoFormatSize(hWndC);
    lpbi = GlobalAllocPtr (GHND, dwSize);
    capGetVideoFormat(hWndC, lpbi, dwSize);

    // Access the video format and then free the allocated memory.

    程序可以使用capSetVideoFormat宏(或WM_CAP_SET_VIDEOFORMAT消息)发送一个BITMAPINFO头结构给捕获窗口, 因为视频格式是设备细节, 你的程序应该检查返回值以便确定此格式是否已被接受.

    8. 预览视频(Previewing Video)
    下面的例子使用capPreviewRate宏来设置每66毫秒显示一帧, 并使用capPreview宏将它放置在捕获窗口里.

    capPreviewRate(hWndC, 66); // rate, in milliseconds
    capPreview(hWndC, TRUE); // starts preview
    // Preview
    capPreview(hWnd, FALSE); // disables preview

    9.将视频设置为overlay模式(Enabling Video Overlay)
    下面的例子: capDriverGetCaps宏确定此捕获卡是否有overlay功能, 如果有就使用宏来设置它

    CAPDRIVERCAPS CapDrvCaps;
    capDriverGetCaps(hW
  • 1.视频捕获驱动的性能:
    你可以通过发送WM_CAP_DRIVER_GET_CAPS消息(或者capDriverGetCaps宏)来得到当前连接的视频驱动的硬件性能. 得到的信息保存在CAPDRIVERCAPS结构中.

    2.视频对话框:
    每一个视频驱动能够提供四个对话框来控制视频捕获和数字化处理, 定义压缩品质等, 这些对话框都定义在视频捕获驱动中.
    Video Source对话框用于控制选择视频来源, 此对话框列举了此视频捕获卡连接的所有视频源(典型的例如:SVHS和合成输入), 并提供了改变色调, 对比度, 饱和度. 如果视频驱动支持此对话框, 你就可以显示并更新它, 使用WM_CAP_DLG_VIDEOSOURCE消息(或capDlgVideoSource宏).
    Video Format对话框定义视频帧的尺寸以及精度, 视频捕获卡的压缩设置. 如果卡支持的话, 可以发送消息WM_CAP_DLG_VIDEOFORMAT消息或(capDlgVideoFormat宏).
    Video Display对话框控制在视频捕获期间在显示器上的显示, 此控制不会影响视频数字数据, 但是他们可能会影响数字信号的表现形式, 例如: 如果捕获设备支持overlay, 可能允许改变色调和饱和度, 关键色彩 或者overlay队列. 如果卡支持, 你可以发送WM_CAP_DLG_VIDEODISPLAY消息(或者使用capDlgVideoDisplay宏).
    Video Compression对话框控制压缩品质, 如果卡支持, 发送消息WM_CAP_DLG_VIDEOCOMPRESSION(或capDlgVideoCompression宏).

    3.Preview 和 Overlay模式:
    一个视频捕获驱动对进入的视频流有两种工作模式: Preview模式和overlay模式, 如果一个捕获驱动能够执行两种方法, 用户可以在其中选择一种.
    Preview模式把从捕获硬件传来的数据送入系统内存并使用图形设备介面(GDI)将数字化帧显示在捕获窗口内. 应用程序可以在父窗口失去焦点时减缓显示速度, 当重新又得到焦点后加快显示速度, 此种模式要占用大量CPU时间.
    有三种消息控制Preview操作:
    WM_CAP_SET_PREIVEW消息(capPreview宏)允许或禁止preview模式
    WM_CAP_SET_PREVIEWRATE(capPreviewRate宏)当帧在preview模式显示时设置速度.
    WM_CAP_SET_SCALE(capPreviewScale宏)允许或禁止preview视频的缩放比例.
    当preview和scaling同时使用, 捕获的视频帧将会根据捕获窗口的尺寸自动缩放, 允许preview模式会自动关闭overlay模式.
    overlay模式是一个硬件函数它将数据送入捕获缓冲区中因而不占用CPU资源. 你可以发送消息WM_CAP_SET_OVERLAY(或capOverlay宏)给捕获窗口来启用或终止overlay模式, 允许overlay模式会自动禁止preview模式.
    你同时也可以在preview模式或overlay模式里发送WM_CAP_SET_SCROLL消息(或capSetScrollPos宏)来设置视频帧的客户区卷轴位置.

    4.视频格式
    你可以通过发送WM_CAP_GET_VIDEOFORMAT消息(或capGetVideoFormat和capGetVideoFormatSize宏)来得到视频格式的结构或结构的尺寸. 你可以通过发送CAP_SET_VIDEOFORMAT消息(或capSetVideoFormat宏)来设置视频格式.

    5.视频捕获设置
    CAPTUREPARMS结构包含了对视频捕获流的控制参数, 你可以完成以下这些任务:
    指定帧数
    指定分配多少视频缓冲
    允许或禁止声频捕获
    指定捕获的时间间隔
    指定在捕获的过程中是否使用MCI设置(VCR或者videodisc)
    指定终止流的键盘或鼠标
    specify the type of video averaging applied during capture.

    得到:WM_CAP_GET_SEQUENCE_SETUP消息(或capCaptureGetSetup宏)
    设置:WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)

    6.声频格式
    你可以通过发送WM_CAP_GET_AUDIOFORMAT消息(或capGetAudioFormat宏和capGetAudioFormatSize宏)来得到当前捕获音频数据的格式或尺寸格式。缺省的声频格式是:单声道、8位、11kHz PCM。 当你使用WM_CAP_GET_AUDIOFORMAT时,总是使用WAVEFORMATEX结构。
    设置发送消息WM_CAP_SET_AUDIOFORMAT消息(或capSetAudioFormat宏),可以传送WAVEFORMAT,WAVEFORMATEX,PCMWAVEFORMAT结构指针。
  • 2005-09-05

    关于捕获窗口 - [VC专栏]

    在捕获之前必须创建一个捕获窗口(capture window),在发送消息或使用宏的过程中都需要使用此窗口。
    1.创建一个AVICap捕获窗口
    你可以使用capCreateCaptureWindow函数来创建一个AVICap捕获窗口, 此函数将会返回一个句柄, 此句柄以后在发送消息时要用.
    你可以在一个程序里创建一个或多个捕获窗口, 然后给每一个窗口连接不同的捕获设置.

    2.将一个捕获窗口连接至捕获设备
    你可以动态的在一个捕获窗口与一个捕获设备之前连接或断接, 你可以发送WM_CAP_DRIVER_CONNECT消息来使一个捕获窗口与一个捕获设备连接或关联. 当连接上以后, 你就可以通过捕获窗口向捕获设备发送各种消息.
    如果你的系统里装有多个捕获设备, 你可以在发送WM_CAP_DRIVER_CONNECT消息时用wParam参数指定使用哪一个, 此参数是登记在SYSTEM.INI文件的[drivers]一节里的列表中的某一项, 0为第一个.
    你可以使用capGetDriverDescription函数来得到已安装的捕获设备的名称及版本, 这样你的程序就可以列举所有已安装的捕获设备和驱动, 这样用户就可以选择其中的一个来与你的捕获窗口连接.
    你可以发送WM_CAP_DRIVER_GET_NAME消息(或capDriverGetName宏)来得到连接到捕获窗口的捕获设备的名称, 得到版本发送WM_CAP_DRIVER_GET_VERSION消息(或capDriverGetVersion宏)
    你可以发送WM_CAP_DRIVER_DISCONNECT消息(或capDriverDisconnect宏)来断接.

    3. 父窗口与子窗口的交互
    一些象WM_PALETTECHANGED和WM_QUERYNEWPALETTE的系统级消息只能发送到顶级窗口或OVERLAPPED窗口, 如果一个捕获窗口是子窗口,就必须通过父窗口转送.
    同样的, 如果父窗口的尺寸改变了, 它就需要通知捕获窗口, 相反地, 如果捕获窗口的尺寸改变了, 捕获窗口就需要发送消息给父窗口, 一个简单的方法就是始终保持捕获窗口的尺寸与视频流的尺寸一致, 并随时将尺寸的改变通知父窗口.

    4.捕获窗口的状态
    你可以发送WM_CAP_GET_STATUS消息(或capGetStatus宏)来得到当前捕获窗口的状态, 得到的是一个CAPSTATUS结构的拷贝, 它包含图片的尺寸, 卷轴的当前位置, overlay和preview是否已设置.
    因为CAPSTATUS信息是动态的, 你的程序应该只要捕获的视频流的尺寸或格式可能发生了改变就应该进行刷新(例如: 显示了捕获设备的视频格式以后).
    改变捕获窗口的尺寸并不影响实际的捕获的视频流的尺寸, 视频捕获设备的格式对话框捕获频流的尺寸.
  • 基本的捕获设置包括:设置捕获速度(每秒捕获多少帧),是否同时捕获声频,捕获缓冲,允许最大丢失多少帧,是否使用DOS内存,以及用键盘的哪个键或鼠标的哪个键来终止捕获等等。这些基本的设置都可以使用CAPTUREPARAMS结构来描述,你可以使用capCaptureGetSetup宏来得到当前的设置,然后改变此结构的成员变量,再使用capCaptureSetSetup宏设置新的设置。
    例如:
    1.设置捕获速度:
    捕捉速度是指捕捉任务每秒钟捕获的帧数, 你可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)来得到当前的捕捉速度, 当前的捕捉速度保存在CAPTUREPARAMS结构的dwRequestMicroSecPerFrame成员变量中, 你可以通过设置此变量来改变当前设置, 单位是每毫秒连续的帧数, 你可以发送WM_CAP_SET_SEQUENCE_SETUP消息(或者使用capCaptureSetSetup宏), dwRequestMicroSecPerFrame的值是66667, 相当于每秒15帧.

    2.设置终止捕获
    你可以允许用户按下某键或某组合键或者鼠标的左右键来终止一个捕获任务, 如果是实时的捕获, 则捕获的文件将会被丢弃; 如果是单步捕获, 在终止之前所捕获的内容将会被保存.
    你可以通过发送WM_CAP_GETQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)来得到当前的设置, 当前的按键设置保存在CAPTUREPARAMS的vKeyAbort成员中, 当前的鼠标设置保存在fAbortLeftMouse和fAbortRightMouse成员中. 你可以设置新的按键或按键组合, 或者鼠标左右键, 当你修改的CAPTUREPARAMS后,应该发送WM_CAP_SET_SEQUENCE_SETUP消息来进行更新(或者使用capCaptureSetSetup宏). 缺省的按键是VK_ESCAPE. 你必须在指定按键之前使用RegisterHotKey函数, 鼠标缺省的值是fAbortLeftMouse和fAbortRightMouse都为TRUE.

    3.捕获的时间限制
    CAPTUREPARAMS结构中的fLimitEnabled指示是否有时间限度, wTimeLimit指示最大的持续时间, 单位为秒.
    得到fLimitEnabled和wTimeLimit的值可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或使用capCatureGetSetup宏), 当设置了这些成员变量后, 应该发送消息WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)来更新CAPTUREPARAMS结构.


  • 视频捕捉将一个视频流和音频流数字化, 然后存储在硬盘或其他存储介质上.
    一个AVICap视窗口句柄描述了声频与视频流的细节, 这样就使你的应用程序从AVI文件格式, 声频视频缓冲管理, 低层声频视频驱动访问等等解脱出来, AVICap为应用程序提供了一个灵活的介面, 你可以仅仅使用如下几行代码就可以将视频捕捉加入你的程序:
    hWndC = capCreateCaptureWindow ( "My Own Capture Window",
    WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);
    SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L);
    SendMessage (hWndC, WM_CAP_SEQUENCE, 0, 0L);
    一个宏其实也是使用SendMessage, 只不过提供给程序一个更易读的代码而已, 下面的这些示例就是使用宏的方法将视频捕捉加入程序:
    hWndC = capCreateCaptureWindow ( "My Own Capture Window",
    WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);
    capDriverConnect (hWndC, 0);
    capCaptureSequence (hWndC);

    当你创建了一个AVICap类的捕捉窗口并将它连接到一个视频驱动时, 此捕捉窗口即可以开始捕捉数据, 你的程序可以简单的发送WM_CAP_SEQUENCE消息(或者使用capCaptureSequence宏)来开始捕捉.
    如果是缺省的设置, WM_CAP_SEQUENCE会开始捕捉视频音频流到CAPTURE.AVI文件中, 直到下面的某一事件发生为止:
    用户按下了ESC键或者一个鼠标键
    你的应用程序终止或异常中断捕捉操作
    磁盘已满

    在一个应用程序里, 你可以发送WM_CAP_STOP消息来终止捕捉数据(或者使用capCaptureStop宏), 你也可以发送WM_CAP_ABORT消息(或者使用capCaptureAbort宏)来终止.
  • 2005-09-05

    客户端的操作 - [VC专栏]

    1.确定版本,创建一个socket, 终止一个socket与服务器端同
    2.设置服务器地址及端口号
    SOCKADDR_IN dest_sin;
    phe = gethostbyname(szServerName);
    if (phe == NULL)
    return FALSE;
    memcpy((char FAR *)&(dest_sin->sin_addr), phe->h_addr,phe->h_length);
    dest_sin->sin_port = "801";
    3.设置端口属性
    int tszOpt = 1;
    setsockopt(ClientSock,IPPROTO_TCP,SO_REUSEADDR,(char *) &tszOpt,sizeof(tszOpt));
    4.与服务器连接
    if (connect( ClientSock, (PSOCKADDR) &dest_sin, sizeof( dest_sin)) < 0)
    closesocket( ClientSock );
    5.向服务器发送数据
    int WriteToSock(int pihsocket, char *piBuf, int picbBuf)
    {
    register int tcbWritten, tcnt, error;
    tcbWritten = 0;
    while (tcbWritten < picbBuf)
    {
    if ((tcnt = send(pihsocket,&piBuf[tcbWritten],picbBuf-tcbWritten,0)) <= 0)
    {
    error = WSAGetLastError();
    break;
    }
    tcbWritten += tcnt;
    }
    return(tcbWritten);
    }
  • 1.确定Socket版本
    WORD tSocketVerNo;
    WSADATA tVerData;
    tSocketVerNo = MAKEWORD(2, 0);
    WSAStartup(tSocketVerNo, &tVerData);
    2. 创建一个Socket
    SOCKET ServerSock;
    ServerSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ServerSock == INVALID_SOCKET) {
    closesocket(ServerSock);
    return -1;
    }
    3.指定服务器的端口号
    SOCKADDR_IN local_sin;
    local_sin.s_addr = INADDR_ANY;
    local_sin.sin_port = "801";
    4.设置socket属性
    int tszOpt = 1;
    setsockopt(ServerSock,IPPROTO_TCP,SO_REUSEADDR,(char*)&tszOpt,sizeof(tszOpt));
    5.将服务器的端口号绑定到socket上
    if (bind( ServerSock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR)
    return -1;
    6.侦听
    if (listen( ServerSock, 4 ) < 0)
    return -1;
    7.建立接收线程
    DWORD threadID;
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)AcceptThreadProc,(LPVOID)ServerSock,0,&threadID);
    8.接收线程体
    void AcceptThreadProc(LPVOID pParam)
    {
    SOCKADDR_IN acc_sin;
    int acc_sin_len;
    char szMsg[ MY_MSG_LENGTH ];
    SOCKET piSocket;

    piSocket = (SOCKET)pParam;
    acc_sin_len = sizeof(acc_sin);
    // 会一直停在这里等待,接通之后返回。
    piSocket = accept( piSocket,(struct sockaddr FAR *) &acc_sin,(int FAR *) &acc_sin_len );
    if (piSocket < 0)
    ExitThread(0);
    // 从socket中读数据
    len = recv( piSocket, szMsg, MY_MSG_LENGTH, 0 );
    }
    9.终止一个socket
    int EndSocket(int pihsocket)
    {
    int tAPIMode;
    send(pihsocket,(char *) &tAPIMode,0,0);
    shutdown(pihsocket,2);
    if (closesocket(pihsocket) == -1)
    return(-1);
    return 0;
    }
  • 2005-09-05

    消息的处理 - [VC专栏]

    TAPI由回调函数进行消息处理,回调函数在初始化TAPI时创建,消息的处理在TAPI的使用过程中是至关重要的。
    以下是一些主要的消息:

    消息 说明
    LINECALLSTATE_IDLE 没有呼叫,为空,此时应断接,释放掉占用的资源
    LINECALLSTATE_BUSY 线路忙或设备忙,此时应断接,释放掉占用的资源
    LINECALLSTATE_SPECIALINFO 特别的消息,此时应断接,释放掉占用的资源
    LINECALLSTATE_OFFERING 应答方已收到呼叫方信号,此时应进行应答,调用lineAnswer函数
    LINECALLSTATE_CONNECTED 已连接成功,此时可进行数据的传输,但必须先得到MODEM的句柄
    LINECALLSTATE_DISCONNECTED 已断接,此时应释放掉占用的资源




  • ReadFile与WriteFile并非TAPI的函数集成员函数,TAPI并不提供数据传输的函数。

    (一)函数调用格式:
    ReadFile的调用格式为:
    ReadFile(文件句柄,发送的数据的缓冲区地址,欲发送多少字节数据,实际发送了多少字节数据,一个指向OVERLAPPED结构的指针);
    1. 文件句柄:此处即为MODEM句柄;
    2. 发送数据的缓冲区地址:可为char *或LPDWORD格式,传送的数据为二进制格式;
    3. 欲发送多少字节数据:用户指定,但也可以使用ClearCommError函数来测到当前MODEM缓冲区有多少字节数据,然后有多少读多少。
    4. 实际发送了多少字节数据:返回值;
    5. OVERLAPPED结构的指针:

    (二)传输模式
    在95/98下:ReadFile与WriteFile有两种模式,一种是等待模式,一种是非等待模式。
    等待模式的格式为:
    DWORD len;
    ReadFile(g_hCommFile,lpBuffer,256,&len,NULL);
    此时必须完成了读写操作函数才会返回,实际读写的字节数在len中,最后一个参数必须为NULL。
    非等待模式的格式为:
    OVERLAPPED myOVLP = {0, 0, 0, 0, NULL};
    myOVLP.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    ReadFile(g_hCommFile,lpBuffer,256,NULL,&myOVLP);
    GetOverlappedResult(g_hCommFile,&myOVLP,&len,TRUE)
    CloseHandle(myOVLP.hEvent);
    此时ReadFile函数会马上返回,第四个参数会被忽略,实际读写的字节数由GetOverlappedResult函数返回,如果读写成功,此函数为返回非0值,最后一个函数为TRUE,表示等待方式,为FALSE表示非等待。

    在NT下必须使用OVERLAPPED参数,调用格式与95/98下的非等待方式类似。

    (三)如何保证数据传输的正确性
    在等待模式下,如果读写操作不完成,函数就会一直处于挂起状态;
    在非等待模式下,函数调用后会马上返回,此时应调用GetOverlappedResult函数来判断,如果此函数返回FALSE,就应进一步判断,例如:
    WriteFile(g_hCommFile,lpBuffer,256,NULL,&myOVLP);
    while(!GetOverlappedResult(g_hCommFile,&myOVLP,&len,TRUE))
    {
    dwError=GetLastError();
    if(dwError == ERROR_IO_INCOMPLETE)
    continue;
    else
    出错;
    }

    (四)如何提高传输性能
    因为MODEM的缓冲区毕竟非常有限,如果某一方出现阻塞,另一方就不得不陷入等待之中,而且双方之间的协调会使速率非常之低,此时可以采用缓冲区的办法,发送与接收双方都开辟一个缓冲区(应是循环的),数据直接写入缓冲区中,然后用线程的方法,发送方不断的判断缓冲区中是否有数据,如果有发送方就发送到MODEM中,接收方则不断判断MODEM中是否有数据,如果有就读入缓冲区中。

    就MODEM传输速度,我使用的环境是内线,没有通过交换机,满载的情况下每秒在6K字节/秒左右。
  • 2005-09-05

    通讯过程 - [VC专栏]

    TAPI的基本通讯过程包括如下几步:

    步骤 说明

    初始化TAPI环境 使用lineInitialize函数初始化TAPI32.DLL,获得TAPI句柄。此时必须指定一个回调函数,由此回调函数处理消息。
    应答方初始化 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本;
    2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_MONITOR+LINECALLPRIVILEGE_OWNER,与呼叫方不同;
    呼叫方进行呼叫 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本;
    2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_NONE;
    3. 创建拨号参数,类型为:LPLINECALLPARAMS;
    4. 用lineMakeCall或lineDial进行拨号,两者的区别是,使用前者是还没有HCALL句柄的情况下,使用此拨号可得到HCALL句柄,而lineDial是在HCALL句柄已得到的情况下使用。
    回调函数的处理 回调函数对消息进行处理,最主要的消息包括:
    1. LINECALLSTATE_OFFERING:此函数由应答方捕获,得到此消息表明已接收到拨号方的呼叫,应进行回应,回应的格式为:lineAnswer( (HCALL)dwDevice,NULL,0 );
    2. LINECALLSTATE_BUSY, LINECALLSTATE_IDLE, LINECALLSTATE_SPECIALINFO:在拨号或应答过程中出现错误,应断接;
    3. LINECALLSTATE_DISCONNECTED:某一方断接,此时可以判断到底是哪一种原因造成断接,同时也应调用断接函数;
    4. LINECALLSTATE_CONNECTED:已连接上并建立线路,此时应调用lineGetID函数得到MODEM的句柄,必要时应清空MODEM缓冲区,以便开始进行数据的传输。
    断接 1. 任何一方可以调用linDrop(HCALL)来停止呼叫,该函数将会发送LINECALLSTATE_IDLE消息给回调函数
    2. 当任何一方收到LINECALLSTATE_IDLE消息时应调用lineDeallocateCall(hCall)来释放掉占用的呼叫资源;
    3. 当收到LINECALLSTATE_DISCONNECTED 消息时应使用lineClose(HLINE)释放由lineOpen 分配的资源,调用lineShutDown(HLINEAPP)释放为线路设备分配的资源
    数据的发送与接收 使用ReadFile与WriteFile函数发送与接收数据,共中的文件句柄好是MODEM句柄。





  • CRITICAL_SECTION csRecvRead = {0};
    InitializeCriticalSection(&csRecvRead);// 临界区初始化
    EnterCriticalSection(&csRecvRead);// 使用临界区变量
    pRightBuffer = pRightBuffer + len;
    LeaveCriticalSection(&csRecvRead);
  • 线程体的一般形式:
    DWORD WINAPI ThreadProc(LPVOID pParam)
    {
    return 0;
    }
    创建时:
    HANDLE hReceiveThread = NULL;
    UINT ThreadID;
    hReceiveThread =
    CreateThread(NULL,0,ThreadProc,hWnd,0, &ThreadID);
    if ( hReceiveThread == NULL )
    return FALSE;
    // 优先级为普通
    SetThreadPriority(hReceiveThread,THREAD_PRIORITY_NORMAL);
  • HANDLE g_hCloseEvent = NULL;
    g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_hCloseEvent == NULL)
    return FALSE;
    设置信号: SetEvent(g_hCloseEvent);
  • 一般使用用户接口线程是想要得到多个顶级窗口,例如你想允许用户运行程序的多个实例,但是你想让所有的实例都共享内存,
    IE就是这样的. 你可以从CwinThread来派生一个类,使用AfxBeginThread的重载版本来启动该线程,派生的这个类具有它自己的
    InitInstance函数,并且具有自己的消息循环.
  • 2005-09-05

    临界段 - [VC专栏]

    使用CcriticalSection类可以将临界段句柄包装起来, 例如:
    CcriticalSection g_cs;
    G_cs.Lock();
    G_nCount++;
    G_cs.Unlock();
  • Cevent类是一个事件类,刚定义时处于"非信号"状态, 可以调用SetEvent()成员函数置它为"信号"状态.
    下面的代码: 线程首先等待开始信号, 如果没有置开始信号会一直挂起等待, 同时在运行的过程中等待结束信号,
    如果结束信号发生就终止线程.
    Cevent g_eventStart,g_eventKill;
    UINT ThreadProc(LPVOID pParam)
    {
    //INFINITE表示等待无限时间
    ::WaitForSingleObject(g_eventtart,INFINITE);
    for (………)
    {
    ……….
    If(::WaitForSingleObject(g_eventKill,0)==WAIT_OBJECT_0))
    Break;
    }
    return 0;
    }
    当启动线程时: g_eventStart.SetEvent();
    当终止线程时: g_eventKill.SetEvent();
    但在终止线程时如果还没有启动线程,则应该先启动线程再终止它.
    注意: 线程如果不正常终止会引起内存泄漏, 例如用关闭进程的方法来强制终止线程,或使用Win32
    TerminateThread函数.
  • 下面的代码: 当线程完成后发送给父进程消息
    UINT ThreadProc(LPVOID pParam)
    {
    ………
    ::PostMessage((HWND)pParam,WM_THREADFINISHED,0,0);
    return 0;
    }
  • 全局变量通讯是最简单而有效的办法.
    例如下面的代码:
    UINT ThreadProc(LPVOID pParam)
    {
    g_nCount = 0;
    while(g_nCount<100)
    ::InterlockedIncrement((long*)&g_nCount);
    return 0;
    }
    InterLockIncrement函数在变量加1期间阻塞其他线程访问该变量.
    如果不使用此函数而直接使用:g_nCount++的话, 可能会出现错误.
  • 线程体一般是如何形式:
    UINT ThreadProc(LPVOID pParam)
    {
    return 0;
    }
    启动线程使用:
    CwinThread* pThread =
    AfxBeginThread(ThreadProc,GetSafeHwnd(),THREAD_PRIORITY_NORMAL);
  • 2005-09-05

    计时器 - [VC专栏]

    计时器是不依赖CPU的时钟速度的. 注意的是因为Windows并不是实时的操作系统,所以,如果你指定的周期小于100毫秒的话,
    计时器事件之间的周期可能不精确.有了计时器,有时可以替代多线程情况, 例如下面的代码就允许在循环内仍然接收处理消息. 这是一个进度条,
    在OnTimer里面改动进度条的显示, 同时可以自定义CANCEL消息, 在OnCancel中将程序终止.
    Void CDlg::OnStart()
    {
    MSG message;
    SetTimer(0,100,NULL);
    GetDlgItem(IDC_START)->EnableWindow(FALSE); // 使按钮无效
    Volatile int nTemp; //使变更不保存在寄存器中, 因为变量如果保存在寄存器中, 在线程的切换过程中可能会出现值的错误.
    For (m_nCount=0;m_nCount<nMaxCount;m_nCount++){
    For (nTemp=0;nTemp<10000;nTemp++){
    ………
    }
    if (::PeekMessage(&message,NULL,0,0,PM_REMOVE)){
    ::TranslateMessage(&message);anslateMessage(&message);
    ::DispatchMessage(&message);
    }
    }
    CDlg::OnOK(); // 线程结束后关闭对话框
    }
  • 2005-09-05

    放弃控制 - [VC专栏]

    如果在等待方式下,DispatchMessage必须等待处理完成后才能返回,在此之前将不能处理任何消息,
    而下面的代码可以做到即使没有消息到达程序的情况下也立即返回
    MSG message;
    While(::PeekMessage(&message,NULL,0,0,PM_REMOVE)){
    ::TranslateMessage(&message);
    ::DispatchMessage(&message);
    }
  • Windows的消息处理机制是用如下代码进行消息处理的:
    MSG message;
    While(::GetMessage(&message,NULL,0,0)){
    ::TranslateMessage(&message);
    ::DispatchMessage(&message);
    }
    当消息到达时,由TranslateMessage进行必要的转换,例如:将WM_KEYDOWN消息转换为包含有ASCII字符的WM_CHAR消息,
    然后由DispatchMessage进行发送,当处理完成后,DispatchMessage返回.
  • 一、考试说明
    1. 考试要求:
    (1) 熟练掌握面向对象编程技术,用C/C++语言熟练编制程序;
    (2) 了解CASL汇编语言的程序编制;
    (3) 掌握软件设计的方法和技术;
    (4) 掌握数据结构、程序语言、操作系统、数据库和软件工程的基础知识;
    (5) 了解多媒体和网络的基础知识;
    (6) 掌握计算机体系结构和主要部件的基础知识;
    (7) 正确阅读和理解计算机领域的英文文献。
    2. 通过本级考试的合格人员能按照软件需求规格说明书进行软件设计和编制程序,具有相当于工程师的实际工作能力和业务水平。
    3. 本级考试范围包括: 基础知识(高级程序员级), 考试时间为150分种;软件设计和程序编制能力,考试时间为150分钟。
    二、考试范围
    (一) 基础知识(高级程序员级)
    1.1 软件基础知识
    1.1.1 数据结构基础
    •数组、记录、列表(list)、队列、栈(stack)、堆(heap)、树、二叉树和
    图的定义、存储和操作
    •序列、集合等的定义、存储和操作
    1.1.2 程序语言基础知识
    •汇编、编译、解释系统的基本原理
    •程序语言的数据类型
    •程序语言的控制结构
    •程序语言的种类、特点及适用范围
    1.1.3 操作系统基础知识
    •操作系统的类型的功能
    •操作系统的层次结构和进程概念
    •作业、处理机、存储、文件和设备等管理的原理和方法
    1.1.4 软件工程基础知识
    •软件生命周期各阶段的任务
    •结构化分析和设计方法
    •面向对象的分析与设计
    •软件开发工具与环境的基础知识
    •软件质量保证的基础知识
    1.1.5 数据库基础知识
    •数据库模型
    •关系数据库的基础知识
    •数据库系统的结构
    •SQL的使用
    •常用数据库管理系统的知识
    1.1.6 多媒体基础知识
    •多媒体基本知识
    •图形绘制基本方法
    •简单的图象处理
    •音频和视频信息的应用
    1.2 硬件基础知识
    1.2.1 计算机的体系结构和主要部件
    •机内代码及运算
    •中央处理器CPU、存储器和输入/输出设备
    •总线结构、指令流和数据流
    1.2.2 存储器系统
    •各类存储器的功能、特性和使用
    •虚拟存储器
    •高速缓冲存储器和多级存储器
    1.2.3 安全性、可靠性与系统性能评价初步
    •数据安全与保密
    •诊断与容错
    •系统可靠性评价和系统性能评价方法
    1.2.4 计算机体系结构的其它基础知识
    •流水线操作
    •精简指令系统计算机
    •多处理机系统
    •并行处理
    1.3 网络基础知识
    1.3.1 网络的功能、分类与组成
    1.3.2 网络协议与标准
    1.3.3 网络结构与通信
    1.3.4 网络的安全性
    1.3.5 Client-Server结构
    1.3.6 Internet 和Intranet初步
    1.3.7 网络管理初步
    1.4 计算机专业英语•具有大学毕业程度的英语词汇量
    •能正确阅读和理解计算机领域的英文文献
    (二) 软件设计和程序编制能力
    2.1 软件设计能力
    2.1.1 简单计算机应用系统的需求分析
    •流行的需求分析方法初步
    •数据流图的设计及改进
    2.1.2 软件界面设计
    •输入输出数据文件的设计
    •用户界面的设计
    2.1.3 概要设计
    •软件设计准则
    •模块划分与调用
    •常用的概要设计方法
    2.1.4 详细设计
    满足指定功能的各种处理过程的算法设计、评价和改进
    2.1.5 数据库/数据结构的设计
    根据不同的要求进行数据库/数据结构的设计
    2.1.6 软件测试
    •测试方法
    •测试用例的设计(白盒测试)
    •测试的静态和动态分析
    2.2 程序编制能力
    2.2.1 按照软件设计说明书,能熟练使用下列程序语言编制程序
    C(美国标准)/C++
    2.2.2 能使用CASL汇编语言(文本见附录)编制程序
    2.2.3 面向对象编程技术
    2.2.4 理解给定程序的功能,能发现程序中的错误或不足并能纠正或改进
    2.2.5 具有良好的程序编制风格
    2.2.6 算法的设计和分析

  • #include <graphics.h>
    #include <stdio.h>
    #include <dos.h>
    #include <conio.h>
    #include <time.h>
    #include <math.h>
    #include <stdlib.h>

    /*定义左上角点在屏幕上的位置*/
    #define MAPXOFT 9
    #define MAPYOFT 5

    /*定义下一个方块显示的位置*/
    #define MAPXOFT1 13
    #define MAPYOFT1 -2


    #define LEFT 0x4b00
    #define RIGHT 0x4d00
    #define DOWN 0x5000 /*此键为加速键*/
    #define UP 0x4800 /*此键为变形键*/
    #define ESC 0x011b /*此键为退出键*/
    #define ENTER 0x1c0d


    #define TIMER 0x1c /* 时钟中断的中断号 */

    /* 中断处理函数在C和C++中的表示略有不同。
    如果定义了_cplusplus则表示在C++环境下,否则是在C环境下。 */

    #ifdef __cplusplus
    #define __CPPARGS ...
    #else
    #define __CPPARGS
    #endif

    int TimerCounter=0; /* 计时变量,每秒钟增加18。 */

    /* 指向原来时钟中断处理过程入口的中断处理函数指针(句柄) */
    void interrupt ( *oldhandler)(__CPPARGS);

    /* 新的时钟中断处理函数 */
    void interrupt newhandler(__CPPARGS)
    {
    /* increase the global counter */
    TimerCounter++;

    /* call the old routine */
    oldhandler();
    }

    /* 设置新的时钟中断处理过程 */
    void SetTimer(void interrupt (*IntProc)(__CPPARGS))
    {
    oldhandler=getvect(TIMER);
    disable(); /* 设置新的时钟中断处理过程时,禁止所有中断 */
    setvect(TIMER,IntProc);
    enable(); /* 开启中断 */
    }

    /* 恢复原有的时钟中断处理过程 */
    void KillTimer()
    {
    disable();
    setvect(TIMER,oldhandler);
    enable();
    }


    struct shape
    {
    int xy[8],next;
    };

    struct shape shapes[19]=
    {
    /*x1,y1,x2,y2,x3,y3,x4,y4 指四个小方块的相对坐标,next指此方块变形后应变为哪个小方块
    { x1,y1,x2,y2,x3,y3,x4,y4,next}*/
    { 0,-2, 0,-1, 0, 0, 1, 0, 1},
    {-1, 0, 0, 0, 1,-1, 1, 0, 2},
    { 0,-2, 1,-2, 1,-1, 1, 0, 3},
    {-1,-1,-1, 0, 0,-1, 1,-1, 0},
    { 0,-2, 0,-1, 0, 0, 1,-2, 5},
    {-1,-1, 0,-1, 1,-1, 1, 0, 6},
    { 0, 0, 1,-2, 1,-1, 1, 0, 7},
    {-1,-1,-1, 0, 0, 0, 1, 0, 4},
    {-1, 0, 0,-1, 0, 0, 1, 0, 9},
    { 0,-2, 0,-1, 0, 0, 1,-1,10},
    {-1,-1, 0,-1, 1,-1, 0, 0,11},
    { 0,-1, 1,-2, 1,-1, 1, 0, 8},
    {-1, 0, 0,-1, 0, 0, 1,-1,13},
    { 0,-2, 0,-1, 1,-1, 1, 0,12},
    {-1,-1, 0,-1, 0, 0, 1, 0,15},
    { 0,-1, 0, 0, 1,-2, 1,-1,14},
    { 0,-3, 0,-2, 0,-1, 0, 0,17},
    {-1, 0, 0, 0, 1, 0, 2, 0,16},
    { 0,-1, 0, 0, 1,-1, 1, 0,18}
    };

    int board[10][20]={0};/*定义游戏板初始化为0*/
    char sp[]="0",le[]="0",sc[]="00000";
    int speed,speed0,level,score;
    int sign,flag;
    int style,style1; /*style为当前方块的种类,style1为即将输出的方块的种类*/

    void draw_block(int x,int y,int style,int way);
    void draw_little_block(int x,int y);
    void init();
    void initialize();
    void speed_change(void);
    void score_change(int);
    void kill_line(int y);
    void fill_board(int x,int y, int style);
    int change(int *i,int *j,int key);
    void renovate(void);
    void ajustment(void);
    void level_change(void);


    main()
    {
    int i,j,key,x0=5,y0=1;
    randomize();
    while(!flag) /*flag为0表示重新开始游戏*/
    {
    level=score=speed=0;
    strcpy(le,"0");
    strcpy(sp,"0");
    strcpy(sc,"00000");
    for(i=0;i<10;i++)
    for(j=0;j<20;j++)
    board[i][j]=0; /*初始化一些变量为0*/
    initialize(); /*初始化进入图形模式*/
    init(); /*初始化游戏板记分器等*/
    SetTimer(newhandler); /* 修改时钟中断 */
    ajustment(); /*开始游戏前调整速度和高度*/

    if(level>0)
    level_change(); /*根据高度随机确定方块是否存在*/

    style=random(19); /*随机确定方块种类*/


    while(1)
    {
    i=x0,j=y0;

    style1=random(19); /*随机确定即将出现的方块种类*/
    setcolor(WHITE);
    sign=1;
    draw_block(MAPXOFT1,MAPYOFT1,style1,1);
    /*画出即将出现的方块*/

    for(j=y0;j<=20;j++) /*使方块下降*/
    {
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值