링크 : http://msdn.microsoft.com/en-us/library/aa770041.aspx 


18 out of 34 rated this helpful Rate this topic

This tutorial shows you several ways to customize the "out of the box" behavior and appearance of the WebBrowser Control. You'll see how to use the advanced hosting interfaces IDocHostUIHandlerIDocHostUIHandler2IDocHostShowUI, and ICustomDoc. This article will also look at other customization techniques such as download control through handling DISPID_AMBIENT_DLCONTROL in the host's IDispatch implementation, and using IHostDialogHelper.

This article is divided into the following sections:

Prerequisites and Requirements

In order to understand and use this tutorial, you need:

  • A good understanding of C++ and the Component Object Model (COM).
  • Familiarity with the Active Template Library (ATL).
  • Microsoft Internet Explorer 6 or later installed.
  • Header files and libraries for Internet Explorer 6 or later for use in your development environment; in particular, you need Mshtmhst.h.

Many of the WebBrowser customization features have been available since Internet Explorer 5 and Internet Explorer 5.5. Only a couple require Internet Explorer 6. One more requires Internet Explorer 6 for Windows XP Service Pack 2 (SP2). Check the reference documentation for versioning information.

Introduction

Hosting the WebBrowser Control is a powerful tool for rapid application development. Through hosting, you can take advantage of easy-to-use technologies—Dynamic HTML (DHTML), HTML, and XML—for displaying content and developing a UI. However, "out of the box," the WebBrowser Controlmay not behave quite the way you want. For instance, in its default state the user can view the source of a displayed page by right-clicking and choosing the View Source option on the shortcut menu. You might want to disable or eliminate that option. You might also want to go even further and replace the default shortcut menu entirely with your own custom menu.

Aside from the customizations just mentioned, the advanced hosting features of Windows Internet Explorer enable you to:

  • Use buttons and other controls on a displayed page to call internal methods in your application, effectively extending the DHTML Object Model.
  • Change drag-and-drop behavior.
  • Control which pages your application can access, restricting navigation, for example, to specify pages, domains, or sites.
  • Intercept user keystrokes and process them however you want. For example, you might intercept CTRL+O to prevent the user from opening a new page in Internet Explorer rather than the host application.
  • Change default font and display settings.
  • Control the kinds of content that are downloaded and what the WebBrowser Control does with them once they are downloaded. For example, you can prevent videos from playing, script from running, or new windows from opening when users click links, or prevent Microsoft ActiveX controls from downloading or executing.
  • Restrict View Source.
  • Capture searches.
  • Capture navigation errors.
  • Replace or modify shortcut menus and shortcut menu entries—disabling, replacing, customizing, or adding to them.
  • Change registry settings for your application.
  • Intercept and change messages dialogs shown by the WebBrowser Control.
  • Control how new windows are created.

In the following sections, we'll look at many, though not all, of these possibilities and discuss how to implement them.

WebBrowser Customization Architecture

Introducing IDocHostUIHandler, IDocHostUIHander2, IDocHostShowUI, and ICustomDoc

Three interfaces are at the heart of WebBrowser Control UI customization: IDocHostUIHandlerIDocHostUIHandler2, and IDocHostShowUI. These are interfaces that you implement in your application when you want to modify the WebBrowser Control. There are also a couple of service interfaces.ICustomDoc is implemented by MSHTML and provides a means to enable WebBrowser Control customization in some scenarios. IHostDialogHelperprovides a means to open trusted dialog boxes, without identification that marks them as Internet Explorer dialog boxes.

In addition to using these interfaces, there are two other things you can do. For one, you can control download by intercepting ambient property changes in your IDispatch implementation. Second, you can control how new windows are created by intercepting DISPID_NEWWINDOW2 in your IDispatchimplementation.

How It Works

The mechanism for WebBrowser Control customization is designed to be automated when a container provides support for ActiveX controls. Whenever theWebBrowser Control is instantiated, it attempts to find IDocHostUIHandlerIDocHostUIHandler2 and IDocHostShowUI implementations from the host, if they are available. The WebBrowser Control does this by a QueryInterface call on the host's IOleClientSite interface.

This architecture works automatically for an application that implements an IOleClientSite interface and that passes an IOleClientSite pointer to theWebBrowser Control through the browser's IOleObject::SetClientSite method. A typical instantiation of the WebBrowser Control might look like this:

// Error checking omitted for clarity
CComPtr<IOleObject> spOleObj;

// Create WebBrowser--store pointer in class member variable m_spWebBrowser
CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IWebBrowser2, (void**)&m_spWebBrowser);

// Query WebBrowser for IOleObject pointer
m_spWebBrowser->QueryInterface(IID_IOleObject, (void**)&spOleObj);

// Set client site
spOleObj->SetClientSite(this);

// In-place activate the WebBrowser control
RECT rcClient
GetClientRect(&rcClient);
spOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, GetTopLevelWindow(), &rcClient);

// Register container to intercept WebBrowser events
AtlAdvise(m_spWebBrowser, GetUnknown(), DIID_DWebBrowserEvents2, &m_dwCookie);

// Navigate to start page
m_spWebBrowser->Navigate(L"res://webhost.exe/startpage.htm", NULL, NULL, NULL, NULL);

However, if your application doesn't have an IOleClientSite interface, all is not lost. Internet Explorer provides the ICustomDoc interface so you can pass Internet Explorer your IDocHostUIHandler interface yourself. You cannot use IDocHostUIHandler2 and IDocHostShowUI without providing anIOleClientSite interface on the object hosting the WebBrowser Control.

When the WebBrowser Control holds a pointer to any of these interfaces, the methods of the interfaces are called at appropriate times during the lifetime of the WebBrowser Control. For instance, when the user right-clicks with the mouse anywhere in the WebBrowser Control's client area, your implementation of IDocHostUIHandler::ShowContextMenu will be called before Internet Explorer displays its default shortcut menu. This gives you a chance to display your own shortcut menu and prevent Internet Explorer from displaying its menu.

There are a few more important points to remember when initializing the WebBrowser Control. Your application should use OleInitialize rather thanCoInitialize to start COM. OleInitialize enables support for the Clipboard, drag-and-drop operations, OLE, and in-place activation. Use OleUninitialize to close the COM library when your application shuts down.

The ATL COM Wizard uses CoInitialize rather than OleInitialize to open the COM libraries. If you use this wizard to build an executable program, you need to change the CoInitialize and CoUninitialize calls to OleInitialize and OleUninitialize. For a Microsoft Foundation Classes (MFC) application, be sure that your application calls AfxOleInit, which calls OleInitialize, in its initialization process.

If you don't want drag-and-drop support in your application, you can call IWebBrowser2::RegisterAsDropTarget, passing in VARIANT_TRUE, to prevent any drag-and-drop operations on your WebBrowser Control instance.

An application hosting the WebBrowser Control will also need an implementation of IOleInPlaceSite, and since IOleInPlaceSite derives from IOleWindow, the application will also need an implementation of IOleWindow. You need these implementations so your application has a window in which to display theWebBrowser Control and so you can manage its display.

The implementations of these interfaces and IOleClientSite can be minimal or nonexistent in many cases. The IOleClientSite methods can all return E_NOTIMPL. Some of the IOleInPlaceSite and IOleWindow methods need an implementation beyond their return value. See the code sample for an example of a minimal implementation of IOleInPlaceSite and IOleWindow.

Now that we have covered the initialization preliminaries, let's take a look at each of the interfaces for WebBrowser Control customization.

IDocHostUIHandler

IDocHostUIHandler has been available since Internet Explorer 5. It provides fifteen methods. In general, some of the more important methods areIDocHostUIHandler::GetExternalIDocHostUIHandler::GetHostInfoIDocHostUIHandler::GetOptionKeyPathIDocHostUIHandler::ShowContextMenu, andIDocHostUIHandler::TranslateAccelerator. Of course, the methods that are most important to you will depend on your application.

IDocHostUIHandler::GetHostInfo

You use IDocHostUIHandler::GetHostInfo to tell MSHTML about your application's capabilities and requirements. With it you can control a variety of things, for instance:

  • You can disable the browser's 3-D border.
  • You can prevent scroll bars or change their appearance.
  • You can define how you want to handle double-clicks.

IDocHostUIHandler::GetHostInfo has one parameter, a pointer to a DOCHOSTUIINFO structure allocated by MSHTML. Your job is to fill this structure with the information you want to pass to MSHTML.

There are four members in the DOCHOSTUIINFO structure. The first member is cbSize, which is the size of the structure. You should set this yourself as the following code sample shows. The second member is dwFlags, which can take any number of flag values, combined with the bitwise OR operator, from theDOCHOSTUIFLAG enumeration. The third member is dwDoubleClick, which takes a value from the DOCHOSTUIDBLCLK enumeration. The fourth member ispchHostCss. You can set pchHostCss to a pointer to a string with Cascading Style Sheets (CSS) rules that you want to apply globally to any page displayed in the WebBrowser control. The final member of DOCHOSTUIINFO is pchHostNs. This is also a string pointer with which you can provide a semicolon-delimited list of namespaces. Use this member when you use custom tags on the pages you're displaying in the WebBrowser Control. This way you can have a global declaration for namespaces and don't have to declare them on each displayed page.

Be sure to use CoTaskMemAlloc to allocate strings for pchHostCss or pchHostNS.

HRESULT GetHostInfo(DOCHOSTUIINFO *pInfo)
{
    WCHAR* szCSS = L"BODY {background-color:#ffcccc}";
    WCHAR* szNS = L"IE;MyTags;MyTags2='www.microsoft.com'";

    #define CCHMAX 256
    size_t cchLengthCSS, cchLengthszNS;

    HRESULT hr = StringCchLengthW(szCSS, CCHMAX, &cchLengthCSS);
    // TODO: Add error handling code here.
    OLECHAR* pCSSBuffer = (OLECHAR*)CoTaskMemAlloc((cchLengthCSS + 1) * sizeof(OLECHAR));
    // TODO: Add error handling code to make sure memory block was allocated successfully.

    hr = StringCchLengthW(szNS, CCHMAX, &cchLengthszNS);
    // TODO: Add error handling code here.
    OLECHAR* pNSBuffer = (OLECHAR*)CoTaskMemAlloc((cchLengthszNS + 1) * sizeof(OLECHAR));
    // TODO: Add error handling code to make sure memory block was allocated successfully.

    hr = StringCchCopyW(pCSSBuffer, cchLengthCSS + 1, szCSS);
    // TODO: Add error handling code here.
    hr = StringCchCopyW(pNSBuffer, cchLengthszNS + 1, szNS);
    // TODO: Add error handling code here.

    pInfo->cbSize = sizeof(DOCHOSTUIINFO);
    pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_SCROLL_NO;
    pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
    pInfo->pchHostCss = pCSSBuffer;
    pInfo->pchHostNS = pNSBuffer;

    return S_OK;
}

