2010年12月7日 星期二

[筆記] shrUtils/oclUtils for MFC

如果你是使用nVidia CUDA Toolkit v3.x來開發OpenCL的話,一般都會利用shrUtils與oclUtils這二個靜態連結(.lib)來簡省一些開發時間。

如果在MFC中直接連結範例建構出來的檔案,通常會產生很多的link error。這是因為nVidia的專案使用的Runtime Library模式與MFC不同。我會修改shrUtils與oclUtils設定讓他能相容於MFC。

Step 1. 打開shrUtils Project,並開啟專案的Properties。
Step 2. 進入Configuration Properties的General頁面。
Step 3. 將Debug模式下的Target Name改為$(ProjectName)NC32D;Release模式下改為$(ProjectName)NC32。(VS 2010限定)
Step 4. 將Debug / Release模式下的Use of MFC都改為Use MFC in a Shared DLL
Step 5. 進入[C/C++]→[Code Generation]頁面。將Debug模式下的Runtine Library改為Multi-threaded Debug DLL (/MDd);Release模式下改為Multi-thread DLL (/MD)
Step 6. 進入[Librarian]→[General]頁面。將Debug模式下的Output File改為$(OutDir)$(ProjectName)NC32D.lib;將Release模式下改為$(OutDir)$(ProjectName)NC32.lib。
Step 7. Rebuild Debug / Release。

Step 8. 打開oclUtils Project並重覆上面的2-6步驟。
Step 9. 在[Librarian]→[General]頁面中。將Debug模式下的Additional Dependencies更改為shrUtilsNC32D.lib;將Release模式下更改為shrUtilsNC32.lib;
Step 10. Rebuild Debug / Release。

  • 更改Output檔名是為了保留原有的static link給範例程式能繼續使用。
  • 先建立shrUtils再建立oclUtils的順序別弄錯了。
  • NC是我隨手取的,當然可以取別的名字。

2010年11月24日 星期三

泛型與設計範式(Design Patterns)缺少了什麼

我最近在看候捷/於春景翻譯Andrei Alexandrescu的Modern C++ Design。從事程式工作這些年來,對於template的使用,我還只是停留在T容器的概念,雖然還在研讀中,不過已讓我對template的設計有了新的體會。對於許多以前想不通的ATL設計方式,覺得慢慢能掌握了。

以一個程式開發者角度來使用與開發template是完全不同的等級(使用與開發class也是不同等級)。使用template相對簡單的多,但為何template始終無法推廣成為設計的主流模式?Template缺少了什麼關鍵東西?

首先,要程式設計師能把行為抽象化來思考本身就是很具有難度的一件事。光是物件虛擬化就可以難倒眾多的"程式設計師",當把行為也抽象化後,每個物件所需具有的行為特性就更難以理解。而目前的STL又無法提供出唯一的模型,許多模板都有各種效能、安全性、擴充性的考量,造成許多的變化與組合,當套用的模板不合宜時,結果往往出乎意料,而template的除錯,則是極為艱困的一件工作。

更別提不同的工具對template語法上的差異,我還記得最早在VC 6.0上學習template時,為了一個分行的問題,耗去我一整天的時間。而且每次Visual Studio改版、移植到Mac平台,或多或少都要調整template的語法對其做修正。

其次,template與繼承不同,我的感覺是template class更像是interface。在實作一個template class時像是在玩數獨遊戲一樣,你必需把所有的空格(function)都填寫完畢,且填寫的答案符合他的遊戲規則時才能正常的運作。但困難點在於,當我們在使用還不熟悉的模板時,我如何能得知我要填入多少個空格?MSDN Help只會告訴我們這個模板有什麼功用,卻不會列出套用這個模板的class T必需要具有什麼樣的功能,看著那一大串有看沒有懂的編譯錯誤,許多許用者寧可回到原始的模式一行一行的打著自已能夠看的懂的code也不願再接觸那傳說很方便的template。

對於一個使用template的開發人員,我們要的是什麼。我認為我們真正要的是一個好的精靈(wizard)工具。如果ATL沒有了ATL wizard,不知道有多少人能夠正確的做出一個ATL元件,奇怪的是,這麼多年來,怎麼都沒有一個像樣的STL wizard來協助我們使用template?

