• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            kenlistian

            厚積薄發(fā). 勤為槳,思為帆

               :: 首頁(yè) :: 新隨筆 ::  :: 聚合  :: 管理 ::
              73 隨筆 :: 4 文章 :: 22 評(píng)論 :: 0 Trackbacks

            2009年3月22日 #

            采用PostThreadMessage即可
            BOOL PostThreadMessage(
              DWORD idThread,     //線程ID,通過(guò)創(chuàng)建線程后的id
              UINT  Msg,           //消息id
              WPARAM wParam,    
              LPARAM lParam);
            然后在線程通過(guò)GetMessage or PeekMessage去獲取該消息.

            代碼片段如下:
            unsigned int CALLBACK thread_func(LPVOID lp)
            {
            while(1)
            {
            MSG msg;
            while (GetMessage(&msg, NULL,  0, 0)) 
            //while (PeekMessage(&msg, NULL,  0, 0,PM_REMOVE)) 
            switch(msg.message) 
            case WM_MYMESSAGE:
            printf("\n *thread_func1:%d", msg.wParam);
            break;
            }
                    } 
            }

            其中發(fā)送線程片段如下:
            UINT dwId
            _beginthreadex(NULL, 0, thread_func, NULL, 0, &dwId);
                    ...



            附:
            GetMessage(LPMSG lpMsg,  HWND hWnd,   UINT wMsgFilterMin,   UINT wMsgFilterMax)
            PeekMessage(LPMSG lpMsg,  HWND hWnd,   UINT wMsgFilterMin,   UINT wMsgFilterMax,UINT wRemoveMsg)

                參數(shù)wRemoveMsg的作用是指定消息獲取的方式,如果設(shè)為PM_NOREMOVE,那么消息將不會(huì)從消息隊(duì)列中被移出,如果設(shè)為PM_REMOVE,那么消息將會(huì)從消息隊(duì)列中被移出;

                兩個(gè)函數(shù)主要有以下兩個(gè)區(qū)別: 
                1.GetMessage將等到有合適的消息時(shí)才返回,而PeekMessage只是撇一下消息隊(duì)列。(GetMessage 處于掛起等待消息來(lái),而PeekMessage則不管有不有消息都返回)
                2.GetMessage會(huì)將消息從隊(duì)列中刪除,而PeekMessage可以設(shè)置最后一個(gè)參數(shù)wRemoveMsg來(lái)決定是否將消息保留在隊(duì)列中。(如果保留在隊(duì)列中,最好立即處理)

                  在Windows的內(nèi)部,GetMessage和PeekMessage執(zhí)行著相同的代碼。而兩者最大的不同之處則體現(xiàn)在沒(méi)有任何消息返回到應(yīng)用程序的情況下。在此種情況下,PeekMessage會(huì)返回一個(gè)空值到應(yīng)用程序,GetMessage會(huì)在此時(shí)讓應(yīng)用程序休眠。
            (如果在線程中使用,用GetMessage or PeekMessage都無(wú)所謂,不需要考慮消息隊(duì)列的remove,消息隊(duì)列在各個(gè)線程中不過(guò)是個(gè)拷貝?而已.)


            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1573835
            posted @ 2009-03-22 13:06 kenlistian 閱讀(2555) | 評(píng)論 (0)編輯 收藏

            2009年3月17日 #

              該函數(shù)等待handle信號(hào)發(fā)來(lái),它的好處在于可以等待多個(gè)信號(hào)發(fā)來(lái).
            原型:
            DWORD WaitForMultipleObjects(
             DWORD nCount,
             const HANDLE* lpHandles,
             BOOL bWaitAll,
             DWORD dwMilliseconds
            );

             第一個(gè)參數(shù)表示信號(hào)數(shù)目.
            當(dāng)設(shè)置bWaitAll = True,表示必須其信號(hào)必須都達(dá)到nCount才可以下一步.而不是lpHandle數(shù)組的信號(hào)都到.
                同時(shí),如果lpHandle的某個(gè)信號(hào)SetEvent多次,是不會(huì)累計(jì)到nCount中,也就是說(shuō),當(dāng)多個(gè)線程在處理完以后,可以在各自的線程體必須調(diào)用不同的event句柄來(lái)發(fā)信號(hào),它才會(huì)被累計(jì)達(dá)到nCount后解鎖進(jìn)入下一步.
            posted @ 2009-03-17 18:50 kenlistian 閱讀(846) | 評(píng)論 (0)編輯 收藏

            2009年3月13日 #

            來(lái)自http://blog.cnii.com.cn/?75821/viewspace-19610.html的開(kāi)發(fā)filter介紹

             pdf

            posted @ 2009-03-13 19:45 kenlistian| 編輯 收藏

            2009年3月9日 #

            對(duì)于pin的連接過(guò)程,總結(jié)下.

               1.
               應(yīng)用程序通過(guò)調(diào)用filter graph 管理器方法來(lái)連接filter.
               應(yīng)用程序調(diào)用IFilterGraph::ConnectDirect
                           IGraphBuilder::Connect來(lái)指定不同的filter直接連接,
                          也可用IGraphBuilder::RenderFile自動(dòng)實(shí)現(xiàn)連接
               應(yīng)用程序可以通過(guò)IFilterGraph::AddFilter將filter 添加graph中,
               當(dāng)一個(gè)filter被添加到graph中時(shí),filter圖表管理器通過(guò)IBaseFilter::JoinFilterGraph來(lái)通知filter.

               這點(diǎn)說(shuō)明, 不是filter的直接連接函數(shù)相互鏈接,而是在以上內(nèi)部調(diào)用實(shí)現(xiàn)的.

               2. 考慮到以前描述
                          FilterA ---->FilterB
                  的連接檢查媒體類型 邏輯基本就是這樣:

             循環(huán)FilterA的輸出pin,再循環(huán)FilterB的輸入Pin媒體類型是否和pmt媒體類型
             匹配

             for (j = 0 ; j<FilterB.PinIn.MediaTypeCount; j++)
             {
                         if (FilterB.PinIn.MediaType[j] = pmt )
                  {
                        if(FilterA.PinIn.ReceiveConnection(FilterA.PinOut, FilterB.MediaType[i]) = OK)
                               return TRUE;
                  }

                    }

                    for (i= 0; i< FilterA.PinOut.MediaTypeCount; i++)
             {
                  if (FilterA.PinOut.MediaType[i] 是否在FilterB.PinIn中是否支持)
                    if (FilterA.PinIn.ReceiveConnection(FilterA.PinOut, FilterA.MediaType[i]) = OK)
                         return TRUE;
             }

              在實(shí)現(xiàn)上,調(diào)用次序以下過(guò)程:


            filterGraph首先調(diào)用FilterA.PinOut::Connect().

            FilterA.IPinOut::Connect()
                   原型:IPin::Connect(IPin* pReceivePin, const AM_MEDIA_TYPE * pmt)     
                   該Connect參數(shù)為
                        pReceivePin 為 FilterB的輸入Pin,
                        pmt         是FilterA的當(dāng)前媒體類型.
             


                  在內(nèi)部調(diào)用(主要)
                     hr = AgreeMediaType(pReceivePin, pmt);
                         檢查pReceivePin 有否pmt的媒體類型.

              有,則自然ok
              沒(méi)有,失敗,退出該函數(shù).
                     則在AgreeMediaType做了以上邏輯循環(huán).

              
               IPin::AgreeMediaType函數(shù)處理如下:
             
                  1.判斷pmt 是否是完全媒體類型,是則按全媒體類型模式出來(lái)
                  2.非完全媒體類型
                 IPin::EnumMediaTypes(IEnumMediaTypes** pEnum)
                  獲取枚舉指針(指向Pin中的媒體類型集合).
               
                       先枚舉filterB的輸入Pin的媒體類型的枚舉集,
                     調(diào)用TryMediaTypes 函數(shù)去判斷是否匹配.
                還不匹配,取出FilterA的枚舉類指針.再調(diào)用TryMediaTyes
                

                IPin::TryMediaType()處理
                原型:
               HRESULT CBasePin::TryMediaTypes(IPin*pReceivePin, const CMediaType*pmt,
                     IEnumMediaType *pEnum)

                在該函數(shù)處理:
                     for (pmt in 所有該枚舉集中的枚舉媒體類型 )
              {
                 AttemptConnect(pReceivePin, pmt)
              }


                在AttemptConnection中調(diào)用
                CBasePin::AttemptConnection(IPin* pReceivePin, const CMediaType*pmt)
               
                  檢查FilterA 的CheckConnect(pReceivePin)
                  FilterA的PInOut::CheckMediaType(pmt)
                       ok,return
                  FilterA的PinOut::SetMediaType(pmt)
                   
                  pReceivePin->ReceiveConnection(...)  (filterB 的PinIn)
                       ok,return
                  FilterA的PinOut::CompleteConnect(pReceivePin)

            posted @ 2009-03-09 22:36 kenlistian 閱讀(709) | 評(píng)論 (0)編輯 收藏

            2009年2月6日 #


            自定義消息處理
            (不知道在wxpython,wxruby如何自定義類型消息?
            最好裝個(gè)wxpython,在其demo現(xiàn)場(chǎng)編寫現(xiàn)場(chǎng)查看結(jié)果)

            步驟如下:

            1.在宏里處理如下:

            a.

            BEGIN_DECLARE_EVENT_TYPES()
                DECLARE_EVENT_TYPE(wxEVT_MYCOMMAND, xxxx)
            END_DECLARE_EVENT_TYPES()


              xxxx 為自定義數(shù)字,不過(guò)翻到wxwidget內(nèi)部,其宏定義中該value好像
              沒(méi)用!(...)

            b. 定義wxEvT_MYCOMMAND 標(biāo)識(shí)

              DEFINE_EVENT_TYPE(wxEVT_MYMYCOMMAND)

             

            c. 定義宏EVT_MYCOMMAND
               格式中把自定義標(biāo)識(shí)改為自己即可.拷貝.

             

            #define EVT_MYCOMMAND(id, fn) \
                DECLARE_EVENT_TABLE_ENTRY( \
                    wxEVT_MYCOMMAND, id, wxID_ANY, \
                    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent( wxCommandEventFunction, 
            &fn ), \
                    (wxObject 
            *) NULL \
                ),

             


            2.在BEGIN_EVENT_TABLE表中自定義映射.
             

               EVT_MY_CUSTOM_COMMAND(wxID_ANY, MyFrame::OnMyfunction)

             

            3.處理投遞

                自定義消息發(fā)送,this 一般是接受消息handler,也可其它wxEvtHandler.

                wxCommandEvent myEvent(wxEVT_MY_CUSTOM_COMMAND);
                wxPostEvent(
            this, myEvent);

             

            4.自定義消息處理函數(shù)
               必須符合wxCommandEventFunction格式,既為如下:

                 void OnMyfunction(wxCommandEvent& event);

             

             

            posted @ 2009-02-06 12:06 kenlistian 閱讀(2742) | 評(píng)論 (3)編輯 收藏

            2009年2月3日 #

            來(lái)源:http://www.oulan.com/com/2008/07/wxwidgets.html
            [Commerial]
             DialogBlocks   http://www.anthemion.co.uk/dialogblocks/
             wxDesigner      http://www.roebling.de/

            [Free]
            wxGlade    http://www.roebling.de/
            VisualWx  http://visualwx.altervista.org/
            XRCed      http://xrced.sourceforge.net/

            wiki: http://wiki.wxformbuilder.org/
            官網(wǎng): http://wxformbuilder.org/

            推薦直接用DialogBlocks, 至于版本問(wèn)題,沒(méi)辦法,先用著再說(shuō).
             Dialogblock是美國(guó)Anthemion公司開(kāi)發(fā)的商業(yè)軟件,如果只是簡(jiǎn)單
            的包含控件,則免費(fèi)使用.

            另:
            在現(xiàn)在編程中,利用界面設(shè)計(jì)工具把界面資源以xrc方式存放,
            在程序中調(diào)用是一種趨勢(shì).



            posted @ 2009-02-03 11:35 kenlistian 閱讀(826) | 評(píng)論 (0)編輯 收藏

            2009年1月5日 #


            幾個(gè)格式細(xì)節(jié)備記(混)


            typedef struct  _MediaType {
                GUID      majortype; 
                GUID      subtype;
                BOOL      bFixedSizeSamples;
                BOOL      bTemporalCompression;
                ULONG     lSampleSize;
                GUID      formattype;
                IUnknown  *pUnk;                  //not use
                ULONG     cbFormat;
                BYTE *pbFormat;
            } AM_MEDIA_TYPE;

            主要有
               majortype  媒體類型大致說(shuō)明
               subtype    更一步的細(xì)致說(shuō)明
               formattype 
                  包括有以下:其對(duì)應(yīng)的不同的數(shù)據(jù)格式
                   FORMAT_None
                   FORMAT_DvInfo
                   FORMAT_MPEGVideo
                   FORMAT_MPEG2Video
                   FORMAT_VideoInfo
                   FORMAT_VideoInfo2 
                   FORMAT_WaveFormatEx  
                   GUID_NULL

              cbForamt成員指定了格式塊pbFormat的大小.
              pbFormat指針指向格式子塊。
                        pbFormat是一個(gè)void*的指針,因?yàn)楦袷綁K會(huì)因?yàn)槊襟w類型
                        的不同而有不同的指向。如音頻填充的是WAVEFORMATEX結(jié)構(gòu)
                 數(shù)據(jù).

                 可以從中取出傳來(lái)的數(shù)據(jù)格式。
                

             

            //TWaveFormatEx 結(jié)構(gòu):
            TWaveFormatEx = packed record
              wFormatTag: Word;       {指定格式類型; 默認(rèn) WAVE_FORMAT_PCM = 1;}
              nChannels: Word;        {指出波形數(shù)據(jù)的通道數(shù); 單聲道為 1, 立體聲為 2}
              nSamplesPerSec: DWORD;  {指定樣本速率(每秒的樣本數(shù))}一般為8000
              nAvgBytesPerSec: DWORD; {指定數(shù)據(jù)傳輸?shù)钠骄俾?每秒的字節(jié)數(shù))} 每秒的字節(jié)數(shù):
              nBlockAlign: Word;      {指定塊對(duì)齊(單位字節(jié)), 塊對(duì)齊是數(shù)據(jù)的最小單位}
              wBitsPerSample: Word;   {采樣大小(字節(jié))}每個(gè)樣本的BIT數(shù)目,一般為16
              cbSize: Word;           {應(yīng)該是該結(jié)構(gòu)的大小}
            end;

            nChannels       :   對(duì)于pcm,其nchannels不超過(guò)2,對(duì)于非pcm格式,則超過(guò)2.
            nSamplesPerSec  :   通常為8kHz, 11.025 kHz, 22.05 kHz, and 44.1 kHz.
            nAvgBytesPerSec :   每秒傳送字節(jié)數(shù) = nSamplesPerSec * nBlockAlign
            nBlockAlign     :   對(duì)齊字節(jié)  = nChannels * wBitsPerSample / 8
                                就是表示一個(gè)樣本的最小字節(jié).
            wBitsPerSample  :   在格式默認(rèn)情況下,一般為8,16,表示的是樣本的bit 數(shù)

            對(duì)于一個(gè)8位,11k傳輸?shù)牧Ⅲw聲則
            nChannels  = 2
            nSamplesPerSec(每秒的樣本數(shù)) = 11025  就是取樣數(shù)
            nBlockAlign  = 2 * 8 / 8= 2           對(duì)齊字節(jié),最小樣本字節(jié)數(shù)
            nAvgBytesPerSec = 11025 *  2 = 22050
            wBitsPerSample  = 8
             

             下面的圖列清楚從另一個(gè)方面表達(dá)樣本
             樣本1  樣本2 ...n
            8位單聲道 0聲道 0聲道
            8位立體聲 0聲道L         1聲道R  0聲道L  1聲R道
            16位單聲道 0聲道(低字節(jié))    0聲道(高字節(jié)) 0聲道(低字節(jié))    0聲道(高字節(jié))
            16位立體聲

            0聲道(低字節(jié))0聲道(高字節(jié))1聲道(低) 1聲道(高) 

            同左

                                                                                                                                 
                                                    
                                               
            ---------

            waveform-audio 緩存格式     
              typedef   struct   {    
                      LPSTR     lpData;           //內(nèi)存指針,放置音頻pcm樣本數(shù)據(jù)
                      DWORD     dwBufferLength;   //長(zhǎng)度    
                      DWORD     dwBytesRecorded;  //已錄音的字節(jié)長(zhǎng)度  
                      DWORD     dwUser;    
                      DWORD     dwFlags;    
                      DWORD     dwLoops;           //循環(huán)次數(shù)  
                      struct   wavehdr_tag* lpNext; //保留    
                      DWORD     reserved;           //保留
              }   WAVEHDR; 
             
              其中l(wèi)pdata 即為pcm格式樣本數(shù)據(jù)。
             
            采樣大小為8位,則采樣的動(dòng)態(tài)范圍為20*log(256)分貝=48db。
            樣本大小為16位,則采樣動(dòng)態(tài)范圍為20*log(65536)大約是96分貝

            振幅大小:   20*log(A1/A2)分貝,A1,A2為兩個(gè)聲音的振幅。
            則對(duì)于的音頻:
                      8位       20 * lg( lpData[0] /256)
               16位      20 * lg( lpData[0]--lpData[1] / 65536)
             考慮到單雙道,還需要相應(yīng)取出左右聲道的值。
             考慮到lg求值為負(fù)48至0之間,則在實(shí)際轉(zhuǎn)換中需要+48or96.


            樣本大小  數(shù)據(jù)格式      最大值  最小值
            8位PCM    unsigned int   256     0
            16位PCM   int            32767  -32767

             8位音頻是unsigned 存放波形,取振幅要-127.
             而16位因其存放為int 類型,直接套用公式.
             

            audiometer左右聲道音量探測(cè)程序(參考代碼(delphi版

             

            posted @ 2009-01-05 14:16 kenlistian 閱讀(806) | 評(píng)論 (0)編輯 收藏

            2008年12月28日 #

            RealMediaSplitter.ax

            Source(.rm/.rmvb)->RealMediaSplitter->Video(Audio)->real a/v decoder->A/V   Render

            其實(shí)復(fù)原并查找該ax很簡(jiǎn)單,安裝暴風(fēng)后在graphedit中,拖一個(gè)rm文件,
            查看解碼過(guò)程,然后在filter列表中找出該ax文件名,即可構(gòu)建到自己的
            播放器中。
            posted @ 2008-12-28 11:21 kenlistian| 編輯 收藏

            2008年12月27日 #

            1、配置DirectDound的開(kāi)發(fā)環(huán)境
            包含以下
            #include <mmsystem.h>
            #include <dsound.h>
            添加Dsound.lib庫(kù)
            comctl32.lib dxerr9.lib winmm.lib dsound.lib dxguid.lib odbc32.lib odbccp32.lib,

            2 DiectDound幾個(gè)對(duì)象
             
              創(chuàng)建一個(gè)設(shè)備對(duì)象,后通過(guò)設(shè)備對(duì)象創(chuàng)建緩沖區(qū)對(duì)象。
              輔助緩沖區(qū)由應(yīng)用程序創(chuàng)建和管理,DirectSound會(huì)自動(dòng)地創(chuàng)建和管理主緩沖區(qū),


            3 播放音頻文件開(kāi)發(fā)的基本流程

             a 創(chuàng)建一個(gè)設(shè)備對(duì)象,設(shè)置設(shè)備對(duì)象的協(xié)作度。

                調(diào)用DirectSoundCreat8創(chuàng)建一個(gè)支持IDirectSound8接口的對(duì)象,
                  這個(gè)對(duì)象通常代表缺省的播放設(shè)備。
               
                   如果沒(méi)有聲音輸出設(shè)備,這個(gè)函數(shù)就返回error,或者,在VXD驅(qū)動(dòng)程序下,
                  如果聲音輸出設(shè)備正被某個(gè)應(yīng)用程序通過(guò)waveform格式的api函數(shù)所控制,
                  該函數(shù)也返回error。 

            LPDIRECTSOUND8 lpDirectSound; 
            HRESULT hr 
            = DirectSoundCreate8(NULL,&lpDirectSound, NULL));

                  當(dāng)創(chuàng)建完設(shè)備對(duì)象后,調(diào)用IDirectSound8::SetCooperativeLevel來(lái)設(shè)置
                  協(xié)作度,否則聽(tīng)不到聲音.

              b.創(chuàng)建一個(gè)輔助Buffer,也叫后備緩沖區(qū)
                  (IDirectSound8::CreateSoundBuffer)
                  創(chuàng)建的buffer稱作輔助緩沖區(qū),Direcsound通過(guò)把幾個(gè)后備緩沖區(qū)的聲音
                  混合到主緩沖區(qū)中,然后輸出到聲音輸出設(shè)備上,達(dá)到混音的效果。

              c. 獲取PCM類型的數(shù)據(jù)
               
              將WAV文件或者其他資源的數(shù)據(jù)讀取到緩沖區(qū)中。

              d. 將數(shù)據(jù)讀取到緩沖區(qū)
                   其中用到以下來(lái)鎖緩沖區(qū)。
                      IDirectSoundBuffer8::Lock
                      IDirectSoundBuffer8::Unlock.

              e. 播放緩沖區(qū)中的數(shù)據(jù)
                   IDirectSoundBuffer8::Play  播放緩沖區(qū)中的音頻數(shù)據(jù),
                   IDirectSoundBuffer8::Stop 暫停播放數(shù)據(jù),
                   
                   獲取或者設(shè)置正在播放的音頻的音量的大小
               IDirectSoundBuffer8::GetVolume
                     IDirectSoundBuffer8::SetVolume

                  獲取設(shè)置音頻播放的頻率
                IDirectSoundBuffer8::GetFrequency
                     IDirectSoundBuffer8::SetFrequency   
                          主緩沖區(qū)的頻率不允許改動(dòng),

                  設(shè)置音頻在左右聲道播放的位置
                      IDirectSoundBuffer8::GetPan
                      IDirectSoundBuffer8::SetPan

              包含全部音頻數(shù)據(jù)的緩沖區(qū)我們稱為靜態(tài)的緩沖區(qū),
                    盡管不同的聲音可能會(huì)反復(fù)使用同一個(gè)內(nèi)存buffer,但靜態(tài)緩沖區(qū)的數(shù)據(jù)只寫入一次。

                    靜態(tài)緩沖區(qū)只填充一次數(shù)據(jù),然后就可以play,
             
              給靜態(tài)緩沖區(qū)加載數(shù)據(jù)分下面幾個(gè)步驟
              1、用IDirectSoundBuffer8::Lock函數(shù)來(lái)鎖定所有的內(nèi)存,
                        指定你鎖定內(nèi)存中你開(kāi)始寫入數(shù)據(jù)的偏移位置,并且取回該偏移位置的地址。
              2、采用標(biāo)準(zhǔn)的數(shù)據(jù)copy方法,將音頻數(shù)據(jù)復(fù)制到返回的地址。
              3、調(diào)用IDirectSoundBuffer8::Unlock.,解鎖該地址。


            用static buffer 播放wav方法

              

             

            LPDIRECTSOUNDBUFFER8    g_pDSBuffer8 = NULL; //buffer
            LPDIRECTSOUND8                   g_pDsd = NULL; //dsound
            CWaveFile                                   *g_pWaveFile= NULL;

            //初始化DirectSound工作
            HRESULT hr;
            if(FAILED(hr = DirectSoundCreate8(NULL,&g_pDsd,NULL)))
             
            return FALSE;

            //設(shè)置設(shè)備的協(xié)作度
            if(FAILED(hr = g_pDsd->SetCooperativeLevel(m_hWnd,DSSCL_PRIORITY)))
             
            return FALSE;

            g_pWaveFile 
            = new CWaveFile;
            g_pWaveFile
            ->Open(_T("c:\\test.wav"), NULL, WAVEFILE_READ);

            DSBUFFERDESC dsbd;
            ZeroMemory( 
            &dsbd, sizeof(DSBUFFERDESC) );
            dsbd.dwSize 
            = sizeof(DSBUFFERDESC);
            dsbd.dwFlags 
            = DSBCAPS_GLOBALFOCUS               //設(shè)置主播
                                            | DSBCAPS_CTRLFX
                                            
            | DSBCAPS_CTRLPOSITIONNOTIFY 
                                            
            | DSBCAPS_GETCURRENTPOSITION2;

            dsbd.dwBufferBytes 
            = g_pWaveFile->GetSize();    
            dsbd.lpwfxFormat 
            = g_pWaveFile->m_pwfx;

            LPDIRECTSOUNDBUFFER lPBuffer;

            //創(chuàng)建輔助緩沖區(qū)對(duì)象
            if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd,&lpbuffer,NULL)))
             
            return ;
            if( FAILED(hr = lpbuffer->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*&g_pDSBuffer8) ) )
             
            return ; 
            lpbuffer
            ->Release();

            //播放
            LPVOID lplockbuf;
            DWORD len;
            DWORD dwWrite;

            g_pDSBuffer8
            ->Lock(0,0&lplockbuf,  &len,  NULL,  NULL, DSBLOCK_ENTIREBUFFER);

            //g_pWaveFile 聲音寫入到lplockbuf所指地址
            g_pWaveFile->Read((BYTE*)lplockbuf, len, &dwWrite);

            g_pDSBuffer8
            ->Unlock(lplockbuf,len,NULL,0);

            g_pDSBuffer8
            ->SetCurrentPosition(0);

            g_pDSBuffer8
            ->Play(0,0,DSBPLAY_LOOPING);




               f  流緩沖區(qū)播放超大型的wave文件

               流緩沖區(qū)就是播放那些比較長(zhǎng)的音頻文件,邊播放,邊填充DirectSound緩沖區(qū)。

               DirectSound的通知機(jī)制
                  因?yàn)镾tream buffer 大小只夠容納一部分?jǐn)?shù)據(jù),在播放完緩沖區(qū)中的數(shù)據(jù)后,
                  DirectSound就會(huì)通知應(yīng)用程序,將新的數(shù)據(jù)填充到DirectSound的緩沖區(qū)中。

             

             

            #define MAX_AUDIO_BUF 4                        //設(shè)置4個(gè)buffer
            #define BUFFERNOTIFYSIZE 1920               //每個(gè)buffer尺寸為1920

            BOOL g_bPlaying     
            = FALSE;                                            //是否正在播放
            LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL; 
            DSBPOSITIONNOTIFY        g_aPosNotify[MAX_AUDIO_BUF];     
            //設(shè)置通知標(biāo)志的數(shù)組

            HANDLE g_event[MAX_AUDIO_BUF];
            for(int i =0; i< MAX_AUDIO_BUF;i++)
            {
             g_aPosNotify[i].dwOffset 
            = i* BUFFERNOTIFYSIZE ;   
                g_aPosNotify[i].hEventNotify 
            = g_event[i];
            }

            if(FAILED(hr = g_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID *&g_pDSNotify )))
             
            return ;

            g_pDSNotify
            ->SetNotificationPositions(MAX_AUDIO_BUF,g_aPosNotify);

            g_pDSNotify
            ->Release();


                 當(dāng)DirectSound播放到buffer的1920,3840,5760,7680等位置時(shí),
            Directsound就會(huì)通知應(yīng)用程序,將g_event,設(shè)置為通知態(tài);
              應(yīng)用程序就通過(guò)WaitForMultipleObjects 函數(shù)等待DirectSound的通知,
            將數(shù)據(jù)填充到DirectSoun的輔助緩沖區(qū)。

             


             

            posted @ 2008-12-27 18:30 kenlistian 閱讀(2940) | 評(píng)論 (1)編輯 收藏

            2008年12月15日 #



              Moniker 是個(gè)接口。
            posted @ 2008-12-15 10:07 kenlistian 閱讀(983) | 評(píng)論 (0)編輯 收藏

            2008年12月10日 #



              測(cè)試神龍卡路數(shù),其中pDecoderFilter要對(duì)應(yīng)不同的設(shè)備。
                     int iDev = 0;
                    
            for (int i = 0; i < 10 ; i++)
                    
            {
                        
            //這里可以判斷有幾路神龍卡
                        hr = CoCreateInstance(CLSID_RM_MPEG2_VIDEO_SUBPICTURE, NULL, CLSCTX_INPROC_SERVER,
                            IID_IBaseFilter, (LPVOID 
            *&pDecoderFilter);
                        
            if(hr != S_OK) 
                            
            break;    
                        
                        iDev
            ++;
                    }


                    
            char sMsg[256];
                    sprintf(sMsg,
            "%d", iDev);        
                    MessageBox(ghApp, sMsg, 
            "Msg", MB_OK);
            posted @ 2008-12-10 10:45 kenlistian 閱讀(497) | 評(píng)論 (0)編輯 收藏

            2008年12月9日 #

            1.采用系統(tǒng)設(shè)備枚舉器來(lái)枚舉.
               根據(jù)硬件系統(tǒng)種類來(lái)枚舉系統(tǒng)中注冊(cè)的filter。
               每一種不同的硬件可能有自己的filter,也可能所有的硬件設(shè)備共用filter。

             系統(tǒng)設(shè)備枚舉器是根據(jù)不同的種類來(lái)創(chuàng)建的,如,音頻壓縮,視頻捕捉。
              不同種類的枚舉器對(duì)于每一種設(shè)備返回一個(gè)獨(dú)立的名稱(moniker)。

            下面的步驟是使用設(shè)備枚舉器來(lái)獲取設(shè)備:

            1) 創(chuàng)建枚舉器組件,CLSID為CLSID_SystemDeviceEnum
            2) 指定某一種類型設(shè)備,獲取該種類枚舉器
                通過(guò)ICreateDevEnum::CreateClassEnumerator獲取某一種類的枚舉器,
                該函數(shù)返回一個(gè)IEnumMoniker接口指針,
                通過(guò)檢查返回值是否為S_OK來(lái)判斷是否獲取到該種類枚舉器.
            3) 用IEnumMoniker::Next枚舉每一個(gè)moniker。
                 這個(gè)方法返回一個(gè)IMoniker接口指針。
            4) 通過(guò)IMoniker::BindToStorage獲取設(shè)備的名稱

            大致例子如下:
               

            HRESULT hr;
            ICreateDevEnum 
            *pSysDevEnum = NULL;
            hr 
            = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                      IID_ICreateDevEnum, (
            void **)&pSysDevEnum);
            if (FAILED(hr)) return hr;

            //獲取video compressor 種類枚舉器 
            IEnumMoniker *pEnumCat = NULL;
            hr 
            = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat,0);
            if (hr == S_OK){

             IMoniker 
            *pMoniker = NULL;
             ULONG cFetched;

             
            while(pEnumCat->Next(1&pMoniker, &cFetched) == S_OK){

              IPropertyBag 
            *pPropBag;
              hr 
            = pMoniker->BindToStorage(00, IID_IPropertyBag, (void **)&pPropBag);
              
            if (SUCCEEDED(hr))
              
            {
               
            // 獲取設(shè)備名稱
               VARIANT varName;
               VariantInit(
            &varName);
               hr 
            = pPropBag->Read(L"FriendlyName"&varName, 0);
               
            if (SUCCEEDED(hr)){   
                    
            //獲取設(shè)備名稱
               }

               VariantClear(
            &varName);
              }

              pMoniker
            ->Release();
             }

             pEnumCat
            ->Release();
            }

            pSysDevEnum
            ->Release();


            2.采用Filer Mapper。
               類似條件查詢。

               比系統(tǒng)設(shè)備枚舉器(System Device Enumerator)的效率要低一些。
               當(dāng)要枚舉某特定種類的filter時(shí),應(yīng)采用系統(tǒng)設(shè)備枚舉器方法,但搜索支持某種
               媒體類型的filter時(shí),用filter mapper.

               Filter Mapper 通過(guò)IFilerMapper2接口搜索接口,
               通過(guò)調(diào)用IFilterMapper2::EnumMatchingFilters方法,傳遞一些參數(shù)來(lái)定義搜索條件,
               返回一個(gè)適合條件的filter的枚舉器,

               返回的是一個(gè)IEnumMoniker接口,并對(duì)于每個(gè)適合的filter都提供一個(gè)單獨(dú)的moniker。

            例子:

            //枚舉所有的支持DV,并且至少有一個(gè)輸出pin的filter,
            //這個(gè)filter支持任何媒體類型。
            IFilterMapper2 *pMapper = NULL;
            IEnumMoniker 
            *pEnum = NULL;

            hr 
            =CoCreateInstance( CLSID_FilterMapper2,NULL, CLSCTX_INPROC, IID_IFilterMapper2, (void **&pMapper);
            if (FAILED(hr)) {   }

            GUID arrayInTypes[
            2];
            arrayInTypes[
            0= MEDIATYPE_Video;
            arrayInTypes[
            1= MEDIASUBTYPE_dvsd;
            hr 
            = pMapper->EnumMatchingFilters(
                         
            &pEnum,
                             
            0,           // Reserved.
                         TRUE,        // Use exact match?
                         MERIT_DO_NOT_USE+1,  // Minimum merit.
                         TRUE,        // At least one input pin?
                         1,                 // Number of major type/subtype pairs for input.
                        arrayInTypes,     // Array of major type/subtype pairs for input.
                        NULL,   // Input medium.
                        NULL,   // Input pin category.
                        FALSE,  // Must be a renderer?
                        TRUE,   // At least one output pin?
                         0,      // Number of major type/subtype pairs for output.
                          NULL,   // Array of major type/subtype pairs for output.
                       NULL,   // Output medium.
                         NULL);  // Output pin category.
                               
            IMoniker 
            *pMoniker;
            ULONG cFetched; 

            //枚舉filter,
            while(pEnumCat->Next(1&pMoniker, &cFetched) == S_OK)
            {
              IPropertyBag 
            *pPropBag = NULL;
              hr 
            = pMoniker->BindToStorage(00, IID_IPropertyBag, (void **)&pPropBag);
              
            if (SUCCEEDED(hr))
              
            {
                        VARIANT varName;
                 VariantInit(
            &varName);
                 hr 
            = pPropBag->Read(L"FriendlyName"&varName, 0);
               
            if (SUCCEEDED(hr))
               
            {
                               
            //處理
                }

                VariantClear(
            &varName);
             }

             pMoniker
            ->Release();
            }


            pMapper
            ->Release();
            pEnum
            ->Release();

            注:
             * 有關(guān)directshow的api函數(shù),估計(jì)只能在網(wǎng)上搜索,至于下載的msdn沒(méi)有,或者下個(gè)dssdk2002版本的help才能有。
             * 在directshow中,關(guān)于capture dev,有專門的demo演示如何獲取音視設(shè)備。這里只是
            明了獲取設(shè)備的方法。以備了解。

            posted @ 2008-12-09 11:43 kenlistian 閱讀(1435) | 評(píng)論 (0)編輯 收藏

            2008年12月8日 #

            使用DirectShow寫一個(gè)音頻捕捉例子

            DirectShow對(duì)硬件的支持是通過(guò)特定的包裝Filter來(lái)實(shí)現(xiàn)的。
            聲卡使用的是Audio Capture Filter,F(xiàn)ilter內(nèi)部使用以waveIn開(kāi)頭
            的一套API實(shí)現(xiàn)(如waveInOpen等)。
            運(yùn)行GraphEdit,插入Filter時(shí),在“Audio Capture Sources”目錄下,
            就能看到所有代表本地機(jī)器上的聲卡的各個(gè)Filter(裝了幾張聲卡,就會(huì)有幾個(gè)Filter)


            DirectShow加入一個(gè)硬件Filter,是靠“枚舉”;聲卡Filter也不例外。
            代表聲卡的Filter都注冊(cè)在CLSID_AudioInputDeviceCategory目錄下,
            使用系統(tǒng)設(shè)備枚舉器枚舉這個(gè)目錄,就能發(fā)現(xiàn)要?jiǎng)?chuàng)建的聲卡對(duì)象。
             (如何枚舉這里就不再贅述了。)
            當(dāng)成功加入聲卡Filter后,接下去的問(wèn)題就是要將這個(gè)Filter與其他Filter相連。


             想捕捉生成一個(gè)Wave文件,采用過(guò)濾器的勾連如下
                 聲卡filter--->Wave Dest Filter  ---->File Writer Filter
             Wave Dest Filter是微軟DirectX SDK自帶的過(guò)濾器
                其功能是,當(dāng)結(jié)束捕捉時(shí),往Wave文件中寫入一個(gè)文件頭信息。

             Filte Write Filter 是微軟系統(tǒng)過(guò)濾器。
            用graphedit可以勾連后測(cè)試下。


            //采用程序來(lái)連接過(guò)濾器的大致方法:
            //沒(méi)有處理錯(cuò)誤
            void BuildAudioCaptureGraph(void)
            {
              IBaseFilter *pSrc = NULL,            //捕捉音頻設(shè)備
                           *pWaveDest = NULL,         //處理音頻過(guò)濾器
                    *pWriter = NULL;                      //產(chǎn)生文件過(guò)濾器

              IFileSinkFilter *pSink= NULL;
              IGraphBuilder *pGraph;

              // Create the Filter Graph Manager.
              CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,     IID_IGraphBuilder, (void**)&pGraph);

              // Add the audio capture filter.
                 //這里省略了枚舉設(shè)備處理
              FindAudioCapture(&pSrc);
             
              // audio capture devices and picks one.
              pGraph->AddFilter(pSrc, L"Capture");

              // Add the WavDest and the File Writer.
              AddFilterByClsid(pGraph, L"WavDest", CLSID_WavDest, &pWavDest);
              AddFilterByClsid(pGraph, L"File Writer", CLSID_FileWriter, &pWriter);

              //是writer接口中屬性
              pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
              pSink->SetFileName(L"C:\test.wav", NULL);

              //連接filter
              ConnectTwoFilters(pGraph, pSrc, pWavDest);
              ConnectTwoFilters(pGraph, pWavDest, pWriter);
            }


             

            posted @ 2008-12-08 20:21 kenlistian 閱讀(801) | 評(píng)論 (0)編輯 收藏

            DirectShow技術(shù)是建立在DirectDraw和DirectSound組件基礎(chǔ)之上的,
            它通過(guò)DirectDraw對(duì)顯卡進(jìn)行控制以顯示視頻,
            通過(guò)DirectSound對(duì)聲卡進(jìn)行控制以播放聲音。

            DirectShow功能實(shí)現(xiàn):

            1.可提供高質(zhì)量的多媒體流的捕獲和回放功能;
            2.支持多種媒體格式,包括ASF(Advanced Systems Format),MPEG(Motion Picture Experts Group),AVI(Audio-Video Interleaved),MP3(MPEG Audio Layer-3)和WAV聲音文件;
            3.可從硬件上捕獲媒體數(shù)據(jù)流;
            4.可自動(dòng)檢測(cè)并使用視頻和音頻加速硬件。

            故,DirectShow是用于多媒體應(yīng)用開(kāi)發(fā)。(其實(shí)就是一個(gè)軟編碼(or解碼))
            它充分發(fā)揮媒體的性能,提高運(yùn)行速度,可以簡(jiǎn)化媒體播放、媒體間的格式轉(zhuǎn)換
            和媒體捕獲等工作。同時(shí),它還具有極大的可擴(kuò)展性和靈活性,可以由用戶自己
            創(chuàng)建組件,并將這個(gè)組件加入DirectShow結(jié)構(gòu)中以支持新的格式或特殊的效果。

            應(yīng)用程序與DirectShow組件以及DirectShow所支持的軟硬件之間的關(guān)系
            如圖1

             


            二。概念
            1.過(guò)濾器
            過(guò)濾器分為以下幾種類型:
             a 源過(guò)濾器(source filter):
                源過(guò)濾器引入數(shù)據(jù)到過(guò)濾器圖表中,數(shù)據(jù)來(lái)源可以是文件、網(wǎng)絡(luò)、照相機(jī)等。
                不同的源過(guò)濾器處理不同類型的數(shù)據(jù)源。

             b 變換過(guò)濾器(transform filter):
               變換過(guò)濾器的工作是獲取輸入流,處理數(shù)據(jù),并生成輸出流。
               變換過(guò)濾器對(duì)數(shù)據(jù)的處理包括編解碼、格式轉(zhuǎn)換、壓縮解壓縮等。

             c 提交過(guò)濾器(renderer filter):
                接收數(shù)據(jù)并把數(shù)據(jù)提交給外設(shè)。

             d 分割過(guò)濾器(splitter filter):
                把輸入流分割成多個(gè)輸出。
                如,AVI分割過(guò)濾器把一個(gè)AVI格式的字節(jié)流分割成視頻流和音頻流。

             e 混合過(guò)濾器(mux filter):
                把多個(gè)輸入組合成一個(gè)單獨(dú)的數(shù)據(jù)流。
                如,AVI混合過(guò)濾器把視頻流和音頻流合成一個(gè)AVI格式的字節(jié)流。
                 過(guò)濾器的這些分類并不是絕對(duì)的,如一個(gè)ASF讀過(guò)濾器(ASF Reader filter)
                   既是一個(gè)源過(guò)濾器又是一個(gè)分割過(guò)濾器。


            2  filter graph

                過(guò)濾器圖表用來(lái)連接過(guò)濾器以控制媒體流,它也可以將數(shù)據(jù)返回給應(yīng)用程序,
                并搜索所支持的過(guò)濾器。
                過(guò)濾器有三種可能的狀態(tài):運(yùn)行、停止和暫停。
                暫停是一種中間狀態(tài),停止?fàn)顟B(tài)到運(yùn)行狀態(tài)必定經(jīng)過(guò)暫停狀態(tài)。
                暫停可以理解為數(shù)據(jù)就緒狀態(tài),是為了快速切換到運(yùn)行狀態(tài)而設(shè)計(jì)的。   
                在暫停狀態(tài)下,數(shù)據(jù)線程是啟動(dòng)的,但被提交過(guò)濾器阻塞了。
               
                通常情況下,過(guò)濾器圖表中所有過(guò)濾器的狀態(tài)是一致的。

            3. 引腳(pin)
             過(guò)濾器可以和一個(gè)或多個(gè)過(guò)濾器相連,
                連接的接口也是COM形式的,稱為引腳。
               
                過(guò)濾器利用引腳在各個(gè)過(guò)濾器間傳輸數(shù)據(jù)。
                每個(gè)引腳都從Ipin這個(gè)COM對(duì)象派生出來(lái)的。
                每個(gè)引腳都是過(guò)濾器的私有對(duì)象,過(guò)濾器可以動(dòng)態(tài)的創(chuàng)建引腳,銷毀引腳,自由控制引腳的生存時(shí)間。
                引腳分輸入引腳(Input pin)和輸出引腳(Output pin)兩種類型,
                兩個(gè)相連的引腳必須是不同種類的,即輸入引腳只能和輸出引腳相連

             過(guò)濾器之間的連接(也就是引腳之間的連接),實(shí)際上是連接雙方媒體類型(Media Type)協(xié)商的過(guò)程。(媒體類型,不完全媒體類型 再下一節(jié)有講解)


               連接的大致過(guò)程為:
                 如果調(diào)用連接函數(shù)時(shí)已經(jīng)指定了完整的媒體類型,則用這個(gè)媒體類型進(jìn)行連接,
              成功與否都結(jié)束連接過(guò)程;
                  如果沒(méi)有指定或不完全指定了媒體類型,
                  則枚舉過(guò)程見(jiàn)后面.其兩個(gè)filter的連接設(shè)定如下.
                
                             Filter A     ------------------> Filter B



            ------------------------------------------------------------- ------------------

            說(shuō)明:
            媒體類型(Media Type)
                兩個(gè)過(guò)濾器相連時(shí),必須使用一致的媒體類型,否則這兩個(gè)過(guò)濾器就不能相連。

                媒體類型能識(shí)別上一級(jí)過(guò)濾器傳送給下一級(jí)過(guò)濾器的數(shù)據(jù)類型,并對(duì)數(shù)據(jù)進(jìn)行分類。
               媒體類型的結(jié)構(gòu)  AM_MEDIA_TYPE
             
              AM_MEDIA_TYPE由三部分組成:
                  Major type
                  Subtype
                  Format type
               
               都使用GUID 來(lái)唯一標(biāo)示
                
                 Major type主要定性描述一種媒體類型,這種媒體類型可以是視頻、音頻、比特?cái)?shù)據(jù)流或MIDI數(shù)據(jù)等;
                 Subtype   進(jìn)一步細(xì)化媒體類型,
                          拿視頻的說(shuō)就是進(jìn)一步指定是RGB-24,還是RGB-32,或是UYVY等;
                 Format type則用一個(gè)結(jié)構(gòu)更進(jìn)一步細(xì)化媒體類型。

             媒體類型的三個(gè)部分都指定了某個(gè)具體的GUID值,則稱這個(gè)媒體類型是完全指定的;
                媒體類型的三個(gè)部分中有任何一個(gè)值是GUID_NULL,則稱這個(gè)媒體類型是不完全指定的。
                GUID_NULL起通配符作用


            pinout和pinin的連接過(guò)程可以用下面邏輯語(yǔ)言表達(dá).

            1.如調(diào)用連接函數(shù)時(shí)已經(jīng)指定了完整的Media type,則用這個(gè)Media type進(jìn)行連接,
            成功與否都結(jié)束連接過(guò)程;

            2.如沒(méi)有指定或不完全指定了Media type,
            則如下:

              BOOL CheckFilterB_PinIn()
              {
                   for(i = 0 ; i < FilterB.FPinIn.MediaTypeCount ; i++)
                  {
                       if (IsSameMediaType(FilterA.FPinOut,FilterB.FPinIn.MediaType[i]) = True)
                      {
                             return TRUE;   //Pin之間的連接成功;
                      }
                   }
                    return FALSE;          //在Input pin不支持該媒體類型,失敗.
                }


              返回FALSE再枚舉Output pin上的所有Media type,并逐一用這些Media type與Input pin進(jìn)行連接。
              
               for(i = 0 ; i < FilterA.FPinOut.MediaTypeCount; i++)
               {
                  if (CheckFilteB_PinIn(FilterA.FPinOut.MediaType[i]) = True )
                  {
                     return TRUE;
                  }
               }
               return FALSE;  //filterA和filterB的連接失敗.



             

            posted @ 2008-12-08 12:50 kenlistian 閱讀(1056) | 評(píng)論 (0)編輯 收藏

            DirectShow 的幾個(gè)接口說(shuō)明
            (1) IGraphBuilder接口
                 用于構(gòu)造Filter Graph的接口,建立和管理一系列的Filter,過(guò)濾和處理源媒體流。
            (2) IMediaControl接口
                 用于控制多媒體流在Filter Graph中的流動(dòng),如流的啟動(dòng)和停止。
            (3) IMediaEvent接口
                 用于捕獲播放過(guò)程中發(fā)生的事件,并通知應(yīng)用程序,如EC_COMPLETE等。
            (4) IVideoWindow接口
                 用于控制視頻窗口的屬性。
            (5) IMeadiaSeeking接口
                 用于查找媒體的接口,定位流媒體,控制多媒體數(shù)據(jù)播放提供精確控制。
            (6) IBaseFilter接口
                從ImediaFilter接口繼承,用來(lái)定義一個(gè)具體的過(guò)濾器指針,并對(duì)多媒體數(shù)據(jù)進(jìn)行處理。
            (7) IPin接口
                用于管理兩個(gè)過(guò)濾器之間的Pin,從而連接過(guò)濾器。
            (8) IsampleGrabberCB接口
               是Sample Grabber過(guò)濾器的一個(gè)接口,用于當(dāng)流媒體數(shù)據(jù)通過(guò)過(guò)濾器時(shí)進(jìn)行采樣以獲得幀圖象。


            Filter必須加入到Filter Graph并接入到工作鏈路中才能發(fā)揮作用。
            如想繞過(guò)Filter Graph而直接使用Filter實(shí)現(xiàn)的功能模塊,那就要將Filter功能
            移植成DirectX媒體對(duì)象(DMO)。
            Filter有3種狀態(tài):停止、暫停和運(yùn)行。

            Filter Chain是相互連接著的一條Filter鏈路,并且鏈路中的每個(gè)Filter全都有一個(gè)處于“已連接”狀態(tài)的
            輸入Pin,至多有一個(gè)處于“已連接”狀態(tài)的輸出Pin,這條Filter鏈路中的數(shù)據(jù)流不依賴鏈路外的其他Filter。

            Filter Chain通過(guò)IFilterChain接口來(lái)進(jìn)行相關(guān)操作。
            當(dāng)Filter Graph處于運(yùn)行狀態(tài)下,F(xiàn)ilter Chain可以在運(yùn)行和停止?fàn)顟B(tài)之間切換;
            當(dāng)Filter Graph處于暫停狀態(tài)下,F(xiàn)ilter Chain可以在暫停和停止?fàn)顟B(tài)之間切換。
            Filter Chain只有兩種狀態(tài)轉(zhuǎn)換。

            Filter的數(shù)據(jù)傳送
            Filter之間以Sample的形式傳送數(shù)據(jù)。
            Sample是一個(gè)封裝了一定大小數(shù)據(jù)內(nèi)存的COM組件。
            用于數(shù)據(jù)傳輸?shù)囊话闶禽斎雙in上實(shí)現(xiàn)的IMemInputPin接口。

            posted @ 2008-12-08 11:38 kenlistian 閱讀(746) | 評(píng)論 (0)編輯 收藏

            注意:
                以下配置均以vc6為主.

            一、配置方法
            1 先編譯Samples\C++\DirectShow\BaseClasses中的工程文件。
            注意工程的默認(rèn)選項(xiàng)是 Debug_Unicode的,在非Unicode的工程中使用還需要編譯Debug版本的。

             注意:
              一般用 ansi debug版.不怕麻煩把release也生成,取出該strmbasd.lib(strmbase.lib).
              放在dxsdk中的lib中.


            2 需要設(shè)置好VC中目錄的設(shè)置 include的目錄中添加 C:\DXSDK\Samples\C++\DirectShow\BaseClasses Lib的目錄中添加 C:\DXSDK\Samples\C++\DirectShow\BaseClasses\Debug
            注意:
              建議把把classes 把頭文件可以單獨(dú)拷貝到dxsdk的include和lib中

            3、使用DirectShow的工程需要添加下面兩個(gè)頭文件
            #include   //DS接口、基類的定義
            #include    //CComPtr模板的定義
            #pragma comment(lib,"strmbasd.lib")
            #pragma comment(lib,"winmm.lib")
            #pragma comment(linker,"/NODEFAULTLIB:libcmtd.lib")

            注意:
              VC自帶的庫(kù)中也有strmbasd.lib文件。
            一定要保證連接到DS的庫(kù)中,否則會(huì)出現(xiàn) 
            strmbasd.lib(dllsetup.obj) : error LNK2001: unresolved external symbol "class CFactoryTemplate * g_Templates" (?g_Templates@@3PAVCFactoryTemplate@@A)
            strmbasd.lib(dllentry.obj) : error LNK2001: unresolved external symbol "class CFactoryTemplate * g_Templates" (?g_Templates@@3PAVCFactoryTemplate@@A)
            strmbasd.lib(dllsetup.obj) : error LNK2001: unresolved external symbol "int g_cTemplates" (?g_cTemplates@@3HA)
            strmbasd.lib(dllentry.obj) : error LNK2001: unresolved external symbol "int g_cTemplates" (?g_cTemplates@@3HA) 之類的錯(cuò)誤。

              以上link報(bào)錯(cuò)各異,最好把lib的位置上下調(diào)調(diào),別鏈到vc自帶的庫(kù)中.鬼問(wèn)題名堂太多,有時(shí)也和direct showsdk版本有關(guān).

            二.編譯后出現(xiàn)的錯(cuò)誤:
            1 syntax error : identifier 'DWORD_PTR'
            如下:
            e:\directx9\samples\c++\directshow\baseclasses\wxutil.h(53) : error C2061: syntax error : identifier 'DWORD_PTR'
            e:\directx9\samples\c++\directshow\baseclasses\ctlutil.h(43) : error C2504: 'IBasicVideo2' : base class undefined
            e:\directx9\samples\c++\directshow\baseclasses\ctlutil.h(904) : error C2146: syntax error : missing ';' before identifier 'm_dwAdvise' e:\directx9\samples\c++\directshow\baseclasses\ctlutil.h(90) : error C2501: 'DWORD_PTR' : missing storage-class or type specifierse
               如果發(fā)生這種情況,你應(yīng)該從"Tools"目錄中選擇"Option",
               然后在include directory中將Platform SDK加到VC inlcude中.

            注意:
             1. 沒(méi)有該sdk,需要先下載platform sdk. 在我的隨筆中包含該鏈接.
            2. 要放在Include最前面.


            2 LINK : fatal error LNK1104: cannot open file "mfc42ud.lib" mfc42ud.lib是專門給unicode用的 build-->set active Configuration--> XXX win32 debug 這樣就可以了 

            3“CComPtr< ”怎么用?
            CComPtr m_spApi;在控制臺(tái)程序中可以編譯成功,
               但mfc中報(bào)錯(cuò),怎么回事?錯(cuò)誤信息:
            error C2143: syntax error : missing ';' before '<'
            error C2501: 'CComPtr' : missing storage-class or type specifiers
            error C2059: syntax error : '<'
            error C2238: unexpected token(s) preceding ';'
            原因:缺少文件,在vc6中是atlbase.h,可能在.net中是atlcomcli.h
            解決方法:在stdafx.h中加入#include
             注意:
                以上lib路徑要對(duì).


            5 調(diào)用CoInitializeEx(),編譯后顯示未定義
            CoInitializeEx()是利用COM組件時(shí)每個(gè)線程都要調(diào)用的函數(shù),
            使用這個(gè)函數(shù)需要有如下設(shè)置:
              在Project -> setting -> C/C++標(biāo)簽下的Preprocessor definitions中加入"_WIN32_WINNT=0x400"語(yǔ)句


            posted @ 2008-12-08 01:11 kenlistian 閱讀(2461) | 評(píng)論 (0)編輯 收藏

            2008年11月14日 #


            經(jīng)常在鏈接是報(bào)link2001錯(cuò)誤,其中一般是函數(shù)體沒(méi)有定義錯(cuò)誤。但是排除以上簡(jiǎn)單的外,還有一種如下無(wú)法解析系統(tǒng)文件中的函數(shù)定義。如下:

            LINK : warning LNK4098: 默認(rèn)庫(kù)“LIBCMT”與其他庫(kù)的使用沖突;請(qǐng)使用 /NODEFAULTLIB:library
            libcpmtd.lib(cerr.obj) : error LNK2001: 無(wú)法解析的外部符號(hào) __CrtDbgReportW
            libcpmtd.lib(stdthrow.obj) : error LNK2001: 無(wú)法解析的外部符號(hào) __CrtDbgReportW
            。。。。。。
            libcpmtd.lib(_tolower.obj) : error LNK2019: 無(wú)法解析的外部符號(hào) __calloc_dbg,該符號(hào)在函數(shù) __Getctype 中被引用

            像這種情況,可以推測(cè)運(yùn)用了錯(cuò)誤的運(yùn)行庫(kù)包。

            在一程序中使用不同類型的運(yùn)行時(shí)庫(kù)或使用調(diào)試和非調(diào)試版本的運(yùn)行時(shí)庫(kù),則將收到此鏈接錯(cuò)誤。

            例如,編譯一個(gè)文件以使用一種運(yùn)行時(shí)庫(kù),而編譯另一個(gè)文件以使用另一種運(yùn)行時(shí)庫(kù)(例如單線程運(yùn)行時(shí)庫(kù)對(duì)多線程運(yùn)行時(shí)庫(kù)),試圖鏈接它們,則將得到此警告。應(yīng)將所有源文件編譯為使用同一運(yùn)行時(shí)庫(kù)。

            其實(shí)就是調(diào)正編譯器選項(xiàng)參數(shù):/MD、/MT   和   /LD

            在vc8中,在“配置屬性-->C/C++-->代碼生成-->運(yùn)行時(shí)庫(kù)”中將“多線程(/MT)”統(tǒng)一改為“多線程調(diào)試(/MTd)”即可。



            posted @ 2008-11-14 01:26 kenlistian 閱讀(5670) | 評(píng)論 (2)編輯 收藏

            2008年11月5日 #

            前言:wxWidgets 是跨平臺(tái)的GUI庫(kù),用VC6會(huì)影響它的跨平臺(tái)性嗎?當(dāng)然不會(huì),我們只是用VC6充當(dāng)編譯器和編輯器,只要編寫代碼時(shí)注意不使用Windows相關(guān)的特性,寫出的代碼仍然是跨平臺(tái)的,仍然是可以在其它操作系統(tǒng)下(如Linux)使用其它C++編譯器(如GCC)編譯并運(yùn)行的。

            為什么用VC6,只不過(guò)此文專門針對(duì)VC6而已。

            創(chuàng)建項(xiàng)目

            點(diǎn)擊菜單:File -> New... 創(chuàng)建一個(gè)"Win32 Application" Project,項(xiàng)目名稱為"wxProject",點(diǎn)擊OK按鈕,

            在下一步的提示中選擇"An Empty Project",點(diǎn)擊Finish按鈕完成項(xiàng)目的創(chuàng)建。

            以下的設(shè)置和操作可能有一些繁瑣,但這是一勞永逸的事情。只要你完成了第一個(gè)空白工程,以后再需要?jiǎng)?chuàng)建工程時(shí)復(fù)制一份就可以了。

             

            設(shè)置項(xiàng)目屬性

            以下四個(gè)編譯配置并不要求都必須設(shè)置好,如果您不打算使用Unicode,那么不用設(shè)置"Win32 Unicode Debug"和"Win32 Unicode Release",如果您僅僅想調(diào)試程序而非發(fā)布,則只需設(shè)置相應(yīng)的"Debug"不用設(shè)置"Release"。最簡(jiǎn)單的情況下,只需設(shè)置"Win32 Debug"。

            還有一點(diǎn)要注意,您需要事先編譯出相應(yīng)版本的 wxWidgets 庫(kù)文件。如"Win32 Unicode Debug"需要 Unicode+Debug 版本的 wxWidgets 庫(kù)。(wxWidgets 各種版本庫(kù)均可通過(guò) \build\msw\wx.dsw 進(jìn)行編譯)。

            點(diǎn)擊菜單:Project -> Settings... 打開(kāi)項(xiàng)目屬性設(shè)置對(duì)話框。

            Win32 Debug:

            C/C++ General:

            Preprocessor definitions: WIN32,_DEBUG,__WXMSW__,__WXDEBUG__,_MBCS,_WINDOWS,NOPCH

            C/C++ Code Generation:

            Use run-time library: Debug Multithreaded DLL

            Link General:

            Object/library modules: wxmsw26d_xrc.lib wxmsw26d_html.lib wxmsw26d_adv.lib wxmsw26d_core.lib wxbase26d_xml.lib wxbase26d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

            Win32 Release:

            C/C++ General:

            Preprocessor definitions: WIN32,NDEBUG,__WXMSW__,_MBCS,_WINDOWS,NOPCH

            C/C++ Code Generation:

            Use run-time library: Multithreaded DLL

            Link General:

            Object/library modules: wxmsw26_xrc.lib wxmsw26_html.lib wxmsw26_adv.lib wxmsw26_core.lib wxbase26_xml.lib wxbase26.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

            進(jìn)行以下操作之前,請(qǐng)先通過(guò)菜單 Build -> Configurations... 增加兩個(gè)編譯配置"Win32 Unicode Debug"和"Win32 Unicode Release"(分別復(fù)制于"Win32 Debug"和"Win32 Release")。

            Win32 Unicode Debug:

            C/C++ General:

            Preprocessor definitions: WIN32,_DEBUG,__WXMSW__,__WXDEBUG__,_UNICODE,_WINDOWS,NOPCH

            C/C++ Code Generation:

            Use run-time library: Debug Multithreaded DLL

            Link General:

            Object/library modules: wxmsw26ud_xrc.lib wxmsw26ud_html.lib wxmsw26ud_adv.lib wxmsw26ud_core.lib wxbase26ud_xml.lib wxbase26ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

            Win32 Unicode Release:

            C/C++ General:

            Preprocessor definitions: WIN32,NDEBUG,__WXMSW__,_UNICODE,_WINDOWS,NOPCH

            C/C++ Code Generation:

            Use run-time library: Multithreaded DLL

            Link General:

            Object/library modules: wxmsw26u_xrc.lib wxmsw26u_html.lib wxmsw26u_adv.lib wxmsw26u_core.lib wxbase26u_xml.lib wxbase26u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

             

            設(shè)置wxWidgets目錄

            在前面的設(shè)置中,指定了wxWidgets的庫(kù)文件(*.lib),但VC可能并不知道到哪個(gè)目錄去尋找這些文件。同時(shí),我們的源代碼中也要包含 (include)wxWidgets的頭文件,其頭文件所在目錄也需要指定。另外,為了更好的調(diào)試wx程序,最好把wxWidgets的源代碼所在目錄 也設(shè)置好。

            點(diǎn)擊菜單 Tools -> Options...,進(jìn)入 Directories 頁(yè),分別加入以下路徑(下面的表示wxWidgets安裝目錄)

            Include files:

            \include

            \include\msvc

            Library files:

            \lib\vc_lib

            Source files:

            \src

            這一設(shè)置是針對(duì)VC全局的,以后再用VC創(chuàng)建wxWigets程序,就不用設(shè)置這些路徑了。

             

            創(chuàng)建wxWidgets預(yù)編譯頭文件

            各個(gè)編譯器不同,有的支持預(yù)編譯頭文件,有的不支持,支持預(yù)編譯頭文件的,使用的語(yǔ)法也有所不同,如果在每個(gè)源文件中都重復(fù)的寫未免不爽,還是集中到一個(gè)頭文件中來(lái)比較好。但是注意,有了此文件并不決定或限制你使用還是不使用預(yù)編譯頭文件,用不用以及怎么用還是在你。

            點(diǎn)擊菜單 File -> New...,新建一個(gè)C/C++頭文件 wx_pch.h,其內(nèi)容如下:

            #ifndef WX_PCH_H_INCLUDED
            #define WX_PCH_H_INCLUDED

            #if ( defined(USE_PCH) && !defined(WX_PRECOMP ) )
            #define WX_PRECOMP
            #endif // USE_PCH

            // basic wxWidgets headers
            #include <wx/wxprec.h>

            // for use xrc files
            #include <wx/xrc/xmlres.h>

            #ifdef __BORLANDC__
            #pragma hdrstop
            #endif

            #ifndef
            WX_PRECOMP
            #include <wx/wx.h>
            #endif

            #ifdef
            USE_PCH
            // put here all your rarely-changing header files

            #endif // USE_PCH

            #endif // WX_PCH_H_INCLUDED

            wxWidgets官方文檔是大概也是這樣推薦,Code::Blocks中基本上就是這樣子,我只是簡(jiǎn)單的增加了一行"#include "(為了使用XRC文件)。

            以后,工程中的源文件,只要包含(include) wx_pch.h 文件就可以了。

             

            創(chuàng)建wxApp子類

            點(diǎn)擊菜單 Insert -> New Class...,新建一個(gè)名稱為"App"的類(類名稱可以隨意),考慮到代碼的跨平臺(tái)性,建議將其所在文件的名稱修改為全部使用小寫字母(如 app.h/app.cpp)。此操作將生成文件 app.h 和 app.cpp。

            VC在這里生成的類代碼顯然是不滿足我們的要求的,需要進(jìn)行以下修改:

            app.h

            增加預(yù)編譯頭文件 wx_pch.h 的包含(以后創(chuàng)建的每個(gè).h文件都要包含它):#include "wx_pch.h"

            指定App類的父類為wxApp:即將"class App"修改為"class App : public wxApp"

            為類增加虛方法OnInit()的聲明:virtual bool OnInit();

            在類聲明的下方增加 wxWidgets App 聲明:DECLARE_APP(App)

            最終 app.h 的內(nèi)容如下(其中經(jīng)過(guò)手工改寫的地方已用黃色背景突出顯示):

            // by: liigo.com

            #if
            !defined( AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_)
            #define AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_

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

            #include "wx_pch.h"

            class App : public wxApp
            {
            public :
               
            App();
               
            virtual ~App();
               
            virtual bool OnInit ();
            };

            DECLARE_APP(App )

            #endif // !defined(AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_)

            app.cpp

            增加頭文件包含(此頭文件將在下面創(chuàng)建MainFrame類時(shí)創(chuàng)建):#include "mainframe.h"

            增加 OnInit() 方法的定義(其中用到的MainFrame類定義于mainframe.h,見(jiàn)后文):

            bool App::OnInit()
            {
                MainFrame* mainFrame = new MainFrame(NULL, _("MainFrame
            by liigo.com"));
               
            mainFrame->Show ();
               
            SetTopWindow(mainFrame);
               
            return true;
            }

            在類定義的上方增加 wxWidgets App 定義:IMPLEMENT_APP(App)

            最終 app.cpp 的內(nèi)容如下(其中經(jīng)過(guò)手工改寫的地方已用黃色背景突出顯示):

            #include "app.h"

            IMPLEMENT_APP (App)

            App:: App()
            {
            }

            App::~App()
            {
            }

            bool App::OnInit()
            {
               
            MainFrame* mainFrame = new MainFrame(NULL, _("MainFrame by liigo.com"));
               
            mainFrame-> Show();
               
            SetTopWindow(mainFrame);
               
            return true;
            }

             

            創(chuàng)建wxFrame子類

            點(diǎn)擊菜單 Insert -> New Class...,新建一個(gè)名稱為"MainFrame"的類(類名稱可以隨意),考慮到代碼的跨平臺(tái)性,建議將其所在文件的名稱修改為全部使用小寫字母 (如 mainframe.h/mainframe.cpp)。此操作將生成文件 mainframe.h 和 mainframe.cpp。

            下面對(duì)VC生成的類代碼進(jìn)行相應(yīng)的修改:

            mainframe.h

            增加預(yù)編譯頭文件的包含:#include "wx_pch.h"

            指定MainFrane類的父類為wxFrame:class MainFrame : public wxFrame

            修改構(gòu)造函數(shù)的聲明:MainFrame(wxWindow* parent, const wxString& title);

            在類定義的末尾增加事件表聲明:DECLARE_EVENT_TABLE()

            最終 mainframe.h 的內(nèi)容如下(其中經(jīng)過(guò)手工改寫的地方已用黃色背景突出顯示):

            #if !defined(AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_ )
            #define AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_

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

            #include "wx_pch.h"

            class MainFrame : public wxFrame
            {
            public:
               
            MainFrame( wxWindow* parent, const wxString & title);
               
            virtual ~MainFrame();

               
            DECLARE_EVENT_TABLE()
            };
            #endif // !defined(AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_)

            mainframe.cpp

            修改構(gòu)造函數(shù)的定義:

            MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)
            {
               
            //wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, _("some text"));
            }

            增加事件表定義(BEGIN_EVENT_TABLE 與 END_EVENT_TABLE 之間保留空白,留待以后綁定事件):

            BEGIN_EVENT_TABLE(MainFrame, wxFrame)

            END_EVENT_TABLE()

            最終 mainframe.cpp 的內(nèi)容如下(其中經(jīng)過(guò)手工改寫的地方已用黃色背景突出顯示):

            #include "mainframe.h"

            BEGIN_EVENT_TABLE (MainFrame, wxFrame)

            END_EVENT_TABLE()


            MainFrame::MainFrame( wxWindow* parent, const wxString& title) : wxFrame (parent, wxID_ANY, title)
            {
                //wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, _("some text"));
            }

            MainFrame ::~MainFrame()
            {
            }

             

            至此,一個(gè)wxWidget的空白Project已經(jīng)創(chuàng)建完畢


            編譯生成的 exe 文件的大小:

            可執(zhí)行文件大小 Debug Release
            Unicode 3.78M 956K
            非Unicode 3.60M 932K

            此數(shù)據(jù)全部是靜態(tài)鏈接wxWidgets的結(jié)果。動(dòng)態(tài)鏈接的話,EXE的大小沒(méi)有意義——?jiǎng)e忘了wxWidgets的版DLLs的大小總共約4到5M(Release版)。

             

            添加子控件

            向 wxFrame 或 wxDialog 中添加子控件是比較容易的,只需在其子類的構(gòu)造函數(shù)中 new 相應(yīng)的子控件就可以了。

            這是最簡(jiǎn)單的情況:

            MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)
            {
               
            wxTextCtrl * text = new wxTextCtrl( this, wxID_ANY, _("some text"));
            }

            沒(méi)錯(cuò),只要"new"一下就搞定了,控件會(huì)自動(dòng)出現(xiàn)在wxFrame中。這是運(yùn)行結(jié)果:


            如果界面再?gòu)?fù)雜一些,上面這種方法就行不通了,我們需要引入"Sizer"(詳見(jiàn)http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html(Sizer一覽)):

            MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)
            {
                wxTextCtrl * textCtrl = new wxTextCtrl( this, ID_TEXTCTRL, _T("some text"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );
               
            wxButton * button = new wxButton(this , ID_BUTTON, _("測(cè)試按鈕"), wxDefaultPosition , wxDefaultSize, 0 );

               
            wxBoxSizer* vBoxSizer = new wxBoxSizer(wxVERTICAL);
               
            this ->SetSizer(vBoxSizer);

               
            vBoxSizer->Add (textCtrl, 1, wxALL|wxEXPAND, 5);
               
            vBoxSizer->Add(button, 0 , wxALIGN_CENTER_HORIZONTAL|wxALL|wxALIGN_BOTTOM, 5);
            }

            上面是多行編輯框控件,下面是按鈕控件,當(dāng)窗口大小變化時(shí),編輯框控件將在水平和垂直方向上自動(dòng)擴(kuò)展,而按鈕始終位于窗口底部居中。


            上述代碼中涉及的控件ID(ID_TEXTCTRL,ID_BUTTON)是我們?cè)?mainframe.cpp 中自行定義的(定義控件ID的目的是為了下一步了事件處理):

            enum CtrlID
            {
               
            ID_TEXTCTRL, ID_BUTTON
            };

             

            參考文檔:http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html(Sizer一覽)

            采用XML格式文件(XRC文件)定義程序界面也是不錯(cuò)的方式,詳見(jiàn):http://www.wxwidgets.org/manuals/2.6.3/wx_xrcoverview.html(基于XML的資源系統(tǒng)一覽)。

            無(wú)論如何,手工進(jìn)行界面布局總是很繁雜,我們需要(可視化)工具的幫助:http://www.wxwidgets.org/apps2.htm

             

            處理事件

            在wxWidgets中處理事件,主要有兩個(gè)步驟:編寫"事件處理函數(shù)(方法)",填寫"事件表(EVENT_TABLE)"。

            事件處理函數(shù)(方法)視事件的不同而有所不同,但也有規(guī)律:沒(méi)有返回值,只有一個(gè)引用型參數(shù)(且一定是wxEvent的子類),不是虛方法(virtual method)。事件處理函數(shù)(方法)的名稱沒(méi)有特殊規(guī)定,可以自行命名。

            作為示例,我們來(lái)處理上圖中"測(cè)試按鈕"被按下的事件。

            根據(jù)wxWidgets文檔,要處理按鈕事件,需在自己的類中添加如下事件處理函數(shù)(方法):void MainFrame::OnButtonClick (wxCommandEvent &event)

            具體說(shuō)來(lái)就是,在 mainframe.h 文件中的 MainFrame 類中增加新的 OnButtonClick() 方法聲明:

            private:
               
            void OnButtonClick( wxCommandEvent& event);

            并在 mainframe.cpp 文件中增加 OnButtonClick() 方法的定義:

            void MainFrame::OnButtonClick( wxCommandEvent &event)
            {
               
            //取編輯框中的文本并用信息框顯示出來(lái)
                wxString text = ((wxTextCtrl*)this-> FindWindow(ID_TEXTCTRL))->GetValue();
               
            wxMessageBox (text);
            }

            下面需要在 mainframe.cpp 中填寫"事件表(EVENT_TABLE)",以便我們的"事件處理函數(shù)(方法)"能在適當(dāng)?shù)臅r(shí)機(jī)(即事件觸發(fā)時(shí))被調(diào)用:

            BEGIN_EVENT_TABLE(MainFrame, wxFrame )
               
            EVT_BUTTON(ID_BUTTON, MainFrame ::OnButtonClick)
            END_EVENT_TABLE()

            在這個(gè)事件表中,我們使用宏 EVT_BUTTON 指定了按鈕的ID,以及"事件處理函數(shù)(方法)"。

            注:上面一直講"事件處理函數(shù)(方法)",其實(shí)是"方法(method)"不是"函數(shù)(function)",只是"方法"這個(gè)詞在編程領(lǐng)域和在日 常生活中可以有不同的理解("方法"也可以理解為"方式"),我如果說(shuō)成"事件處理方法",難免會(huì)產(chǎn)生歧義。當(dāng)然,"事件處理函數(shù)(方法)"似乎也并不十 分合適,應(yīng)稱為"事件處理'方法'"或"事件處理方法(method)"?再深究下去就有咬文嚼字的嫌疑了,聰明的讀者早已明白我的意思了吧?



             

            如何處理其它事件?

            說(shuō)白了,關(guān)鍵要知道兩點(diǎn):事件處理函數(shù)(方法)的參數(shù)是什么類型,填寫參數(shù)表時(shí)用哪一個(gè)宏(EVT_*)。

            再補(bǔ)充一點(diǎn):要知道"什么控件"在"什么時(shí)機(jī)"會(huì)觸發(fā)"什么事件"。

            要知道這些,就需要對(duì)wxWidgets的事件處理有一個(gè)比較全面的了解。

            建議看一下wxWidgets官方文檔中的這篇文章:http://www.wxwidgets.org/manuals/2.6.3/wx_eventhandlingoverview.html(事件處理一覽)

            尤其是其中的 Event macros summary(事件宏概要)一段。

            電子書《Cross-Platform GUI Programming with wxWidgets》附錄9(Appendix I, 617頁(yè))中對(duì)事件處理時(shí)所涉及的事件類型(wxXXXEvent)和事件宏(EVT_*)有比較好的總結(jié),建議看一下,最好打印出來(lái)放在手邊,以便隨時(shí)參考。

            本文所涉及的完整源代碼可在此下載:http://liigo.diy.myrice.com/article/wxProject/wxProject.zip

             

            更進(jìn)一步

            了解 Sizer,熟悉界面設(shè)計(jì):http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html

            了解 事件處理:http://www.wxwidgets.org/manuals/2.6.3/wx_eventhandlingoverview.html

            了解 wxWidgets 提供了哪些控件,它們各自的屬性、方法、事件,以及它們的用法。

            去 wxWidgets.org 上找第三方的控件/庫(kù):http://www.wxwidgets.org/contrib2.htm#classes

            wxWiki 上找第三方的控件/庫(kù):http://www.wxwidgets.org/wiki/index.php/Table_Of_Contents#Pages_about_classes.2C_functions_or_macros

            GUI庫(kù)嘛?無(wú)非就是控件(component)的使用:布局、操作、事件處理。

            posted @ 2008-11-05 00:55 kenlistian 閱讀(1677) | 評(píng)論 (0)編輯 收藏

            2008年10月29日 #


            一個(gè)函數(shù)指針的理解:
             有一段程序存儲(chǔ)在起始地址為 0的一段內(nèi)存上,如果我們想要調(diào)用這段程序,請(qǐng)問(wèn)該如何去做?
             答案是 (*(void (*)( ) )0)( )。

             首先,最基本的函數(shù)聲明: void function (paramList);
             最基本的函數(shù)調(diào)用: function(paramList);

              鑒于問(wèn)題中的函數(shù)沒(méi)有參數(shù),函數(shù)調(diào)用可簡(jiǎn)化為 function();
               根據(jù)問(wèn)題描述,可以知道 0是這個(gè)函數(shù)的入口地址,也就是說(shuō),0是一個(gè)函數(shù)的指針。
               使用函數(shù)指針的函數(shù)聲明形式是:void (*pFunction)(),相應(yīng)的調(diào)用形式是: (*pFunction)(),
               則問(wèn)題中的函數(shù)調(diào)用可以寫作:(*0)( )。

              大家知道,函數(shù)指針變量不能是一個(gè)常數(shù),因此上式中的 0必須要被轉(zhuǎn)化為函數(shù)指針。

              我們先來(lái)研究一下,對(duì)于使用函數(shù)指針的函數(shù):
                    比如 void (*pFunction)( ),函數(shù)指針變量的原型是什么?
                這個(gè)問(wèn)題很簡(jiǎn)單,pFunction函數(shù)指針原型是( void (*)( ) ),即去掉變量名,
                清晰起見(jiàn),整個(gè)加上()號(hào)。

              所以將 0強(qiáng)制轉(zhuǎn)換為一個(gè)返回值為void,參數(shù)為空的函數(shù)指針如下:( void (*)( ) )。

               OK,結(jié)合2)和3)的分析,結(jié)果出來(lái)了,那就是:(*(void (*)( ) )0)( ) 。

              答案分析:從頭到尾理解答案

               (void (*)( )) ,是一個(gè)返回值為void,參數(shù)為空的函數(shù)指針原型。
               (void (*)( ))0,把0轉(zhuǎn)變成一個(gè)返回值為void,參數(shù)為空的函數(shù)指針,指針指向的地址為0.
               *(void (*)( ))0,前面加上*表示整個(gè)是一個(gè)返回值為void的函數(shù)的名字
               (*(void (*)( ))0)( ),這當(dāng)然就是一個(gè)函數(shù)了。

              我們可以使用 typedef清晰聲明如下:

               typedef void (*pFun)();

            這樣定義之后,pFun就是一個(gè)返回類型為void無(wú)參數(shù)的函數(shù)指針變量了。

              這樣函數(shù)變?yōu)?(*(pFun)0)();


            ----
            在調(diào)用動(dòng)態(tài)庫(kù)時(shí),習(xí)慣用typedef重新定義動(dòng)態(tài)庫(kù)函數(shù)中的函數(shù)地址(函數(shù)指針),
            如在動(dòng)態(tài)庫(kù)(test.dll)中有如下函數(shù):
            int DoCase(int, long);

            則,在調(diào)用動(dòng)態(tài)庫(kù)是有兩種方法:
            1. 先聲明一個(gè)與動(dòng)態(tài)庫(kù)中類型一致的指針函數(shù)變量:
            int (*DOCASE)(int ,long);  //用于指向動(dòng)態(tài)庫(kù)中的DoCase函數(shù)地址
            HINSTANCE gLibMyDLL = NULL;
            gLibMyDLL 
            = LoadLibrary("test.dll");
            if(gLibMyDLL != NULL)
            {
              DOCASE 
            = (int(*)(int,long))GetProcAddress(gLibMyDLL, "DoCase");
            }
            int s = DOCASE(1,1000);

            2.用typedef定義一個(gè)指針函數(shù):
            typedef (
            *DOCASE)(int ,long);

            HINSTANCE gLibMyDLL 
            = NULL;
            DOCASE _docase;
            gLibMyDLL 
            = LoadLibrary("test.dll");
            if(gLibMyDLL != NULL)
            {
              _docase 
            = (DOCASE)GetProcAddress(gLibMyDll, "DoCase");
            }

            int s=_docase(1,1000);


            ----------------
            在C++類中使用函數(shù)指針。
            //typedef 返回類型(類名::*新類型)(參數(shù)表)
            class CA
            {
            public:
              
            char lcFun(int a){ return; }
            };

            CA ca;
            typedef 
            char (CA::*PTRFUN)(int);
            PTRFUN pFun;
            void main()
            {
              pFun 
            = CA::lcFun;
              ca.(
            *pFun)(2);
            }


            指針的定義與使用都加上了“類限制”或“對(duì)象”,用來(lái)指明指針指向的函數(shù)是哪個(gè)類的,
            這里的類對(duì)象也可以是使用new得到的。
            如:
            CA *pca = new CA;
            pca->(*pFun)(2);
            delete pca;
            而且這個(gè)類對(duì)象指針可以是類內(nèi)部成員變量,你甚至可以使用this指針。
            如:類CA有成員變量PTRFUN m_pfun;
            void CA::lcFun2()
            {
               (this->*m_pFun)(2);
            }
            一句話,使用類成員函數(shù)指針必須有“->*”或“.*”的調(diào)用。


            --------------------

            void test(void* );
            void tt()
            {
               printf(
            "kao,沒(méi)玩過(guò)這種\n");
            }

            int main(int argc, char* argv[])
            {
                typedef 
            void(*Fun)();
                Fun mytest;
                mytest 
            = tt;
                test((
            void*)mytest);
                
            return 0;
            }

            void test(void * p)
            {
                (
            *(void(*)())p)();
            }


            posted @ 2008-10-29 22:23 kenlistian 閱讀(808) | 評(píng)論 (1)編輯 收藏

            2008年10月27日 #



            關(guān)于偏移量的宏定義

            #define offsetof(s,m)   (size_t)&(((s *)0)->m)

            s是一個(gè)結(jié)構(gòu)名,它有一個(gè)名為m的成員(s和m 是宏offsetof的形參,
            它實(shí)際是返回結(jié)構(gòu)s的成員m的偏移地址


            (s *)0 是騙編譯器說(shuō)有一個(gè)指向類(或結(jié)構(gòu))s的指針,其地址值0 

            &((s *)0)->m   是要取得類s中成員變量m的地址 
                      因基址為0,這時(shí)m的地址當(dāng)然就是m在s中的偏移

            最后轉(zhuǎn)換size_t 型,即unsigned int。

            有例子如: 
              struct   AAA 
              { 
                int   i; 
                int   j; 
              }; 
              
              struct   AAA   *pAAA; 
              pAAA = new   AAA; 
              這時(shí),pAAA實(shí)際上是一個(gè)Pointer, 指向某一確定的內(nèi)存地址,
                 如0x1234; 
              而pAAA->i 整體是一個(gè)int型變量,其地址是&(pAAA->i),
              '&'為取址運(yùn)算符; 
              那么&(pAAA->i)一定等于0x1234,因 i 是結(jié)構(gòu)體AAA的第一個(gè)元素。 
              而&(pAAA->j)一定是0x1234 + 0x4 = 0x1238; 因?yàn)閟izeof(int) = 4; 
              
              這個(gè)做法的巧妙之處就是:它把“0”作為上例中的pAAA,那么&(pAAA->j)就是j的 
              offset啦, 

              解析結(jié)果是: 
              (s*)0,將 0 強(qiáng)制轉(zhuǎn)換為Pointer to "s"   
              可以記 pS = (s*)0,pS是指向s的指針,它的值是0; 
              那么pS->m就是m這個(gè)元素了,而&(pS->m)就是m的地址,
              就是offset啦    




            posted @ 2008-10-27 17:16 kenlistian 閱讀(691) | 評(píng)論 (0)編輯 收藏

            2008年7月17日 #

            ado在dll中使用如下的頭文件

            #pragma warning(disable:4146)
            #import "C:\Program Files\Common Files\System\ADO\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
            #pragma warning(default:4146)

            using namespace ADODB;

            "Provider=SQLOLEDB,Data Source=serverName;Initial Catalog=databaseName;User ID=userName;Password=userPassword;"

            #import "c:\program files\common files\system\ado\msado15.dll" no_namespaces rename("EOF","adoEOF")


            http://www.pconline.com.cn/pcedu/empolder/gj/vc/0507/653859.html
            posted @ 2008-07-17 10:21 kenlistian 閱讀(726) | 評(píng)論 (0)編輯 收藏

            2008年7月15日 #


            vc6 下的msdn版本:最后一個(gè)支持VS6的版本是2001年10月版;


            DirectX SDK:DirectX 9.0 Summer 2004 SDK Update,

            下載頁(yè)面:http://www.microsoft.com/downloads/details.aspx?FamilyID=fd044a42-9912-42a3-9a9e-d857199f888e&DisplayLang=en
            這個(gè)版本的Direct 9 SDK是不完全支持VC6的,

            注意下載頁(yè)面的Overview:
            This download contains all the extra files that are not included in the DirectX 9.0 SDK Summer Update 2004 release such as a d3dx.lib which supports VC 6.0, older plug-ins and Japanese version of the documentation.
            即DirectX SDK Summer Update 2004里面附帶的d3dx.lib是不支持VC6的,支持VC6的版本在Extras包里面。

            還須下DirectX 9.0 Summer 2004 SDK Update Extras(http://www.microsoft.com/downloads/details.aspx?FamilyID=736585e1-10f0-4e85-b940-828cba9971f1&DisplayLang=en),

            另如編譯時(shí)出現(xiàn)了DWORD_PTR或者其他什么類型未定義之類的錯(cuò)誤,是因?yàn)槲④洶袯ASETSD.H從DirectX SDK發(fā)行包里拿掉了,這個(gè)文件在Platform SDK里有,在VC的Include路徑中把Platform SDK的include路徑提到最前面就可以了。
            Platform SDK,最后一個(gè)支持VC6的Platform SDK是February 2003 Edition,
            下載:http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm


            ****************************************************
            因?yàn)楝F(xiàn)在從微軟下載需要驗(yàn)證

            可以下DirectX 9 SDK 精簡(jiǎn)版,7M大,這里DirectX 9 SDK 精簡(jiǎn)版的下載地址:
            http://www.gameres.com/Resource/dx9sdk.zip

            從MSDN6里面提取出來(lái)的DirectShow頭文件:
            http://www.shenglu.com/UserFiles/File/files/DirectShow_Include_files_from_msdn6cd1.zip



            感謝上面提供者。
            posted @ 2008-07-15 19:28 kenlistian 閱讀(1380) | 評(píng)論 (0)編輯 收藏

            2008年7月14日 #


             采用bind1st和bind2nd的意思,就是把參數(shù)綁定在第一位還是第二位。
            繼承于binary_function 類.
            描述如下
            Class binder1st binds the value to the first argument of the binary function, and binder2nd does the same thing
            for the second argument of the function.

            如下:例子

            struct compare_str :binary_function<ST_DataResult*, char*, bool>
            {
            public:
                bool operator()(ST_DataResult* pDataRet, char* szTypeCode) const
                {
                    return strcmp(pDataRet->sType , szTypeCode) == 0 ? true : false;
                   
                }
            };


            。。。

            char szTypeCode[4] = {'\0'};
            strcpy(szTypeCode, sTypeCode);
            pIt = find_if(m_d_ret_data.begin(), m_d_ret_data.end(),
                    bind2nd(compare_str(), szTypeCode));

            其中把szTypeCode變量傳入到compare_str所定義的第二個(gè)參數(shù)位置傳入。
            如果寫成bind1nd, 則是把szTypeCode作為第一個(gè)參數(shù)傳入,那么會(huì)報(bào)錯(cuò)。
            因?yàn)轭愋筒粚?duì)。


            posted @ 2008-07-14 16:56 kenlistian 閱讀(882) | 評(píng)論 (0)編輯 收藏

            2008年6月3日 #

            wxWidgets框架例子。
            直接粘貼到vc中,運(yùn)行即可。當(dāng)然需要安裝好wxwidgets及其配置好lib和include設(shè)置。

            其解釋部分非常詳細(xì),基本上wxwidgets和mfc差不了多少,但是擴(kuò)充性和跨平臺(tái)性是大于mfc,同時(shí)
            也對(duì)熟悉ruby下的wxruby和wxpython是非常快速的。

            #include "wx/wx.h"

            // 定義應(yīng)用程序類
            class MyApp : public wxApp
            {
            public:
                virtual bool OnInit();
            };

            // 定義主窗口類
            class MyFrame : public wxFrame
            {
            public:
                // 主窗口類的構(gòu)造函數(shù)
                MyFrame(const wxString& title);

                // 事件處理函數(shù)
                void OnQuit(wxCommandEvent& event);
                void OnAbout(wxCommandEvent& event);

            private:
                // 聲明事件表
                DECLARE_EVENT_TABLE()
            };

            // 有了這一行就可以使用 MyApp& wxGetApp()了
            DECLARE_APP(MyApp)

            // 告訴wxWidgets主應(yīng)用程序是哪個(gè)類
            IMPLEMENT_APP(MyApp)

            bool MyApp::OnInit()
            {
                // 創(chuàng)建主窗口
                MyFrame *frame = new MyFrame(wxT("Minimal wxWidgets App"));

                // 顯示主窗口
                frame->Show(true);
                return true;
            }

            // MyFrame類的事件表
            BEGIN_EVENT_TABLE(MyFrame, wxFrame)
                EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
                EVT_MENU(wxID_EXIT,  MyFrame::OnQuit)
            END_EVENT_TABLE()

            void MyFrame::OnAbout(wxCommandEvent& event)
            {
                wxString msg;
                msg.Printf(wxT("Hello and welcome to %s"),
                           wxVERSION_STRING);

                wxMessageBox(msg, wxT("About Minimal"),
                             wxOK | wxICON_INFORMATION, this);
            }

            void MyFrame::OnQuit(wxCommandEvent& event)
            {
                Close();
            }

            MyFrame::MyFrame(const wxString& title)
                   : wxFrame(NULL, wxID_ANY, title)
            {
                // 設(shè)置窗口圖標(biāo)
                //SetIcon(wxIcon(mondrian_xpm));

                // 創(chuàng)建菜單條
                wxMenu *fileMenu = new wxMenu;

                // 添加“關(guān)于”菜單項(xiàng)
                wxMenu *helpMenu = new wxMenu;
                helpMenu->Append(wxID_ABOUT, wxT("&About...\tF1"),
                                 wxT("Show about dialog"));

                fileMenu->Append(wxID_EXIT, wxT("E&xit\tAlt-X"),
                                 wxT("Quit this program"));

                // 將菜單項(xiàng)添加到菜單條中
                wxMenuBar *menuBar = new wxMenuBar();
                menuBar->Append(fileMenu, wxT("&File"));
                menuBar->Append(helpMenu, wxT("&Help"));

                // ...然后將菜單條放置在主窗口上
                SetMenuBar(menuBar);

                // 創(chuàng)建一個(gè)狀態(tài)條來(lái)讓一切更有趣些。
                CreateStatusBar(2);
                SetStatusText(wxT("歡迎使用wxWidgets!"));
            }



            注意的是:
            1.必須是windows框架而不是control程序框架
            2.通過(guò)向?qū)傻膕tdafx.h 可以通過(guò)project/set/ "c/c++"中選擇無(wú)編譯頭來(lái)去掉
            stdafx.h
            3.直接拷貝這些lib到工程設(shè)置
            wxmsw28d_aui.lib wxmsw28d_html.lib wxmsw28d_xrc.lib wxmsw28d_adv.lib wxmsw28d_core.lib wxbase28d_xml.lib wxbase28d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib
            kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib
            4.附加該lib的位置,或者把以上lib放置到vc6可以找到地方.
            5.如果報(bào)有LIBCD.lib重復(fù)定義,填到ignore lib中
            6.以上都是在vc6環(huán)境測(cè)試下通過(guò)。


            posted @ 2008-06-03 22:54 kenlistian 閱讀(3115) | 評(píng)論 (1)編輯 收藏

             
            目前是wxWidgets-2.8.7,
            下載http://sourceforge.net/project/showfiles.php?group_id=9863

            其中在windows下,則選擇其中msw的壓縮下載。
            按照install文檔要求,編譯wxWdigets中的builder目錄中的wx。bsp。
            由于該wxwdgets支持多種編譯器,選擇相關(guān)的工程文件加載。

            傳統(tǒng)采用vc6.

            生成的編譯文件在lib目錄中。如果采用vc則目錄為vc_lib.

            生成的lib文件有(release or debug):
             wxbase28.lib              wxbase28d.lib
            wxbase28_net.lib          wxbase28d_net.lib
            wxbase28_xml.lib          wxbase28d_xml.lib
            wxmsw28_core.lib          wxmsw28d_core.lib
            wxmsw28_html.lib          wxmsw28d_html.lib
            wxmsw28_adv.lib           wxmsw28d_adv.lib
            在vc中設(shè)置lib,可以把該目錄加入,也可以直接把生成的文件丟在vc的lib目錄。
            關(guān)于unicode模式編輯如上。
            在include設(shè)置中,需要加入wxWidgets目錄中的include目錄。
            運(yùn)行demo程序測(cè)試.

            注意:在include也需要把setup.h頭文件加入。該頭也根據(jù)生成的lib目錄中調(diào)用。
            不過(guò)最好加在預(yù)處理的include directory目錄編輯框中。
             
              
             
             
            posted @ 2008-06-03 18:13 kenlistian 閱讀(922) | 評(píng)論 (1)編輯 收藏

            2008年5月8日 #


            這個(gè)一定要改,磨刀不誤砍柴人,老話了,看來(lái)就是拿科學(xué)道理來(lái)講,
            道理就是有道理。


            早在75年之前,大多數(shù)行業(yè)就已經(jīng)放棄了加班趕進(jìn)度。數(shù)不清的行業(yè)經(jīng)驗(yàn)和研究事實(shí)證明:要想完成工作,加班趕工是成本最高的做法。

            文/Evan Robinson  譯/喬梁(《程序員》2008年3月刊)

            緣起

            2004 年,某國(guó)際電子游戲公司員工的家人以Ea_spouse為名,在某網(wǎng)站上發(fā)布文章,講述了其配偶由于高強(qiáng)度、長(zhǎng)時(shí)間的加班,對(duì)自己的身體健康以及家庭生活造成了很不好的影響。一石激起千層浪,關(guān)于電子游戲開(kāi)發(fā)行業(yè)人士生活質(zhì)量的話題再次引起了大家的熱烈討論。Ea_spouse收到了上千個(gè)回復(fù),此事也很快被主要媒體跟蹤報(bào)導(dǎo)。通過(guò)網(wǎng)絡(luò),上千人參與了這個(gè)自發(fā)的大規(guī)模討論,內(nèi)容涉及強(qiáng)制加班、工作效率、怠惰、工會(huì)、訴訟以及對(duì)很多公司的控訴等議題。

            我已經(jīng)做了二十多年軟件項(xiàng)目的開(kāi)發(fā)與管理工作。過(guò)去的每一年,做過(guò)的每一個(gè)項(xiàng)目,都讓我更堅(jiān)信:加班趕進(jìn)度——“趕工”——是一種高成本、低產(chǎn)出且極具破壞性的工作方式。“工作持續(xù)時(shí)間越長(zhǎng),工作效率越低”已是常識(shí)了。然而隨著時(shí)間的推移,我已注意到:由于過(guò)多額外加班導(dǎo)致工作效率下降的速度,要超過(guò)大多數(shù)軟件行業(yè)管理者的認(rèn)知。隨著調(diào)查深入,我驚訝地發(fā)現(xiàn)我并不是第一個(gè)認(rèn)識(shí)到這一點(diǎn)的人:近一個(gè)世紀(jì)以來(lái),傳統(tǒng)工業(yè)的工程師們對(duì)于我所觀察到的問(wèn)題早已達(dá)成共識(shí)。

            歷史

            1908 年,工業(yè)效率研究先驅(qū) Ernst Abbe公布了他的研究結(jié)論,即每天的工作時(shí)間從九小時(shí)減少到八小時(shí),其日產(chǎn)出量會(huì)增加。而他也不是第一個(gè)注意到這件事的人。早在1893年,William Mather在Salford鋼鐵公司(Salford Iron Works)就已經(jīng)采納了每天八小時(shí)的工作制。在1909年,Sidney J. Chapman發(fā)表了《工作實(shí)踐》(Hours of Labour)。他在文中描述了每天的工作小時(shí)數(shù)與工人的生產(chǎn)力之間的變化。下文會(huì)對(duì)其結(jié)論進(jìn)行詳細(xì)闡述。亨利?福特在1926年大張旗鼓地采納每周40小時(shí)工作制,至少已經(jīng)進(jìn)行了十二年的試驗(yàn)讓他確信:將每日工作10小時(shí)調(diào)整為8小時(shí),并把每周工作六天改為五天,實(shí)際上會(huì)增加總產(chǎn)出并降低生產(chǎn)成本。福特大肆宣揚(yáng)縮短工作時(shí)間的社會(huì)效益,并強(qiáng)調(diào)增加消費(fèi)時(shí)間對(duì)每個(gè)人都有好處。但其核心觀點(diǎn)仍是:減少當(dāng)班時(shí)間可以帶來(lái)更多產(chǎn)出。

            我發(fā)現(xiàn)很多組織(如商業(yè)、大學(xué)、工業(yè)協(xié)會(huì)和軍事單位等)進(jìn)行的研究結(jié)果都持有同樣的基本觀點(diǎn),即對(duì)于大多數(shù)人來(lái)說(shuō),“每天工作八小時(shí)、每周工作五天”是保證高產(chǎn)出和低身心消耗之間的最佳平衡點(diǎn)。在十九世紀(jì)的三十年代至五十年代,類似研究進(jìn)行過(guò)上百次;到了六十年代,40小時(shí)工作制所帶來(lái)的好處在美國(guó)企業(yè)界已被無(wú)可爭(zhēng)辯地接受了。1962年,商業(yè)協(xié)會(huì)(Chamber of Commerce)甚至專門發(fā)行了一個(gè)小宣傳手冊(cè),來(lái)稱贊減少工作時(shí)間所帶來(lái)的生產(chǎn)率提高。

            然而,不知何故,硅谷卻把其拋在了腦后。Ea_spouse寫到:

            “現(xiàn)在的強(qiáng)制工作時(shí)間是從早上九點(diǎn)到晚上十點(diǎn),且每周工作七天,基于員工的良好表現(xiàn),星期六的下班時(shí)間偶爾會(huì)提前到晚上六點(diǎn)半。平均算下來(lái),這相當(dāng)于每周工作85小時(shí)。”

            實(shí)際上,一周有六天是從早上九點(diǎn)工作到晚上十點(diǎn),還有一天是從早上九點(diǎn)到晚上六點(diǎn)半,算下來(lái)是每周工作87.5小時(shí)(6×13+9.5=87.5)。可是在工作這么長(zhǎng)時(shí)間以后,誰(shuí)還會(huì)算得那么詳細(xì)呢?

            在這一方面,電子藝界與其它高科技公司沒(méi)什么不同。請(qǐng)希望提高員工生產(chǎn)力且同時(shí)讓他們保持頭腦清醒的人一起來(lái)看一下:管理者在工作時(shí)間、產(chǎn)出、效率以及生產(chǎn)成本這幾個(gè)方面做出的假設(shè),以及一個(gè)世紀(jì)的產(chǎn)業(yè)研究如何證明這些假設(shè)是完全錯(cuò)誤的。

            管理者想要什么?

            當(dāng)管理層將員工送上“死亡之旅”時(shí),他們究竟想要得到什么呢?我們真的相信EA的CEO想看到員工撅著屁股在辦公室里連續(xù)工作7×24小時(shí)嗎?

            管理者想從雇員那里得到最大的產(chǎn)出,是用最少的成本投入生產(chǎn)出最好的產(chǎn)品。不到萬(wàn)不得已,他們不希望為了完成產(chǎn)品出錢雇傭額外的人力資源,從而增加成本。表面看來(lái),要想達(dá)到這兩個(gè)目的,“趕工”好象是最顯而易見(jiàn)且合理的方法了。

            假設(shè)產(chǎn)出是利用計(jì)件方式來(lái)衡量的話,沒(méi)讀過(guò)上述研究報(bào)告的管理者可能會(huì)推斷:假如一個(gè)人在八小時(shí)內(nèi)生產(chǎn)16件產(chǎn)品,那么,他在九個(gè)小時(shí)內(nèi)應(yīng)該能生產(chǎn)出18件產(chǎn)品,在十個(gè)小時(shí)內(nèi)可能生產(chǎn)20件。我們可以用下面這個(gè)簡(jiǎn)單的公式來(lái)表示這種觀點(diǎn):

            O = X/Y * t

            其中,O是總產(chǎn)出,X是基準(zhǔn)時(shí)間Y(以小時(shí)為計(jì)量單位)內(nèi)的產(chǎn)出,而t是實(shí)際的工作小時(shí)數(shù)。在這種假設(shè)前提下,增加時(shí)間t是提高產(chǎn)出O的最簡(jiǎn)方法。在個(gè)別情況下,這種假設(shè)是有效的,例如只在很短的一段時(shí)間里延長(zhǎng)工作小時(shí)數(shù)以便能在最后期限時(shí)交付。但是,其它行業(yè)的長(zhǎng)期試驗(yàn)和研究表明:加班沖刺的極限持續(xù)時(shí)間比大家想象的要短;而且當(dāng)達(dá)到極限以后,這種沖剌也會(huì)陷入困境。

            小時(shí)生產(chǎn)率很重要

            對(duì)于如何看待“工人的產(chǎn)出”,更現(xiàn)實(shí)的方法是考慮小時(shí)生產(chǎn)率會(huì)隨著工作時(shí)間的長(zhǎng)短發(fā)生變化。這種變化有兩種主要來(lái)源:在一天中最后幾個(gè)小時(shí)里,產(chǎn)生在腦力和體力上的明顯疲勞狀況;隨著已經(jīng)延長(zhǎng)了工作時(shí)間的工作日不斷增加,腦力和體力的疲勞也不斷積聚。

            下面的等式表達(dá)了這種更復(fù)雜一點(diǎn)兒的情況:

            O = P(t1 , t2 , t3 , … , tn )

            其中,O代表總產(chǎn)出,P( )表示小時(shí)生產(chǎn)率隨時(shí)間(t1-tn)的變化。在這個(gè)等式中,P( )是一個(gè)函數(shù),不是一個(gè)常量。P( )根據(jù)工人的不同而變化,因?yàn)槟承┤松a(chǎn)力要高于其他人。P( )也隨時(shí)間變化,因?yàn)槿瞬皇菣C(jī)器,在第14個(gè)小時(shí)完成的工作并不完全等于在第1個(gè)小時(shí)完成的工作。另外,P ( )也會(huì)隨工人最近的狀態(tài)而變化,例如,開(kāi)夜車后的早上與睡了一個(gè)好覺(jué)后的早上,其工作效率也不可能是完全一樣的。

            在1909年Sidney J. Chapman發(fā)表的《工作時(shí)間》一文中展示了下面這張示意圖:



            其中,曲線P表示“固定數(shù)量的勞動(dòng)力(根據(jù)每個(gè)工作日的小時(shí)數(shù))產(chǎn)出邊際價(jià)值的長(zhǎng)期變化值”。OX軸表示一天內(nèi)的工作時(shí)間,OY軸表示產(chǎn)出的價(jià)值。在OX軸的n點(diǎn)上,也就是說(shuō)如果每天工作n個(gè)小時(shí),其總產(chǎn)值是圖形Onda的面積(詳情請(qǐng)參見(jiàn) http: //www.worklessparty.org/timework/chapman.htm)。可以發(fā)現(xiàn):曲線P的高度就代表了工人的工作效率(每天在給定的工作小時(shí)數(shù)下,每個(gè)單位時(shí)間的產(chǎn)出)。

            聰明的讀者已經(jīng)注意到在點(diǎn)b,工作更多小時(shí)不會(huì)創(chuàng)造更多的價(jià)值。而且在b點(diǎn)以后,每多工作一個(gè)小時(shí),產(chǎn)出反而是負(fù)值。怎么會(huì)這樣呢?

            Chapman 的工作曲線圖假設(shè)給定工作小時(shí)數(shù)的工作日會(huì)持續(xù)一定的時(shí)間。因此,它將每天的疲勞狀況和長(zhǎng)期的疲勞累計(jì)統(tǒng)一在一個(gè)模型之內(nèi)體現(xiàn)。首先,小時(shí)產(chǎn)出率的下降反映了在接近一天工作結(jié)束時(shí),疲勞效應(yīng)對(duì)工作質(zhì)量和數(shù)量上的影響。但是到后來(lái),每天的疲勞是由累積疲勞復(fù)合組成的。也就是說(shuō),在頭一天加班時(shí)間導(dǎo)致疲勞產(chǎn)生的生產(chǎn)力下降,會(huì)對(duì)此后工作日的小時(shí)生產(chǎn)率下降產(chǎn)生疊加效應(yīng)。

            即便是在一天之內(nèi),當(dāng)精疲力竭的雇員再也無(wú)法投入工作時(shí),產(chǎn)出就會(huì)停滯不前。如果某個(gè)已經(jīng)變得麻木的雇員犯了災(zāi)難性的錯(cuò)誤,破壞了前面已經(jīng)完成的工作和投入資本,那產(chǎn)出就會(huì)變成負(fù)數(shù)。

            就工廠而言,一個(gè)工人的生產(chǎn)率會(huì)隨時(shí)間下降。某工人在一個(gè)班次開(kāi)始,可能每小時(shí)生產(chǎn)10個(gè)工件,而在接近下班時(shí),可能每小時(shí)只能生產(chǎn)6個(gè),在這其間,有幾個(gè)小時(shí)可能會(huì)達(dá)到峰值12個(gè)。再往后,其工作會(huì)變得更慢,而且會(huì)犯更多錯(cuò)誤。這種減速和錯(cuò)誤最終會(huì)使生產(chǎn)力成為零,即花了很長(zhǎng)時(shí)間才生產(chǎn)一個(gè)工件,而到了最后一個(gè)總會(huì)有點(diǎn)毛病。流水線的管理者很久之前就發(fā)現(xiàn):當(dāng)達(dá)到這種疲勞程度時(shí),會(huì)帶來(lái)較大的失誤,并導(dǎo)致更大的成本損失,如昂貴的機(jī)器受損,存貨被毀,或者工人嚴(yán)重受傷等。

            作為腦力工作者,得到充分休息后,程序員會(huì)生產(chǎn)更多高質(zhì)量的代碼,而且bug也會(huì)比較少。在開(kāi)始工作的第一個(gè)小時(shí),程序員會(huì)逐漸進(jìn)入狀態(tài),接下來(lái)的 幾個(gè)小時(shí)是最佳狀態(tài)。在此之后,我們會(huì)感到疲勞,小時(shí)生產(chǎn)率也就下降;我們會(huì)花很長(zhǎng)時(shí)間才能修復(fù)一個(gè)簡(jiǎn)單的bug,或增加一個(gè)簡(jiǎn)單的特性。而這樣的事情如果拿到前幾個(gè)小時(shí)做,可能也就是幾分鐘的活兒。再惡劣一些的狀況—似乎很多電子娛樂(lè)行業(yè)的公司大部分時(shí)間都在這種極端狀況下工作—一個(gè)過(guò)度疲倦的IT技術(shù)人員可能會(huì)刪除很有價(jià)值的文件,從而要花費(fèi)額外的工作去恢復(fù)備份;他也可能在回家的路上遇到交通事故,結(jié)果在幾個(gè)月內(nèi)都無(wú)法上班。

            這就是第一課:在一個(gè)工作日中,生產(chǎn)率隨時(shí)間h變化。在前四至六個(gè)小時(shí)里,生產(chǎn)效率最高。隨著時(shí)間的流逝,生產(chǎn)率會(huì)降為0,甚至?xí)兂韶?fù)數(shù)。

            平衡點(diǎn)在哪里?

            如果在一個(gè)工作日內(nèi),生產(chǎn)率本來(lái)就會(huì)隨時(shí)間下降,而且長(zhǎng)時(shí)間工作會(huì)導(dǎo)致低生產(chǎn)率,那么我們?nèi)绾握业揭环N方法來(lái)達(dá)到最大產(chǎn)出呢,平衡點(diǎn)又在哪里?

            不幸的是,希望量化腦力勞動(dòng)者的產(chǎn)出是相當(dāng)困難的。我也希望能給出一個(gè)簡(jiǎn)單的公式,只要代入幾個(gè)數(shù)字,就可以計(jì)算得出每個(gè)人達(dá)到最大產(chǎn)出所需工作小時(shí)數(shù)。但是我不能,因?yàn)榧词勾嬖谶@樣的公式,也不能就找到并代入哪些基本數(shù)據(jù)達(dá)成一致。常見(jiàn)軟件度量方式,如代碼行法或功能點(diǎn)法,要么只是簡(jiǎn)單收集數(shù)字,無(wú)法讓人信服其價(jià)值,要么就是數(shù)據(jù)很難定義和收集。有用的度量數(shù)據(jù),如造成bug數(shù)目和修復(fù)bug數(shù)目等,其可信度也不高,并有可能會(huì)被不公平地用于年度考評(píng)上,也可能被聰明的程序員利用來(lái)算計(jì)年終考評(píng)或績(jī)效獎(jiǎng)金。架構(gòu)師的產(chǎn)出可以很容易地用某些數(shù)據(jù)(如模型或架構(gòu)圖的數(shù)量)來(lái)衡量,但同樣很難用另外一些數(shù)據(jù)(如主觀質(zhì)量、觀感,及模型復(fù)雜度)衡量。如果用發(fā)現(xiàn)的獨(dú)特bug數(shù)來(lái)衡量測(cè)試人員的產(chǎn)出很容易,但用代碼覆蓋率來(lái)衡量就有點(diǎn)兒難了,假如用發(fā)現(xiàn)缺陷的總百分比來(lái)衡量就難上加難。

            總而言之,對(duì)于團(tuán)隊(duì)的產(chǎn)出,大多數(shù)公司好象陷入了一種“最小公分母式”的度量。要么用游戲出廠數(shù)量和銷售量,要么就不用。雖然這些的確是大多數(shù)股東所在意的度量數(shù)據(jù),但對(duì)于生產(chǎn)率的度量完全沒(méi)用,尤其是每日或每小時(shí)的生產(chǎn)效率。

             第二課:對(duì)于腦力勞動(dòng)者,生產(chǎn)效率很難量化。

            所以,我們只好同其它行業(yè)中進(jìn)行類比:以下內(nèi)容來(lái)自Work Less Institute of Technology名為《IT行業(yè)中的精神物理學(xué)》的一篇文章,是對(duì)Ea_spouse的回復(fù):

            “這是一個(gè)多世紀(jì)前,Dr. Ernst Abbe 對(duì)德國(guó)耶拿的蔡司光學(xué)工廠(Zeiss Optical Works)工作時(shí)間與產(chǎn)出的調(diào)察。 Dr. Abbe是工廠的董事,他將工作時(shí)間從每日9小時(shí)減少到8小時(shí),并詳細(xì)記錄了調(diào)整前后每個(gè)工人的每日產(chǎn)出,其結(jié)果與十九世紀(jì)的其他研究一致,即適當(dāng)減少工作時(shí)間確實(shí)提高了總產(chǎn)出。”

            Tom Walker指出:

            “產(chǎn)出的增減與工作時(shí)間的長(zhǎng)短不成正比”好象是每一代人的必修課。1848年英國(guó)議會(huì)通過(guò)10小時(shí)工作法案,結(jié)果每個(gè)人每天的總產(chǎn)出增加了。到十九世紀(jì)九十年代,雇主又廣泛嘗試8小時(shí)工作制,不斷發(fā)現(xiàn)每個(gè)工人的總產(chǎn)出仍在提升。“科學(xué)管理理論”創(chuàng)始人泰勒 (Frederick W. Taylor)指出減少工作時(shí)間會(huì)顯著增加個(gè)人產(chǎn)出。

            在上個(gè)世紀(jì)二十年代,亨利?福特對(duì)工作時(shí)間安排進(jìn)行了多年試驗(yàn),最終在1926年引入每周工作五天共計(jì)40小時(shí),卻付六天薪水的工作制度。福特為什么要這么做呢?因?yàn)樗脑囼?yàn)表明,其工廠在五天內(nèi)的產(chǎn)出要比六天的產(chǎn)出還多。而在工作制變遷的每一步上(1840s,1890s和1920s),總有些行業(yè)人士堅(jiān)持認(rèn)為:縮短工作時(shí)間會(huì)降低產(chǎn)出,使經(jīng)濟(jì)受損。

             第三課:經(jīng)過(guò)上一個(gè)世紀(jì)的研究表明,每周五天且每天8小時(shí)的工作時(shí)間,從長(zhǎng)遠(yuǎn)看其產(chǎn)出將會(huì)最大。有什么理由讓我們認(rèn)為:我們這個(gè)行業(yè)可以不遵守這個(gè)規(guī)則呢?

            短期的產(chǎn)出又當(dāng)如何?

            如果每周40小時(shí)工作制是收到最多產(chǎn)出的最佳工作時(shí)間安排方式,我們能否期待“短期內(nèi)的延長(zhǎng)工作時(shí)間”能夠有短期的成效?簡(jiǎn)而言之,從幾天到幾個(gè)月,你通過(guò)延長(zhǎng)工作時(shí)間能夠得到多少額外的產(chǎn)出,取決于一個(gè)工作日工作多長(zhǎng)時(shí)間。

            顯然,如果在八小時(shí)工作制下一個(gè)工人每小時(shí)生產(chǎn)一個(gè)工件,他在十六小時(shí)工作制下生產(chǎn)的工件數(shù)會(huì)在八到十六個(gè)之間。這是隱藏在“趕工”背后不那么明顯的本質(zhì)。另外,工人生產(chǎn)率還依賴于其所處狀態(tài)。

            與每周工作40小時(shí)相比,每周工作60小時(shí)會(huì)導(dǎo)致生產(chǎn)效率下降。剛開(kāi)始時(shí),這額外的20個(gè)小時(shí)會(huì)彌補(bǔ)生產(chǎn)效率的下降,使總產(chǎn)出增加。但研究表明,當(dāng)改為每周工作60小時(shí)以后,建筑工人的生產(chǎn)率很快就會(huì)開(kāi)始下降。這種下降很快可以感覺(jué)到,一周之內(nèi)就會(huì)很明顯,而且還將不斷下滑。在兩個(gè)月內(nèi),累積生產(chǎn)力的損失會(huì)下降到與每周工作40小時(shí)的產(chǎn)出同樣的水平。同一篇報(bào)告引證的研究顯示出:每天工作八小時(shí)的總產(chǎn)出會(huì)高于每天工作九小時(shí)總產(chǎn)出16%或者20%。

            所以,沒(méi)錯(cuò),“趕工”可在短期內(nèi)提高產(chǎn)出。但在每周工作60小時(shí)的情況下,這個(gè)“短期”絕不能超過(guò)八個(gè)星期。因?yàn)閺倪@一點(diǎn)開(kāi)始,成本耗費(fèi)會(huì)嚴(yán)重超過(guò)帶來(lái)的收益。不僅會(huì)失去趕工帶來(lái)的成果,還會(huì)使員工感到疲倦、易怒、情緒難于控制。當(dāng)恢復(fù)為每周工作40小時(shí)后,還需要一段時(shí)間,他們的產(chǎn)出才能恢復(fù)到原來(lái)的水平。

            一周如果工作87.5小時(shí)會(huì)怎么樣呢?雖然缺乏確鑿的數(shù)據(jù),但我估計(jì)效率在一個(gè)月內(nèi)將跌至原來(lái)的50%,即使每周額外的47.5小時(shí)工作(兩倍于"正常"工作時(shí)間)在最初階段可能有相當(dāng)高的產(chǎn)出。

            ◆ 第四課:每星期工作60小時(shí)的情況下,由于長(zhǎng)時(shí)間工作所導(dǎo)致的生產(chǎn)率下降,會(huì)抵消幾個(gè)月超時(shí)工作所帶來(lái)的產(chǎn)出。

            睡眠因素

            在評(píng)估“趕工”是否有用時(shí),還要考慮另一個(gè)因素:如果工人沒(méi)有足夠的睡眠,他們能保持高產(chǎn)多長(zhǎng)時(shí)間?

            Gregory Belenky上校是Walter Reed 軍隊(duì)研究所神經(jīng)精神科的主任。他曾為五角大樓研究如何使士兵在戰(zhàn)斗條件下最大化其效率和警惕性。在其1997年的論文《持續(xù)作戰(zhàn)中睡眠、剝奪睡眠與人體的表現(xiàn)》中,他指出:

            “實(shí)驗(yàn)室內(nèi)研究表明,每連續(xù)保持清醒24小時(shí),神智功能下降25%。被剝奪睡眠的個(gè)體雖然能夠保持認(rèn)知活動(dòng)的準(zhǔn)確性,但是反應(yīng)速度明顯下降。”

            在我們的研究中,來(lái)自第82空降兵團(tuán)炮火指示中心的團(tuán)隊(duì)接受了模擬持續(xù)戰(zhàn)斗狀態(tài)的測(cè)試,共持續(xù)了36小時(shí)。在測(cè)試開(kāi)始階段,當(dāng)我們要求向一所醫(yī)院進(jìn)行模擬開(kāi)火時(shí),團(tuán)隊(duì)還能查看方位圖,評(píng)估目標(biāo)狀況,拒絕開(kāi)火請(qǐng)求。到模擬測(cè)試后期,他們就無(wú)視目標(biāo)性質(zhì)毫不猶豫地開(kāi)火了。

            在進(jìn)行模擬演習(xí)的第15天,每晚睡四小時(shí)的炮兵連的成績(jī)僅是每晚睡七小時(shí)的炮兵連的三分之一。

             第五課:每連續(xù)工作24小時(shí),認(rèn)知能力會(huì)下降25%。連續(xù)開(kāi)夜車的人會(huì)產(chǎn)生嚴(yán)重的累積影響。

            認(rèn)知遲鈍與錯(cuò)誤率

            “趕工”引起生產(chǎn)率下降的重要因素之一就是產(chǎn)生錯(cuò)誤數(shù)量的增加。盡管大多數(shù)錯(cuò)誤很容易修復(fù),但還是有一些錯(cuò)誤會(huì)把從“趕工”中得到的收益消耗殆盡。“趕工”時(shí)間越長(zhǎng),相關(guān)人員遇到大麻煩的機(jī)會(huì)就越大。

            程序員、架構(gòu)師和測(cè)試人員能夠拿到薪水,不是由于他們擁有發(fā)達(dá)的肌肉或者什么超能力能把重物從一點(diǎn)移到另一點(diǎn),而是因?yàn)樗麄兊拇竽X。工作時(shí)間越長(zhǎng),或者缺乏充足的睡眠(好比每晚只睡1~2小時(shí))會(huì)大大降低他們使用大腦的效率。

            Belenky上校指出:士兵即使每天僅少睡一個(gè)小時(shí),后果是“減少了……保持清醒地進(jìn)行高條理性腦力工作的能力。降低了個(gè)體和團(tuán)隊(duì)的效率”。

            知識(shí)工作者應(yīng)該感到幸運(yùn)他們不必?fù)?dān)心發(fā)生“友軍誤傷”的問(wèn)題。

            在《持續(xù)減少睡眠會(huì)產(chǎn)生嚴(yán)重惡劣后果》一文中提到:

            “賓夕法尼亞大學(xué)的研究人員發(fā)現(xiàn):連續(xù)十四天內(nèi)每天只睡4至6小時(shí)的研究對(duì)象在認(rèn)知表現(xiàn)方面顯示出明顯不足,其水平相當(dāng)于連續(xù)三天不睡覺(jué)的人。然而,這些研究對(duì)象卻說(shuō)自己只感覺(jué)有點(diǎn)兒困,并沒(méi)有意識(shí)到他們的狀況有多糟。”

            在2005年1月的《洛杉磯時(shí)報(bào)》上,Karen Kaplan有一篇名為《瞌睡的醫(yī)學(xué)院實(shí)習(xí)生成為馬路殺手》的文章:

            “研究表明,在21個(gè)小時(shí)內(nèi)沒(méi)有睡眠的司機(jī),其狀態(tài)相當(dāng)于血液內(nèi)酒精含量檢測(cè)達(dá)到0.08的人,而0.08是美國(guó)對(duì)非營(yíng)利性駕駛司機(jī)進(jìn)行血液內(nèi)酒精含量檢測(cè)是否超標(biāo)的法定界限。”

            令人感到諷刺的是,大多數(shù)軟件公司會(huì)解雇一個(gè)工作時(shí)間喝酒的人,卻能毫不猶豫地將今年最重要的項(xiàng)目交到由于缺少睡眠(相當(dāng)于達(dá)到法定司機(jī)酒精含量超標(biāo)標(biāo)準(zhǔn))的人手里。事實(shí)上,是他們要求這些人在“違法醉酒狀態(tài)”進(jìn)行工作的,并以此作為雇員被繼續(xù)雇傭的條件。

            帶來(lái)的風(fēng)險(xiǎn)很現(xiàn)實(shí)—引發(fā)的錯(cuò)誤真能導(dǎo)致災(zāi)難發(fā)生。在Dr. William Dement寫的《睡眠的承諾》一書寫道:

            “1989 年3月24日的夜晚清冷寧?kù)o,空氣如水晶般透明。埃克森石油公司的油輪離開(kāi)了阿拉斯加的瓦爾迪茲市,駛向威廉王子海灣。在如此清澈的氣候條件下,油輪按原計(jì)劃放下了輸油管道,卻沒(méi)有及時(shí)收回。巨大的油輪擱淺,數(shù)百萬(wàn)加侖的原油泄漏到海灣之中。……在最后的報(bào)告中,國(guó)家運(yùn)輸安全委員會(huì) (NTSB)發(fā)現(xiàn),缺少睡眠是事件的直接原因。……導(dǎo)致美國(guó)歷史上最嚴(yán)重的漏油事件的直接責(zé)任人是船上的三副,他在事發(fā)前48小時(shí)內(nèi)僅睡6個(gè)小時(shí),睡眠嚴(yán)重不足。”

            羅杰斯調(diào)查委員會(huì)(Rogers Commission)在關(guān)于美國(guó)挑戰(zhàn)者號(hào)航天飛機(jī)失事分析最終報(bào)告中說(shuō):在關(guān)鍵時(shí)刻的電話會(huì)議上,做出發(fā)射決策是有問(wèn)題的。在“人為因素分析”章節(jié)提到睡眠缺乏“對(duì)此有重大影響”。

            如果由于缺乏睡眠可以導(dǎo)致戰(zhàn)斗失利,危害病人,擱淺油輪,引爆太空飛船;仔細(xì)想想這會(huì)為價(jià)值一千五百萬(wàn)美金的游戲項(xiàng)目帶來(lái)什么?

            ◆ 第六課:錯(cuò)誤率會(huì)隨連續(xù)工作時(shí)間而攀升,尤其是睡眠時(shí)間不足的情況下。最終,失敗會(huì)找上門來(lái),導(dǎo)致災(zāi)難發(fā)生。當(dāng)時(shí)間緊且預(yù)算投入大時(shí),你真能承擔(dān)得起這個(gè)風(fēng)險(xiǎn)嗎?

            這意味著什么?

            意味著生產(chǎn)率下降。在保持每周五天共約40小時(shí)工作時(shí)間的情況下,工人可以維持生產(chǎn)率。工作更長(zhǎng)時(shí)間,生產(chǎn)率開(kāi)始下降。在四天和兩個(gè)月之間某個(gè)時(shí)間點(diǎn)上,從加班工作中得到的收益會(huì)被小時(shí)生產(chǎn)率下降所抵消。在某些極端情況下(當(dāng)工人無(wú)法保證每晚至少7到8小時(shí)睡眠的狀況下,一到兩天之內(nèi)),效率會(huì)直線下降。

            上面的研究?jī)?nèi)容很多都是來(lái)自于工業(yè)產(chǎn)業(yè)環(huán)境,可能有人會(huì)認(rèn)為這些結(jié)論不適用那些更多地使用腦力的程序員、架構(gòu)師和測(cè)試人員身上,因?yàn)樗麄兣c普通的體力勞動(dòng)者不同。事實(shí)上,的確不同,Belenky上校明確表示: 

            “與復(fù)雜的腦力活動(dòng)相比,可以說(shuō),簡(jiǎn)單的心理活動(dòng)、體力勞動(dòng)和耐力基本不受缺乏睡眠的影響。”

            需要完成復(fù)雜任務(wù)的腦力勞動(dòng)者受睡眠缺乏影響比體力勞動(dòng)者更明顯,生產(chǎn)率下降得更快。在知識(shí)工作者中,由于過(guò)度工作而導(dǎo)致的生產(chǎn)率損失會(huì)比普通士兵更早更快,因?yàn)槲覀兊墓ぷ鞲苣X力疲乏的影響。

            Ea_spouse想告訴我們,她老公所在團(tuán)隊(duì)的工作效率遠(yuǎn)遠(yuǎn)低于最佳效率。在老板讓他們進(jìn)行每周87.5小時(shí)的超級(jí)“趕工”之前,每周工作60小時(shí)以上的狀態(tài)就已持續(xù)幾個(gè)月了。

            二十世紀(jì),在大部分的時(shí)間、地點(diǎn)和行業(yè)中,讓手下雇員這樣工作的管理者就被認(rèn)為是不能勝任其工作。這不只是因?yàn)樗麄兺{到了良好的雇傭關(guān)系,還由于他們的錯(cuò)誤管理方式將公司的生產(chǎn)力和資產(chǎn)至于危險(xiǎn)境地。

            一百多年的業(yè)界研究已經(jīng)毋庸置疑地證明:?jiǎn)T工因精疲力盡所產(chǎn)生的錯(cuò)誤會(huì)推遲計(jì)劃,損壞設(shè)備,增加成本,降低產(chǎn)品質(zhì)量,最終威脅組織的生存。這是對(duì)項(xiàng)目的威脅,也是對(duì)其管理者、雇主、每個(gè)人,以及其自身的威脅。

            無(wú)論如何, 將“趕工”用作長(zhǎng)期策略,在經(jīng)濟(jì)上是不可行的。延時(shí)工作不能增加產(chǎn)出,除非是短期行為。另外,“趕工”也不能使產(chǎn)品更快推出,只會(huì)導(dǎo)致產(chǎn)品延遲發(fā)布。“趕工”不能提高產(chǎn)品質(zhì)量,只能使其更糟糕。“趕工”增加了引發(fā)重大錯(cuò)誤的機(jī)會(huì),比如交付會(huì)擦除客戶硬盤數(shù)據(jù)的軟件,或者刪除代碼樹(shù),或者把可樂(lè)灑到最近沒(méi)有做過(guò)備份的服務(wù)器中,甚至引起火災(zāi)。的確是這樣,在“趕工”最后那些睡眼惺忪的日子里,我曾親眼目睹過(guò)前三個(gè)后果。第四個(gè)后果遲早會(huì)發(fā)生,大概只是時(shí)間上的問(wèn)題。

            管理者決定趕工,是因?yàn)樗麄兿敫嬖V他們的老板“我已盡我所能”。他們趕工,是因?yàn)樗麄冊(cè)u(píng)估的是放在椅子上的“草人”而不是那些能開(kāi)發(fā)游戲的“大 腦”。他們趕工,是因?yàn)樗麄儧](méi)有認(rèn)真考慮要做的工作,或沒(méi)有考慮做工作的是人。他們趕工,是因?yàn)橹恢酪憩F(xiàn)出自己在盡力做好工作的重要性,而不是真正去把工作做好。他們趕工,是因?yàn)樗麄兓叵氲疆?dāng)他們還是程序員、測(cè)試人員、“助理制片人”或“副制片人”時(shí),他們也是被要求這樣做的。

            但這不是唯一的方法。事實(shí)上,很多文獻(xiàn)一次又一次地表明:加班趕工是最差的方式。這也是很多行業(yè)七十五年前就已放棄這種工作方式的根本原因。管理者、股東和員工都堅(jiān)信:使用經(jīng)過(guò)時(shí)間檢驗(yàn)的——每天工作8小時(shí)、每周5天——管理實(shí)踐,大家會(huì)因更快、更省地交付更好的產(chǎn)品而獲益,而且不會(huì)損耗組織的人力資源和在公眾中的聲望。
            posted @ 2008-05-08 17:51 kenlistian 閱讀(910) | 評(píng)論 (0)編輯 收藏

            2008年4月29日 #

            1.正則表達(dá)式筆記

            必須記住的幾個(gè)符號(hào)和組合
            .        匹配除換行符以外的所有字符一次
            ?        匹配 0 次或一次
            *        匹配 0 次或多次
            +        匹配 1 次或多次

                           使用范例:

                            x?        匹配 0 次或一次 x 字符串
                            x*        匹配 0 次或多次 x 字符串,但匹配可能的最少次數(shù),*必須跟隨一個(gè)字符后面,不能單獨(dú)出現(xiàn)
                            x+        匹配 1 次或多次 x 字符串,但匹配可能的最少次數(shù),+必須跟隨一個(gè)字符后面,不能單獨(dú)出現(xiàn)
                            .*        匹配 0 次或一次的任何字符
                            .+        匹配 1 次或多次的任何字符

            界定范圍和位置
            ^        匹配字符開(kāi)頭的字符
            $         匹配字符結(jié)尾的字符
            {m}          匹配剛好是 m 個(gè) 的指定字符串
            {m,n}        匹配在 m個(gè) 以上 n個(gè) 以下 的指定字符串
            {m,}          匹配 m個(gè) 以上 的指定字符串
            []           匹配符合 [] 內(nèi)的字符
            [^]         匹配不符合 [] 內(nèi)的字符
            [0-9]         匹配所有數(shù)字字符
            [a-z]        匹配所有小寫字母字符
            [^0-9]        匹配所有非數(shù)字字符
            [^a-z]        匹配所有非小寫字母字符

            \b         匹配以英文字母,數(shù)字為邊界的字符串
            \d         匹配一個(gè)數(shù)字的字符,和 [0-9] 語(yǔ)法一樣
            \w        英文字母或數(shù)字的字符串,和 [a-zA-Z0-9] 語(yǔ)法一樣
            \s         空格,和 [\n\t\r\f] 語(yǔ)法一樣
            \B         匹配不以英文字母,數(shù)值為邊界的字符串
            \D         非數(shù)字,其他同 \d
            \S         非空格,和 [^\n\t\r\f] 語(yǔ)法一樣
            \W        非英文字母或數(shù)字的字符串,和 [^a-zA-Z0-9] 語(yǔ)法一樣

            a|b|c       匹配符合a字符 或是b字符 或是c字符 的字符串
            abc         匹配含有 abc 的字符串
            轉(zhuǎn)義:      使用\ 來(lái)取消元字符的特殊意義。包括 . * + \  [ ] { } ( ) ^ $


            ?的多重定義-懶惰限定符
            *?    重復(fù)任意次,但盡可能少重復(fù)
            +?    重復(fù)1次或更多次,但盡可能少重復(fù)
            ??    重復(fù)0次或1次,但盡可能少重復(fù)
            {n,m}?    重復(fù)n到m次,但盡可能少重復(fù)
            {n,}?    重復(fù)n次以上,但盡可能少重復(fù)


            斷言匹配: 有4個(gè)

            (?=exp)   零寬先行斷言,它匹配文本中的某些位置,這些位置的后面能匹配給定的后綴exp。
                         比如/b/w+(?=ing/b),匹配以ing結(jié)尾的單詞的前面部分(除了ing以外的部分),
                          zc:根據(jù)后綴匹配而已。和$區(qū)別在于$是行尾匹配。
                          如I'm singing while you're dancing.  它會(huì)匹配sing和danc。

            (?<=exp)  零寬后行斷言,它匹配文本中的某些位置,這些位置的前面能給定的前綴匹配exp。
                           如(?<=\bre)\w+\b會(huì)匹配以re開(kāi)頭的單詞的后半部分(除了re以外的部分),
                           zc:匹配前綴。
                            例如在查找reading a book時(shí),它匹配ading。

            (?!exp)    零寬負(fù)向先行斷言。會(huì)匹配后綴exp不存在的位置。
                          zc: 若不是exp或者沒(méi)有則匹配,用^在于存在一個(gè)不匹配某個(gè)exp的其他字符,而!保證不匹配exp外可以不跟任何字符。
                         
            如:\d{3}(?!\d)匹配三位數(shù)字,而且這三位數(shù)字的后面不能是數(shù)字。 也可以是只有前3位數(shù)字。比較\d{3}(^\d)有區(qū)別.

            (?<!exp)   零寬負(fù)向后行斷言。查找前綴exp不存在的位置.

            條件匹配: (zc:這個(gè)復(fù)雜,但也不復(fù)雜。屬于perl中的擴(kuò)展部分,略)

            Conditional Expressions

            (?(condition)yes-pattern|no-pattern)
               attempts to match yes-pattern if the condition is true, otherwise attempts to match no-pattern.

            (?(condition)yes-pattern) attempts to match yes-pattern if the condition is true, otherwise fails.

            condition may be either a forward lookahead assert, or the index of a marked sub-expression
            (the condition becomes true if the sub-expression has been matched).

             

             

            2. boost中分有match,search,replace,在vc中,其正則表達(dá)式以上\表示需要雙反斜杠表示。其中在

            match是匹配整個(gè)句子,在實(shí)際應(yīng)用中,必須是構(gòu)造整個(gè)句子的正則表達(dá),而在一篇文章匹配的話,以search用的

            比較多,如下見(jiàn)一個(gè)片段。沒(méi)有用到std,可見(jiàn)用boost在匹配查詢時(shí),其比較麻煩:

            void test123()
            {      
                CString str = "singing while youre dancing.";
                regex ee_all("\\b\\w+\\b");
                cmatch result;
                CString ret;
                while(regex_search(str, result, ee_all,match_perl)){           
                    for(int i=0; i < result.size(); i++){           
                        ret = result[i].str().c_str();
                        AfxMessageBox(ret);               
                        str = result[i].second;
                    }           
                }   
            }

            posted @ 2008-04-29 11:26 kenlistian 閱讀(1254) | 評(píng)論 (0)編輯 收藏

            2008年4月23日 #

                 摘要: Base64 編碼和解碼, 說(shuō)到Base64編碼和解碼,不過(guò)就是把3個(gè)字節(jié)進(jìn)行處理后放置到4個(gè)字節(jié)空間。其原理就是:     待編碼數(shù)據(jù),以3個(gè)字節(jié)為單位,依次取6位數(shù)據(jù)并在前面補(bǔ)上兩個(gè)0形成新的8位編碼, 由于3*8=4*6,這樣3個(gè)字節(jié)的輸入會(huì)變成4個(gè)字節(jié)的輸出,長(zhǎng)度上增加了1/3。     以上處理存在著字符不是可見(jiàn)字符,...  閱讀全文
            posted @ 2008-04-23 12:06 kenlistian 閱讀(805) | 評(píng)論 (0)編輯 收藏

            2008年4月22日 #

            1.這里下載boots,http://downloads.sourceforge.net,我下載的是boost_1_35_0.zip,鏈接如下:

                http://downloads.sourceforge.net/boost/boost_1_35_0.zip?modtime=1206795434&big_mirror=0

            2.解壓后打開(kāi)目錄

                \boost_1_35_0\boost_1_35_0\libs\regex\build編譯vc6下版本vc6.mak文件,具體如下:

                a。打開(kāi)dos環(huán)境,把vc6目錄中的Microsoft Visual Studio\VC98\Bin下的VCVARS32.BAT拖入dos窗做環(huán)境設(shè)置。

                b。運(yùn)行namke -f vc6.mak。

                c。把生成的dll和lib放在自己的目錄中,最好在vc6的vc98下建一個(gè)目錄,畢竟是vc6下的編譯碼。然后在vc6中設(shè)置包含lib file。

                   文件include包含目錄 boost_1_35_0\boost_1_35_0\boost_1_35_0

            3.測(cè)試,粘貼到vc中直接編譯。

            如果編譯出現(xiàn)minimal builder不支持的話,則去掉set中的minimal builder勾選。

            還有若出現(xiàn)類似fatal error C1001: INTERNAL COMPILER ERROR錯(cuò)誤的話,則采用rebuilder來(lái)重建,一般是可以通過(guò)。

            為啥為什么出現(xiàn)這個(gè)內(nèi)部編譯錯(cuò)誤,倒不清楚。不過(guò),不影響vc下的正則處理使用暫時(shí)不管了。

            #include "stdafx.h"
            #include <boost/regex.hpp>
            #include <string>
            #include <iostream>

            using namespace std;
            using namespace boost;

            regex ee("a+b");

            int main()
            {
                string str = "aaaaaaab";
                if(regex_match(str.c_str(), ee))
                {
                  cout<<"match ok"<<endl;
                }
                getchar();
                return 0;
            }

             

            備注原文:http://dotnet.csdn.net/page/66e7a1c1-981e-4609-93fc-a3c34a6a5308

            posted @ 2008-04-22 16:40 kenlistian 閱讀(926) | 評(píng)論 (0)編輯 收藏

            2008年4月21日 #

              其實(shí),插件不過(guò)就是調(diào)用dll中的函數(shù)而已,不過(guò)通過(guò)類似一個(gè)com中的接口,再通過(guò)接口查詢到相應(yīng)的服務(wù)來(lái)處理。

            復(fù)雜的插件,當(dāng)然有考慮采用com方式的,不過(guò)作為編寫程序的原則是簡(jiǎn)單,實(shí)效,通用。又何須采用太過(guò)專業(yè)的方法。

            技術(shù)不過(guò)是手段,能在達(dá)到目的的最大化程度上實(shí)現(xiàn),就足矣。

              下面的例子來(lái)自網(wǎng)上,作者不詳,稍微整編下。直接貼代碼在上面。源碼打包放在自己博客的文檔中。算是自己學(xué)習(xí)整理,

            也感謝提供者。

            源碼學(xué)習(xí):http://www.shnenglu.com/Files/kenlistian/test_plus.rar

             

            1.定義插件的接口結(jié)構(gòu)

            /*
              定義一個(gè)plus 接口結(jié)構(gòu)
            */
            typedef struct PlugInModule{
                DWORD Ver ;                    //版本
                char *Author ;                  //作者說(shuō)明
                char *Description;             //模塊說(shuō)明

                BYTE *InputPointer;          //輸入數(shù)據(jù)
                DWORD dwSize ;               //輸入數(shù)據(jù)的大小
                HWND hParentWnd ;           //主程序的父窗口
                HINSTANCE hDllInst ;          //Dll句柄

                void (*PlugIn_Config)( struct PlugInModule * pModule ); //設(shè)置函數(shù)
                void (*PlugIn_Init)( struct PlugInModule * pModule );    //初始化函數(shù)
                void (*PlugIn_Quit)( struct PlugInModule * pModule );   //退出函數(shù)
                void (*PlugIn_Run )( struct PlugInModule * pModule );   //執(zhí)行函數(shù)
            } PlugInModule;

            其中接口結(jié)構(gòu)函數(shù),被規(guī)定了4個(gè),也就是說(shuō)這個(gè)接口函數(shù)定死了,如果以后應(yīng)為功能增加等等,

            則估計(jì)這個(gè)結(jié)構(gòu)都要改寫。所以采用com方式接口方式則是一種好的選擇,而那種模式,每次還要注冊(cè)com,

            則莫免麻煩和釘死在windows平臺(tái)上。

            2.以上接口結(jié)構(gòu)放置在頭文件中。作為主程序和dll共享的頭文件,其中,再在頭文件中具體聲明以上結(jié)構(gòu)體中函數(shù)。

            void plusDll_Config( struct PlugInModule * pModule);  //設(shè)置函數(shù)
            void PlusDll_Init( struct PlugInModule * pModule );   //初始化函數(shù)
            void plusDll_Quit( struct PlugInModule * pModule );   //退出函數(shù)
            void plusDll_Run( struct PlugInModule * pModule );     //執(zhí)行函數(shù)

            3.在頭文件中聲明一個(gè)返回該結(jié)構(gòu)的函數(shù)。其實(shí)就是一個(gè)回調(diào)函數(shù)。把該結(jié)構(gòu)返回給主程序的一個(gè)export 函數(shù)。

            typedef PlugInModule* (*GETPLUGINMODULE)();       //聲明接口函數(shù)地址

            /**
              導(dǎo)出函數(shù),主程序首先獲取該接口函數(shù),獲得 dll中的函數(shù)地址,調(diào)用
            */
            DLL_001_API PlugInModule* GetPlugInModuleFunction();     //DLL_001_API ==> __declspec(dllexport)

            4.在dll中定義該插件結(jié)構(gòu),把地址通過(guò)GetPlugInModuleFunction傳入到主程序。

            5.分別實(shí)現(xiàn)dll中和主程序的定義部分。通過(guò)動(dòng)態(tài)加載方式即可實(shí)現(xiàn)取出dll的結(jié)構(gòu)體指針。

            如下示:

                    hDLL = LoadLibrary("dll_001\\debug\\dll_001.dll");
                    if (hDLL)
                            MessageBox(NULL,"plus_Dll load ok", "", MB_OK);
                    else
                    {                       
                                    MessageBox(NULL, "not found plus_dll","",MB_OK);
                                    return 0;
                                }
                                pFunction = (GETPLUGINMODULE)::GetProcAddress(hDLL,"GetPlugInModuleFunction");
                                if (pFunction != NULL)
                                {
                                   dllplus_module = (*pFunction)();
                                      dllplus_module->PlugIn_Init(dllplus_module);
                                   dllplus_module->PlugIn_Run(dllplus_module);
                                   dllplus_module->PlugIn_Quit(dllplus_module);

                                }
                                ::FreeLibrary(hDLL);//卸載MyDll.dll文件;


                  好長(zhǎng)時(shí)間沒(méi)更新了,主要這段時(shí)間忙學(xué)ruby。沒(méi)多少時(shí)間溫故一下c++及其細(xì)節(jié)。對(duì)于編程為生涯的人生中,其c/c++是一把好的工具,但是在長(zhǎng)年累月編寫代碼中,更加明白的編程的思想才是靈魂,沒(méi)有思想的編程,也不過(guò)如同拿把寶劍而無(wú)所作為。就是再精通c++也不過(guò)是稱之為某語(yǔ)言專家。而對(duì)于使用工具的人來(lái)說(shuō),不僅是要善于運(yùn)用工具,而且更應(yīng)該是發(fā)揮工具去實(shí)現(xiàn)你的要求。

                  但很多人在對(duì)待c/c++時(shí),太多的時(shí)候,如同時(shí)常撫摸自己手中寶劍而忘卻了要去面對(duì)的項(xiàng)目。

                  我時(shí)常以CS游戲中的武器向同事和朋友來(lái)比喻,c如同CS中的阻擊步槍,而C++如同Ak47,而java,c#如同制式武器中的m41突擊步槍,這些都是在編程世界中的主力廝殺武器,我們可以選擇C可以瞄準(zhǔn)任何項(xiàng)目,也能性能極高的射中項(xiàng)目的要害,但是持C之人,所積累的經(jīng)驗(yàn),所對(duì)項(xiàng)目的開(kāi)發(fā)時(shí)間緩急,都在一定程度上影響了采用c的要求,C++和AK47有點(diǎn)類似,狂暴有效,但不是一般人能夠在項(xiàng)目開(kāi)發(fā)中始終保持開(kāi)發(fā)的速度和效率,在CS中AK47,頭三槍效率極高,但如在編程世界中項(xiàng)目的大量和迫近時(shí),往往和CS中AK47中子彈都飛散到敵手周圍一樣變得雜亂低效。所以一般而言,采用制式武器中的java,C#是作為現(xiàn)代工業(yè)編程的主要制式武器,精確有效,彈量充足。

                 也許,選擇以上制式武器也都是一種個(gè)人愛(ài)好和企業(yè)的側(cè)重,但是再怎么著,也必須在裝備了一主力武器外還得配備近戰(zhàn)的武器,和朋友說(shuō)起,你得像cs中配了主力武器外,還得配把近戰(zhàn)手槍,以備各種要求。如同CS中持槍盤斗到最后,也得抽出短槍盤環(huán)近斗,這也類似在項(xiàng)目吃緊時(shí),臨時(shí)有額外的要求時(shí),用制式工具也許來(lái)不及或者不值得花大工夫去處理時(shí),用用那些膠水語(yǔ)言(動(dòng)態(tài)語(yǔ)言)則是非常省力和高效率的。也就是說(shuō)膠水語(yǔ)言就如同近戰(zhàn)手槍,簡(jiǎn)單有效實(shí)用。

                談到動(dòng)態(tài)語(yǔ)言,有太多,其實(shí)熟悉和能運(yùn)營(yíng)一,二門即可,如老牌的perl,稱之為千年老妖的python,新興的殺手工具ruby,簡(jiǎn)單平淡的vb or javascript,還有那些我從來(lái)沒(méi)用過(guò)的的lua,lisp,schema等,會(huì)這些,不能自以為是的又以為精通了一門語(yǔ)言來(lái)比較c/c++的優(yōu)劣,但也不能以c/c++ 性能優(yōu)異理由而拒絕學(xué)習(xí)其他的理由。只是方便我們達(dá)到我們的目的。

            posted @ 2008-04-21 16:25 kenlistian 閱讀(850) | 評(píng)論 (2)編輯 收藏

            2008年3月5日 #

            補(bǔ):沒(méi)辦法;俺的cnit的博客發(fā)布不上去,就發(fā)在俺的cpp博客吧。

                  由于工作緣故,需要處理一接口發(fā)送來(lái)的xml串,對(duì)方采用java以字節(jié)流模式post一個(gè)xml串,
              在asp中采用request估計(jì)把它加載到xml解析器中應(yīng)該報(bào)無(wú)法解析。

                 采用Request.binaryRead 即可解決,但是要注意幾個(gè)細(xì)節(jié)方面。

                 一一到來(lái)。

              1. 讀取字節(jié)流

                   Dim vtBody

                   iReceive = Request.TotalBytes

                   vtBody = Request.BinaryRead(iReceive)

            2.   轉(zhuǎn)換字節(jié)流為字符串,有以下幾個(gè)函數(shù)可以任選。

              

            ' a。byte --> str  ,該轉(zhuǎn)換只適用小數(shù)據(jù),但是所有ie沒(méi)有問(wèn)題,,,
            Function bytes2BSTR(vIn)
                strReturn = ""
                For i = 1 To LenB(vIn)
                    ThisCharCode = AscB(MidB(vIn,i,1))
                    If ThisCharCode < &H80 Then
                        strReturn = strReturn & Chr(ThisCharCode)
                    Else
                        NextCharCode = AscB(MidB(vIn,i+1,1))
                        strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode))
                        i = i + 1
                    End If
                Next
                bytes2BSTR = strReturn
            End Function

            'b。采用 ado record來(lái)轉(zhuǎn)換,該轉(zhuǎn)換速度快,轉(zhuǎn)換數(shù)據(jù)大,據(jù)說(shuō)對(duì)ie5支持不夠好,
            function rsbinarytostring(xbinary)
            Dim binary

              If  vartype(xbinary)= 8 then
                binary = multibytetobinary(xbinary)
              Else 
                binary = xbinary
              End If
              Dim rs, lbinary
              const adlongvarchar = 201
              Set rs = createobject("adodb.recordset")
              lbinary = lenb(binary)

              If lbinary>0 Then
                rs.fields.append "mbinary", adlongvarchar, lbinary
                rs.open
                rs.addnew
                rs("mbinary").appendchunk binary
                rs.update
                rsbinarytostring = rs("mbinary").Value
                rs.Close
              Else
               rsbinarytostring = ""
              end if
            End Function

            '***************************************************
            'c。采用流解析字符串,該轉(zhuǎn)換速度快,轉(zhuǎn)換數(shù)據(jù)大,據(jù)說(shuō)對(duì)ie5支持不夠好,
            function stream_binarytostring(binary, charset)
              set binarystream = createobject("adodb.stream")
              '讀入字節(jié)流
              binarystream.type = 1
              binarystream.open
              binarystream.write binary

              '內(nèi)部以字符方式返回
              binarystream.position = 0
              binarystream.type = 2
              If len(charset) > 0 Then
                binarystream.charset = charset
              Else
                binarystream.charset = "us-ascii"
              End If
              stream_binarytostring = binarystream.readtext
            end function

            則調(diào)用上面任一個(gè)進(jìn)行轉(zhuǎn)換即可。如:

              strBody = stream_binarytostring(vtBody, "utf-8")

            3. 將strBody解碼

               如果調(diào)用Response.Write strBody ,則在ie上可以看到正常的xml結(jié)構(gòu)體部分。

               但是如果你要是寫在文本中,你將會(huì)看到的是如下的樣式:

            %3C%3Fxml+version+%3D+%221.0%22+encoding%3D%22UTF-8%22+%3F%3E%3CROOT%3E%3CUSER%3Egtzx%3C%2FUSER%3E%3CPASS%3Egtzx%3C%2FPASS%3E%3CMO%3E%3CMOID%3E291AC5FDBD0DF4EF2B2A2950FB730610%3C%2FMOID%3E%3CMSGFORMAT%3E15%3C%2FMSGFORMAT%3E%3CCLASSID%3Ehttp%3A%。。。。%3E%3CSERVICEID%3E1981%3C%2FSERVICEID%3E%3CCITYID%3E102%3C%2FCITYID%3E%3CPROVINCEID%3E16%3C%2FPROVINCEID%3E%3CMOUSEID%3E2%3C%2FMOUSEID%3E%3CSPNUMBER%3E10666066%3C%2FSPNUMBER%3E%3CLINKID%3E%3C%2FLINKID%3E%3CREMARK%3E%3C%2FREMARK%3E%3C%2FMO%3E%3C%2FROOT%3E

              這表示是url 編碼方式,它把utf-8編碼進(jìn)行了再一次編碼,如果你要是xml解析器來(lái)解析的話,恐怕它是干不了活的。(也許有,但是asp中玩那個(gè)xmldocument實(shí)在是不想研究下去)

              不過(guò)再需要做個(gè)urlDecode轉(zhuǎn)換,這個(gè)asp函數(shù),網(wǎng)上一大把,搜出一個(gè),粘貼下來(lái)就可以去掉%并轉(zhuǎn)換utf-8格式。這里貼出一個(gè)修改的urlDecode函數(shù),

            '*****************************************************
            '功能描述:URL解碼碼函數(shù)
            '輸入?yún)?shù):vURL編碼的字符串
            '返回值:解碼后的字符串
            Public Function URLDecoding(sIn)
            Dim s,i,l,c,t,n : s="" : l=Len(sIn)

            For i=1 To l
                c=Mid(sIn,i,1)
                If c<>"%" Then
                    s = s & c
                Else
                    c=Mid(sIn,i+1,2) : i=i+2 : t=CInt("&H" & c)
                    If t<&H80 Then
                        s=s & Chr(t)
                    Else
                        c=Mid(sIn,i+1,3)
                        If Left(c,1)<>"%" Then
                            URLDecoding=s
                            Exit Function
                        Else
                            c=Right(c,2) : n=CInt("&H" & c)
                            t=t*256+n-65536
                            s = s & Chr(t) : i=i+3
                        End If
                    End If
                End If
            Next
            s=Replace(s, "+"," ")
            URLDecoding=s
            End Function

              4. 調(diào)用xml解析器,加載以上字符串,即可解決。

            '***********************************************************
            '解析xml文件
            '***********************************************************
            Dim xml
            Set xml = Server.CreateObject ("msxml2.DOMDocument")
            xml.Async = False
            xml.Loadxml(strBody)

            5.讀出xml中的節(jié)點(diǎn),寫入文本或者寫入數(shù)據(jù)庫(kù),ok。

             

            總結(jié):

                  在asp中采用xmlhttp發(fā)送或者接收,是不考慮字節(jié)流模式發(fā)送的,直接調(diào)用xmlhttp中的send即可。管它是

            按啥模式發(fā)送的,但是在其他語(yǔ)言編程中,比如java,c#,or vc中,有可能是按字節(jié)流方式發(fā)送出去的,那么,

            如果想圖個(gè)方便,直接用asp寫個(gè)接口處理下,就要考慮下和其他程序處理的細(xì)節(jié)。

            posted @ 2008-03-05 16:12 kenlistian 閱讀(2069) | 評(píng)論 (0)編輯 收藏

            2008年2月18日 #

                今天本來(lái)想偷個(gè)懶,直接拿demo的一個(gè)chat代碼做一個(gè)監(jiān)控服務(wù)程序。采用的是傳統(tǒng)的CSocket和CArchive方式處理序列化消息發(fā)送既可以了。

            在做的途中,覺(jué)得搞一個(gè)線程處理讀一個(gè)list字符串,有字符串就調(diào)用CSocket繼承類,通過(guò)序列化方法發(fā)送出去就可以了。

               結(jié)果修改運(yùn)行后,卻本來(lái)想偷懶卻變出偷不了懶,在通過(guò)序列化方式發(fā)送老是報(bào)一個(gè)Sockcore.cpp中的566的ASSERT錯(cuò)誤。

            #ifdef _DEBUG
            void CAsyncSocket::AssertValid() const
            {
                CObject::AssertValid();
                ASSERT(m_hSocket == INVALID_SOCKET || CAsyncSocket::FromHandle(m_hSocket) != NULL);
            }

            仔細(xì)看這個(gè)ASSERT,就是報(bào)socket的問(wèn)題。

            程序反復(fù)看了看,也沒(méi)有查出問(wèn)題。在google搜了搜,

            http://topic.csdn.net/t/20020521/20/741527.html

            http://topic.csdn.net/t/20020626/12/830990.html

               從中有些啟發(fā),把send部分從線程中采用主線程發(fā)送,立馬解決問(wèn)題。看來(lái)CSocket的繼承類是估計(jì)不支持在線程模式下運(yùn)行。也有人說(shuō)CSocket繼承類只能用于主線程,而不能在線程中,當(dāng)然,是否這樣,還需要確認(rèn)。不過(guò),如果想寫線程下的socket類,最好還是從socket構(gòu)造吧,免得繞彎路。否則,調(diào)試來(lái)調(diào)試去,困惑在深深的MFC代碼內(nèi)核代碼中簡(jiǎn)直是浪費(fèi)時(shí)間。

            posted @ 2008-02-18 20:18 kenlistian 閱讀(1455) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題  下一頁(yè)
            亚洲国产日韩欧美久久| 国产国产成人精品久久| 国产精品久久亚洲不卡动漫| 日本免费一区二区久久人人澡 | 久久国产视屏| 狠狠综合久久综合中文88| 久久久久久久亚洲Av无码| 中文字幕乱码人妻无码久久| 国产精品久久久久久久app| 亚洲中文字幕伊人久久无码| 91精品国产乱码久久久久久| 久久无码AV一区二区三区| 日本国产精品久久| 无码国内精品久久综合88| 色综合久久久久网| 久久天天躁狠狠躁夜夜avapp| 99re久久精品国产首页2020| 久久久久亚洲AV无码专区首JN| 青青草原综合久久| 久久精品国产亚洲AV大全| 久久久高清免费视频| 人妻无码久久一区二区三区免费| 久久久久亚洲AV成人网| 四虎国产精品成人免费久久| 久久国产乱子伦精品免费午夜| 国产日产久久高清欧美一区| 久久狠狠一本精品综合网| 99热成人精品热久久669| 无码伊人66久久大杳蕉网站谷歌| 国产一区二区三区久久精品| 欧美精品久久久久久久自慰| 国产99久久久国产精品小说| 色综合久久精品中文字幕首页| 亚洲精品国产字幕久久不卡| 久久久亚洲欧洲日产国码是AV | 97视频久久久| 亚洲国产一成久久精品国产成人综合| 亚洲狠狠久久综合一区77777| 久久久久人妻一区精品| 国产精品综合久久第一页| 91视频国产91久久久|