If you have nothing special to say to MSHTML, you can return E_NOTIMPL from this method.

IDocHostUIHandler::ShowContextMenu

By implementing this method, you gain control over the shortcut menus displayed by the WebBrowser Control when a user right-clicks. You can prevent Internet Explorer from displaying its default menu by returning S_OK from this method. Returning some other value, like S_FALSE or E_NOTIMPL, allows Internet Explorer to go ahead with its default shortcut menu behavior.

If you return S_OK from this method and nothing more, you can prevent any right-click behavior by the WebBrowser Control. This may be all you desire in many scenarios but you can do more. Often, you use this method to create and display your own shortcut menu before returning S_OK. If you know the resources from which the WebBrowser Control displays its menus, and how it chooses them, you can also effectively customize the default WebBrowser Control shortcut menus themselves.

Refer to WebBrowser Customization (Part 2) for an implementation example of this method.

IDocHostUIHandler::GetExternal: Extending the DOM

IDocHostUIHandler provides you with a means to extend the Internet Explorer Document Object Model (DOM) with objects, methods, and properties of your own, implemented in your own application. You do this by providing MSHTML a pointer to the IDispatch interface for the COM automation object that implements your custom object, properties, and methods. These objects, properties, and methods will then be available to any page displayed in theWebBrowser Control through the document's external object.

You can easily implement this method, assuming your IDispatch interface is on the same object that implements IDocHostUIHandler.

HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch) 
{
    *ppDispatch = this;
    return S_OK;
}

Once MSHTML has a pointer to your IDispatch, MSHTML will pass any calls on Web pages to automation methods on your application through the externalobject:

<SCRIPT language="JScript">
function MyFunc(iSomeData)
{
    external.MyCustomMethod("Some text", iSomeData);
}
</SCRIPT>

You can also use this technique to pass whole objects to a page. To do this, create a method in your IDispatch implementation that passes back the object you want to make available.

<SCRIPT language="JScript">
function MyFunc(iSomeData)
{
    var oCustCalendarObj;
    external.GetCustomCalender(oCustCalenderObj);
    oCustCalerdarObj.doStuffWithIt();
    .
    .
    .
}
</SCRIPT>

See the sample application for an example of IDispatch automation implementation using ATL.

IDocHostUIHandler::GetOptionKeyPath

IDocHostUIHandler::GetOptionKeyPath is a very powerful tool for customizing the WebBrowser Control. Many of the WebBrowser Control display and behavior settings are stored in the registry under the HKEY_CURRENT_USER key. IDocHostUIHandler::GetOptionKeyPath gives you an opportunity to override these registry settings for your specific instance of the WebBrowser Control. It does this by letting you supply an alternate registry location from which the WebBrowser Control will read in registry settings.

An implementation of IDocHostUIHandler::GetOptionKeyPath passes a string to the WebBrowser Control for the registry location you want it to read from. The WebBrowser Control will look for this key under the HKEY_CURRENT_USER key.

HRESULT CBrowserHost::GetOptionKeyPath(LPOLESTR *pchKey, 
                                       DWORD dwReserved) 
{
    HRESULT hr;

    #define CCHMAX 256
    size_t cchLength;

    if (pchKey)
    {
        WCHAR* szMyKey = L"Software\\MyCompany\\MyApp";
        hr = StringCchLengthW(szMyKey, CCHMAX, &cchLength);
        // TODO: Add error handling code here.
        
        *pchKey = (LPOLESTR)CoTaskMemAlloc((cchLength + 1) * sizeof(WCHAR));
        if (*pchKey)
            hr = StringCchCopyW(*pchKey, cchLength + 1, szKey);
            // TODO: Add error handling code here.

        hr = (*pchKey) ? S_OK : E_OUTOFMEMORY;
    }
    else
        hr = E_INVALIDARG;

    return hr;
}

As with IDocHostUIHandler::GetHostInfo, be sure to allocate memory for your strings using CoTaskMemAlloc.

Telling the WebBrowser Control where to look for your registry settings is the first step—actually, it's the second step as far as program execution is concerned. Your program must set the keys at the location specified by IDocHostUIHandler::GetOptionKeyPath so the WebBrowser Control can read them. There are a variety of ways to do this. One way would be with a registry script that runs when the application is installed. Another way might be to do it programmatically when the application starts. Here's a function that sets keys to change the default font, size, and color.

HRESULT SetSomeKeys()
{
    HKEY hKey = NULL;
    HKEY hKey2 = NULL;
    HKEY hKey3 = NULL;
    DWORD dwDisposition = NULL;
    LONG lResult = NULL;
    #define CBMAX 256
    size_t cbLength;
	
    RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\MyCompany\\MyApp"),
                   NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
                   NULL, &hKey, &dwDisposition);

    RegCreateKeyEx(hKey, _T("Main"), NULL, NULL, REG_OPTION_NON_VOLATILE,
                   KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);

    RegSetValueEx(hKey2, _T("Use_DlgBox_Colors"), NULL, REG_SZ,
                  (CONST BYTE*)_T("no"), sizeof(_T("no")));

    RegCloseKey(hKey2);

    RegCreateKeyEx(hKey, _T("Settings"), NULL, NULL, REG_OPTION_NON_VOLATILE,
                   KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);

    RegSetValueEx(hKey2, _T("Anchor Color"), NULL, REG_SZ,
                  (CONST BYTE*)_T("0,255,255"), sizeof(_T("0,255,255")));

    RegSetValueEx(hKey2, _T("Text Color"), NULL, REG_SZ,
                  (CONST BYTE*)_T("255,0,255"), sizeof(_T("255,0,255")));

    RegCloseKey(hKey2);

    RegCreateKeyEx(hKey, _T("International\\Scripts"), NULL, NULL,
                   REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
                   &hKey2, &dwDisposition);

    BYTE bDefaultScript = 0x3;

    RegSetValueEx(hKey2, _T("Default_Script"), NULL, REG_BINARY,
                  &bDefaultScript, sizeof(bDefaultScript));

    RegCreateKeyEx(hKey2, _T("3"), NULL, NULL, REG_OPTION_NON_VOLATILE,
                   KEY_SET_VALUE, NULL, &hKey3, &dwDisposition);

    BYTE bSize = 0x4; // Value from 0 - 4. 2 is medium.
    TCHAR* szFontName = _T("Comic Sans MS");
    TCHAR* szFixedFontName = _T("Courier");
	
    HRESULT hr = StringCbLength(szFontName, CBMAX, &cbLength);
    // TODO: Add error handling code here.
    RegSetValueEx(hKey3, _T("IEPropFontName"), NULL, REG_SZ,
                  (CONST BYTE*)szFontName, cbLength + sizeof(TCHAR));

    hr = StringCbLength(szFixedFontName, CBMAX, &cbLength);
    // TODO: Add error handling code here.
    RegSetValueEx(hKey3, _T("IEFixedFontName"), NULL, REG_SZ,
                  (CONST BYTE*)szFixedFontName, cbLength + sizeof(TCHAR));

    RegSetValueEx(hKey3, _T("IEFontSize"), NULL, REG_BINARY, &bSize, sizeof(bSize));

    RegCloseKey(hKey3);
    RegCloseKey(hKey2);
    RegCloseKey(hKey);

    return S_OK;
}

IDocHostUIHandler2

IDocHostUIHandler2 has a single method, IDocHostUIHandler2::GetOverrideKeyPath. It performs a function very similar toIDocHostUIHandler::GetOptionKeyPath. It points your hosted WebBrowser to registry settings to modify the default Internet Explorer registry settings. An implementation of IDocHostUIHandler2::GetOverrideKeyPath will look much the same as an implementation of IDocHostUIHandler::GetOptionKeyPath.

GetOptionKeyPath and GetOverrideKeyPath Compared

The difference between IDocHostUIHandler::GetOptionKeyPath and IDocHostUIHandler2::GetOverrideKeyPath is subtle, but significant. If you implementIDocHostUIHandler::GetOptionKeyPath, your WebBrowser Control instance will ignore any user settings for Internet Explorer. These settings are stored in the registry under HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer. If you implement IDocHostUIHandler2::GetOverrideKeyPath, yourWebBrowser Control instance will incorporate any user settings—font settings, menu extensions, and so forth—into the way it displays and behaves.

To illustrate the difference between IDocHostUIHandler::GetOptionKeyPath and IDocHostUIHandler2::GetOverrideKeyPath, refer to the code example for adding extensions to the context menu in WebBrowser Customization (Part 2). Recall the line:

spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);

If you've implemented IDocHostUIHandler::GetOptionKeyPath, this line would add none of the menu extension information that is stored in the registry for the current user. Instead, it would only add menu items that you have created. If you've implemented IDocHostUIHandler2::GetOverrideKeyPath, this line would add any extensions defined for the current user under HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt  unless you explicitly supply an empty or alternative MenuExt key in your custom registry key.

Controlling Navigation

You may have wondered why the section on IDocHostUIHandler didn't mention IDocHostUIHandler::TranslateUrl as a method to implement when you wish to control page navigation. The reason is that this method is not the most general purpose technique with which to control navigation. Unless you are hosting MSHTML directly, this method will have no effect on navigation. Instead, you can control navigation by implementing your IDispatch::Invoke method to handle DISPID_BEFORENAVIGATE2. As an example, the following code prevents navigation to a particular URL, displaying the standard "Navigation Canceled" error page if the user attempts to do so.

