Sheng's profile蒋晟(JIANG,Sheng), a Micro...PhotosBlogListsMore Tools Help

Blog


    May 19

    the Windows Server 2008 SP2 and Windows Vista SP2 Beta program has concluded.

    Nice run

    uninstalling pre-release SP and installing the RTM version…

    May 03

    Talking about Windows XP APIs

     

    Quote

      

    March 24

    Walkaround for Error : An add-on for this website failed to run. When opening Visual C++'s Add Variable Wizard after IE 8 is installed

    Download: http://jiangsheng.net/Documents/IE8AddZone.zip

    Prerequisite: Microsoft Visual C++ 2005 SP1 Redistributable Package (x86)

    This program is designed to temporary circumvent the "An add-on for this web site failed to run. Check the security settings in Internet options for potential conflicts" error after installing IE8. For more details about this problem, visit https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=425510

    Update: the VC team's workaround is at http://blogs.msdn.com/vcblog/archive/2009/03/28/some-vs2005-and-vs2008-wizards-pop-up-script-error.aspx. However, I still suggest copying other settings from a restricted zone before modify the 1207 key.

    Clicking the create button will create another Internet Explorer security zone with the id 1000 that will affect all urls not included in other zones. This will allow separate security settings for webbrowser control hosting application such as Visual Studio. If settings are copied from a trusted configuration such as "my computer",a webbrowser control hosting application may not experience the aforementioned error.

    Security Remarks: do not visit unsecured web site or use email software on the same computer after creating the new zone as the security impact of the new zone is not yet tested. Applications that host webbrowser control should implement their own security manager if they want to run ActiveX in the browser control.

    If you received a fix from the IE team for this problem, please delete the zone created by this problem by clicking the delete button. Visit https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=425510 for the status of this problem.

    For more information about adding zones to IE, visit http://www.nthelp.com/50/addazone.htm .

    For more information about Internet Explorer security zones, visit http://support.microsoft.com/kb/182569

    For more information about Visual C++ wizards, visit http://msdn.microsoft.com/en-us/library/aa730846(VS.80).aspx

    Suspicious url actions from Regmon logs:

    • 2700 URLACTION_INPRIVATE_BLOCKING (Don’t think Visual Studio know what is the private mode)
    • 2106 URLACTION_FEATURE_DATA_BINDING (a lot of these in the registry log)
    • 1207 URLACTION_ACTIVEX_OVERRIDE_REPURPOSEDETECTION (not sure what this is doing, but it is the only one between URLACTION_ACTIVEX_MIN and URLACTION_ACTIVEX_MAX)
    • 160A URLACTION_HTML_INCLUDE_FILE_PATH
    February 14

    415 Unsupported Media Type when WSE is NOT configured

    I have a web service that runs fine on my Windows XP. However, when I deploy to the production server, the web service returns 415 Unsupported Media Type when calling.

    I have seen this error when WSE is not enabled on the client. The problem is, the web service is NOT using WSE. so I did the usual, uninstall ASP.Net, reinstalling, adding asmx extension to IIS, same error.

    Now I need to fire a debugger to see what’s going on. Surprisingly, Microsoft.Web.Services3.dll is loaded even when there is no trace of it in my projects. Now I probably know what’s going on. There is another web service in a different virtual directory that uses WSE.

    OK, I will isolate my web service to a new application pool. Well, that does not help. In the end I have to add WSE configuration to both my web service and my Windows client.

    February 03

    Links

    January 30

    Microsoft MVP, MSDN Forum Influencer

    今年微软最有价值专家的奖品好重,是一个很大的水晶奖杯

    20090129234731

    后面那个红红的忘记是去年的最有价值专家还是最有影响力开发者的奖杯了,前后脚到的。微软中国也比较浪费,不同项目的奖品也不一起邮寄。

    刚刚又收到英文MSDN论坛的通知说我是MSDN Forum Influencer,奖励一个背包,我的2004年MVP奖的那个背包要挂了,正好能用上。

    October 11

    Visual C++ 10 和 MFC 10的新特性

    Boris Jabes 和Damien Watkins将会在PDC上演示Visual C++10中的新功能。IDE的新功能包括基于SQL Compact的智能提示支持自定义插件的新的项目和编译系统、面向大型应用的优化和改善的调试体验。MFC库增加了对Windows 7 中新增的多点触摸检测功能高DPI支持,以及Windows Vista中集成的功能,例如高彩图标Windows 搜索重启管理器。Visual C++程序员们才习惯不用MFC来直接调API。

    MFC是很老了,不容易学,也不优雅,但是很稳定,也有很多第三方扩展和示例支持。其他的用户界面库还有很多,但是单单用户界面并不能完成一个程序。在调用操作系统的底层功能的时候,有一个面向对象的接口还是很方便的。

    October 05

    Type 'System.Web.UI.WebControls.Parameter' does not have a public property named 'DbType'

    In Visual C# 2005 SP1, I added an object data source to a web page that uses my business class as the select method. The method has one parameter of type Guid. The data source wizard generates code like this

    <asp:Parameter DbType="Guid" Name="rowId" />

    Although the web server has .Net 2.0 SP1 installed (I checked the registry), it still throws an error

    Type 'System.Web.UI.WebControls.Parameter' does not have a public property named 'DbType'

    The walk around is easy:

    <asp:Parameter Type="Object" Name="rowId" />

    September 08

    When Microsoft Office Live Meets Google Chrome

    To use Microsoft Office Live, your computer must meet one of the following requirements:

    Microsoft Internet Explorer 6 or 7, running on Microsoft Windows XP, Windows Server 2003 or Windows Vista. You can download Internet Explorer from the Windows Internet Explorer page.
    Mozilla Firefox running on Windows XP, Windows Server 2003, Windows Vista, or Mac OS X 10.2.x and later. You can download Firefox from the Firefox download page.

    April 07

    MFC Feature Pack发布

    Visual C++项目组今天发布了Visual C++ 2008 Feature Pack。这个Feature Pack包含了一些以前需要付费给BCG Soft才可以使用的控件,例如BCG著名的窗口布局和风格自定义功能,不过也有一些有用的控件,例如文件夹列表文件夹树属性窗格等等。

    这个Feature Pack也包含从Dinkumware获得授权的一些对STL的扩展,实现了TR1草案。这包含新的随机算法、集合类和正则表达式支持。关于TR1的更多信息,可以参考Dinkumware的网站

    安装了这个Feature Pack之后,生成的应用程序在发布时需要同时发布新版本的MFC和CRT组件。预计对这个Feature Pack的技术支持策略会和VC6中从Dinkumware获得授权的STL库会是一个级别,也就是说,BCG Soft、Dinkumware和微软都会提供技术支持。如果在安装了这个Feature Pack之后需要安装Windows SDK 6.1,那么在安装完SDK之后需要修复Visual Studio 2008以保持文件是最新的。

    目前发布的这个补丁只支持英文版的Visual Studio 2008 标准版或更高版本,其他语言版的Visual Studio 2008 要获得这些新的功能的话,需要等到Visual Studio 2008 SP1发布。Visual Studio 2008会包含这个Feature Pack。

    March 24

    Detect if a MSI component is installed

    A C# program for those who don't know MSI SDK or C++. C++ programmers can find the API inside the msi.h  file in Windows SDK.

    class Program
    {
        static int Main(string[] args)
        {
            uint pathSize = 0;
            try
            {
                foreach (string componentId in args)
                {
                    MsiInstallState state = MsiLocateComponent(
                        componentId, null, ref pathSize);
                    if (state != MsiInstallState.Local)
                    {
                        return ERROR_UNKNOWN_COMPONENT;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
                return ERROR_MOD_NOT_FOUND;
            }
            return 0;
        }
        [DllImport("msi.dll", CharSet = CharSet.Auto)]
        extern static internal MsiInstallState MsiLocateComponent(string component, string path, ref uint pathSize);
        /// <summary>Enumeration of MSI install states.</summary>
        internal enum MsiInstallState : int
        {
            Local = 3
        }
        const int ERROR_MOD_NOT_FOUND = 126;
        const int ERROR_UNKNOWN_COMPONENT = 1607;
    }

    March 17

    Hard Drive broke

    The first 3 Dell support technicians tried to persuade me to clean the temp files, to remove unnecessary startup programs and to reinstall Windows. It does not help if the computer complains an error during self test. I keep saying a self test error can not be solved by twisting the OS, but they won't listen. After educating the representatives for a week I finally got one that has better experience (and better English) to examine my computer. A full system test shows bad sectors on the hard drive, and totally toasted the hard drive. Luckily I just bought the computer for 6 months so I asked for a replacement. Dell sent me one in 2 days and I sent back the broken one.

    Now my back up hard drive started to fail ... Time to call Seagate.

    January 09

    Smart harddrive error

    In case anyone is googling this message
    Hard Drive SELF MONITOR SYSTEM has reported that a parameter has exceed its normal operating range
    Dell recommends that you back up your data regularly
    Press F1 to continue, Press F2 to enter setup
     
    Run event log and look for warning messages. I have several messages looked like this
    C:\Program Files\Microsoft Windows OneCare Live\Database\WinSS_st.edb (3612) C:\Program Files\Microsoft Windows OneCare Live\Database\WinSS_st.edb: A request to read from the file "C:\Program Files\Microsoft Windows OneCare Live\Database\WinSS_st.edb" at offset 180224 (0x000000000002c000) for 4096 (0x00001000) bytes succeeded, but took an abnormally long time (60 seconds) to be serviced by the OS. In addition, 0 other I/O requests to this file have also taken an abnormally long time to be serviced since the last message regarding this problem was posted 0 seconds ago. This problem is likely due to faulty hardware. Please contact your hardware vendor for further assistance diagnosing the problem.
     
    msnmsgr (2980) \\.\C:\Users\jiangsheng\AppData\Local\Microsoft\Messenger\<windows live id>\SharingMetadata\Working\database_8E6C_5899_6C58_7E41\dfsr.db: A request to write to the file "\\.\C:\Users\jiangsheng\AppData\Local\Microsoft\Messenger\<windows live id>\SharingMetadata\Working\database_8E6C_5899_6C58_7E41\fsr.log" at offset 19456 (0x0000000000004c00) for 512 (0x00000200) bytes succeeded, but took an abnormally long time (75 seconds) to be serviced by the OS. This problem is likely due to faulty hardware. Please contact your hardware vendor for further assistance diagnosing the problem.
     
    The reaction is obvious: contact Dell Customer Support.
     
    thankfully, I have dialy incremental backup and weekly full system backup, but my dialy backup has been failing. So I will lose a week's data, not a big deal.
    January 07

    MFC更新Beta版

    一个面向Visual C++ 2008的MFC更新测试版已经发布,同时也提供了文档的下载。这个版本包含新的界面的特性,例如Office Ribbon、2003和XP风格,Visual Studio风格和MDI标签。另外,这个版本也包含部分TR1的实现,例如正则表达式、更加丰富的集合和智能指针。

    另外,在下载页面居然说这个版本还不支持Visual Studio 2008 Service Pack 1的Beta版,正式版才出来几天SP1的测试版就出来了?

    September 18

    科学,我的信仰?

    花が咲き そして散る
    Flowers open out and then they fade
    花开,然后花落
    星が輝き やがていつかは消える
    Stars shine and later or sooner they go off
    星光闪耀,不知何时熄灭
    この地球も 太陽も 銀河系 大いなる宇宙も いつか死するときがくる
    Both this Earth, the Sun, the Milky Way, and even for the big Universe which is growing, later or sooner the time to die will come
    这个地球,太阳,银河系,甚至整个宇宙也总会有消失的时候
    人間の一生などそれらに比べれば瞬きほどの僅かなものであろう
    And compared to these things, human life is insignificant.
    人的生命和那些相比只不过是一瞬间吧
    その僅かなひとときに 人は生まれ
    In that little moment, a man is born
    在那一瞬间中,人诞生
    笑い 涙し 戦い 傷つき 喜び 悲しみ 誰かを憎み 誰かを愛し
    Laughs, cries, fights, is injured, feels joy and feels sadness, loves someone, hates someone
    微笑,哭泣,战斗,伤害,喜悦,悲伤,憎恨谁,喜欢谁
    全ては刹那の邂逅
    Everything is random and in a instant
    所有的一切都是刹那间的邂逅
    そして誰しもが死という永遠の眠りに包まれる
    And, in the end, covered by an eternal sleep called Death.
    谁都不能逃脱死亡的长眠
    ——沙加在沙罗双树涅磐时的遗言,引自《圣斗士·冥王篇·沙罗双树下之卷》
    昨天有位朋友问我,你到底信仰什么,我当时的回答是科学;我不信仰一切把自然科学解释不了的现象归类于一个超自然的存在的宗教。我当时的逻辑是超自然是无法被定义的。这是因为你可以定义一个东西是什么,但是你不能定义一个东西为不是什么,因为定义是确定的,而定义的反面(不是什么)的范围是无法确定的。后来想想,我自己对我的信仰下的定义也不是很准确,因为科学是在不断发展的。
    毕达哥拉斯曾认为万物之源是数,但是他对数的定义仅限制于有理数。当他的学生希帕索斯用毕达哥拉斯定理发现当两个直角边长度都是1时斜边的长度是不是一个有理数的时候,这个发现震撼了整个毕达哥拉斯学派的根基。在传说中,希帕索斯被愤怒的毕达哥拉斯学派的人沉到海中淹死了。现在我们知道除了无理数之外还存在复数,在高维空间内我们还有超复数。人的信仰,即使是一个数学伟人的信仰,也阻挡不了数学发展的脚步。
    再举一个例子。几何学是基于一系列假设(数学上叫做公理)的。在欧几里得的《几何原本》中,被称为平行公理的第五条公理(或者其等价命题)表述如下:
        通过一个不在直线上的点,有且仅有一条不与该直线相交的直线。 
    这个定理只在证明第二十九个命题中才用到,以后再也没有用到。也就是说,看起来欧几里得几何中此定理的地位可有可无。关于第五公理的地位争论了两千多年,终于在十九世纪二十年代得到解决。俄国数学家罗巴切夫斯基试图用反证法证明这个命题。他用一个和第五公理冲突的公理替代了第五公理,然后试图推导出一个自相矛盾的理论体系。和他的预期相反,他推导出了一门新的几何学,在后来被称为罗巴切夫斯基几何。如果认为空间的曲率是可以变化的,那么第五公里就可以被去掉,而欧几里德几何和罗巴切夫斯基几何成为鲍耶提出的绝对几何在常曲率空间的特例。
    我们上学的时候都学过,太阳系有九大行星。然而在2006年,国际天文家联合会将冥王星从行星中除名,太阳系只剩下八大行星。如果客观真理可以这样更改的话,究竟什么才是科学?建立在科学方法上的主流意见就是科学么?我们都知道哥白尼这个反例了。另外,科学真的可以用科学手段来验证么?像恒星的演化这样的课题,短命的人类永远只能用理论来解释观测到的现象,没有人能有足够的寿命来验证恒星演化论的真伪。科学,不过是人类对有限的世界的解读而已。
    我对这个问题没有答案。我对那个朋友的回答可能需要更改一下,我可能信仰客观唯心主义。我认为物质的存在不能用客观的方式来证明,而只能用主观的理论来表述。我认为科学是建立在一系列假设上的,也只能描述基于这些假设的世界。科学越发展,可以描述的现象就越多,但是——引用一下哥德尔第二条定理——任何相容的形式体系不能用于证明它本身的相容性,科学是不能用科学方法来验证的。
    August 05

    AutoComplete with DataSource

    Download the code for this article (22KB)

    .Net 2.0 introduced autocompletion in TextBox and ComboBox. It is obvious that autocomplete is not very useful when the number of options is small. However, when your option becomes too many, pre-filling of all options to an AutoCompleteStringCollection becomes impractical, especially when your data comes from a remote computer. An alternative is to replace the AutoCompleteCustomSource in a TextChanged event, however, users are getting random AccessViolationException when trying to replace it.  In this article I will demonstrate another alternative, using a BindingSource as the data source of options and bypassing the .Net framework and call the underline Windows API directly.

    The first thing I need to do is to port the APIs to managed code. The autocompletion API is exposed as a COM object, so I need to write managed version of its interfaces:

        [ComImport]
        [InterfaceType(ComInterfaceType::InterfaceIsIUnknown)]
        [Guid("EAC04BC0-3791-11D2-BB95-0060977B464C")]
        [SuppressUnmanagedCodeSecurity]
        interface  class IAutoComplete2
        {
                 [PreserveSig] int Init(
                HandleRef hwndEdit,
                IEnumString^ punkACL,
                 String^ pwszRegKeyPath,           
                String^ pwszQuickComplete
            );
            void Enable( [MarshalAs(UnmanagedType::Bool)] bool value);

            int SetOptions(int dwFlag);

            void GetOptions([Out]IntPtr pdwFlag);
        };
    Second, I need to create the autocomplete object and query the IAutoComplete2 interface:

           Type^ autoCompleteType = Type::GetTypeFromCLSID(CLSID_AutoComplete);
            try{
                autoComplete2 =(IAutoComplete2^)(Activator::CreateInstance(autoCompleteType));
            }
            catch(Exception^ e)
            {
                Marshal::ReleaseComObject(autoComplete2);
                autoComplete2 = nullptr;
            }

    Third, I need to bind it to an TextBox control:

        bool AutocompleteBindingSource::Bind()
        {
            if (nullptr==this->autoComplete2)
                return false;
            try
            {
                this->autoComplete2->SetOptions((int)ControlToBind->AutoCompleteMode);
                this->autoComplete2->Init(
                    HandleRef(ControlToBind,ControlToBind->Handle),
                    this,
                    String::Empty
                    ,String::Empty);
                return true;
            }
            catch(Exception^e)
            {
                return false;
            }

        }

    Finally, I need to implement IEnumString to provide a list of options. Luckily, .Net has declared this interface, so I don't need to port it to managed code, however, I still need to write my binding code in my implementation of IEnumString.

        void AutocompleteBindingSource::Reset()
        {
            this->current = 0;
            if(BindingSource!=nullptr)
                this->size=BindingSource->Count;
        }

            int AutocompleteBindingSource::Next(
            int celt, [Out, MarshalAs(UnmanagedType::LPArray, ArraySubType=UnmanagedType::LPWStr, SizeParamIndex=0)] array<String^>^ rgelt, IntPtr pceltFetched)
        {
            if (celt < 0)        {
                return E_INVALIDARG;
            }
            int index = 0;
            while ((this->current < this->size) && (celt > 0))
            {
                Object^ item=this->BindingSource->default[this->current];

                bool useDisplayMember=false;

                if(!String::IsNullOrEmpty(DisplayMember))
                {
                    ICustomTypeDescriptor^ customTypeDescriptor=dynamic_cast<ICustomTypeDescriptor^>(item);
                    if(customTypeDescriptor!=nullptr)
                    {
                        PropertyDescriptorCollection^ propertyDescriptorCollection=
                            customTypeDescriptor->GetProperties();
                        if(propertyDescriptorCollection!=nullptr)
                        {
                            PropertyDescriptor^ propertyDescriptor=propertyDescriptorCollection->default[DisplayMember];
                            if(propertyDescriptor!=nullptr)
                            {
                                rgelt[index] = propertyDescriptor->GetValue(item)->ToString();
                                useDisplayMember=true;
                            }
                        }
                    }
                }

                if(!useDisplayMember)
                {
                    if(item!=nullptr)
                    {
                        rgelt[index] = item->ToString();
                    }
                }
                this->current++;
                index++;
                celt--;
            }
            if ((pceltFetched != IntPtr::Zero))
            {
                Marshal::WriteInt32(pceltFetched, index);
            }
            if ((celt != 0))
            {
                return 1;
            }
            return 0;

        }

    Here the DisplayMember property is the name of the property in the data source to be displayed. If the property specified by the value of the DataMember property does not exist, I use ToString to get a text representation of the current item in the data source.

    You may want to ask, where is the filtering code? Well, that is implemented by BindingSource class.

           System::Void FormTest::textBoxDemo_TextChanged(System::Object^  sender, System::EventArgs^  e)
        {
            static bool inThisFunction=false;
            if(!inThisFunction)
            {
                inThisFunction=true;
                if(String::IsNullOrEmpty(textBoxDemo->Text))
                    bindingSourceAutoComplete->Filter=nullptr;
                else
                {
                    System::String^ addText=textBoxDemo->Text+"og/NextElement";
                    dataSetDemo->Tables[0]->DefaultView->Sort="Text";
                    if(dataSetDemo->Tables[0]->DefaultView->FindRows(addText)->Length==0)
                    {
                        System::Data::DataRow^ row=dataSetDemo->Tables[0]->NewRow();
                        row->default[0]=addText;
                        dataSetDemo->Tables[0]->Rows->Add(row);
                    }
                    bindingSourceAutoComplete->Filter=
                        String::Format("{0} LIKE '{1}%'"
                        ,dataSetDemo->Tables[0]->Columns[0]->Caption
                        ,textBoxDemo->Text);
                }
                if(textBoxDemo->SelectionStart>0)
                {
                    autocompleteBindingSource1->Reset();
                    autocompleteBindingSource1->Bind();
                    String^ text=textBoxDemo->Text;
                    int selectionStart=textBoxDemo->SelectionStart;
                    int selectionLength=textBoxDemo->SelectionLength;
                    textBoxDemo->SelectionStart=0;
                    textBoxDemo->SelectionLength=0;
                    textBoxDemo->SelectAll();            
                    System::Windows::Forms::SendKeys::SendWait("{BACKSPACE}");
                    textBoxDemo->Text=text;
                    textBoxDemo->SelectionStart=selectionStart-1;
                    textBoxDemo->SelectionLength=selectionLength+1;
                    System::Windows::Forms::SendKeys::SendWait(textBoxDemo->SelectedText);
                }
                inThisFunction=false;
            }
        }

    Somehow Windows caches the result. If I don't clear the text, my IEnumString implementation won't be asked again for candidate strings (pointed out by Andy Gilman). 

    The BindingSource class checks the data source to see if they support the IBindingListView. If IBindingListView is supported, the BindingSource class delegates sorting and filtering to the data source.  In this sample, the data source of the BindingSource object is a DataSet, and the DataMember of BindingSource object is the name of the first table , so BindingSource creates a DataView as its data source. The DataView class implements IBindingListView and filters its data using expressions parsed from the filter string. In reality, the data source could be a business object that implements IBindingListView and supports filtering and sorting with stored procedures.

    This sample does not consider compound autocomplete object support. If you want to get your options from multiple sources, you need to use IObjMgr to add sources to the autocomplete object.

    Reference

    July 29

    Visual C++ 2008 Beta2 里面的Class Designer

    image

    Visual Studio 2008 Beta2中的Class Designer终于支持C++了,上面是一个MFC程序的类图,可以看到已经支持扩展MFC的宏了,可惜只能看不能重构代码。尽管Class Designer这功能相当不错,但是设计师们可能还是更习惯IBM 的Rational Rose Developer for Visual Studio和UML。我用Class Designer的C#支持的时候也就是加加注释而已,重构我更习惯用DevExpress提供的工具Refactor来做,类则用XSD.exe生成,因为Class Designer生成的属性只会扔NotImplementedException异常。

    Visual C++项目组在做下一个版本的市场调查,有兴趣的可以去提提要求。

     

     

    July 03

    Handle NewWindow3 and ShowModalDialog in CHtmlView

    CHTMLView does not support NewWindow3 as of MFC 9.0. It is relatively easy to add this support, given the event sink code in atlmfc\src\viewhtml.cpp

    Add the following into the declaration of the derived CHtmlView class (named CHtmlViewTestView in this example)

    void NewWindow3(     
            IDispatch **ppDisp,
            VARIANT_BOOL *Cancel,
            DWORD dwFlags,
            BSTR bstrUrlContext,
            BSTR bstrUrl
        );

    DECLARE_EVENTSINK_MAP()

    Add the following to the implementation file of the CHtmlViewTestView class

    #include <exdisp.h> //For IWebBrowser2* and others
    #include <exdispid.h>
    #include <Mshtml.h>
    #include <Mshtmdid.h>
    #include <shobjidl.h>

    BEGIN_EVENTSINK_MAP(CHtmlViewTestView, CHtmlView)
        ON_EVENT(CHtmlViewTestView, AFX_IDW_PANE_FIRST,DISPID_NEWWINDOW3,NewWindow3,VTS_PDISPATCH VTS_PBOOL VTS_I4 VTS_BSTR VTS_BSTR)
    END_EVENTSINK_MAP()

    void CHtmlViewTestView::NewWindow3(     
        IDispatch **ppDisp,
        VARIANT_BOOL *Cancel,
        DWORD dwFlags,
        BSTR bstrUrlContext,
        BSTR bstrUrl
    )
    {
        CDocTemplate* pDocTemplate=GetDocument()->GetDocTemplate();
        CDocument* pDocument=pDocTemplate->OpenDocumentFile(NULL);
        POSITION pos= pDocument->GetFirstViewPosition();
        CHtmlViewTestView* pNewView=(CHtmlViewTestView*)pDocument->GetNextView(pos);
        pNewView->SetRegisterAsBrowser(TRUE);
        *ppDisp=pNewView->GetApplication();
    }

    Handling ShowHtmlDialog in CHtmlView needs some extension the browser control site, however, the MFC implementation is not reusable. Heck, for some reason the two MFC HTML Classes, namely CHtmlView and CDHtmlDialog, are NOT reusing their own extension inside the same class library. They use two almost identical extension control sites to redirect IDocHostUIHandler methods. The one used CHtmlView, CHtmlControlSite, is not even in MFC header file, while the one used by CDHtmlDialog, CBrowserControlSite, is in afxdhtml.h, leaving some room to extend it by exposing GetInterfaceHook.

    Now back to CHtmlView. To create a control site extension, you need to override CWnd::CreateControlSite, which is added in MFC 7.0 specifically for extending the web browser control ,  but is used in MFC 8.0 for embedding .Net Windows Forms controls.

    BOOL CHtmlViewTestView::CreateControlSite(COleControlContainer* pContainer,
       COleControlSite** ppSite, UINT /* nID */, REFCLSID /* clsid */)
    {
        ASSERT(ppSite != NULL);
        *ppSite = new CExtendedHtmlControlSite(pContainer,this);
        return TRUE;
    }

    Actually, pContainer->m_pWnd is this (CHtmlViewTestView), so I can emit a parameter here and cast the window pointer to CHtmlViewTestView, but this is not obvious to me when I wrote this class.

    The control site extension needs to extend COleControlSite, an internal class in MFC 6.0 but is documented in MFC 7.0, again, to support class level customization of the control site. Previously, you can only replace the global control container by calling AfxEnableControlContainer.

    class CExtendedHtmlControlSite :
        public COleControlSite
    {
    public:
        CExtendedHtmlControlSite(COleControlContainer* pContainer,CHtmlViewTestView* pView);
        virtual ~CExtendedHtmlControlSite(void);
    protected:
        CHtmlViewTestView* m_pView;
    }

    CExtendedHtmlControlSite::CExtendedHtmlControlSite(COleControlContainer* pContainer,CHtmlViewTestView* pView)
    :COleControlSite(pContainer),m_pView(pView)
    {
    }

    CExtendedHtmlControlSite::~CExtendedHtmlControlSite(void)
    {
    }

    Here m_pView is saved to delegate INewWindowManager calls to the CHtmlViewTestView class.

    Now it is the fun part. The web browser control does not actually query the INewWindowManager interface from the control site, instead, it calls the control site's implementation of IServiceProvider::QueryService, so I need to implement IServiceProvider first, then answer the service query call with my INewWindowManager implementation.

        BEGIN_INTERFACE_PART(ServiceProvider, IServiceProvider)
            STDMETHOD(QueryService)(REFGUID,REFIID,void**);
        END_INTERFACE_PART(ServiceProvider)

        BEGIN_INTERFACE_PART(NewWindowManager, INewWindowManager)       
            STDMETHOD(EvaluateNewWindow)(
                LPCWSTR pszUrl,
                LPCWSTR pszName,
                LPCWSTR pszUrlContext,
                LPCWSTR pszFeatures,
                BOOL fReplace,
                DWORD dwFlags,
                DWORD dwUserActionTime);
        END_INTERFACE_PART(NewWindowManager);

    ULONG FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::AddRef()
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
        return pThis->ExternalAddRef();
    }

    ULONG FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::Release()
    {                           
        METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
        return pThis->ExternalRelease();
    }

    HRESULT FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::QueryInterface(REFIID riid,
        void** ppvObj)
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
        HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
        return hr;
    }
    STDMETHODIMP CExtendedHtmlControlSite::XServiceProvider::QueryService(REFGUID guidService, 
        REFIID riid,
        void** ppvObject)
    {
        if (riid == IID_INewWindowManager)
        {
            METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider);
            HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObject);
            return hr;
        }
        else
        {
            *ppvObject = NULL;

        }
        return E_NOINTERFACE;
    }

    ULONG CExtendedHtmlControlSite::XNewWindowManager::AddRef()
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

        return pThis->ExternalAddRef();
    }

    ULONG CExtendedHtmlControlSite::XNewWindowManager::Release()
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

        return pThis->ExternalRelease();
    }

    HRESULT CExtendedHtmlControlSite::XNewWindowManager::QueryInterface(REFIID riid, void ** ppvObj)
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

        return pThis->ExternalQueryInterface( &riid, ppvObj );
    }

    HRESULT CExtendedHtmlControlSite::XNewWindowManager::EvaluateNewWindow(
    LPCWSTR pszUrl,
    LPCWSTR pszName,
    LPCWSTR pszUrlContext,
    LPCWSTR pszFeatures,
    BOOL fReplace,
    DWORD dwFlags,
    DWORD dwUserActionTime
    )
    {
        METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

        return pThis->m_pView->EvaluateNewWindow(
            pszUrl,
            pszName,
            pszUrlContext,
            pszFeatures,
            fReplace,
            dwFlags,
            dwUserActionTime);
    }

    Actually, I can implementation INewWindowManager in another class and return another object in QueryService, but since INewWindowManager is used exclusively for web browser customization, this INewWindowManager implementation is not going to be reusable anyway.

    Finally, to make CHtmlView's IDocHostUIHandler implementation happy, I have to redirect IDocHostUIHandler method calls to it:

    DECLARE_INTERFACE_MAP()

        BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)
            STDMETHOD(ShowContextMenu)(DWORD, LPPOINT, LPUNKNOWN, LPDISPATCH);
            STDMETHOD(GetHostInfo)(DOCHOSTUIINFO*);
            STDMETHOD(ShowUI)(DWORD, LPOLEINPLACEACTIVEOBJECT,
                LPOLECOMMANDTARGET, LPOLEINPLACEFRAME, LPOLEINPLACEUIWINDOW);
            STDMETHOD(HideUI)(void);
            STDMETHOD(UpdateUI)(void);
            STDMETHOD(EnableModeless)(BOOL);
            STDMETHOD(OnDocWindowActivate)(BOOL);
            STDMETHOD(OnFrameWindowActivate)(BOOL);
            STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEUIWINDOW, BOOL);
            STDMETHOD(TranslateAccelerator)(LPMSG, const GUID*, DWORD);
            STDMETHOD(GetOptionKeyPath)(OLECHAR **, DWORD);
            STDMETHOD(GetDropTarget)(LPDROPTARGET, LPDROPTARGET*);
            STDMETHOD(GetExternal)(LPDISPATCH*);
            STDMETHOD(TranslateUrl)(DWORD, OLECHAR*, OLECHAR **);
            STDMETHOD(FilterDataObject)(LPDATAOBJECT , LPDATAOBJECT*);
        END_INTERFACE_PART(DocHostUIHandler)

     

    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetExternal(LPDISPATCH *lppDispatch)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnGetExternal(lppDispatch);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ShowContextMenu(
        DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnShowContextMenu(dwID, ppt, pcmdtReserved, pdispReserved);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetHostInfo(
        DOCHOSTUIINFO *pInfo)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnGetHostInfo(pInfo);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ShowUI(
        DWORD dwID, LPOLEINPLACEACTIVEOBJECT pActiveObject,
        LPOLECOMMANDTARGET pCommandTarget, LPOLEINPLACEFRAME pFrame,
        LPOLEINPLACEUIWINDOW pDoc)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::HideUI(void)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)

        return pThis->m_pView->OnHideUI();
    }
    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::EnableModeless(BOOL fEnable)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnEnableModeless(fEnable);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL fActivate)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnDocWindowActivate(fActivate);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::OnFrameWindowActivate(
        BOOL fActivate)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnFrameWindowActivate(fActivate);
    }

    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ResizeBorder(
        LPCRECT prcBorder, LPOLEINPLACEUIWINDOW pUIWindow, BOOL fFrameWindow)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnResizeBorder(prcBorder, pUIWindow, fFrameWindow);
    }
    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::TranslateAccelerator(
        LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnTranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
    }
    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetOptionKeyPath(
        LPOLESTR* pchKey, DWORD dwReserved)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnGetOptionKeyPath(pchKey, dwReserved);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetDropTarget(
        LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnGetDropTarget(pDropTarget, ppDropTarget);
    }

    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::TranslateUrl(
        DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnTranslateUrl(dwTranslate, pchURLIn, ppchURLOut);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::FilterDataObject(
        LPDATAOBJECT pDataObject, LPDATAOBJECT* ppDataObject)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->m_pView->OnFilterDataObject(pDataObject, ppDataObject);
    }
    STDMETHODIMP_(ULONG) CExtendedHtmlControlSite::XDocHostUIHandler::AddRef()
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->ExternalAddRef();
    }
    STDMETHODIMP_(ULONG) CExtendedHtmlControlSite::XDocHostUIHandler::Release()
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->ExternalRelease();
    }

    STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::QueryInterface(
              REFIID iid, LPVOID far* ppvObj)    
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
        return pThis->ExternalQueryInterface(&iid, ppvObj);
    }STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::UpdateUI(void)
    {
        METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)

        return pThis->m_pView->OnUpdateUI();
    }

    That's it, you can handle ShowModalDialog now

    HRESULT CHtmlViewTestView::EvaluateNewWindow(
        LPCWSTR pszUrl,
        LPCWSTR pszName,
        LPCWSTR pszUrlContext,
        LPCWSTR pszFeatures,
        BOOL fReplace,
        DWORD dwFlags,
        DWORD dwUserActionTime
    )
    {
        CString url(pszUrl);
        if(url.MakeLower().Find(_T("showdialogtest.htm"))!=-1)
        {
            return S_FALSE;//block the new window
        }
        return E_FAIL;//default
    }

    Well, here you can add as many policies as you like , people can never be creative enough on making policies.

    This should be enough for adding your web browser customization. If you want to add more interfaces, such as IDocHostUIHandler2, IInternetSecurityManager, IDocHostShowUI, IOleCommandTarget or IAuthenticate, to of the customized control site, simply add more interface parts and answer QueryService calls if necessary.

    About the author

    Sheng Jiang has been a Microsoft MVP in Visual C++ since 2004. He is a student in Austin Community College in Austin, Texas, USA.