這個STL wizard至少應提供2個功能,一個是提供我們"組合"上的選擇測試,另一個則是必需能夠引導我們一步一步的完成這個模板所需的所有函式(至少編譯無錯)。在這個工具出現前,我相信設計範式的模板只是少數人才玩的起的工匠技藝。

2010年11月11日 星期四

CMyCommandLineInfo

在遠古的DOS時代,我們常會在可執行檔後面加上一大堆的command來指定特殊的功能,進到了視窗模式後command已經很少有人在使用了。今天就來談談要始何在透過MFC實現這種原始人才會想用的特殊技藝。

不論產生的是MDISDI或是dialog baseapp,都可以在自動產生的CWinAppInitInstance中找到這段code

CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

if (!ProcessShellCommand(cmdInfo))
    return FALSE;

ParseCommandLine會將執行的指令進行剪貼轉換為CCommandLineInfo物件的格式,而ProcessShellCommand則依傳入的cmdInfo所擁有的屬性進行相對應的處理。

ParseCommandLine內的作用方式如下,如果我們想做的指令不在預設的列表中時,我們必需改寫ParseParam來對應我們想要的功能。

void CWinApp::ParseCommandLine(CCommandLineInfo& rCmdInfo)
{
    for (int i = 1; i < __argc; i++)
    {
        LPCTSTR pszParam = __targv[i];
        BOOL bFlag = FALSE;
        BOOL bLast = ((i + 1) == __argc);
        if (pszParam[0] == '-' || pszParam[0] == '/')
        {
            // remove flag specifier
            bFlag = TRUE;
            ++pszParam;
        }
        rCmdInfo.ParseParam(pszParam, bFlag, bLast);
    }
}

極簡單的範例:

我在開發一些演算法時常需要反覆測試一些已知的影像比較程式修改前修改後的差異,我懶得每次開啟程式時都要去按File選單來選擇我上一次測試的影像,這時我就會在我的debug條件中加入”/default”command

繼承自CcommandLineInfo產生CMyCommandLineLifo類別,並覆載(override) ParseParam函式。小心,UnicodeMulti-Byte有一點點的不同,若想讓這類別二者通吃必需多覆載針對UnicodeParseParam

完成後記得把InitInstanceCCommandLineInfo cmdInfo改為CMyCommandLineInfo cmdInfo

class CMyCommandLineInfo :  public CCommandLineInfo
{
public:
    virtual void ParseParam(const TCHAR* pszParam, BOOL bFlag, BOOL bLast);
#ifdef _UNICODE
    virtual void ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast);
#endif
};

void CMyCommandLineInfo::ParseParam(const TCHAR* pszParam, BOOL bFlag, BOOL bLast)
{
    if (bFlag)
    {
        const CString strParam(pszParam);
        if(strParam.CompareNoCase(_T("default")) == 0)
        {
            m_nShellCommand = CCommandLineInfo::FileOpen;
            m_strFileName   = AfxGetApp()->GetProfileString(_T("Recent File List"),_T("File1"));

            ParseLast(bLast);

            return;
        }
    }

    CCommandLineInfo::ParseParam(pszParam, bFlag, bLast);
}

#ifdef _UNICODE
void CMyCommandLineInfo::ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast)
{
    const CString strParam(pszParam);

    ParseParam(strParam, bFlag, bLast);
}
#endif

只為了擴充單一功能而必需繼承產生一個新類別,每次遇到這種例子我就有點懷念Object-C

2010年11月4日 星期四

Button Array

我想建一個如下圖所示的對話盒界面。


當我在Resource Edit中double click [Method 1]的check box,Visual Studio 會自動幫我們加入相對應的click message函式

afx_msg void OnBnClickedCheckMethod1();

與其Message Map

ON_BN_CLICKED(IDC_CHECK_METHOD_1, &CSetCompareMethodDlg:: OnBnClickedCheckMethod1)

我程式中的功能會將此時的相對應Method元件設為enable或disable