case DISPID_BEFORENAVIGATE2:
{
    // Is navigation to specified Url disallowed? 
    ATLASSERT((*pDispParams).rgvarg[5].vt = VT_BYREF | VT_BSTR);
    CComBSTR url = ((*pDispParams).rgvarg)[5].pvarVal->bstrVal;
    if (url == "http://www.adatum.com" || url == "http://www.adatum.com/")
    {
        // If so, navigate the browser frame to standard resource page 
        CComQIPtr<IWebBrowser2> spBrowser = ((*pDispParams).rgvarg)[6].pdispVal;
        if (spBrowser != NULL)
        {
            static const CComBSTR newURL = L"res://ieframe.dll/navcancl.htm";
            spBrowser->Navigate(newURL, NULL, NULL, NULL, NULL);
            // Set Cancel parameter to TRUE to cancel the current event
            *(((*pDispParams).rgvarg)[0].pboolVal) = TRUE;
        }
    }
    break;
}

Keep in mind that event parameters are passed to Invoke in the opposite order as they appear in the event description. Therefore, the pDisp and Urlparameters are elements 6 and 5 of the rgvarg array, respectively. The Cancel parameter (the last parameter of the event) is the first element in the array.

IDocHostShowUI

This interface gives you control over the message boxes and help files displayed by the WebBrowser Control. It works the same way as IDocHostUIHandlerand IDocHostUIHandler2 in that you implement it so the WebBrowser Control can call your IDocHostShowUI methods before it displays any messages or help menus of its own. This gives you a chance to prevent the WebBrowser Control from displaying anything and enables you to display your own custom message or help instead. IDocHostShowUI has two methods, IDocHostShowUI::ShowMessage and IDocHostShowUI::ShowHelp.

IDocHostShowUI::ShowMessage

Return S_OK to disable WebBrowser Control messages. Any other return value, like S_FALSE or E_NOTIMPL, allows the WebBrowser Control to display with its message box.

One nice thing you can do with this method is customize the message box captions for your application so they don't read "Microsoft Internet Explorer." You can do this by comparing the caption string in lpstrCaption with the string resource Internet Explorer uses, which is stored in Shdoclc.dll. It is identified by the symbol IDS_MESSAGE_BOX_TITLE, whose value is 2213. The following code snippet shows how you might do this.

HRESULT CBrowserHost::ShowMessage(HWND hwnd,
                                  LPOLESTR lpstrText,
                                  LPOLESTR lpstrCaption,
                                  DWORD dwType,
                                  LPOLESTR lpstrHelpFile,
                                  DWORD dwHelpContext,
                                  LRESULT *plResult) 
{
    USES_CONVERSION;
    TCHAR pBuffer[50];

    // resource identifier for window caption "Microsoft Internet Explorer"
    #define IDS_MESSAGE_BOX_TITLE 2213

    // Load Shdoclc.dll and the IE message box title string
    HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));

    if (hinstSHDOCLC == NULL)
    {
        // Error loading module -- fail as securely as possible
        return;
    }
    LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE, pBuffer, 50);

    // Compare the IE message box title string with lpstrCaption
    // If they're the same, substitute your own Caption
    if (_tcscmp(OLE2T(lpstrCaption), pBuffer) == 0)
        lpstrCaption = L"Custom Caption";

    // Create your own message box and display it
    *plResult = MessageBox(OLE2T(lpstrText), OLE2T(lpstrCaption), dwType);

    // Unload Shdoclc.dll and return
    FreeLibrary(hinstSHDOCLC);
    return S_OK;
}

security note Security Alert  Using LoadLibrary incorrectly can compromise the security of your application by loading the wrong DLL. Refer to the LoadLibrarydocumentation for information on how to correctly load DLLs with different versions of Windows.

IDocHostShowUI::ShowHelp

This method is called whenever Internet Explorer Help would be shown, for instance when the F1 key is pressed, and works analogously toIDocHostShowUI::ShowMessage. Return S_OK to override Internet Explorer Help, or another HRESULT value to let Internet Explorer proceed with its Help.

Controlling Download and Execution

The WebBrowser Control gives you control over what it downloads, displays, and executes. To gain this control, you need to implement your host'sIDispatch so it handles DISPID_AMBIENT_DLCONTROL. When the WebBrowser Control is instantiated, it will call your IDispatch::Invoke with this ID. SetpvarResult to a combination of following flags, using the bitwise OR operator, to indicate your preferences.

  • DLCTL_DLIMAGES, DLCTL_VIDEOS, and DLCTL_BGSOUNDS: Images, videos, and background sounds will be downloaded from the server and displayed or played if these flags are set. They will not be downloaded and displayed if the flags are not set.
  • DLCTL_NO_SCRIPTS and DLCTL_NO_JAVA: Scripts and Java applets will not be executed.
  • DLCTL_NO_DLACTIVEXCTLS and DLCTL_NO_RUNACTIVEXCTLS : ActiveX controls will not be downloaded or will not be executed.
  • DLCTL_DOWNLOADONLY: The page will only be downloaded, not displayed.
  • DLCTL_NO_FRAMEDOWNLOAD: The WebBrowser Control will download and parse a frameSet, but not the individual frame objects within theframeSet.
  • DLCTL_RESYNCHRONIZE and DLCTL_PRAGMA_NO_CACHE: These flags cause cache refreshes. With DLCTL_RESYNCHRONIZE, the server will be asked for update status. Cached files will be used if the server indicates that the cached information is up-to-date. With DLCTL_PRAGMA_NO_CACHE, files will be re-downloaded from the server regardless of the update status of the files.
  • DLCTL_NO_BEHAVIORS: Behaviors are not downloaded and are disabled in the document.
  • DLCTL_NO_METACHARSET_HTML: Character sets specified in meta elements are suppressed.
  • DLCTL_URL_ENCODING_DISABLE_UTF8 and DLCTL_URL_ENCODING_ENABLE_UTF8: These flags function similarly to theDOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8 and DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8 flags used withIDocHostUIHandler::GetHostInfo. The difference is that the DOCHOSTUIFLAG flags are checked only when the WebBrowser Control is first instantiated. The download flags here for the ambient property change are checked whenever the WebBrowser Control needs to perform a download.
  • DLCTL_NO_CLIENTPULL: No client pull operations will be performed.
  • DLCTL_SILENT: No user interface will be displayed during downloads.
  • DLCTL_FORCEOFFLINE: The WebBrowser Control always operates in offline mode.
  • DLCTL_OFFLINEIFNOTCONNECTED and DLCTL_OFFLINE: These flags are the same. The WebBrowser Control will operate in offline mode if not connected to the Internet.

DISPID_AMBIENT_DLCONTROL and the flag values are defined in mshtmdid.h.

Initially, when the function call to IDispatch::Invoke starts, the VARIANT to which the parameter pvarResult points is of type VT_EMPTY. You must switch the type to VT_I4 for any settings to have an effect. You can place your flag values in the lVal member of the VARIANT.

Most of the flag values have negative effects, that is, they prevent behavior that normally happens. For instance, scripts are normally executed by theWebBrowser Control if you don't customize its behavior. But if you set the DLCTL_NOSCRIPTS flag, no scripts will execute in that instance of the control. However, three flags—DLCTL_DLIMAGES, DLCTL_VIDEOS, and DLCTL_BGSOUNDS—work exactly opposite. If you set flags at all, you must set these three for the WebBrowser Control to behave in its default manner vis-a-vis images, videos and sounds.

The following code sample causes a WebBrowser Control instance to download and display images and videos, but not background sounds, since the DLCTL_BGSOUNDS is not explicitly set. Also, script execution on pages displayed by the WebBrowser Control is disabled.

STDMETHODIMP CAtlBrCon::Invoke(DISPID dispidMember, REFIID riid,
                               LCID lcid, WORD wFlags, 
                               DISPPARAMS* pDispParams,
                               VARIANT* pvarResult, 
                               EXCEPINFO* pExcepInfo,
                               UINT* puArgErr)
{
    switch (dispidMember)
    {
    case DISPID_AMBIENT_DLCONTROL:
        pvarResult->vt = VT_I4;
        pvarResult->lVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_NO_SCRIPTS;
        break;

    default:
        return DISP_E_MEMBERNOTFOUND;
    }

    return S_OK;
}

IHostDialogHelper

IHostDialogHelper is an interface you can use to create dialog boxes according to your liking. This interface has one method,IHostDialogHelper::ShowHTMLDialog. This method provides the same service as the function ShowHTMLDialog, but it's a little easier to use.

To use IHostDialogHelper, you create the dialog helper object from scratch. Here's how you would do it using CoCreateInstance. The interface and IDs are defined in mshtmhst.h.

IHostDialogHelper* pHDH;
IMoniker* pUrlMoniker;
BSTR bstrOptions = SysAllocString(L"dialogHeight:30;dialogWidth:40");
BSTR bstrPath = SysAllocString(L"c:\\dialog.htm");

CreateURLMoniker(NULL, bstrPath, &pUrlMoniker);

// Create the dialog helper object
CoCreateInstance(CLSID_HostDialogHelper, 
                 NULL, 
                 CLSCTX_INPROC,
                 IID_IHostDialogHelper, 
                 (void**)&pHDH);

// Call ShowHTMLDialog to create your dialog box
pHDH->ShowHTMLDialog(NULL, 
                     pUrlMoniker, 	
                     NULL, 
                     bstrOptions,
                     NULL, 
                     NULL);

// Free resources
SysFreeString(bstrPath);
SysFreeString(bstrOptions);
pUrlMoniker->Release();
pHDH->Release();

Controlling New Windows

One important way to take control of the WebBrowser Control is to control navigation. You saw earlier how you can intercept DISPID_BEFORENAVIGATE2 in an IDispatch::Invoke implementation to control where your WebBrowser Control will navigate. Another important aspect of navigation is to control how the navigation occurs, especially when opening new windows. Let's say, for instance, that the user clicks the right mouse button over a link and chooses "Open in New Window" or that a page contains a script like this:

window.open("www.msn.com");

By default, the WebBrowser Control deals with this code by opening a new instance of Internet Explorer to display the page. This may be fine for your application. But then again, it may not. Perhaps you'll want all links to open in your current WebBrowser Control instance. Or perhaps you'll want to open a link in a new WebBrowser Control instance under your control, with your user interface and with your branding.

You can intercept an event, DWebBrowserEvents2::NewWindow2, in your IDispatch implementation to control this. Your control needs to connect to theDWebBrowserEvents2 connection point to intercept this event.

