• <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ā). 勤為槳,思為帆

               :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理 ::
              73 隨筆 :: 4 文章 :: 22 評論 :: 0 Trackbacks

            2009年3月22日 #

            采用PostThreadMessage即可
            BOOL PostThreadMessage(
              DWORD idThread,     //線程ID,通過創(chuàng)建線程后的id
              UINT  Msg,           //消息id
              WPARAM wParam,    
              LPARAM lParam);
            然后在線程通過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,那么消息將不會從消息隊列中被移出,如果設(shè)為PM_REMOVE,那么消息將會從消息隊列中被移出;

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

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


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

            2009年3月17日 #

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

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

            2009年3月13日 #

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

             pdf

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

            2009年3月9日 #

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

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

               這點說明, 不是filter的直接連接函數(shù)相互鏈接,而是在以上內(nèi)部調(diào)用實現(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;
             }

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


            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的當前媒體類型.
             


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

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

              
               IPin::AgreeMediaType函數(shù)處理如下:
             
                  1.判斷pmt 是否是完全媒體類型,是則按全媒體類型模式出來
                  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 閱讀(703) | 評論 (0)編輯 收藏

            2009年2月6日 #


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

            步驟如下:

            1.在宏里處理如下:

            a.

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


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

            b. 定義wxEvT_MYCOMMAND 標識

              DEFINE_EVENT_TYPE(wxEVT_MYMYCOMMAND)

             

            c. 定義宏EVT_MYCOMMAND
               格式中把自定義標識改為自己即可.拷貝.

             

            #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 閱讀(2730) | 評論 (3)編輯 收藏

            2009年2月3日 #

            來源: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, 至于版本問題,沒辦法,先用著再說.
             Dialogblock是美國Anthemion公司開發(fā)的商業(yè)軟件,如果只是簡單
            的包含控件,則免費使用.

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



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

            2009年1月5日 #


            幾個格式細節(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  媒體類型大致說明
               subtype    更一步的細致說明
               formattype 
                  包括有以下:其對應(yīng)的不同的數(shù)據(jù)格式
                   FORMAT_None
                   FORMAT_DvInfo
                   FORMAT_MPEGVideo
                   FORMAT_MPEG2Video
                   FORMAT_VideoInfo
                   FORMAT_VideoInfo2 
                   FORMAT_WaveFormatEx  
                   GUID_NULL

              cbForamt成員指定了格式塊pbFormat的大小.
              pbFormat指針指向格式子塊。
                        pbFormat是一個void*的指針,因為格式塊會因為媒體類型
                        的不同而有不同的指向。如音頻填充的是WAVEFORMATEX結(jié)構(gòu)
                 數(shù)據(jù).

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

             

            //TWaveFormatEx 結(jié)構(gòu):
            TWaveFormatEx = packed record
              wFormatTag: Word;       {指定格式類型; 默認 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;      {指定塊對齊(單位字節(jié)), 塊對齊是數(shù)據(jù)的最小單位}
              wBitsPerSample: Word;   {采樣大小(字節(jié))}每個樣本的BIT數(shù)目,一般為16
              cbSize: Word;           {應(yīng)該是該結(jié)構(gòu)的大小}
            end;

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

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

             下面的圖列清楚從另一個方面表達樣本
             樣本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;   //長度    
                      DWORD     dwBytesRecorded;  //已錄音的字節(jié)長度  
                      DWORD     dwUser;    
                      DWORD     dwFlags;    
                      DWORD     dwLoops;           //循環(huán)次數(shù)  
                      struct   wavehdr_tag* lpNext; //保留    
                      DWORD     reserved;           //保留
              }   WAVEHDR; 
             
              其中l(wèi)pdata 即為pcm格式樣本數(shù)據(jù)。
             
            采樣大小為8位,則采樣的動態(tài)范圍為20*log(256)分貝=48db。
            樣本大小為16位,則采樣動態(tài)范圍為20*log(65536)大約是96分貝

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


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

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

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

             

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

            2008年12月28日 #

            RealMediaSplitter.ax

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

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

            2008年12月27日 #

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

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


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

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

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

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

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

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

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

              d. 將數(shù)據(jù)讀取到緩沖區(qū)
                   其中用到以下來鎖緩沖區(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ū)的頻率不允許改動,

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

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

                    靜態(tài)緩沖區(qū)只填充一次數(shù)據(jù),然后就可以play,
             
              給靜態(tài)緩沖區(qū)加載數(shù)據(jù)分下面幾個步驟
              1、用IDirectSoundBuffer8::Lock函數(shù)來鎖定所有的內(nèi)存,
                        指定你鎖定內(nèi)存中你開始寫入數(shù)據(jù)的偏移位置,并且取回該偏移位置的地址。
              2、采用標準的數(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ū)對象
            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ū)就是播放那些比較長的音頻文件,邊播放,邊填充DirectSound緩沖區(qū)。

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

             

             

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

            BOOL g_bPlaying     
            = FALSE;                                            //是否正在播放
            LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL; 
            DSBPOSITIONNOTIFY        g_aPosNotify[MAX_AUDIO_BUF];     
            //設(shè)置通知標志的數(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();


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

             


             

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

            2008年12月15日 #



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

            2008年12月10日 #



              測試神龍卡路數(shù),其中pDecoderFilter要對應(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 閱讀(492) | 評論 (0)編輯 收藏

            2008年12月9日 #

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

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

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

            1) 創(chuàng)建枚舉器組件,CLSID為CLSID_SystemDeviceEnum
            2) 指定某一種類型設(shè)備,獲取該種類枚舉器
                通過ICreateDevEnum::CreateClassEnumerator獲取某一種類的枚舉器,
                該函數(shù)返回一個IEnumMoniker接口指針,
                通過檢查返回值是否為S_OK來判斷是否獲取到該種類枚舉器.
            3) 用IEnumMoniker::Next枚舉每一個moniker。
                 這個方法返回一個IMoniker接口指針。
            4) 通過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)的效率要低一些。
               當要枚舉某特定種類的filter時,應(yīng)采用系統(tǒng)設(shè)備枚舉器方法,但搜索支持某種
               媒體類型的filter時,用filter mapper.

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

               返回的是一個IEnumMoniker接口,并對于每個適合的filter都提供一個單獨的moniker。

            例子:

            //枚舉所有的支持DV,并且至少有一個輸出pin的filter,
            //這個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ù),估計只能在網(wǎng)上搜索,至于下載的msdn沒有,或者下個dssdk2002版本的help才能有。
             * 在directshow中,關(guān)于capture dev,有專門的demo演示如何獲取音視設(shè)備。這里只是
            明了獲取設(shè)備的方法。以備了解。

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

            2008年12月8日 #

            使用DirectShow寫一個音頻捕捉例子

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


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


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

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


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

              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 閱讀(793) | 評論 (0)編輯 收藏

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

            DirectShow功能實現(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.可自動檢測并使用視頻和音頻加速硬件。

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

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

             


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

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

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

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

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


            2  filter graph

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

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

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


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



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

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

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

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


            pinout和pinin的連接過程可以用下面邏輯語言表達.

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

            2.如沒有指定或不完全指定了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進行連接。
              
               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 閱讀(1049) | 評論 (0)編輯 收藏

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


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

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

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

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

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

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

            一、配置方法
            1 先編譯Samples\C++\DirectShow\BaseClasses中的工程文件。
            注意工程的默認選項是 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 把頭文件可以單獨拷貝到dxsdk的include和lib中

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

            注意:
              VC自帶的庫中也有strmbasd.lib文件。
            一定要保證連接到DS的庫中,否則會出現(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) 之類的錯誤。

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

            二.編譯后出現(xiàn)的錯誤:
            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. 沒有該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;在控制臺程序中可以編譯成功,
               但mfc中報錯,怎么回事?錯誤信息:
            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路徑要對.


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


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

            2008年11月14日 #


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

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

            像這種情況,可以推測運用了錯誤的運行庫包。

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

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

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

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



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

            2008年11月5日 #

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

            為什么用VC6,只不過此文專門針對VC6而已。

            創(chuàng)建項目

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

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

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

             

            設(shè)置項目屬性

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

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

            點擊菜單:Project -> Settings... 打開項目屬性設(shè)置對話框。

            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 

            進行以下操作之前,請先通過菜單 Build -> Configurations... 增加兩個編譯配置"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的庫文件(*.lib),但VC可能并不知道到哪個目錄去尋找這些文件。同時,我們的源代碼中也要包含 (include)wxWidgets的頭文件,其頭文件所在目錄也需要指定。另外,為了更好的調(diào)試wx程序,最好把wxWidgets的源代碼所在目錄 也設(shè)置好。

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

            Include files:

            \include

            \include\msvc

            Library files:

            \lib\vc_lib

            Source files:

            \src

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

             

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

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

            點擊菜單 File -> New...,新建一個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中基本上就是這樣子,我只是簡單的增加了一行"#include "(為了使用XRC文件)。

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

             

            創(chuàng)建wxApp子類

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

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

            app.h

            增加預(yù)編譯頭文件 wx_pch.h 的包含(以后創(chuàng)建的每個.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)過手工改寫的地方已用黃色背景突出顯示):

            // 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類時創(chuàng)建):#include "mainframe.h"

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

            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)過手工改寫的地方已用黃色背景突出顯示):

            #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子類

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

            下面對VC生成的類代碼進行相應(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)過手工改寫的地方已用黃色背景突出顯示):

            #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)過手工改寫的地方已用黃色背景突出顯示):

            #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()
            {
            }

             

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


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

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

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

             

            添加子控件

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

            這是最簡單的情況:

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

            沒錯,只要"new"一下就搞定了,控件會自動出現(xiàn)在wxFrame中。這是運行結(jié)果:


            如果界面再復(fù)雜一些,上面這種方法就行不通了,我們需要引入"Sizer"(詳見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, _("測試按鈕"), 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);
            }

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


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

            enum CtrlID
            {
               
            ID_TEXTCTRL, ID_BUTTON
            };

             

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

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

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

             

            處理事件

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

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

            作為示例,我們來處理上圖中"測試按鈕"被按下的事件。

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

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

            private:
               
            void OnButtonClick( wxCommandEvent& event);

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

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

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

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

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

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



             

            如何處理其它事件?

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

            再補充一點:要知道"什么控件"在"什么時機"會觸發(fā)"什么事件"。

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

            建議看一下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頁)中對事件處理時所涉及的事件類型(wxXXXEvent)和事件宏(EVT_*)有比較好的總結(jié),建議看一下,最好打印出來放在手邊,以便隨時參考。

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

             

            更進一步

            了解 Sizer,熟悉界面設(shè)計: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 上找第三方的控件/庫:http://www.wxwidgets.org/contrib2.htm#classes

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

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

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

            2008年10月29日 #


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

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

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

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

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

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

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

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

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

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

               typedef void (*pFun)();

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

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


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

            則,在調(diào)用動態(tài)庫是有兩種方法:
            1. 先聲明一個與動態(tài)庫中類型一致的指針函數(shù)變量:
            int (*DOCASE)(int ,long);  //用于指向動態(tài)庫中的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定義一個指針函數(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);
            }


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


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

            void test(void* );
            void tt()
            {
               printf(
            "kao,沒玩過這種\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 閱讀(800) | 評論 (1)編輯 收藏

            2008年10月27日 #



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

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

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


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

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

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

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

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




            posted @ 2008-10-27 17:16 kenlistian 閱讀(687) | 評論 (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 閱讀(720) | 評論 (0)編輯 收藏

            2008年7月15日 #


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


            DirectX SDK:DirectX 9.0 Summer 2004 SDK Update,

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

            注意下載頁面的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),

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


            ****************************************************
            因為現(xiàn)在從微軟下載需要驗證

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

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



            感謝上面提供者。
            posted @ 2008-07-15 19:28 kenlistian 閱讀(1372) | 評論 (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所定義的第二個參數(shù)位置傳入。
            如果寫成bind1nd, 則是把szTypeCode作為第一個參數(shù)傳入,那么會報錯。
            因為類型不對。


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

            2008年6月3日 #

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

            其解釋部分非常詳細,基本上wxwidgets和mfc差不了多少,但是擴充性和跨平臺性是大于mfc,同時
            也對熟悉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)用程序是哪個類
            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è)置窗口圖標
                //SetIcon(wxIcon(mondrian_xpm));

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

                // 添加“關(guān)于”菜單項
                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"));

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

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

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



            注意的是:
            1.必須是windows框架而不是control程序框架
            2.通過向?qū)傻膕tdafx.h 可以通過project/set/ "c/c++"中選擇無編譯頭來去掉
            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.如果報有LIBCD.lib重復(fù)定義,填到ignore lib中
            6.以上都是在vc6環(huán)境測試下通過。


            posted @ 2008-06-03 22:54 kenlistian 閱讀(3107) | 評論 (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目錄。
            運行demo程序測試.

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

            2008年5月8日 #


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


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

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

            緣起

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

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

            歷史

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

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

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

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

            實際上,一周有六天是從早上九點工作到晚上十點,還有一天是從早上九點到晚上六點半,算下來是每周工作87.5小時(6×13+9.5=87.5)。可是在工作這么長時間以后,誰還會算得那么詳細呢?

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

            管理者想要什么?

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

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

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

            O = X/Y * t

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

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

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

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

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

            其中,O代表總產(chǎn)出,P( )表示小時生產(chǎn)率隨時間(t1-tn)的變化。在這個等式中,P( )是一個函數(shù),不是一個常量。P( )根據(jù)工人的不同而變化,因為某些人生產(chǎn)力要高于其他人。P( )也隨時間變化,因為人不是機器,在第14個小時完成的工作并不完全等于在第1個小時完成的工作。另外,P ( )也會隨工人最近的狀態(tài)而變化,例如,開夜車后的早上與睡了一個好覺后的早上,其工作效率也不可能是完全一樣的。

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



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

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

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

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

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

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

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

            平衡點在哪里?

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

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

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

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

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

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

            Tom Walker指出:

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

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

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

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

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

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

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

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

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

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

            睡眠因素

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

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

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

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

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

             第五課:每連續(xù)工作24小時,認知能力會下降25%。連續(xù)開夜車的人會產(chǎn)生嚴重的累積影響。

            認知遲鈍與錯誤率

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

            程序員、架構(gòu)師和測試人員能夠拿到薪水,不是由于他們擁有發(fā)達的肌肉或者什么超能力能把重物從一點移到另一點,而是因為他們的大腦。工作時間越長,或者缺乏充足的睡眠(好比每晚只睡1~2小時)會大大降低他們使用大腦的效率。

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

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

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

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

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

            “研究表明,在21個小時內(nèi)沒有睡眠的司機,其狀態(tài)相當于血液內(nèi)酒精含量檢測達到0.08的人,而0.08是美國對非營利性駕駛司機進行血液內(nèi)酒精含量檢測是否超標的法定界限。”

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

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

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

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

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

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

            這意味著什么?

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

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

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

            需要完成復(fù)雜任務(wù)的腦力勞動者受睡眠缺乏影響比體力勞動者更明顯,生產(chǎn)率下降得更快。在知識工作者中,由于過度工作而導(dǎo)致的生產(chǎn)率損失會比普通士兵更早更快,因為我們的工作更受腦力疲乏的影響。

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

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

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

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

            管理者決定趕工,是因為他們想告訴他們的老板“我已盡我所能”。他們趕工,是因為他們評估的是放在椅子上的“草人”而不是那些能開發(fā)游戲的“大 腦”。他們趕工,是因為他們沒有認真考慮要做的工作,或沒有考慮做工作的是人。他們趕工,是因為只知道要表現(xiàn)出自己在盡力做好工作的重要性,而不是真正去把工作做好。他們趕工,是因為他們回想到當他們還是程序員、測試人員、“助理制片人”或“副制片人”時,他們也是被要求這樣做的。

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

            2008年4月29日 #

            1.正則表達式筆記

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

                           使用范例:

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

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

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

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


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


            斷言匹配: 有4個

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

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

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

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

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

            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中,其正則表達式以上\表示需要雙反斜杠表示。其中在

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

            比較多,如下見一個片段。沒有用到std,可見用boost在匹配查詢時,其比較麻煩:

            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 閱讀(1249) | 評論 (0)編輯 收藏

            2008年4月23日 #

                 摘要: Base64 編碼和解碼, 說到Base64編碼和解碼,不過就是把3個字節(jié)進行處理后放置到4個字節(jié)空間。其原理就是:     待編碼數(shù)據(jù),以3個字節(jié)為單位,依次取6位數(shù)據(jù)并在前面補上兩個0形成新的8位編碼, 由于3*8=4*6,這樣3個字節(jié)的輸入會變成4個字節(jié)的輸出,長度上增加了1/3。     以上處理存在著字符不是可見字符,...  閱讀全文
            posted @ 2008-04-23 12:06 kenlistian 閱讀(794) | 評論 (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.解壓后打開目錄

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

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

                b。運行namke -f vc6.mak。

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

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

            3.測試,粘貼到vc中直接編譯。

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

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

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

            #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 閱讀(919) | 評論 (0)編輯 收藏

            2008年4月21日 #

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

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

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

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

            也感謝提供者。

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

             

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

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

                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個,也就是說這個接口函數(shù)定死了,如果以后應(yīng)為功能增加等等,

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

            則莫免麻煩和釘死在windows平臺上。

            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.在頭文件中聲明一個返回該結(jié)構(gòu)的函數(shù)。其實就是一個回調(diào)函數(shù)。把該結(jié)構(gòu)返回給主程序的一個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),把地址通過GetPlugInModuleFunction傳入到主程序。

            5.分別實現(xiàn)dll中和主程序的定義部分。通過動態(tài)加載方式即可實現(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文件;

            posted @ 2008-04-21 18:54 kenlistian 閱讀(2005) | 評論 (2)編輯 收藏

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

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

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

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

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

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

            2008年3月5日 #

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

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

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

                 一一到來。

              1. 讀取字節(jié)流

                   Dim vtBody

                   iReceive = Request.TotalBytes

                   vtBody = Request.BinaryRead(iReceive)

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

              

            ' a。byte --> str  ,該轉(zhuǎn)換只適用小數(shù)據(jù),但是所有ie沒有問題,,,
            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來轉(zhuǎn)換,該轉(zhuǎn)換速度快,轉(zhuǎn)換數(shù)據(jù)大,據(jù)說對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ù)說對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)用上面任一個進行轉(zhuǎn)換即可。如:

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

            3. 將strBody解碼

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

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

            %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編碼進行了再一次編碼,如果你要是xml解析器來解析的話,恐怕它是干不了活的。(也許有,但是asp中玩那個xmldocument實在是不想研究下去)

              不過再需要做個urlDecode轉(zhuǎn)換,這個asp函數(shù),網(wǎng)上一大把,搜出一個,粘貼下來就可以去掉%并轉(zhuǎn)換utf-8格式。這里貼出一個修改的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é)點,寫入文本或者寫入數(shù)據(jù)庫,ok。

             

            總結(jié):

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

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

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

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

            2008年2月18日 #

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

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

               結(jié)果修改運行后,卻本來想偷懶卻變出偷不了懶,在通過序列化方式發(fā)送老是報一個Sockcore.cpp中的566的ASSERT錯誤。

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

            仔細看這個ASSERT,就是報socket的問題。

            程序反復(fù)看了看,也沒有查出問題。在google搜了搜,

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

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

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

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

            僅列出標題  下一頁
            亚洲精品无码久久久久sm| 性做久久久久久免费观看| 国产毛片欧美毛片久久久| 久久亚洲欧洲国产综合| 热99re久久国超精品首页| 99久久精品国内| 久久精品人人做人人妻人人玩| 久久久噜噜噜久久中文字幕色伊伊 | 伊人久久综合无码成人网| 久久精品国产一区二区| 成人午夜精品久久久久久久小说 | 久久国产精品免费| 伊人久久综在合线亚洲2019| 国内精品久久久久久野外| 99精品久久精品| 99国产精品久久| 亚洲国产精品久久久久婷婷老年| 国产精品久久永久免费| 久久婷婷久久一区二区三区| 97久久综合精品久久久综合| 日本加勒比久久精品| 久久婷婷五月综合97色一本一本| 久久精品国产亚洲AV香蕉| 一本一本久久aa综合精品 | 99久久www免费人成精品| 国产高清国内精品福利99久久| 7国产欧美日韩综合天堂中文久久久久| 久久久青草久久久青草| 国产精自产拍久久久久久蜜| 激情久久久久久久久久| 日本亚洲色大成网站WWW久久| 亚洲国产成人久久精品99| 72种姿势欧美久久久久大黄蕉| 国产精品久久网| 精品综合久久久久久88小说| 中文字幕无码久久精品青草 | 狠狠久久亚洲欧美专区| 93精91精品国产综合久久香蕉| 久久久久久极精品久久久| 狠狠精品久久久无码中文字幕| 久久国产精品成人片免费|