void CSetCompareMethodDlg::OnBnClickedCheckMethod1()
{
    if(CButton* pCheck = (CButton*)GetDlgItem(IDC_CHECK_METHOD_1))
    {
        m_pObj[0]->Enable((pCheck->GetCheck() == 0) ? false : true);
    }
}

以我的例子而言,我要做8個非常類似的函式,其中只有ID與index不同。這並不會造成我們什麼困擾只是程式的一點怪味而已。

那,該如何讓這個動作變得優雅呢。

關鍵在於能否讓UI的動作包含ID。我們可以用ON_COMMAND_EX取代ON_BN_CLICKED來改寫。

1. 在class宣告中加入

afx_msg BOOL OnBnClickedCheckMethodEX(UINT uid);

2. 把

ON_BN_CLICKED(IDC_CHECK_METHOD_1, &CSetCompareMethodDlg:: OnBnClickedCheckMethod1)

改寫為

ON_COMMAND_EX(IDC_CHECK_METHOD_1, &CSetCompareMethodDlg::OnBnClickedCheckMethodEX)

同樣的動作把IDC_CHECK_METHOD_2 ~IDC_CHECK_METHOD_8都用ON_COMMAND_EX取代

3. 實作OnBnClickedCheckMethodEX

BOOL CSetCompareMethodDlg:: OnBnClickedCheckMethodEX(UINT uid)
{
    if(CButton* pCheck = (CButton*)GetDlgItem(uid))
    {
        // m_pObj[index]->Enable((pCheck->GetCheck() == 0) ? false : true);
    }

    return TRUE;
}

4. 現在的問題是如何把uid和index對應起來,有2種方法可行。如果你無法更動Resource.h的話。

BOOL CSetCompareMethodDlg:: OnBnClickedCheckMethodEX(UINT uid)
{
    const UINT nCheckID[8] = {
        IDC_CHECK_METHOD_1, IDC_CHECK_METHOD_2, IDC_CHECK_METHOD_3, IDC_CHECK_METHOD_4,
        IDC_CHECK_METHOD_4, IDC_CHECK_METHOD_6, IDC_CHECK_METHOD_7, IDC_CHECK_METHOD_8
    };

    for(int index=0 ; index<8 ; index++)
    {
        if(uid == nCheckID[index])
        {
            if(CButton* pCheck = (CButton*)GetDlgItem(uid))
            {
                pObj[index]->Enable((pCheck->GetCheck() == 0) ? false : true);
            }

            return TRUE;
        }
    }
    return FALSE;
}

如果你可以更動Resource.h的話,我會先到Resource.h中把ID做排序

#define IDC_CHECK_METHOD_1 1004
#define IDC_CHECK_METHOD_2 1005
#define IDC_CHECK_METHOD_3 1006
#define IDC_CHECK_METHOD_4 1007
#define IDC_CHECK_METHOD_5 1008
#define IDC_CHECK_METHOD_6 1009
#define IDC_CHECK_METHOD_7 1010
#define IDC_CHECK_METHOD_8 1011

然後函式能寫的更簡捷

BOOL CSetCompareMethodDlg::OnBnClickedCheckMethodEX(UINT uid)
{
    int index = uid - IDC_CHECK_METHOD_1;

    if(CButton* pCheck = (CButton*)GetDlgItem(uid))
    {
        pObj[index]->Enable((pCheck->GetCheck() == 0) ? false : true);

        return TRUE;
    }

    return FALSE;
}

當然要改ID你必需要很小心很小心。

2010年10月22日 星期五

MFC PropertyGrid Control

我的工作經常要做些測試用的程式來調整參數或是驗証功能,這其中最大的UI使用就是拉出一堆Edit Box來做為輸入的界面,有時候為了美觀(or自虐)會把這些參數設定做成像Visual Studioproperty page的功能,可能我是個老傢伙了,我的作法還停留在太古時代,先繼承自ClistBox產生我自己的property grid class,然後再用subclass的方法,把原先resource上的list換成我寫好的property grid class以產生出我要的property grid style class。嗯,這還不是最麻煩的,最麻煩的是property必需要能edit吧,所以我還要偵測mouse的點擊動作,當點在edit區時create一個edit box把他蓋在原本的位置上方讓,還記得我第一次作出這樣功能時是VC 4.2時代吧,想不到這個class用了10年還在用真是一點都不長進。