Once you're connected to DWebBrowserEvents2, implement your IDispatch::Invoke so that it handles DISPID_NEWWINDOW2. During the IDispatch::Invokefunction call for DISPID_NEWWINDOW2, the array pDispParams contains two parameters. The first one, at index zero, is a Boolean value that tells theWebBrowser Control whether to cancel the new window or not. By default, it is FALSE and a new window will open. If you want to cancel new window creation completely, set the flag to TRUE.

The parameter at index one is a pointer to an IDispatch interface. You can set this parameter to the IDispatch of a WebBrowser Control that you've created. When you pass back an IDispatch like this, MSHTML will use the control you've given it to open the link.

Information Bar

Internet Explorer 6 for Windows XP SP2 introduced a new security UI element called the Information bar. The Information bar displays a UI element to users of Internet Explorer when certain actions are prevented. Specifically, it displays when the following are blocked.

  • Pop-up window instantiation (see Pop-up Blocker)
  • File downloads (see File Download Restrictions)
  • ActiveX control installation (see ActiveX Restrictions)
  • ActiveX control security prompts because of the user's security settings or because the control is not marked safe for scripting
  • Files that have a mismatch between the file name extension and the MIME type (see MIME Handling)
  • Content restricted by the network protocol lockdown (see Protocols)
  • Internet Explorer 7 or later. Windows opened by the Microsoft JScript prompt method and the Microsoft Visual Basic Scripting Edition (VBScript) InputBox Function.

The Information bar is one of the feature controls introduced in Internet Explorer 6 for Windows XP SP2. Like the other feature controls, it is managed through a registry key (FEATURE_SECURITYBAND). Internet Explorer (iexplorer.exe) and Windows Explorer (explorer.exe) run under this feature control by default. The following shows the registry key and enabled processes.

HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
     SOFTWARE
          Microsoft
               Internet Explorer
                    Main
                         FeatureControl
                              FEATURE_SECURITYBAND
                                   iexplorer.exe = 0x00000001
                                   explorer.exe = 0x00000001
                                   process name.exe = 0x00000001

The FEATURE_SECURITYBAND feature control only affects whether Internet Explorer displays the Information bar, which alerts the user that an action has been mitigated. It does not control the mitigation of the action.

An application hosting the WebBrowser Control can enable the Information bar by adding its process to this registry key. This can be done programmatically by using the CoInternetSetFeatureEnabled function. If an application does not run under this feature control, the WebBrowser Controlbehaves the same as Internet Explorer 6 SP1b.

There is no access to this feature through script.

Applications running under the FEATURE_SECURITYBAND and related feature controls can also use the Information bar APIs to customize the UI displayed when a URL action is disallowed. There are several new OLECMDID commands for the Information bar. The first three are part of the CGID_DocHostCommandHandler group. Hosting applications should implement IOleCommandTarget on their implementation of IDocHostUIHandler to receive IOleCommandTarget::Exec calls from the WebBrowser Control.

  • OLECMDID_PAGEACTIONBLOCKED
  • OLECMDID_PAGEACTIONUIQUERY
  • OLECMDID_FOCUSVIEWCONTROLS

Hosting applications can use the following two new OLECMDID commands to make IOleCommandTarget::Exec calls on the WebBrowser Control.

  • OLECMDID_FOCUSVIEWCONTROLSQUERY
  • OLECMDID_SHOWPAGEACTIONMENU
This example uses IWebBrowser2::ExecWB to execute the OLECMDID_SHOWPAGEACTIONMENU command.
   POINT pt = { 0 };
   GetCursorPos(&pt);

   CComVariant varHwnd((LONG)hwnd);
   CComVariant varX(pt.x);
   CComVariant varY(pt.y);

   SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 3);

   LONG lIndex = 0;
   SafeArrayPutElement(psa, &lIndex, &varHwnd);
   lIndex++;
   SafeArrayPutElement(psa, &lIndex, &varX);
   lIndex++;
   SafeArrayPutElement(psa, &lIndex, &varY);

   CComVariant varArgIn;
   V_VT(&varArgIn) = VT_ARRAY | VT_I4;
   V_ARRAY(&varArgIn) = psa;

   pBrowser-&lt;>*Fix pointer operator!!ExecWB(OLECMDID_SHOWPAGEACTIONMENU, (OLECMDEXECOPT)dwPageActionFlags, &varArgIn, NULL);

Also, an application can override the default security zone settings by implementing IInternetSecurityManager. See Implementing a Custom Security Managerfor more information.

Conclusion

You now have a number of techniques at your disposal to customize the WebBrowser Control. This article is by no means exhaustive, but hopefully you also now have a sense of some of the possibilities you may discover on your own beyond the techniques in this article. Check out the Internet Explorer registry to see the kinds of information stored there that you can modify with IDocHostUIHandler::GetOptionKeyPath or IDocHostUIHandler2::GetOverrideKeyPath. Keep in mind that many registry settings are interdependent with others. You may have to do some experimentation to discover how particular registry settings can be customized effectively. You can also look into IDocHostUIHandler::GetDropTarget if you want to control what the WebBrowser Control does during drag-and-drop operations.

Related Topics

8 out of 10 rated this helpful Rate this topic

Part 1 of this tutorial introduced several of the standard hosting interfaces for the WebBrowser ControlIDocHostUIHandlerIDocHostShowUI, andICustomDoc, to name a few. Part 2 of this article explains how to retrieve a reference to the client site while processing IPersistMoniker::Load, and how to use the IServiceProvider and IOleCommandTarget interfaces to send messages between the Active document (DocObject) and its host.

IPersistMoniker and IBindCtx

When the WebBrowser Control determines that a hyperlinked resource is associated with a helper application, it creates an instance of the target application and prompts the Active document (DocObject) to load the resource by using the IPersistMoniker interface. Before the browser navigates to a document, it registers a reference to its IOleClientSite interface in the binding context by calling IBindCtx::RegisterObjectParam. This reference allows the DocObject to retrieve and use the client site during the call to IPersistMoniker::Load.

The following example demonstrates how to retrieve and set the client site from the binding context:

HRESULT CDoc::Load(BOOL fFullyAvailable, IMoniker *pimkName, LPBC pibc, DWORD grfMode)
{
    HRESULT hr = S_OK;
    CComPtr<IUnknown> spUnk;
    CComPtr<IBindCtx> spBindCtx;

    // SHDOCVW puts the client site in the bind context.
    if (pibc)
    {
        pibc->GetObjectParam(SZ_HTML_CLIENTSITE_OBJECTPARAM, &spUnk);
        if (spUnk)
        {
            CComQIPtr<IOleClientSite> spSite(spUnk);
            
            if (spSite)
            {
                hr = SetClientSite(spSite);
            }
        }
    }
}

IServiceProvider

The IServiceProvider interface is a generic access mechanism to locate a GUID-identified service that is provided through a control or any other object. Using IServiceProvider::QueryService, the caller specifies a service identifier (SID), the IID of the desired interface, and the address of the interface pointer variable that is set upon a successful return.

In many ways, IServiceProvider::QueryService functions like QueryInterface. However, the more adaptable IServiceProvider mechanism enables an implementer to delegate the query to one of its member objects, or to hand off the request to a callback hierarchy that searches for an object to support the requested IID. In this way, the implementer is not required to recognize the requested interfaces. In common usage, IServiceProvider::QueryService is used to improve the discoverability of some interfaces.

QueryService Example

For example, a call to QueryService with SID_SShellBrowser retrieves the nearest shell browser, such as the Windows Internet Explorer WebBrowser Control. If the DocObject is hosted in the search pane, this technique will prevent it from mistakenly executing commands on the main browser window.SID_SShellBrowser service is specific to the WebBrowser control; any DocObject that implements the IBrowserService interface should respond to queries for this service.

The following example demonstrates this basic scenario:

    CComPtr<IServiceProvider> pSvc; 
    CComPtr<IShellBrowser>    pShb;
    HRESULT                   hr;
    
    hr = m_pUnk->QueryInterface(IID_IServiceProvider, &pSvc);
    if (S_OK == hr && pSvc)
    {
        hr = pSvc->QueryService(SID_SShellBrowser, IID_IShellBrowser, &pShb);
        if (E_NOINTERFACE == hr)
            return;   // pSvc released automatically.

        // . . . Use the interface here.
    }

CLSID_CMarkup

The special CLSID_CMarkup GUID is used to determine whether an object is a native MSHTML markup object. No code except Internet Explorer should use this CLSID. Your QueryService or QueryInterface implementation should return E_NOINTERFACE if invoked with this GUID.

DEFINE_GUID(CLSID_CMarkup, 0x3050F4FB, 0x98B5, 0x11CF, 0xBB, 0x82, 0, 0xAA, 0, 0xBD, 0xCE, 0x0B);
Note   The MSHTML implementation uses a non-standard QueryInterface for CLSID_CMarkup to obtain an object pointer without adding a reference. In other words, the out parameter ppv does not receive an interface pointer; it contains the address of the object behind pUnknown.

IOleCommandTarget

The IOleCommandTarget interface enables objects and their containers to dispatch commands to each other. Available commands are defined by integer identifiers from a command group, which is itself identified by command group ID (also a GUID). The interface enables a caller to query for one or more supported commands within a group and to issue a command to the object.

The WebBrowser Control uses the IOleCommandTarget interface extensively. The following sections highlight just a few of the ways that the client site can communicate with its control.

Showing a Certificate Dialog

With Microsoft Internet Explorer 6 for Windows XP Service Pack 2 (SP2) and later, you can show the Certificate dialog box when the user is viewing a valid Secure Hypertext Transfer Protocol (HTTPS) site. This has the same result as the user clicking the lock icon in Internet Explorer. You can use theDWebBrowserEvents2::SetSecureLockIcon event to show your own icon.

#define SHDVID_SSLSTATUS 33

IOleCommandTarget *pct;
if (SUCCEEDED(pWebBrowser2->QueryInterface(IID_IOleCommandTarget, (void **)&pct)))
{
   pct->Exec(&CGID_ShellDocView, SHDVID_SSLSTATUS, 0, NULL, NULL);
   pct->Release();
}

Controlling Navigation (Revisited)

If the MSHTML control is aggregated, the controlling DocObject is in a position to regulate navigation events. The fact that a document can navigate on its own implies that it will also take care of updating the navigation history.