我這元件只能單純的輸入文字而已,想說10多年了,來加個新功能把combo box也弄進來試試,結果

我在拉元件時不小心撇見了Visual Studio 2010Toolbox中的一角

難道說時代已經進步到這東西己是MFC內建的元件了嗎,癈話不多說直接拉元件就上了

例用Add Member Variable Wizard把這元件加進來方便操作。

Dialog::OnInitDialog中把我要的property加入

BOOL CSetCompareMethodDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    CMFCPropertyGridProperty* pProp = new CMFCPropertyGridProperty(_T("Different Threshold"),_T("1"));

    pProp->AllowEdit(TRUE);

    m_wndPropClass.AddProperty(pProp);
    m_wndPropClass.AdjustLayout();

    return TRUE;  // return TRUE unless you set the focus to a control
}

結果,好像有點怪怪的


首先,我有設定border,但似乎沒畫出來,其次就是property namewidth似乎是太太太太小了。

研究了這2class的相關成員發現似乎沒有辦法指定我要的欄位大小(很神奇,可以get不能set這不像是M$style,只能用AdjustLayout讓他自己算),border style也嘗試了幾種組合也都無法顯現。在計無可施下,突然有一個聲音,告訴我這是M$bug, bug, bug, bug…,可能是DDX的交握出了問題,我把他改成用老方法subclass試試

BOOL CSetCompareMethodDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    CWnd* pWnd = GetDlgItem(IDC_PROPERTYGRID_METHOD);
    CRect rectPropList;
    pWnd->GetClientRect(&rectPropList);
    pWnd->MapWindowPoints(this, &rectPropList);

    m_wndPropClass.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, rectPropList, this, (UINT)-1);

    m_wndPropClass.EnableHeaderCtrl(FALSE);
    m_wndPropClass.SetVSDotNetLook(FALSE);
    m_wndPropClass.MarkModifiedProperties(TRUE);
    m_wndPropClass.SetAlphabeticMode(FALSE);
    m_wndPropClass.SetShowDragContext(FALSE);
   
    CMFCPropertyGridProperty* pProp = new CMFCPropertyGridProperty(_T("Different Threshold"),_T("1"));

    pProp->AllowEdit(TRUE);

    m_wndPropClass.AddProperty(pProp);
    m_wndPropClass.AdjustLayout();

    return TRUE;  // return TRUE unless you set the focus to a control
}

別忘了把原本的DDX斷掉

void CSetCompareMethodDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    //  DDX_Control(pDX, IDC_PROPERTYGRID_METHOD, m_wndPropClass);
}

正確的style出現啦

如果想要做出combo的效果,就把想加入的CMFCPropertyGridProperty objectAddOption的方式一項一項的加進去,也可以用AddSubItem做成樹狀結構。相關可使用的調整還很多,有興趣可參考相關連結。

CMFCPropertyGridProperty的生成方式很像Composite的做法,特別是要做成樹狀的結構時,非常的容易操作。以我的懶性而言,要做出樹狀應該是不可能吧。

create的方式建立的CMFCPropertyGridProperty必需設resouceinvisible否則會產生2個property,一個無法控制。


2010年10月18日 星期一

Manifest Style

從.Net 2005之後,AppWizard在產生MFC application時,多了[Common Control Manifest]的選項

使用Manifest產生的對話盒元件看起來比較圓滑舒服,不像之前的3D按鈕那樣的生硬,下圖左是使用Manifest產生的About對話盒,下圖右為傳統的Style。

使用Manifest的project,在stdafx.h中會多出一段碼:

#ifdef _UNICODE
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else

#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif

#endif

如果是使用Multi-Byte的project,即使選擇了Manifest,產生的對話盒元件就仍然是傳統型式。

事實上,我把#ifdef _UNICODE這行直接註解掉,Multi-Byte程式還是可以正常執行,而且對話盒就變成Manifest的Style了。所以,這段的UNICODE保護到底有沒有用仍然是個謎?

2010年10月5日 星期二

設定Folder Browser的初始路徑

想不到只是要設定Folder Browser的初始路徑居然要用到回呼(callback)函式

首先必需建立一個Callback Function讓Folder Browser初始化時呼叫,並送出BFFM_SETSELECTION來指定路徑(即我們的初始化路徑),注意BrowseCallbackProc必需為static。

int CALLBACK CMainFrame::BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
{
  switch(uMsg)
  {
  case BFFM_INITIALIZED:
    ::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,lpData);
    break;
  default:
    break;
  }
  return 0;  
}


指定我們的路徑與Callback Function

BROWSEINFO info = { 0 };
info.lpfn = BrowseCallbackProc;
info.lParam = (LPARAM)(strDefault.GetString());


呼叫SHBrowserForFolder產生Folder Browser

if(PIDLIST_ABSOLUTE pIDL = ::SHBrowseForFolder(&info))
{
  if(::SHGetPathFromIDList(pIDL,(LPSTR)&szPath))
  {

    ::CoTaskMemFree(pIDL);

    AfxGetApp()->OpenDocumentFile(szPath,FALSE);
  }
}


執行結果如下圖

2010年10月3日 星期日

初試Visual Studio 2010平行處理函式 (PPL)

VS2010發表時很強調他的平行處理函式的功能,因此我也嘗試看看是不是真如廣告說的那麼神奇。測試使用一張8MB(32bit)的影像進行500次的平滑化處理。

平滑化處理公式為

I'(x) = (I(x) - dc) * W(x)

首先,必載入平行處理函式的檔頭,由於這是2010才支援的功能,我會先判斷_MSC_VER是否為1600以上的版本,以確認編譯器有支援

#if  _MSC_VER >= 1600
#include "ppl.h"

using namespace Concurrency;
using namespace std;
#endif  //  _MSC_VER

接著將原有的

unsigned long dwPos = 0;
long value = 0;