In Internet Explorer 6 and later, the DocObject can indicate to the client site that it can navigate using CGID_DocHostCmdPriv (a privately defined command group GUID) and the DOCHOST_DOCCANNAVIGATE command. A pointer to the object that implements the IHTMLWindow2 interface is passed with the command in the VARIANTARG* parameter pvaIn. (Set pvaIn to NULL if the document cannot perform its own navigation.)

DEFINE_GUID(CGID_DocHostCmdPriv, 0x000214D4L, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
#define DOCHOST_DOCCANNAVIGATE	0

// Create variant, and initialize with a pointer to our IUnknown.
VARIANTARG var;

V_VT(&var)      = VT_UNKNOWN;
V_UNKNOWN(&var) = (IUnknown*)this;

// Execute IOleCommandTarget with command group and ID.
m_pCmdTarg->Exec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0L, &var, NULL);

Specifying the Zone Icon

In conjunction with CGID_Explorer, the SBCMDID_MIXEDZONE command is used to determine the zone icon and text displayed in the Internet Explorer status bar.

Active document objects hosted in Internet Explorer 6 for Windows XP SP2 or later can respond to the SBCMDID_MIXEDZONE command to determine the zone icon and text displayed in the Internet Explorer status bar. The document object should implement IOleCommandTarget, and Internet Explorer callsIOleCommandTarget::Exec with the CGID_Explorer command group.

To specify the zone icon:

  1. Define SBCMDID_MIXEDZONE.

    #define SBCMDID_MIXEDZONE 39
    
  2. Initialize pvaOut with VariantInit.

  3. Determine the zone you want to specify and then set pvaOut as follows. To specify:

    • Internet, Intranet, Trusted or Untrusted zones: set pvaOut->vt to VT_UI4, and set pvaOut->ulVal to URLZONE_INTERNET, URLZONE_INTRANET, URLZONE_TRUSTED or URLZONE_UNTRUSTED respectively.
    • A mixed zone: set pvaOut->vt to VT_NULL.
    • An unknown zone: set pvaOut->vt to VT_EMPTY.

If an active document does not handle SBCMDID_MIXEDZONE, the default behavior will show Unknown Zone in the status bar.

security note Security Alert  If you cannot reliably determine your document's zone, you should not handle this command.

More Examples of Commands

The IDM_CONTEXT command (CGID_EditStateCommands command group) is used to determine whether editing commands should be routed to the host first. An IOleCommandTarget implementation returns S_OK to indicate that editing commands should be routed to the host first.

If the host is designed to handle editing commands, it will respond to the SID_SEditCommandTarget service identifier by providing an IOleCommandTargetinterface to process editing commands. The editing commands are defined in the CGID_MSHTML command group, as shown in the following code.

DEFINE_GUID(CGID_MSHTML, 0xde4ba900, 0x59ca, 0x11cf, 0x95, 0x92, 0x44, 0x45, 0x53, 0x54, 0, 0);

For more information on editing commands, see Introduction to MSHTML Editing and MSHTML Command Identifiers reference.

Context Menus and Extensions

The IDocHostUIHandler::ShowContextMenu method was introduced in part one of this article. The IDocHostUIHandler interface is designed to be overridden by applications that host the WebBrowser control. It is not intended for use by Browser Helper Objects (BHOs) because, in addition to the problem of appending items to the standard menu discussed below, only one add-on at a time can override IDocHostUIHandler and multiple add-ons can easily conflict with each other.

Important    In Internet Explorer 7, the technique for overriding the context menu from a DocObject host is the same as Internet Explorer 6; however, the host must implement its own menu resources. The internal resources of Internet Explorer should not be used as they may change or move (as has been done in Internet Explorer 7). See Appending Extensions to Your Custom Menu below.

The following examples demonstrate how to use the IOleCommandTarget interface with SHDVID_GETMIMECSETMENU and SHDVID_ADDMENUEXTENSIONS (CGID_ShellDocView command group) to add and remove custom menu options from the standard Internet Explorer context menu:

#define SHDVID_GETMIMECSETMENU   27
#define SHDVID_ADDMENUEXTENSIONS 53 

In Internet Explorer 6 and earlier, the WebBrowser Control gets its context menu resources from Shdoclc.dll. The following code loads the WebBrowser Control shortcut menu resource from Shdoclc.dll, chooses the correct menu for the context, loads language and extension resources from the registry, removes the menu item corresponding to the IDM_VIEWSOURCE command, and then displays the pop-up menu.

HRESULT CBrowserHost::ShowContextMenu(DWORD dwID,
                                     POINT *ppt,
                                     IUnknown *pcmdTarget,
                                     IDispatch *pdispObject) 
{
   #define IDR_BROWSE_CONTEXT_MENU  24641
   #define SHDVID_GETMIMECSETMENU   27
   #define SHDVID_ADDMENUEXTENSIONS 53

   HRESULT hr;
   HINSTANCE hinstSHDOCLC;
   HWND hwnd;
   HMENU hMenu;
   CComPtr<IOleCommandTarget> spCT;
   CComPtr<IOleWindow> spWnd;
   MENUITEMINFO mii = {0};
   CComVariant var, var1, var2;

   hr = pcmdTarget->QueryInterface(IID_IOleCommandTarget, (void**)&spCT);
   hr = pcmdTarget->QueryInterface(IID_IOleWindow, (void**)&spWnd);
   hr = spWnd->GetWindow(&hwnd);

   hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));
   
   if (hinstSHDOCLC == NULL)
   {
       // Error loading module -- fail as securely as possible.
       return;
   }

   hMenu = LoadMenu(hinstSHDOCLC,
                    MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));

   hMenu = GetSubMenu(hMenu, dwID);

   // Get the language submenu.
   hr = spCT->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var);

   mii.cbSize = sizeof(mii);
   mii.fMask  = MIIM_SUBMENU;
   mii.hSubMenu = (HMENU) var.byref;

   // Add language submenu to Encoding context item.
   SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);

   // Insert Shortcut Menu Extensions from registry.
   V_VT(&var1) = VT_INT_PTR;
   V_BYREF(&var1) = hMenu;

   V_VT(&var2) = VT_I4;
   V_I4(&var2) = dwID;

   hr = spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);

   // Remove View Source.
   DeleteMenu(hMenu, IDM_VIEWSOURCE, MF_BYCOMMAND);

   // Show shortcut menu.
   int iSelection = ::TrackPopupMenu(hMenu,
                                     TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
                                     ppt->x,
                                     ppt->y,
                                     0,
                                     hwnd,
                                     (RECT*)NULL);

   // Send selected shortcut menu item command to shell.
   LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);

   FreeLibrary(hinstSHDOCLC);
   return S_OK;
}
security note Security Alert  Using LoadLibrary incorrectly can compromise the security of your application by loading the wrong DLL. Refer to the LoadLibrarydocumentation for information on how to correctly load DLLs with different versions of Windows.

Appending Extensions to Your Custom Menu

In the previous example, the menu (hMenu) and menu context identifier (dwID) are passed as arguments to the IOleCommandTarget with the SHDVID_ADDMENUEXTENSIONS command. This operation inserts the registered menu extensions into the menu.

If you choose to replace the standard menu with your own, you can still append menu extensions to your custom menu. Simply include a blank IDM_MENUEXT_PLACEHOLDER menu option in your menu definition to indicate where the custom commands are to be inserted. Then pass your own menu as the command target. Menu extensions will be inserted just before the placeholder.

You can also add your own custom command to the standard menu by inserting the menu option before IDM_MENUEXT_PLACEHOLDER, as shown in the following example.

#define IDM_MENUEXT_PLACEHOLDER	 6047

// If the placeholder is gone or was never there, then just exit.
if (GetMenuState(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND) != (UINT) -1)
{
   InsertMenu(hMenu,                    // The Context Menu
       IDM_MENUEXT_PLACEHOLDER,         // The item to insert before
       MF_BYCOMMAND|MF_STRING,          // Insert by item ID and str value
       IDM_MENUEXT_FIRST__ + nExtCur,   // The command ID
       (LPTSTR)aOpts[nExtCur].pstrText);// Some menu command text
	
   // Remove placeholder.
   DeleteMenu(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND);
}

The menu IDs for extensions fall between IDM_MENUEXT_FIRST__ and IDM_MENUEXT_LAST__ for a maximum of 32 custom commands.

Enabling and Disabling Menu Items

The menus in Internet Explorer use resource identifiers from mshtmcid.h to specify which command is executed when the menu item is clicked. Using the same resource IDs, you can easily determine whether the command has been enabled or disabled by calling IOleCommandTarget::QueryStatus, as shown in the following code snippet.

for (i = 0; i < GetMenuItemCount(hMenu); i++)
{
    OLECMD olecmd.cmdID = GetMenuItemID(hMenu, i);
    if (olecmd.cmdID > 0)
    {
        UINT mf;
        spCmdTarget->QueryStatus(&CGID_MSHTML, 1, &olecmd, NULL);
        switch (olecmd.cmdf)
        {
        case OLECMDSTATE_UP:
        case OLECMDSTATE_NINCHED:
            mf = MF_BYCOMMAND | MF_ENABLED | MF_UNCHECKED;
            break;

        case OLECMDSTATE_DOWN:
            mf = MF_BYCOMMAND | MF_ENABLED | MF_CHECKED;
            break;

        case OLECMDSTATE_DISABLED:
        default:
            mf = MF_BYCOMMAND | MF_DISABLED | MF_GRAYED;
            break;
        }
        CheckMenuItem(hMenu, olecmd.cmdID, mf);
        EnableMenuItem(hMenu, olecmd.cmdID, mf);
    }
}

References

The following table indicates where the identifiers described in this article are defined:

IdentifierHeader File 
CGID_EditStateCommandsMshtml.h
CGID_ExplorerShlguid.h
CGID_MSHTMLMshtmhst.h
CGID_ShellDocViewShlguid.h
IDM_CONTEXTMshtmcid.h
SID_SEditCommandTargetMshtml.h
SID_SShellBrowserShlguid.h
SZ_HTML_CLIENTSITE_OBJECTPARAM Mshtmhst.h

posted by 뚱2

[Zlib] zlib 메뉴얼

C/C++/Zlib 2012. 1. 5. 16:14
posted by 뚱2
Visual Studio 2008 MFC 프로그램에서 Excel Automation을 사용(Add Class From Typelib)할려고 디폴트로 사용할려고 하니