for(unsigned long i=0 ; i<dwSize ; i++)
{
    value = (((pIData[dwPos] - DC[0]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[1]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[2]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos+=2;
}

改寫為

unsigned long dwPos = 0;
long value = 0;

parallel_for(unsigned long(0),dwSize,[&](unsigned long i)
{
    dwPos = (i << 2);

    value = (((pIData[dwPos] - DC[0]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[1]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[2]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos+=2;
});

這裡我犯了一個明顯的錯誤,在做並行化處理時必注意資料必需保持獨立性,共享資料則必需唯讀或同步化,上述的程式執行後,影像上會有明顯的雜點,主要原因是共享資料dwPosvalue並未保持唯讀的特性,使得資料混亂。

解決方案為將dwPosvalue變為local的變數不共享,程式修改如下:

parallel_for(unsigned long(0),dwSize,unsigned long(1),[&](unsigned long i)
{
    for(int c=0 ; c<3 ; c++)
    {
        unsigned long dwPos = (i << 2) + c;

        long value = (((pIData[dwPos] - DC[c]) * pWeight[dwPos]) >> 8);

        if(value < 0)         pOData[dwPos] = 0;
        else if(value > 255)  pOData[dwPos] = 255;
        else                  pOData[dwPos] = (unsigned char)value;
    }
});
 
先在Core dual 2.2G + XP系統上執行,比較serial / parallel上執行的效能

serial:       0.01639851 sec / frame
paralle:     0.03472216 sec / frame

在雙核主機上serial的效能比parallel

換成 Coro i7 920 2.7G + XP64再次比較
serial:       0.01312059
parallel:    0.01394287

平行處理的效能與使用單處理器相當,也就是說,單純使用平行處理函式的速度並未有提升而且可能降低原有的效能。看來事實並不如Microsoft廣告說的如此簡單,要發揮出真正的效能仍有不少最佳化的動作要做。

2010年9月29日 星期三

設定class的const member初值

設計一個Circle類別,其中有成員常數PI。

class CCircle
{
protected:
    const double PI;

public:
    CCircle(void);
};

假如我在建構子以如下方式給定PI = 3.14159265的話

CCircle::CCircle(void)
{
    PI = 3.14159265;
}

在VS會產生error C2758: 'CCircle::PI' : must be initialized in constructor base/member initializer list

正確的寫法為

CCircle::CCircle(void) : PI(3.14159265)
{
}

2010年9月8日 星期三

安裝DirectShow 小技巧一

我經常開發一些DirectShow的Filter,在測試時就像開發COM元件使用ActiveX Control Test Container來測試元件的完整性一樣,完成的DirectShow Filter我會使用DirectShow SDK Filter Graph Editor來測試。

我們可以透過以下的方法,把GraphEdit放在Visual Studio 2010的選單中。

1. 點選Visual Studio 2010的選單[Tools] -> [External Tools...]

2. 點選後會出現[External Tools]對話盒,依圖中的1、2、3順序完成設定
(預設的路徑為C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\graphedt.exe)

3. 選單中多出了DirectShow SDK Filter Graph Editor的選項,點選後GraphEdit視窗出現了


2010年9月2日 星期四

安裝DirectShow開發工具

如果你使用的工具是Visual Studio 2010或之後的版本,建議使用Windows SDK 7.0以後的版本。DirectShow目前已整合在SDK中,不像以前是DirectX的擴充元件。

微軟的版本可能會持續更新,在寫這篇文章時我所使用的是

Microsoft Windows SDK for Windows 7 and .NET Framework 4

依預設的下載安裝完成後,可以在

C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow

找到DirectShow的範例程式。

完成安裝後的第一件事是建立BaseClasses Library,這個Library是以後要使用DirectShow時都會使用到的程式庫,裡面有許多已包裝好的類別可加速程式的開發。BaseClasses應該是安裝在

C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses

可依以下方法建立

1. 使用Visual Studio 2010開啟baseclasses.vcproj專案。

2. 確認後會出現專案轉換精靈協助我們把專案轉為Visual Studio 2010的格式。

3. baseclasses包含有4種不同的設定,分別為:Debug、Debug_MBCS、Release、Release_MBCS。

Debug / Release與Debug_MBCS / Release_MBCS的差別在一個使用Unicode別一個使用Multi-Byte。我較習慣使用Multi-Byte,所以將建置Debug_MBCS與Release_MBCS版本。
以前我使用Visual Studio 2005建立64-bit程式時發現,使用Unicode建立的程式在XP-64系統上執行都會出現模組錯誤的訊息無法執行,所以我會很習慣的都把專案改為Multi-Byte。
4. 選擇Debug_MBCS後執行[Build Solution]

理論上會出現2個編號 MSB8012的warning

這是因為VS2010的project proterty新增了TargetName的欄位,我嘗試用以下方式修正:

a. 從選單的[Project] -> [Properties]開啟properties視窗
b. 在[Configuration Properties] -> [General]中找到[Target Name]欄位
c. 將[Target Name]欄位中原本的$(ProjectName)改為strmbasd
d. 確定後重新執行[Build Solution]。

5. 改成Release_MBCS後依上面步驟,但將[Target Name]設為strmbase後執行[Build Solution]。

6. 你可以在專案的目錄下找到Debug_MBCS與Release_MBCS,而建立好的strmbasd.lib與strmbase.lib檔就放在其中。要特別注意,當連結使用DirectShow的baseclasses時是有分Debug與Release模式,若使用的是Debug設定,需連結strmbasd.lib反之使用strmbase.lib才能避免許多錯誤。


我在做完BaseClasses後通常會做2個步驟方便日後使用。

首先,我會把strmbasd.lib與strmbase.lib複製到C:\Program Files\Microsoft SDKs\Windows\v7.0A\Lib目錄下。

其次,我會在C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include目錄下建立directshow\baseclasses子目錄,並且把baseclasses中所有的Header Files都複製到此目錄下。

會這麼做而不是直接連結到專案目錄,是因為我有時會追蹤baseclasses的一些行為而加入一些程式碼,為避免不穩定的情形所以把最原使可運作的Library與Header Files保持在v7.0A的子錄目下。正常情況下都會連結此目錄下的版本來使用。