알수 없는 에러가 발생했다.





#해결1
http://www.ms-news.net/f3295/ms-office-type-library-problem-2533193.html

참조 : http://support.microsoft.com/kb/308292 
         http://support.microsoft.com/kb/311407/EN-US 


#해결2
에러는 해결했는데 Warning이 발생한것도 있고 Typelib를 이용해서 클래스 만들어주는게 다 만들어주는것도 아니고해서
결국직접 typelib를 임포트 해서 스마트포인터를 사용해서 해결했다.
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE11\\MOS.DLL" \
    rename("RGB", "excelRGB")
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB"
#import "C:\\Program Files (x86)\\Microsoft Office\\OFFICE11\\excel.exe" \
    rename("DialogBox", "excelDialogBox") \
    rename("RGB", "excelRGB") \
    rename("CopyFile", "excelCopyFile") \
    no_dual_interface
using namespace Excel;

* office11은 MS Office 2003 버전이다.
* 32bit 버전이라면 'Program Files (x86)'이 아니라 'Program Files' 이다.

     

'C/C++ > VC++ / MFC' 카테고리의 다른 글

[C++] C++11  (0) 2013.01.29
[COM] WebBrowser Customization  (0) 2012.12.29
[MFC] MFC에서 Token 분리  (0) 2011.07.21
[MFC] CListCtrl 현재 행 선택하기  (0) 2011.03.20
COM Automation 에서 옵션인자 설정 방법  (0) 2011.03.03
posted by 뚱2

* This global function can be used to extract a substring from a given source string.

BOOL AFXAPI AfxExtractSubString (
   CString& rString,
   LPCTSTR lpszFullString,
   int iSubString,
   TCHAR chSep = '\n'
);

* Sample Source
// The following example extracts a series of name, value pairs from a
// given source string:

// Input string consisting of a number of name, value pairs
LPCTSTR lpszSource = _T("\"Name\"=\"John Smith\"\n")
   _T("\"Company\"=\"Contoso, Ltd\"\n\"Salary\"=\"25,000\"");

CString strNameValue; // an individual name, value pair

int i = 0; // substring index to extract
while (AfxExtractSubString(strNameValue, lpszSource, i))
{
   // Prepare to move to the next substring
   i++;

   CString strName, strValue; // individual name and value elements

   // Attempt to extract the name element from the pair
   if (!AfxExtractSubString(strName, strNameValue, 0, _T('=')))
   {
      // Pass an error message to the debugger for display
      OutputDebugString(_T("Error extracting name\r\n"));
      continue;
   }

   // Attempt to extract the value element from the pair
   if (!AfxExtractSubString(strValue, strNameValue, 1, _T('=')))
   {
      // Pass an error message to the debugger for display
      OutputDebugString(_T("Error extracting value element\r\n"));
      continue;
   }

   // Pass the name, value pair to the debugger for display
   CString strOutput = strName + _T(" equals ") + strValue + _T("\r\n");
   OutputDebugString(strOutput);
}

 
posted by 뚱2

프로그램 업데이트 목록을 만들다 보면은 ListCtrl에 업데이트 목록을 넣어두고
업데이트를 할때마다 리스트 현재 행을 변경시킬때가 있다 그럴때 유용하다.

    // 먼저 현재 선택상태를 해제합니다
    m_listResult.SetItemState( -1, 0, LVIS_SELECTED|LVIS_FOCUSED );
    // 원하는 아이템을 선택합니다
    m_listResult.SetItemState(m_nCurrentItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
    // 선택된 아이템을 표시합니다
    m_listResult.EnsureVisible(m_nCurrentItem, false);
    // 리스트 컨트롤에 포커스를 맞춥니다
    m_listResult.SetFocus(); 
posted by 뚱2

ADO를 이용해서 Recordset 개체를

Open하는데 두번째 인자(ActiveConnection)는 옵션인자입니다.

'Visual Basic
recordset.Open Source, ActiveConnection, CursorType, LockType, Options



이걸 그냥 NULL 이렇게 주면은 실행시 예외를 떨굽니다.
이럴떼 NULL 대신
// Visual C++
_variant_t vOPTION;
vOPTION.vt = VT_ERROR;
vOPTION.scode = DISP_E_PARAMNOTFOUND;


해주시면 됩니다.

p.s. 2011-08-06 추가
MFC에서 컴관련 클래스로 COleVariant가 있습니다.
COleVariant vOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
 

'C/C++ > VC++ / MFC' 카테고리의 다른 글

[MFC] MFC에서 Token 분리  (0) 2011.07.21
[MFC] CListCtrl 현재 행 선택하기  (0) 2011.03.20
#pragma message  (0) 2011.02.25
Predefines Macros  (0) 2011.02.16
[링크] 일정 시간이 흐른후 메세지 박스 종료하기  (0) 2011.02.15
posted by 뚱2

#pragma message

C/C++/VC++ / MFC 2011. 2. 25. 13:15
참 재미있는 기능입니다.
이걸로 TODO를 만들면 VC++에서 점프가 가능합니다.


위와 같이 Output 창에서 해당 라인을 더블클릭하면 실제 소스로 이동합니다.

// with line number
#define STRING2(x) #x
#define STRING(x) STRING2(x)
#define TODO(x) message(__FILE__"("STRING(__LINE__)") : ▶"x"◀")


사용할때는
// Used
#pragma TODO("테스트")


이렇게 사용하시면 됩니다.
posted by 뚱2

Predefines Macros

C/C++/VC++ / MFC 2011. 2. 16. 17:59
컴파일시 변수와 비슷하게 사용할수 있는 Macro들이 있습니다.
그중 __FILE__, __LINE__, __FUNCTION__은  디버깅시 유용합니다.

__FILE__           : 현재 소스의 파일 경로 문자열을 리턴
__LINE__          : 소스 파일의 라인번호 숫자를 리턴
__FUNCTION__ : 현재 위치의 함수 이름 문자열을 리턴

참고 ( http://msdn.microsoft.com/en-us/library/b0084kay(VS.71).aspx )

posted by 뚱2
출처 ( http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=8514&page=1 )

데브피아 살펴보다가 유용한 팁이 있어서 링크 없어질것 대비하여 블로그에 옮겨 놓습니다.

나중에 써먹어야지!!


글 내용
---------------------------------------------------------------------------------------------------
팁이 될지 모르겠네요. 모르는분들을 위한 팁이에요...양해 바랍니다. ^^
어떤분이 질문을 올리셨기에...
질문의 내용은 A프로그램에서 B프로그램 실행후 B프로그램이 끝날때까지 A프로그램은 계속 대기
B프로그램에서 메시지박스가 떠 있기때문에 무한정 기다려야 하는 문제가 있을 경우 사용하면 될것 같습니다.
또는 그냥 시간되면 메시지 박스를 종료하고 싶을때 사용하면 될것 같습니다.
사용 방법은 SetTime를 이용합니다. 
---------------------------------------------------------------------------------------------------
SetTimer(101, 1000, NULL);
if(AfxMessageBox("박스다..") == IDOK)
{
    // AfxMessageBox("OK");
}

//OnTimer() 안에서 사용하시면 됩니다.
HWND wndDlg = ::GetLastActivePopup(m_hWnd);
if(wndDlg && wndDlg != m_hWnd)
{
    char buffer[256] = {0};
    ::GetClassName(wndDlg, buffer, 256);
    if(CString("#32770") == buffer) //메시지 박스는 분명히 다이얼로그이며 클래스명이 #32770
    {
        ::EndDialog(wndDlg, IDOK);
    }
}

posted by 뚱2

물론 MultiByteToWideChar API를 사용하면  Ansi->Unicode로 전환할수 있습니다.

MultiByteToWideChar를 보시면 알겠지만 인자가 많습니다. ㅡㅡ;

int MultiByteToWideChar(
  UINT CodePage, 
  DWORD dwFlags,         
  LPCSTR lpMultiByteStr, 
  int cbMultiByte,       
  LPWSTR lpWideCharStr,  
  int cchWideChar        
);

이럴때 MFC에서만 사용할수 있는 꼼수

char szBuffer[] = "Ansi 스트링 입니다.";
CString strUnicode = (CString)szBuffer;

이렇게만 하면 끝났습니다.
다만 그냥 컨버팅 되는게 아니라
프로그램 베이스가 Unicode 기반으로 작성된 프로그램에서
Ansi 문자열을 Unicode 기반으로 컨버팅 하실때 편하게 사용하실수 있습니다.

소스를 쫓아 들어가보면 알겠지만

형변환 내부적으로 MultiByteToWideChar 함수를 호출하고 있습니다.

// 데이터 길이 가져오는 함수
static int __cdecl GetBaseTypeLength( _In_z_ LPCSTR pszSrc ) throw()
{
    // Returns required buffer size in wchar_ts
    return ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0 )-1;
}

// 데이터 변환 하는 함수
static void __cdecl ConvertToBaseType( _Out_cap_(nDestLength) LPWSTR pszDest
, _In_ int nDestLength, _In_z_ LPCSTR pszSrc, _In_ int nSrcLength = -1) throw()
{
    // nLen is in wchar_ts
    ::MultiByteToWideChar( _AtlGetConversionACP()
                          , 0, pszSrc, nSrcLength, pszDest, nDestLength );
}

그런데 막상 해보면 글자가 한글이 깨집니다.
이유는 http://msdn.microsoft.com/ko-kr/library/w1sc4t4k(VS.80).aspx
문자열 변환 보시면 알겠지만 기본 코드 페이지가 변경되었습니다.


문자열 변환

Visual C++ 6.0의 ATL 3.0 및 그 이전 버전에서는 atlconv.h의 매크로를 사용하는 문자열 변환이 항상 시스템의 ANSI 코드 페이지(CP_ACP)를 사용하여 수행되었습니다. Visual C++ .NET의 ATL 7.0부터는 _CONVERSION_DONT_USE_THREAD_LOCALE이 정의되지 않은 경우 문자열 변환이 현재 스레드의 기본 ANSI 코드 페이지를 사용하여 수행됩니다. _CONVERSION_DONT_USE_THREAD_LOCALE이 정의된 경우에는 이전과 같이 시스템의 ANSI 코드 페이지가 사용됩니다.

CW2AEX 등의 문자열 변환 클래스를 사용하면 변환에 사용할 코드 페이지를 해당 생성자에 전달할 수 있습니다. 코드 페이지를 지정하지 않으면 해당 클래스에서는 매크로와 동일한 코드 페이지를 사용합니다.

자세한 내용은 ATL and MFC String Conversion Macros를 참조하십시오.


결국 컨버전 할때 내부적으로

inline UINT WINAPI _AtlGetConversionACP() throw()
{
#ifdef _CONVERSION_DONT_USE_THREAD_LOCALE
    return CP_ACP;
#else
    return CP_THREAD_ACP;
#endif
}

이 함수를 호출하는데 _CONVERSION_DONT_USE_THREAD_LOCALE 매크로가 없기 때문에 CP_THREAD_ACP 코드
페이지가 작성됩니다.

그래서 매크로를 프로젝트에 추가(Property Pages->Configuration Properties->C/C++->Preprocessor->Preprocessor Definitions)해주고

Rebuild All 해서 사용하시면 됩니다.

posted by 뚱2
데이터를 파일로 저장하는 프로그램을 만들다 보면은 꼭 필요한게
데이터 저장 폴더가 유효한지 아닌지 판단한는 일입니다.
그럴대 유용한 API 입니다.

PathIsDirectory Function


Verifies that a path is a valid directory.


Syntax

BOOL PathIsDirectory(LPCTSTR pszPath);


Parameters

pszPath
[in] A pointer to a null-terminated string of maximum length MAX_PATH that contains the path to verify.


Return Value

Returns TRUE if the path is a valid directory, or FALSE otherwise.


Function Information

Minimum DLL Version shlwapi.dll version 4.71 or later
Custom Implementation No
Header shlwapi.h
Import library shlwapi.lib
Minimum operating systems Windows 2000, Windows NT 4.0 with Internet Explorer 4.0, Windows 98, Windows 95 with Internet Explorer 4.0
Unicode Implemented as ANSI and Unicode versions.

posted by 뚱2

MFC를 사용 할 때 전역적으로 사용할수 있는 API 앞머리에 Afx가 붙습니다.
그중 현재 프로그램의 Instance Handle을 구할수 있는 API입니다.

AfxGetInstanceHandle();

그런데 이걸 Winapi로 하면 어떻게 될까요?  우선 GetWindowLong API를 이용하면 해결됩니다.
// 원형
LONG GetWindowLong(          
    HWND hWnd,
    int nIndex
);

아래와 같이 호출해 주시면 됩니다.  hWnd는 호출하는 쪽의 윈도우 핸들 입니다.
GetWindowLong(hWnd, GWL_HINSTANCE);

posted by 뚱2

WINDOWS API 폴더 선택

C/C++/VC++ / MFC 2011. 1. 21. 21:49
윈도우 프로그래밍을 하다보면은 많이 사용하는 파일, 색상, 폰트, 프린트 등을 공통대화상자로 만들어두워
재사용 할수 있게 했습니다.

그런데 이상하게도 폴더를 선택할수 있는 공통대화 상자는 없습니다. 다른 것보다 덜 사용해서 그런지...
없다고 안되는건 아니고 Shell쪽에서 함수를 제공하고 있습니다.

간단하게 다음과 같이 사용하실수 있습니다.

BOOL ChoiceFolder(HWND hWndOwner, TCHAR* pszFolderPath)
{
	ASSERT( IsWindow(hWndOwner) == TRUE && pszFolderPath != NULL);
	
	LPITEMLIST pidl;
	BROWSEINFO bi;

	bi.hwndOwner      = hWndOwner;
	bi.pidRoot        = NULL;
	bi.pszDisplayName = NULL;
	bi.lpszTitle      = _T("폴더를 선택해 주세요.");
	bi.ulFlags        = BIF_NEWDIALOGSTYLE|BIF_EDITBOX|BIF_RETURNONLYFSDIRS;
	bi.lpfn           = NULL;
	bi.lParam         = 0;

	pidl = SHBrowseForFolder(&bi);
	if ( pidl == NLULL)
		return FALSE;
	
   	return SHGetPathFromIDList(pidl, pszFolderPath);
}



posted by 뚱2
VC++에서 ADO를 사용하는 방법은 크게 2가지가 있다.

1. OLE DB SDK라이브러리를 이용하는 방법
2. #import를 이용하여 Type Library를 이용하는 방법

전 개인적으로 2번째 방법을 선호합니다.
그리고 Type Library를 import 하면 스마트 포인터를 사용할수 있기때문에 편리합니다.

#import를 할려면 msadoxx.tlb 파일이나 msadoxx.dll 파일이 필요합니다.
ado는 버전별로 있기때문에 본인에게 맞는 버전을 사용하면 됩니다.

import할 파일은 C:\Program Files\Common Files\System\ado\ 에 있습니다.
MFC를 이용해서 프로그래밍을 한다면 StdAfx.h에 선언해 주는게  편합니다.
StdAfx.h에 선언하지 않으면 사용하는 곳마다 계속 선언을 해줘야 합니다.

//StdAfx.h
//...
#import "C:\\Program Files\\Common Files\\System\\ado\\msado26.tlb" \
        no_namespace rename("EOF", "adoEOF")
//...

type library를 임포트 할려면 .tlb라는 파일이 필요한데 ado는 .dll에도 type library가 저장되어 있습니다.
따라서 .tlb, .dll 둘중에 아무거나 임포트 하시면 됩니다.
namespace를 사용하지 않게 옵션을 주어서 코딩시 편하게 할 수 있습니다.
간혹 이름 충돌이 발생할수 있는데 그럴때는 rename_namespace("새로운이름") 옵션으로 이름을 변경해
주시면 됩니다.

rename("EOF", "adoEOF")는 혹 다른곳에서 EOF를 사용함으로 해서 충돌나는 것을 방지해줍니다.
posted by 뚱2
Visual Studio 2008에서 C++ 프로그램을 컴파일 하면은 기본적으로 두가지 모드가 있습니다.

Release /Debug 두 버전의 차이점은 다른 소소한 성능 향상을 위한 컴파일 옵션도 있지만

가장 중요한건 디버깅이 가능하느냐 가능하지 않냐의 차이점 이라고 생각입니다.

그런데 기존에 구축되어 있는 프로그램을 유지 보수 하다보면은 디버깅 할일이 발생합니다.

기존의 선임이 Release / Debug  별로 프로젝트를 잘 관리했으면 좋겠지만

세상 사는 일이 다 내맘데로 안되듯이 너무 오랜기간 유지 보수를 하다보니 Debug 정보가 유실되는 경우가 있습니다.

프로젝트가 하나의 exe이면 다시 Debug 컴파일 하면되지만 exe와 dll이 약 40~50개 연결된 프로그램이라면

헉 소리 납니다.

이럴때 기본적으로 되어 있는 Release에서 디버깅 정보를 삽입해서 컴파일 할 수 있습니다.

프로젝트 속성 (ALT + F7) 으로 들어가서 아래 그림과 같이 3개의 설명을 그림에 맞게 해주시면 Release 에서도 

디버깅 가능합니다.

* Property Page -> Configuration Properties -> C++ -> General -> Debug Information Format

* Property Page -> Configuration Properties -> C++ -> Optimization -> Optimization 

* Property Page -> Configuration Properties -> Linker -> Debugging -> Generate Debug Info

  
posted by 뚱2

현재 WTL 최신 버전은 WTL81_9127 입니다.

WTL 설치하고 Visual Studio 2008에서 실행시키면

일반 AppWiz는 잘 되는데 모바일 용인 AppWizMobile은 안됩니다.

이리 저리 삽질 하고 수정한 버전 올립니다.

추가로 Windows 7 x64에서도 구동되게 수정했습니다.

posted by 뚱2

저는 대부분 MFC로 응용프로그램을 만들때 공유 DLL로 MFC 라이브러리 포함을 선택합니다. 

Static Library로 해서 소스코드가 커지는게 싫기도 하고

제 프로그램 개발방식이 exe +  확장 dll로 구성되어 있기때문에(확장 dll이 많게는 20개 이상도 됩니다.)

MFC dll 하나더 포함된다고 관리가 불편해 지는 것도 아니기에 소스크기를 줄일려고 DDL로 사용합니다.

그러지만은 디버깅시에는 MFC소스 코드를 쫓아가기 힘듭니다.

왜!! MFC 소스 코드를 쫓아가야 하냐?? 제가 만든 오류와 버그가 MFC쪽 소스코드에서 잡히는 경우가 있기때문입니다.

MFC 소스코드를 보면은 ASSERT로 Validation체크를 많이 해 놓는데 이게 Debug시에만 나타납니다.


그런데 그 소스 코드라인이 제 코드가 아니라 MS MFC 코드 속입니다.
결국 MFC 소스코드를 쫓가가서 직접 눈으로 확인해야 합니다.

이럴때는 Static Library로 연결해 놓으면 디버깅을 걸어서 소스코드를 쫓아갈수 있습니다.

posted by 뚱2

링크 : http://syung1104.blog.me/181447343 

Dynamic-Link Library 가 생성되면 .lib .dll 이 생성된다.
dll를 링크 걸어서 실행시킬때
error LNK2019: unresolved external symbol "__declspec(dllimport)
가 발생하는 경우가 있는데

다음의 2가지를 의심해 본다.
1. 오류가 발생하면 기본적으로 .lib 파일이 제대로 연결되었는지 확인한다.
2. 그래도 찾을수 없다면 dumpbin 도스 유틸리티로 .dll .lib 파일을 직접 확인해서
   함수목록이 제대로 임포트 되었는지 확인해야 한다.

명령>>dumpbin /exports "읽을파일"




ps. 위의 이미지는 SQLite3를 Windows CE용으로 컴파일 한 .lib 파일 입니다.

posted by 뚱2

C++ 캐스트 연산자

C/C++/VC++ / MFC 2009. 9. 15. 14:50

C++로 프로그래밍을 할때도 C 캐스팅을 사용하고 있습니다.
이유는 C++ 캐스트 연산자 보다 타이핑 작성라인이 적어지고 익숙해서 편하기 때문이죠...
그렇지만 여러 자료나 많은 분들이 타입변환시 더 안정적인 C++ 캐스트 연산자를 사용하라고
조언하고 있습니다.


* static_cast<타입>(대상) : 논리적으로 변환 가능한 타입을 변환한다.
설명 : 포인터끼리 타입을 변환할 때는 상속 관계에 있는 포인터끼리만 변환이 허용되면 상속 관계가 아닌
         포인터끼리는 변환을 거부한다.

* dynamic_cast<타입>(대상)
설명 : 부모 타입의 포인터를 자식 타입의 포인터로 다운 캐스팅할 때 무조건 변환을 허용하지 않고
         안전하다고 판단될 때만 허용한다.
         다운 캐스팅 할때 static_casts는 무조건 변환을 허가하지만 dynamic_cast는 실행 중에 타입을
         점검하여 안전한 캐스팅만 허가한다.
         따라서 이 연산자가 변환 가능성을 판한하기 위해서는 실행 중에 객체의 실제 타입을 판별할 수 있어야 한다.

* const_cast<타입>(대상)
설명 : 포인터의 상수성만 변경하고 싶을 때 사용한다.

* reinterpret_cast<타입>(대상)
설명 : 임이의 포인터 타입끼리 변환을 허용하는 상당히 위험한 캐스트 연산자.
         이 연산자는 포인터 타입간의 변환이나 포인터와 수치형 데이터의 변환에만 사용하며 기본 타입들끼리의
         변환에는 사용할 수 없다.

static_cast 상속 관계의 클래스 포인터 및 레퍼런스. 기본 타입. 타입 체크 안함.
dynamic_cast 상속 관계의 클래스 포인터 및 레퍼런스. 타입 체크. RTTI 기능 필요
const_cast const. volatile 등의 속성 변경
reinterpret_cast 포인터끼리, 포인터와 수치형간의 변환  
참고 : 혼자 연구하는 C/C++ (김상형 저/와우북스)
posted by 뚱2

타입 변환 연산자

C/C++/VC++ / MFC 2009. 8. 13. 17:05

C++ 에서는 타입 변환 연산자라는 기능이 있습니다.
이걸 사용하면은 클래스가 일반 타입처럼 암시적 변환이 가능합니다.

아래는 예제 클래스 입니다.
차때고 포때고 간단하게 작성했습니다.
클래스의 목적은 int의 기능을 대신하는 클래스입니다.
class MyInt
{
private:
    int m_nNum;

public:
    MyInt(int i) : m_nNum(i)  {}
    operator int() const {
      return m_nNum;
    }
    MyInt& operator =(int i)  { 
         m_nNum = i; return *this; 
    }
};
보통 연산자 오버로딩 후에는 반환값이 있습니다.
    MyInt& operator =(int i)      {   m_nNum = i; return *this; }
이런것 들 말이죠
그렇지만

연산자 오버로딩은 연산자가 함수 형태라고 해서 
리턴값을 써주면 안됩니다.
    operator int() const    {  return m_nNum;  }         // 컴파일됨
    int operator int() const    {  return m_nNum;  }     // 에러


참고 : The C++ Programming Language
posted by 뚱2

PDA 업체에서 제공 하는 API를 사용하기 위해서 .Lib 을 링크하고 .h파일을 인클루드 했는데
위와 같은 에러가 발생했다.
구전(VC++ 6.0 이하)에서는 허용되었던 함수의 리턴값 생략이 VC++ 7.0이상으로 오면서
명시적으로 바뀌면서 생기는 오류이다.
컴파일 옵션을 바꿔서 해도 되지만... 그것보다는
함수를 명시적으로 바꾸는게 바른 방법인데...
이거... 내가 만든것도 아니고 .h 바꾸면 찝찝한데... ㅡㅡ;
그렇다고 컴파일 옵션을 바꾸자니 그건 더 찝찝하고...
결국 난 .h를 변경했다.
posted by 뚱2

C, C++는 파일을 작성할때는

보통 헤더파일에는 선언

구현파일에는 정의를 많이 넣는데요

템플릿을 사용할때는 헤더와 정의를 헤더파일에 넣어야 합니다.

이유인즉슨 컴파일 과정에서 템플릿의 정의부분이 필요하기 때문입니다.


ps. 한파일에 넣지 않고 하는 방법도 있다고 하는데 가장 손쉬은 방법은

      헤더에 다 작성하는 방법입니다.
posted by 뚱2

오늘 하루종일 바보 같은 짓을 한 하루 였습니다.
제가 MFC 클래스 구조를 잘 모르는 상태에서 쓰기 급급했는데
스택으로 생성한 다이알로그도 PostNcDestroy를 상속받아서 거기서
delete this; 해줬습니다.
평소에는 이렇게 해도 괜찮았는데
CString를 선언해주면은 스택오버플로우가 나버립니다.
정확하게는 모르겠으나 스택으로 생성한 것을 자신이 delete this;
해버리는 바람에 CString 안에서 힙으로 생성한걸 해제하지 못해서
오버플로우가 나는것 같습니다.

하나 하나 정확하게 알고 사용해야 한다는 교훈을 ㅡㅡ;
MFC는 심오하네요~~
posted by 뚱2

재미있는 기능도 많이 있고 가장큰 이점은 코드를 만들어 주는 기능이다.
아래에서 다운받을수 있다.
Visual Studio .net Visual Studio 2500

http://www.cupla.net/CodeWiz2/
posted by 뚱2

간단하지만 요긴한 단축키 입니다. ^^;

우선 주석을 하고 싶은 구간을 선택하세요
주석 : Ctrl+K+C
해제 : Ctrl+K+U

PS. Visual Studio C++ 6.0 이하에서는 안됩니다.
posted by 뚱2

오류를 처리하는 가장 발전적이고 효과적인 방법이 C++ 예외 처리 기능입니다.
그런데 VC++ (6 or 7)의 설명서에 약간 오해의 소지가 있는 부분이 있습니다.
예를 들어,

try 
{ 
    int* p = 0; 
    *p = 0; 
} 
catch (...) 
{ 
    // do something 
} 

이렇게 하면, 위 *p = 0 줄에서 access violation 예외가 발생하고
그 예외가 아래 catch 블럭에서 잡힙니다.

문제는 debug 빌드인 경우에는 예상대로 예외 처리가 되는데
release 빌드일 때는 그렇지 않다는 데 있습니다.

MSDN이나 VC++ 도움말에서 예외 처리에 관련된 부분을 보면
항상 컴파일러 옵션 /GX 또는 /EHsc를 지정하라는 말이 나옵니다.
"win32 structured exception" 방식을 쓰려면 /GX 없이 그냥 컴파일하고
C++ 예외 처리 방식을 쓰려면 /GX로 컴파일하라..." 이런 식으로 설명되고 있죠.

디폴트 옵션도 /EHsc 입니다.
/EHs 는 syncronous 예외 처리 모델을 가리키고
/EHc 는 C 함수는 예외를 throw하지 않는 다고 가정하는 것을 가리킵니다.

syncronous 모델은 뭐냐하면, 명시적으로 throw가 사용되지 않은 함수에 대해서는
예외 처리에 대한 지원을 하지 않는 것을 말합니다.

따라서

try 
{ 
    int* p = 0; 
    *p = 0; 
} 
catch (...) 
{ 
    // do something 
} 

이런 프로그램은 디버그 빌드일 때는 잘 돌아가고
예외 처리도 잘 되고,
잘못된 동작이 있을 때 이쁘게 메시지 박스 띄우고 부드럽게 종료하고
다 잘 됩니다.

그러다가 릴리즈 빌드로 바꾸면 디폴트로 /EHsc 옵션이 먹으면서
예외 처리도 안 되고
access violation 같은 거 발생할 때 프로그램이 죽어버립니다.

해결책은 단순하죠.
컴파일러 옵션 대화상자에서 C++ 예외 처리를 사용하지 않음으로 선택하고
직접 /EHa 옵션을 추가해주면 됩니다.

/EHa 옵션을 주고
메인 프로그램 진입점을

try 
{ 

} 
catch (...) 
{

} 

이런 블럭으로 감싸면 프로그램이 예기치 않게 종료되는 일을 방지할 수 있습니다.
위 내용은 VC6, VC7 공통입니다.
이상입니다.

** 경고
예외 처리는 복잡한 고려사항이 많기 때문에
충분히 공부한 후 사용해야 합니다.
예외 처리는 체계적으로 사용해야만 효과적이며
그렇지 않으면 부작용이 더 클 수 있습니다.

posted by 뚱2

LNK 4099 에러관련

C/C++/VC++ / MFC 2008. 9. 18. 16:27
posted by 뚱2

VC++ 빠르게 컴파일 하기 위해서 Precompiled header 라는 것을 만들어둡니다.
그런데 다른 외부의 컴파일러로 만들어진 라이브러리(소스)에서는 이게 없습니다.
그래서  VC++에서는 프리컴파일 헤더가 필요하다고 에러를 발생시킵니다.

해결방법은 포함하는 라이브러리 소스(.cpp) 맨 윗줄에 #include "stdafx.h"
추가해 주시면 됩니다.

posted by 뚱2

RasAPI를 사용하여 간단하게 접속하는 프로그램을 작성했습니다.
그런데 디버그 모드에서는 너무나도 잘 되는게 릴리즈 모드에서만 되면은
안되더라구요.

이것 저것 하루종일 삽질(?)을 하다가 결국 초기화 문제였습니다. ㅠㅠ
초기화 되지 않은 포인터의 경우 디버그 모드에서는 자동으로 임의값 0xCD로
초기화를 수행하지만 릴리즈에서는 수행하지 않는다고 합니다.
그래서

// 수정전
m_RasDialParams.dwSize = sizeof(RASDIALPARAMS);

...

// 수정후
ZeroMemory(&m_RasDialParams, sizeof(RASDIALPARAMS));
m_RasDialParams.dwSize = sizeof(RASDIALPARAMS);

...

이렇게 해주니까 잘~~~~~ 됩니다.
정말 단순한거지만 너무 중요한 사항같습니다.
초기화 생활화 합시다.

posted by 뚱2

간단하지만 유용하게 사용하는 함수입니다.

CCmdTarget::BeginWaitCursor(); // 모래시계중 작업들... CCmdTarget::EndWatiCursor();

저 같은 경우는 MFC로 프로그래밍 할때 중간에 로딩시간이나 소켓통신이 길어질때
사용합니다.

posted by 뚱2