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

 

'.Net > .Net' 카테고리의 다른 글

[.Net] Thread Pool  (0) 2013.06.19
[.Net] 런타임에서 어셈블리를 찾는 방법  (0) 2013.05.20
[.Net] 닷넷프레임워크 버전 확인  (0) 2013.05.10
[.Net] TransactedInstaller  (0) 2013.04.22
[.Net] Windows Service Current Directory  (0) 2013.04.20
posted by 뚱2

[.Net] Thread Pool

.Net/.Net 2013. 6. 19. 13:50

링크 : http://msdn.microsoft.com/ko-kr/library/3dasc8as(v=vs.80).aspx 

 

 

using System;
using System.Threading;

public class Fibonacci
{
    public Fibonacci(int n, ManualResetEvent doneEvent)
    {
        _n = n;
        _doneEvent = doneEvent;
    }

    // Wrapper method for use with thread pool.
    public void ThreadPoolCallback(Object threadContext)
    {
        int threadIndex = (int)threadContext;
        Console.WriteLine("thread {0} started...", threadIndex);
        _fibOfN = Calculate(_n);
        Console.WriteLine("thread {0} result calculated...", threadIndex);
        _doneEvent.Set();
    }

    // Recursive method that calculates the Nth Fibonacci number.
    public int Calculate(int n)
    {
        if (n <= 1)
        {
            return n;
        }

        return Calculate(n - 1) + Calculate(n - 2);
    }

    public int N { get { return _n; } }
    private int _n;

    public int FibOfN { get { return _fibOfN; } }
    private int _fibOfN;

    private ManualResetEvent _doneEvent;
}

public class ThreadPoolExample
{
    static void Main()
    {
        const int FibonacciCalculations = 10;

        // One event is used for each Fibonacci object
        ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
        Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
        Random r = new Random();

        // Configure and launch threads using ThreadPool:
        Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
        for (int i = 0; i < FibonacciCalculations; i++)
        {
            doneEvents[i] = new ManualResetEvent(false);
            Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
            fibArray[i] = f;
            ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
        }

        // Wait for all threads in pool to calculation...
        WaitHandle.WaitAll(doneEvents);
        Console.WriteLine("All calculations are complete.");

        // Display the results...
        for (int i= 0; i<FibonacciCalculations; i++)
        {
            Fibonacci f = fibArray[i];
            Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
        }
    }
}

 

posted by 뚱2

[C#] ref, out의 차이

.Net/C# 2013. 6. 12. 13:54

링크 : http://blog.naver.com/thx4alice?Redirect=Log&logNo=110023260951 

'.Net > C#' 카테고리의 다른 글

[C#] Linq for xml tutorial  (0) 2013.05.23
[C#] XML Serialize Tutorial  (0) 2013.05.08
[C#] Mutex를 통한 다중 인스턴스 실행방지  (0) 2013.02.08
[C#] Visual Studio TODO 만들기  (0) 2013.01.30
[C#] Form close와 Dispose  (0) 2013.01.28
posted by 뚱2

링크 : http://msdn.microsoft.com/ko-kr/library/yx7xezcf.aspx 

 

'.Net > .Net' 카테고리의 다른 글

[.Net] Regular Expression Quick Reference  (0) 2013.09.12
[.Net] Thread Pool  (0) 2013.06.19
[.Net] 닷넷프레임워크 버전 확인  (0) 2013.05.10
[.Net] TransactedInstaller  (0) 2013.04.22
[.Net] Windows Service Current Directory  (0) 2013.04.20
posted by 뚱2

링크 : http://jmnote.com/wiki/%EB%8B%B7%EB%84%B7%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC_%EB%B2%84%EC%A0%84_%ED%99%95%EC%9D%B8


dir %windir%\Microsoft.NET\Framework | findstr DIR


'.Net > .Net' 카테고리의 다른 글

[.Net] Thread Pool  (0) 2013.06.19
[.Net] 런타임에서 어셈블리를 찾는 방법  (0) 2013.05.20
[.Net] TransactedInstaller  (0) 2013.04.22
[.Net] Windows Service Current Directory  (0) 2013.04.20
[.Net] Changing Start Mode of a Windows Service  (0) 2013.04.20
posted by 뚱2

[.Net] TransactedInstaller

.Net/.Net 2013. 4. 22. 17:23

TransactedInstaller : http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=KO-KR&k=k(SYSTEM.CONFIGURATION.INSTALL.TRANSACTEDINSTALLER);k(SOLUTIONITEMSPROJECT);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true 


InstallContext : http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=KO-KR&k=k(%22SYSTEM.CONFIGURATION.INSTALL.INSTALLCONTEXT.%23CTOR%22);k(SOLUTIONITEMSPROJECT);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true


TransactedInstaller 를 사용하면은 Installutil.exe(설치 관리자 도구)의 구현과 유사한 구현을 제공 할수 있다.



ArrayList myOptions = new ArrayList();

String myOption;

bool toUnInstall = false;

bool toPrintHelp = false;

TransactedInstaller myTransactedInstaller = new TransactedInstaller();

AssemblyInstaller myAssemblyInstaller;

InstallContext myInstallContext;


try

{

   for(int i = 0; i < args.Length; i++)

   {

      // Process the arguments.

      if(args[i].StartsWith("/") || args[i].StartsWith("-"))

      {

         myOption = args[i].Substring(1);

         // Determine whether the option is to 'uninstall' an assembly.

         if(String.Compare(myOption, "u", true) == 0 ||

            String.Compare(myOption, "uninstall", true) == 0)

         {

            toUnInstall = true;

            continue;

         }

         // Determine whether the option is for printing help information.

         if(String.Compare(myOption, "?", true) == 0 ||

            String.Compare(myOption, "help", true) == 0)

         {

            toPrintHelp = true;

            continue;

         }

         // Add the option encountered to the list of all options

         // encountered for the current assembly.

         myOptions.Add(myOption);

      }

      else

      {

         // Determine whether the assembly file exists.

         if(!File.Exists(args[i]))

         {

            // If assembly file doesn't exist then print error.

            Console.WriteLine("\nError : {0} - Assembly file doesn't exist.",

               args[i]);

            return;

         }


         // Create a instance of 'AssemblyInstaller' that installs the given assembly.

         myAssemblyInstaller = 

            new AssemblyInstaller(args[i], 

            (string[]) myOptions.ToArray(typeof(string)));

         // Add the instance of 'AssemblyInstaller' to the 'TransactedInstaller'.  

         myTransactedInstaller.Installers.Add(myAssemblyInstaller);

      }

   }

   // If user requested help or didn't provide any assemblies to install

   // then print help message.

   if(toPrintHelp || myTransactedInstaller.Installers.Count == 0)

   {

      PrintHelpMessage();

      return;

   }


   // Create a instance of 'InstallContext' with the options specified.

   myInstallContext = 

      new InstallContext("Install.log", 

      (string[]) myOptions.ToArray(typeof(string)));

   myTransactedInstaller.Context = myInstallContext;


   // Install or Uninstall an assembly depending on the option provided.

   if(!toUnInstall)

      myTransactedInstaller.Install(new Hashtable());

   else

      myTransactedInstaller.Uninstall(null);

}

catch(Exception e)

{

   Console.WriteLine("\nException raised : {0}", e.Message);

}


참고 : http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1971&page=2 

 

MFC C++쓸땐 Self 서비스 등록 코드를 썼는데

 

C#에서는 InstallUtil.exe를 써야 하는데

 

InstallUtil.exe이 패스에 안걸려 있으면 찾아서 써야 하니 귀찮기도 하구 해서

 

아침에 삽질 삽질 하다가..  거의 포기하려는 순간에 찾았네요.

 

서비스로 만드는 법은 뭐 간단하니 아실거구.

 

서비스 인스톨러도 추가해 놓으심 되구요..

 

그 다음에 Main을 아래와 같이 넣어서 고쳐 쓰심 됩니다.

 

 

/install 과 /uninstall 로 서비스에 넣었다 뺏다 합니다.

ServiceMain은 내가 만든 서비스 실행 클래스 일 뿐이구요

추가 하셔서 일반에서 서비스를 선택하시면 됩니다.

 

[STAThread]
static void Main(string[] args)
{
    if (args.Length == 1)
    {
 try
 {
     using (TransactedInstaller ti = new TransactedInstaller())
     {
  using (ProjectInstaller pi = new ProjectInstaller())
  {
      ti.Installers.Add(pi);

      string[] cmdline = { string.Format("/assemblypath={0}", System.Reflection.Assembly.GetExecutingAssembly().Location) };

      pi.Context = new InstallContext(null, cmdline);

      if (args[0].ToLower() == "/install")
   pi.Install(new Hashtable());
      else if (args[0].ToLower() == "/uninstall")
   pi.Uninstall(null);
      else
   throw new Exception("Invalid command line");
  }
     }
 }
 catch (Exception ex)
 {
     Console.Error.WriteLine(ex.Message);
 }
    }
    else
    {
 System.ServiceProcess.ServiceBase[] ServicesToRun;
 ServicesToRun = new System.ServiceProcess.ServiceBase[] { new ServiceMain() };
 System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }
}

posted by 뚱2

윈도우즈 서비스에서


System.Environment.CurrentDirectory 을 호출하면 "C:\Windows\System32"를 카리킨다.


그럴때 두가지 방법이 있다.


1. System.Windows.Forms 을 참조하고 System.Windows.Forms.Application.StartupPath을 호출


2. System.Reflection.Assembly.GetEntryAssembly().Location



'.Net > .Net' 카테고리의 다른 글

[.Net] 닷넷프레임워크 버전 확인  (0) 2013.05.10
[.Net] TransactedInstaller  (0) 2013.04.22
[.Net] Changing Start Mode of a Windows Service  (0) 2013.04.20
[.Net] Mutex 클래스  (0) 2013.04.19
[.Net] ServiceController  (0) 2013.04.18
posted by 뚱2

참고 : http://peterkellyonline.blogspot.kr/2011/04/configuring-windows-service.html

 

결국 어플리 케이션을 디테일하게 컨트롤 할려면 .Net에서 WinApi를 호출해야 한다는 결론

위 Url참고 하여 GetStartMode메소드 추가

 

    public static class ServiceHelper
    {
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean ChangeServiceConfig(
            IntPtr hService,
            UInt32 nServiceType,
            UInt32 nStartType,
            UInt32 nErrorControl,
            String lpBinaryPathName,
            String lpLoadOrderGroup,
            IntPtr lpdwTagId,
            [In] char[] lpDependencies,
            String lpServiceStartName,
            String lpPassword,
            String lpDisplayName);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr OpenService(
            IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr OpenSCManager(
            string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
        public static extern int CloseServiceHandle(IntPtr hSCObject);

        [DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
        public static extern Boolean QueryServiceConfig(IntPtr hService, IntPtr intPtrQueryConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);

        [DllImport( "advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W" )]
        public static extern Boolean QueryServiceConfig2( IntPtr hService, UInt32 dwInfoLevel, IntPtr buffer, UInt32 cbBufSize, out UInt32 pcbBytesNeeded );


        private const uint SERVICE_NO_CHANGE     = 0xFFFFFFFF;
        private const uint SERVICE_QUERY_CONFIG  = 0x00000001;
        private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
        private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;

        [StructLayout(LayoutKind.Sequential)]
        public class QUERY_SERVICE_CONFIG
        {
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
            public UInt32 dwServiceType;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
            public UInt32 dwStartType;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
            public UInt32 dwErrorControl;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
            public String lpBinaryPathName;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
            public String lpLoadOrderGroup;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
            public UInt32 dwTagID;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
            public String lpDependencies;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
            public String lpServiceStartName;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
            public String lpDisplayName;
        };

        public static void ChangeStartMode(ServiceController svc, ServiceStartMode mode)
        {
            var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (scManagerHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Manager Error");
            }

            var serviceHandle = OpenService(
                scManagerHandle,
                svc.ServiceName,
                SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);

            if (serviceHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Error");
            }

            var result = ChangeServiceConfig(
                serviceHandle,
                SERVICE_NO_CHANGE,
                (uint)mode,
                SERVICE_NO_CHANGE,
                null,
                null,
                IntPtr.Zero,
                null,
                null,
                null,
                null);

            if (result == false)
            {
                int nError = Marshal.GetLastWin32Error();
                var win32Exception = new Win32Exception(nError);
                throw new ExternalException("Could not change service start type: "
                    + win32Exception.Message);
            }

            CloseServiceHandle(serviceHandle);
            CloseServiceHandle(scManagerHandle);
        }

        /// <summary>
        /// Automatic = 2, Manual = 3, Disabled = 4
        /// </summary>
        /// <param name="svc"></param>
        /// <returns></returns>
        public static uint GetStartMode(ServiceController svc)
        {
            var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (scManagerHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Manager Error");
            }

            var serviceHandle = OpenService(
                scManagerHandle,
                svc.ServiceName,
                SERVICE_QUERY_CONFIG);

            if (serviceHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Error");
            }

            UInt32 dwBytesNeeded = 0;
            IntPtr ptr = Marshal.AllocHGlobal(4096);// Allocate memory for struct.

            bool result = QueryServiceConfig(
                serviceHandle,
                ptr,
                4096,
                out dwBytesNeeded);

            QUERY_SERVICE_CONFIG qUERY_SERVICE_CONFIG = new QUERY_SERVICE_CONFIG();
            Marshal.PtrToStructure(ptr, qUERY_SERVICE_CONFIG);// Copy;
            Marshal.FreeHGlobal(ptr);

            if (result == false)
            {
                int nError = Marshal.GetLastWin32Error();
                var win32Exception = new Win32Exception(nError);
                throw new ExternalException("Could not QueryServiceConfig : "
                    + win32Exception.Message);
            }

            CloseServiceHandle(serviceHandle);
            CloseServiceHandle(scManagerHandle);

            return qUERY_SERVICE_CONFIG.dwStartType;
        }
    }

 

var svc = new ServiceController("BITS");
ServiceHelper.ChangeStartMode(svc, ServiceStartMode.Automatic);

// You can then Start the service if necessary.
if (svc.Status != ServiceControllerStatus.Running)
{
   svc.Start();
}

// And of course you should close the service when no longer needed
svc.Close();

'.Net > .Net' 카테고리의 다른 글

[.Net] TransactedInstaller  (0) 2013.04.22
[.Net] Windows Service Current Directory  (0) 2013.04.20
[.Net] Mutex 클래스  (0) 2013.04.19
[.Net] ServiceController  (0) 2013.04.18
[.Net] Windows 서비스 만들기  (0) 2013.04.17
posted by 뚱2

[.Net] Mutex 클래스

.Net/.Net 2013. 4. 19. 15:40

Mutex        : http://msdn.microsoft.com/ko-kr/library/01985e8f(v=vs.100).aspx

WaitHandle : http://msdn.microsoft.com/ko-kr/library/system.threading.waithandle(v=vs.100).aspx

 

posted by 뚱2

[.Net] ServiceController

.Net/.Net 2013. 4. 18. 20:48

링크 : http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=KO-KR&k=k(SYSTEM.SERVICEPROCESS.SERVICECONTROLLER);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true 

'.Net > .Net' 카테고리의 다른 글

[.Net] Changing Start Mode of a Windows Service  (0) 2013.04.20
[.Net] Mutex 클래스  (0) 2013.04.19
[.Net] Windows 서비스 만들기  (0) 2013.04.17
[.Net] HTTP POST/WebClient (C#) and CSV formated string  (0) 2013.04.12
[.Net] RSA 암호화  (0) 2013.03.13
posted by 뚱2

[WinForm] OpenFileDialog

.Net/WinForm 2013. 3. 13. 15:46
OpenFileDialog open_file_dialog = new OpenFileDialog();
open_file_dialog.Filter = "dat Files|*.dat|All Files|*.*";
open_file_dialog.FilterIndex = 1;
open_file_dialog.RestoreDirectory = true;

try
{
    if (open_file_dialog.ShowDialog() == DialogResult.OK)
    {
        import_path = open_file_dialog.FileName;
    }
}
catch (System.Exception)
{
}

'.Net > WinForm' 카테고리의 다른 글

[.Net] FolderbrowserDialog Custom Action  (0) 2014.02.08
[WinForm] 서브클래스에서 OnPaint가 안될때...  (0) 2013.02.22
posted by 뚱2

[.Net] RSA 암호화

.Net/.Net 2013. 3. 13. 11:03

TextBox에 PlaceHolder 기능을 추가해야 해서 이것 저것 해보다가

 

OnPaint로 그릴 생각까지 했는데 분면 OnPaint를 오버라이드 했는데도 이벤트가 발생하지 않았습니다.

그럴때는 서브클래싱한 컨트롤에서 SetStyle을 설정('SetStyle(ControlStyles.userPaint, true)')해줘야 합니다.

 

SetStyle : http://msdn.microsoft.com/ko-kr/library/system.windows.forms.control.setstyle.aspx

 

그런데 구글링으로 찾은 소스중 보다 Win32 API를 이용해서 텍스트박스에 PlaceHolder 기능을 구현한게 있네요.

 

public class CueTextBox : TextBox

{  

    private static class NativeMethods

    {

        private  const uint ECM_FIRST       = 0x1500;

        internal const uint EM_SETCUEBANNER = ECM_FIRST + 1;

        [DllImport("user32.dll", EntryPoint = "SendMessageW")]

        public static extern IntPtr SendMessageW(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

    }

    private string _cue;

    [

        Description("PlaceHolder로 사용될 텍스트"),

        Category("Appearance"),

        Browsable(true),

    ]        

    public string Cue

    {

        get

        {

            return _cue;

        }

        set

        {

            _cue = value;

            UpdateCue();

        }

    }

    public CueTextBox()

    {

        this.SetStyle(ControlStyles.UserPaint, false);

        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

    }

    protected override void OnPaint(PaintEventArgs e)

    {

        base.OnPaint(e);

    }

    private void UpdateCue()

    {

        if (IsHandleCreated && _cue != null)

        {

            NativeMethods.SendMessageW(Handle, NativeMethods.EM_SETCUEBANNER, (IntPtr)1, _cue);

        }

    }

    protected override void OnHandleCreated(EventArgs e)

    {

        base.OnHandleCreated(e);

        UpdateCue();

    }

}


'.Net > WinForm' 카테고리의 다른 글

[.Net] FolderbrowserDialog Custom Action  (0) 2014.02.08
[WinForm] OpenFileDialog  (0) 2013.03.13
posted by 뚱2

링크 : http://blog.naver.com/PostView.nhn?blogId=monk773&logNo=90144334661 

'.Net > .Net' 카테고리의 다른 글

[.Net] HTTP POST/WebClient (C#) and CSV formated string  (0) 2013.04.12
[.Net] RSA 암호화  (0) 2013.03.13
[.Net] Castle DynamicProxy Tutorial  (0) 2013.01.23
[.Net] NuGet  (0) 2013.01.23
[.Net] AOP 프로그래밍  (0) 2013.01.23
posted by 뚱2

[JSON.net] Json.Net

.Net/Json.net 2013. 2. 10. 01:22

Homepage: http://james.newtonking.com/projects/json-net.aspx 

Document : http://james.newtonking.com/projects/json/help/

posted by 뚱2

링크 : http://www.cyworld.com/programer1234/9368817 

'.Net > C#' 카테고리의 다른 글

[C#] Linq for xml tutorial  (0) 2013.05.23
[C#] XML Serialize Tutorial  (0) 2013.05.08
[C#] Visual Studio TODO 만들기  (0) 2013.01.30
[C#] Form close와 Dispose  (0) 2013.01.28
[C#] 정적 생성자 (static 멤버 초기화)  (0) 2013.01.15
posted by 뚱2

[C#] Form close와 Dispose

.Net/C# 2013. 1. 28. 20:19

링크 (Form.Dispose) : http://msdn.microsoft.com/ko-kr/library/aw58wzka(v=vs.90).aspx

링크 (Form.Close) : http://msdn.microsoft.com/ko-kr/library/system.windows.forms.form.close(v=vs.90).aspx 


모달리스로 닫을시에는 Form.Close()를 호출

모달로 닫을시에는 Form.DialogResult 프로퍼티에 값을 대입

posted by 뚱2

링크 : http://kozmic.net/dynamic-proxy-tutorial/ 

링크 : http://docs.castleproject.org/Tools.DynamicProxy.ashx

 

'.Net > .Net' 카테고리의 다른 글

[.Net] RSA 암호화  (0) 2013.03.13
[.Net] EXE를 포함한 외부 DLL을 같이 배포하기  (0) 2013.02.19
[.Net] NuGet  (0) 2013.01.23
[.Net] AOP 프로그래밍  (0) 2013.01.23
[.Net] CodeDomProvider  (0) 2013.01.15
posted by 뚱2

[.Net] NuGet

.Net/.Net 2013. 1. 23. 14:27

링크 : http://debop.blogspot.kr/2012/03/nuget.html

 

이클립스 보면은 마켓이 있어서 다양한 패키지들을 다운로드 받는다.

 

이거랑 비슷한게 NuGet이라고 Visual Studio 2010 부터 지원한다.

 

 

'.Net > .Net' 카테고리의 다른 글

[.Net] EXE를 포함한 외부 DLL을 같이 배포하기  (0) 2013.02.19
[.Net] Castle DynamicProxy Tutorial  (0) 2013.01.23
[.Net] AOP 프로그래밍  (0) 2013.01.23
[.Net] CodeDomProvider  (0) 2013.01.15
[.Net] Aspect Oriented Programming With .Net  (0) 2013.01.14
posted by 뚱2

[.Net] AOP 프로그래밍

.Net/.Net 2013. 1. 23. 12:20

링크 : http://doc.postsharp.org/postsharp/documentation#getting-started 

링크 : http://msdn.microsoft.com/ko-kr/library/ee650325 

링크 : http://rafi.inha.ac.kr/~wbstory/tc/wbstory/82 

링크 : http://iyou72.tistory.com/20

링크 : http://blog.naver.com/ansysda?Redirect=Log&logNo=10003899076

 

목차

소개 소개
Aspect란 Aspect란
AOP의 장단점 AOP의 장단점
Trace.WriteLine을 넘어서 Trace.WriteLine을 넘어서
Mixin의 배후 구현 Mixin의 배후 구현
동작 위빙(Weaving) 동작 위빙(Weaving)
정리 정리
결론 결론

소개

AOP(Aspect Oriented Programming)는 개발된 지 오래되었지만 최근에야 Microsoft .NET 개발 커뮤니티에서 주목을 받기 시작했습니다. 새로 채택되는 모든 기술이 그렇듯이 기술과 기술의 사용 방법에 대해서는 많은 오해가 발생하기 마련이며 AOP도 예외는 아닙니다. 이 기사와 다음 코드 샘플에서는 AOP의 실제적인 응용 방법과 AOP에서 처리할 수 있는 몇 가지 일반적인 문제를 설명하며 AOP에 대해 집중적으로 다룰 예정입니다. 또한 웹 서비스를 사용하는 응용 프로그램을 살펴보고, AOP 프레임워크를 사용하여 웹 서비스에서 반환하는 개체에 새로운 aspect를 적용함으로써 개체의 기능을 확장해 보겠습니다. 이러한 aspect는 WSDL로 생성된 개체 모델과는 별개로 이 기능만을 위한 것입니다.

Aspect란

개체와 다른 개체 간의 관계를 떠올리면 대개 상속의 관점에서 생각하게 됩니다. 여기서는 Dog라는 추상 클래스를 예로 들어 보겠습니다. 저마다 고유한 동작을 가지는 유사한 클래스를 정의할 때는 대개 상속을 사용하여 기능을 확장하게 됩니다. 예를 들어 Poodle을 정의하는 경우 PoodleDog이므로 PoodleDog를 상속한다고 할 수 있을 것입니다. 지금까지는 괜찮지만 나중에 Obedient Dog라는 이름으로 또 다른 고유한 동작을 정의하면 어떻게 될까요 당연히 모든 Dog가 고분고분한(obedient) 것은 아니므로 Dog 클래스에는 obedience 동작이 포함될 수 없습니다. 또한 Dog에서 상속된 Obedient Dog 클래스를 만드는 경우 Poodle은 이 계층 구조에서 어디에 위치할까요 PoodleDog입니다. 그러나 Poodle고분고분(obedient)하거나 그렇지 않을 수 있습니다. 그렇다면 PoodleDog에서 상속되는 것일까요, 아니면 Obedient Dog에서 상속되는 것일까요 obedience를 Dog 계층 구조에 부적절하게 강제로 적용하는 대신 고분고분한 모든 유형의 Dog에 적용할 aspect로 볼 수도 있습니다.

소 프트웨어 측면에서 볼 때, AOP(Aspect-Oriented Programming)를 사용하면 상속 계층 구조와 별개로 클래스나 개체의 동작을 변경하는 aspect를 적용할 수 있습니다. 그러면 이러한 aspect를 런타임이나 컴파일 시에 적용하게 됩니다. AOP는 설명하는 것보다 예제를 통해 보여 주는 편이 더 간단합니다. 시작하기 전에 반복해서 사용할 네 개의 주요 AOP 용어를 정의해야 합니다.

  • Joinpoint—식별 가능하며 제대로 정의된 코드 지점

  • Pointcut—구성 또는 코드를 사용하여 joinpoint를 지정하는 방법

  • Advice—발생해야 하는 크로스 커팅(Cross Cutting) 작업을 표현하는 방법

  • Mixin—새 동작을 적용할 클래스의 대상 인스턴스와 혼합될 클래스의 인스턴스

이 들 용어에 대한 이해를 돕기 위해 joinpoint를 프로그램의 흐름에 정의된 지점으로 생각해 봅시다. 코드에서 메서드를 호출할 때 해당 호출이 발생하는 지점이 joinpoint의 좋은 예입니다. pointcut을 사용하면 프로그램 흐름을 차단할 joinpoint를 지정하거나 정의할 수 있습니다. 또한 pointcut에는 joinpoint에 도달할 경우 발생하는 advice가 들어 있습니다. 따라서 호출 중인 특정 메서드에 pointcut을 정의하는 경우, 호출이 발생하거나 joinpoint가 호출되면 AOP 프레임워크에 의해 프로그램의 흐름이 중단되고 pointcut의 advice가 실행됩니다. advice는 여러 가지일 수 있지만 호출할 또 다른 메서드로 간주하는 것이 가장 일반적입니다. 그러므로 pointcut이 있는 메서드를 호출하는 경우 실행할 advice는 호출할 또 다른 메서드가 됩니다. 이 advice 또는 호출할 메서드는 메서드가 차단된 개체에 있거나 여기서 혼합한 다른 개체에 있을 수 있습니다. mixin에 대해서는 나중에 좀 더 자세히 설명하겠습니다.

AOP의 장단점

흔히들 AOP를 차단으로 오해하지만 사실은 그렇지 않습니다. AOP는 차단 기능의 힘을 빌어 advice를 적용하거나 동작을 함께 위빙(weaving)할 뿐입니다. 현재 ContextBoundObject를 사용하여 AOP를 연상하게 하는 스타일로 차단하는 방법을 보여 주는 다양한 .NET 코드 샘플을 확인할 수 있습니다. 하지만 이 접근 방식에서는 사전 요구 사항에 따라 차단을 허용해야 하는 모든 클래스가 ContextBoundObject에서 상속되어야 하므로 ContextBoundObject는 이 작업에 적합하지 않습니다. ContextBoundObject 같은 사전 요구 사항이 있는 AOP에 대한 접근 방식은 요구 사항으로 인해 좋지 않은 영향이 발생하므로 복잡한 접근 방식으로 간주될 수 있으며 가능한 한 사용하지 않도록 합니다. 복잡한 접근 방식은 시스템에서 많은 공간을 차지하여 잠재적으로 모든 클래스에 영향을 미치므로 향후 시스템을 변경하거나 수정하는 능력을 떨어뜨릴 수 있습니다.

필자는 Encase라는 간단한 프레임워크를 만들었습니다. 여기서 "간단한"이라는 용어는 시스템 전체에 전혀 영향을 주지 않는다는 의미로 사용한 것입니다. AOP를 사용하면 한 시스템의 서로 다른 부분이 영향을 받게 되지만 간단한 프레임워크를 선택하고 뛰어난 프로그래밍 방식을 적용하면 부정적인 문제를 대부분 완화할 수 있습니다. Encase 프레임워크는 pointcut, mixin 및 aspect 위빙을 단순화하도록 설계되었습니다. 개발자는 대부분의 다른 간단한 AOP 프레임워크에 사용되는 XML 같은 구성 파일 대신 Encase가 있는 코드를 통해 aspect를 적용할 수 있습니다.

AOP가 널리 채택되지 않는 이유로는 복잡한 프레임워크를 꼽을 수 있겠지만, 가장 큰 원인은 현재 사용되고 있는 AOP 예제에서 찾을 수 있습니다. 이들 예제는 거의 모두가 실행 전에 메서드를 차단하고 Trace.WriteLine("Method entered.")을 실행하는 aspect를 적용하도록 구성되어 있습니다. 이러한 일반적인 인식과는 달리 AOP는 로깅, 보안, 계측 등을 제외한 문제를 해결하는 데 유용하게 사용할 수 있습니다.

Trace.WriteLine을 넘어서

AOP에 대한 보다 실제적인 접근 방식을 설명하기 위해 ContactService.Service라 는 웹 서비스에서 사람 개체 컬렉션을 수신하는 응용 프로그램을 만들어 보겠습니다. 현재 .NET 개발에 웹 서비스를 사용하는 가장 일반적인 방법은 XML을 반환하는 웹 서비스를 호출하고 프레임워크에서 자동으로 이 XML을 개체로 deserialize하는 것입니다. 이러한 개체에는 데이터만 있고 동작은 들어 있지 않습니다. .NET Framework 2.0에서는 partial 키워드를 사용하고 동작을 만들어 이러한 자동 코드 생성 개체에 기능을 추가할 수 있습니다. 그러나 다양한 웹 서비스(또는 프록시) 개체에서 일부 특정 동작을 다시 사용하려고 하면 여전히 문제가 발생하게 됩니다. 앞서 언급했듯이 대부분의 경우 공유하는 공통 동작은 추상 클래스에 있고 다른 모든 클래스는 이 클래스에서 상속됩니다. 그러나 웹 서비스 개체를 사용하면 개체에서 기능을 상속할 수 없습니다. 이 문제는 AOP가 얼마나 강력한지 보여 줄 수 있는 좋은 기회로 활용할 수 있습니다.

여기서 소개하는 응용 프로그램의 용도는 연락처 정보를 표시하는 것입니다. 본래는 정보를 표시하는 것이 목적이었지만 이제는 여기에 몇 가지 동작을 추가해야 합니다. 코드 샘플을 살펴보기 위해 TheAgileDeveloper.ContactService라는 가상 디렉터리를 만들어야 합니다. 이 디렉터리는 로컬 시스템에서 TheAgileDeveloper.ContactService 프로젝트가 위치한 폴더를 가리켜야 합니다.

참고 이 프로젝트는 http://localhost/TheAgileDeveloper.ContactService를 통해 액세스할 수 있어야 합니다.

aoparticle01.gif

그림 1. 응용 프로그램 스크린

응용 프로그램에 있는 단일 뷰는 MainForm이라는 WinForm이며, 이 뷰에는 웹 서비스에서 반환된 연락처 개체가 ListView의 왼쪽에 표시됩니다. 연락처를 선택하면 오른쪽에 있는 텍스트 상자에 성과 이름, 그리고 웹 페이지가 표시됩니다. MainForm이 로드되면 ServiceManager 클래스를 호출하여 연락처 정보를 가져옵니다. 다음 ServiceManager 클래스는 언뜻 보면 폼과 웹 서비스 사이의 또 다른 계층을 제외하고는 값을 추가하지 않는 것처럼 보입니다. 그러나 이 클래스는 코드를 복제하지 않고도 웹 서비스에 새 기능을 추가할 수 있는 하나의 공간을 제공하므로 매우 중요한 역할을 수행합니다. 또 다른 이점으로는 전체 응용 프로그램에서 웹 서비스의 자취를 추상화하여 제거한다는 점을 들 수 있습니다.

Public Class ServiceManager
 
    Public Shared Function GetAllContacts() As ContactService.Contact()
        Dim service As ContactService.Service = New ContactService.Service
        Dim contacts() As ContactService.Contact = service.GetAllContacts
        Return contacts
    End Function
 
    Public Shared Sub SaveContact(ByVal contact As ContactService.Contact)
        Dim service As ContactService.Service = New ContactService.Service
        service.SaveContact(contact)
    End Sub
 
End Class

이제 Reference.vb 파일의 TheAgileDeveloper.Client 프로젝트를 살펴보겠습니다. 이 프로젝트는 ContactService의 웹 참조를 가져올 때 wsdl.exe에 의해 만들어진 것입니다. 또한 이 프로젝트는 WSDL에서 다음 Contact 클래스를 자동으로 생성합니다.

'<remarks/>
    <System.Xml.Serialization.XmlTypeAttribute(_
[Namespace]:=http://tempuri.org/TheAgileDeveloper.ContactService/Service1 _ )>  _
    Public Class Contact
 
        '<remarks/>
        Public Id As Integer
 
        '<remarks/>
        Public FirstName As String
 
        '<remarks/>
        Public LastName As String
 
        '<remarks/>
        Public WebSite As String
    End Class

위의 코드에서 현재 Contact 개체는 데이터만 처리합니다. 또한 이 코드는 wsdl.exe에 의해 자동으로 생성되며 다음에 다시 생성하면 모든 변경 내용을 잃어버리게 되므로 편집하지 않습니다. 여기서는 Save라 는 메서드를 호출하여 개체를 저장하는 동작을 소개하겠습니다. 이 동작은 mixin을 사용하여 손쉽게 수행할 수 있습니다. mixin은 인터페이스 구현만 혼합할 수 있는 등의 제한 사항을 제외하면 다중 상속을 연상하게 합니다. 여기서 사용하는 Encase 프레임워크에는 개체를 가져와 래핑하는 Encaser 클래스가 들어 있습니다. 개체를 래핑하면 실제로 새 개체가 만들어지며(이 경우 새 Contact 개체) 여기에는 구성된 mixin과 pointcut이 포함됩니다.

mixin을 만들어 Contact 개체에서 Save 메서드를 호출하려면 인터페이스를 지정해야 하는데, 여기서는 이를 ISavable이라고 하겠습니다. 실제로 개체에 혼합될 것이 바로 이 ISavable 인터페이스입니다. 이 인터페이스는 ContactSave라는 또 다른 새로운 클래스에 구현해야 합니다.

Public Interface ISaveable
    Sub Save()
End Interface
 
Public Class ContactSave
    Implements ISavable
 
    Public Contact As ContactService.Contact
 
    Public Sub Save() Implements ISavable.Save
        ServiceManager.SaveContact(Me.Contact)
    End Sub
 
End Class

이 응용 프로그램에서 Contact 개체에 ContactSave 구현을 혼합하기에 가장 적합한 위치는 ServiceManager입니다. 이 동작을 혼합할 수는 있지만 클라이언트 코드, 즉 MainForm을 변경할 필요는 없습니다. 이는 mixin을 적용한 후에도 ContactContactSave가 통합된 새 Contact 개체가 여전히 본래의 Contact 형식을 유지하기 때문입니다. 다음은 이를 처리할 ServiceManager의 변경된 GetAllContacts 메서드입니다.

 
Public Shared Function GetAllContacts() As ContactService.Contact()
       Dim service As ContactService.Service = New ContactService.Service
       Dim contacts() As ContactService.Contact = service.GetAllContacts
 
       '//각 contact 개체를 래핑합니다.
       For i As Integer = 0 To contacts.Length-1
           '//개체를 래핑하는
     '//encaser의 새 인스턴스를 만듭니다.
           Dim encaser As encaser = New encaser
 
           '//ContactSave의 mixin 인스턴스를 추가합니다.
           Dim saver As ContactSave = New ContactSave
           encaser.AddMixin(saver)
 
           '//Contact 및 ContactSave 구현을 사용하여
     '//새 개체를 만듭니다.
           Dim wrappedObject As Object = encaser.Wrap(contacts(i))
 
           '//새로 래핑된 contact 개체를
     '//이전 contact 개체에 할당합니다.
           contacts(i) = DirectCast(wrappedObject, _
           ContactService.Contact)
           '//래핑된 개체의 형식이 여전히 동일한지 확인합니다.
 
           '//새로 래핑된 Contact 개체를
     '//혼합된 ContactSave의 대상 필드에 할당합니다.
           saver.Target = contacts(i)
        Next
 
        Return contacts
    End Function(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)

Mixin의 배후 구현

프레임워크에서 pointcut과 advice 또는 aspect를 적용하는 방법은 프레임워크별로 다르지만 적용 목적과 개념은 같습니다. 이 기사에서 Encaser가 개체를 래핑하면 실제로는 System.Reflection.Emit 네임스페이스의 클래스를 사용하여 MSIL 코드를 내보내 즉석에서 새 Contact 형식이 만들어집니다. 새 Contact 형식은 Contact 클래스에서 파생되므로 계속해서 형식을 공유하지만 새로 래핑된 개체에도 여기서 혼합한 ContactSave 개체에 대한 참조가 유지됩니다. ISavable.Save 메서드는 새 Contact 개체에 구현되므로 Save를 호출하면 실제로는 호출을 혼합된 ContactSave 개체에 위임하게 됩니다. 여기서의 이점은 새 Contact 개체를 혼합된 개체에 구현된 모든 인터페이스에 캐스팅할 수 있다는 점입니다.

aoparticle02.gif

그림 2. 래핑된 개체의 UML 다이어그램

.NET Framework 2.0의 부분 클래스 언어 기능을 사용하면 또 다른 partial 클래스에 Save 동작을 추가할 수 있는 것으로 생각할 수도 있습니다. 이는 가능한 일이지만 필자는 코드가 이전 버전의 .NET Framework 1.x와 호환되도록 하기 위해 이러한 방식을 피하고 있습니다. 이제 부분 언어 기능이 있으므로 이전 샘플에서도 일반적인 경우에는 mixin이 필요 없을 것입니다. 하지만 mixin은 개발자가 다른 비관련 개체의 계층 구조에 속한 개체의 다시 사용 가능한 동작을 혼합하도록 하여 partial 클래스보다 더 많은 작업을 수행할 수 있으므로 여전히 중요한 역할을 합니다. partial 키워드를 사용할 때 추가하는 코드는 클래스나 형식이 동일하며 물리적인 위치만 다릅니다. 다음에 소개할 mixin에서는 Contact 클래스에 국한되지 않고 대신 FieldUndoer라는 다시 사용 가능한 클래스인 동작을 추가하는 작업에 대해 설명하겠습니다. FieldUndoerIUndoable 인터페이스를 구현하며 수정된 개체를 원래 상태로 복원할 수 있도록 해 줍니다.

Public Interface IUndoable
        ReadOnly Property HasChanges() As Boolean
        Sub Undo()
        Sub AcceptChanges()
    End Interface

HasChanges 속성은 변경 사항이 발생했음을 나타내며 Undo는 개체를 원래 상태로 되돌리고, AcceptChanges는 개체의 현재 변경 사항을 적용합니다. 따라서 나중에 Undo를 호출하면 마지막으로 변경 사항이 적용된 상태로 복원됩니다. 이 인터페이스가 부분 클래스에 구현된 경우 이 세 개의 메서드는 이 동작을 포함시킬 클래스마다 반복해서 복제되도록 구현되어야 합니다. 필자는 실용주의적인 성향의 프로그래머로 "한 번 코딩하여 한 번만 사용한다"는 원칙을 고수하려 노력하므로 코드를 복제하지 않을 뿐 아니라 복사하여 붙여넣는 경우도 상당히 적습니다. mixin을 사용하면 IUndoable을 구현하는 FieldUndoer 개체를 다시 사용할 수 있습니다. 필자는 이 새로운 기능을 ServiceManager에 다시 혼합하는 방법을 채택하고 있습니다. 모든 클라이언트 코드는 여전히 새로운 mixin을 인식하지 않으며 IUndoable 인터페이스를 사용해야 할 경우가 아니면 변경할 필요도 없습니다. MainFormContact 개체를 변경한 다음 undo를 클릭하여 이 동작을 테스트해 보십시오.

Public Shared Function GetAllContacts() As ContactService.Contact()
        Dim service As ContactService.Service = New ContactService.Service
        Dim contacts() As ContactService.Contact = service.GetAllContacts
 
        '//각 contact 개체를 래핑합니다.
        For i As Integer = 0 To contacts.Length-1
            '//개체를 래핑하는
      '//encaser의 새 인스턴스를 만듭니다.
            Dim encaser As encaser = New encaser
 
            '//ContactSave의 mixin 인스턴스를 추가합니다.
            Dim saver As ContactSave = New ContactSave
            encaser.AddMixin(saver)
 
            '//FieldUndoer의 mixin 인스턴스를 추가합니다.
            Dim undoer As FieldUndoer = New FieldUndoer
            encaser.AddMixin(undoer)
 
            '//Contact 및 ContactSave 구현을 사용하여
      '//새 개체를 만듭니다.
            Dim wrappedObject As Object = encaser.Wrap(contacts(i))
 
            '//새로 래핑된 contact 개체를
      '//이전 contact 개체에 할당합니다.
            contacts(i) = DirectCast(wrappedObject, _
            ContactService.Contact)
            '//래핑된 개체의 형식이 여전히 동일한지 확인합니다.
 
            '//새로 래핑된 Contact 개체를 대상 필드에 할당합니다.
            saver.Target = contacts(i)
            undoer.Target = contacts(i)
        Next
 
        Return contacts
End Function

동작 위빙(Weaving)

mixin은 전체의 작은 부분에 지나지 않습니다. AOP가 진정한 인정을 받는 부분은 혼합된 동작이 함께 위빙될 때 나타납니다. 예를 들어 새 Contact 개체를 사용하는 경우 ISavable.Save 메서드를 호출하면 클라이언트 코드에서는 다음에 IUndoable.Undo를 호출할 때 마지막으로 저장된 변경 내용을 사용하도록 IUndoable.AcceptChanges 메서드를 호출해야 합니다. 이를 검토하여 작은 MainForm에 추가하는 것은 간단하지만 이 규칙을 단일 사용자 인터페이스보다 큰 시스템에 코딩하는 일은 상당한 노력이 요구되는 작업입니다. 이렇게 하려면 Save 메서드가 호출된 모든 항목을 찾은 다음 AcceptChanges를 추가로 호출해야 합니다. 또한 새 코드가 만들어지면 개발자는 Save를 호출할 때마다 이 기능을 잊지 않고 추가해야 합니다. 이는 곧바로 계단식 효과로 이어져 시스템을 순식간에 불안정한 상태로 만들고 추적하기 힘든 다양한 버그를 만들어 낼 수 있습니다. 하지만 AOP(Aspect-Oriented Programming)를 사용하면 이러한 메서드를 함께 위빙할 수 있습니다. 메서드를 위빙하려면 Save 메서드가 호출될 때 Contact 개체가 백그라운드에서 자동으로 AcceptChanges를 호출하도록 pointcut과 advice를 지정합니다.

응용 프로그램에 위빙을 구현하려면 ServiceManager에 코드 한 줄을 추가해야 합니다. 이 코드는 FieldUndoer mixin을 추가한 후에 추가합니다.

'//joinpoint를 저장한 다음 AcceptChanges 메서드를 실행합니다.
encaser.AddPointcut("Save", "AcceptChanges")

AddPointcut 메서드는 서로 다른 여러 개의 서명과 함께 오버로드되어 pointcut을 상당히 자유롭게 지정할 수 있도록 해 줍니다. 호출되는 AddPointcutSave 메서드에 해당하는 문자열을 joinpoint 이름으로, AcceptChanges라는 메서드를 실행할 advice로 사용합니다. 실행을 확인하려면 FieldUndoer.AcceptChanges 메서드와 ContactSave.Save 메서드에 각각 중단점을 설정합니다. MainFormSave 단추를 클릭하면 joinpoint가 차단되어 먼저 AcceptChanges 메서드인 advice를 실행하게 됩니다. advice의 실행이 완료되면 계속해서 Save 메서드를 실행합니다.

이 간단한 예제에서는 전체 응용 프로그램에 걸쳐 새 동작을 추가하는 매우 강력한 기능을 보여 주고 있습니다. AOP는 단순히 기능을 효과적으로 추가하는 방법이 아니라 든든한 조력자의 역할을 담당합니다. AOP는 코드를 다시 사용하고 시스템 관리 용이성을 향상시키는 등의 다양한 이점을 통해 새로운 요구 사항에 발맞추어 손쉽게 발전시켜 나갈 수 있도록 해줍니다. 그러나 이와 동시에 AOP를 잘못 사용하면 시스템 유지 관리에 매우 심각한 영향을 미칠 수 있으므로 AOP의 사용 시기와 방법을 적절히 알아 두는 것이 중요합니다.

정리

AOP 는 아직 대규모 또는 중요한 프로덕션 시스템에 사용할 만큼 발전하지는 못했지만 언어에 대한 지원이 늘어나면 보다 널리 채택될 것으로 예상됩니다. 또한 AOP를 활용하는 소프트웨어 팩토리 같은 새로운 소프트웨어 개발 패러다임은 이러한 지원을 촉진하는 사례로 볼 수 있습니다. 현재 .NET 영역에는 다양한 AOP 프레임워크가 있으며 이들은 각각 고유한 접근 방식과 장단점을 보유하고 있습니다.

  • Encase (영문)— 이 코드 샘플에 포함된 Encase 프레임워크는 AOP를 신속하게 실행하여 AOP의 기반이 되는 개념을 이해하는 수단으로 사용하기 위한 것입니다. Encase는 런타임에 개체에 개별적으로 추가할 수 있는 aspect를 적용합니다.

  • Aspect# (영문)—aspect를 선언 및 구성하는 언어를 기본적으로 제공하는 CLI를 위한 AOP 지원 호환 프레임워크입니다.

  • RAIL (영문)—RAIL 프레임워크는 가상 시스템에서 클래스를 JIT 컴파일 중일 때 aspect를 적용합니다.

  • Spring.NET (영문)—현재 널리 사용되는 Java Spring 프레임워크의 .NET 버전으로 향후 릴리스에 AOP와 함께 구현될 예정입니다.

  • Eos (영문)—C#을 위한 aspect 지향 확장입니다.

결론

이 기사의 목적은 기존의 로깅 또는 보안 예제에 비해 새롭고 보다 실제적인 AOP 적용 방식을 보여 주는 데 있습니다. AOP를 올바르게 사용하면 많은 이점이 있을 뿐 아니라 기존 프로그래밍 옵션으로는 불가능한 결과를 손쉽게 얻을 수 있습니다. 아울러 AOP의 적용 시기와 방법에 대한 결정을 내릴 때는 가능한 한 인터넷에 나와 있는 다양한 리소스를 검색하여 도움을 얻도록 합니다.

저자 소개

Matthew Deiters는 소프트웨어 개발에 깊은 관심을 갖고 있으며 ThoughtWorks에서 컨설턴트로 활동하고 있습니다. 또한 .NET Framework를 사용한 금융 및 보험 업계의 다양한 엔터프라이즈 시스템 개발 업무에 참여했습니다. 그는 XP 프로그래밍과 TTD 방법론을 높이 평가하며 인간이 겪는 문제의 대부분은 디자인 패턴이나 뛰어난 단위 테스트를 통해 해결할 수 있다고 생각하고 있습니다. Matthew의 개인 웹 페이지는 http://www.theagiledeveloper.com/ (영문)입니다.

'.Net > .Net' 카테고리의 다른 글

[.Net] Castle DynamicProxy Tutorial  (0) 2013.01.23
[.Net] NuGet  (0) 2013.01.23
[.Net] CodeDomProvider  (0) 2013.01.15
[.Net] Aspect Oriented Programming With .Net  (0) 2013.01.14
[.Net] 웹 자동화 구현 클래스  (0) 2013.01.09
posted by 뚱2

[.Net] CodeDomProvider

.Net/.Net 2013. 1. 15. 18:20

링크 : http://msdn.microsoft.com/ko-kr/library/system.codedom.compiler.codedomprovider.aspx 

 

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using Microsoft.JScript;

// This example demonstrates building a Hello World program graph 
// using System.CodeDom elements. It calls code generator and
// code compiler methods to build the program using CSharp, VB, or
// JScript.  A Windows Forms interface is included. Note: Code
// must be compiled and linked with the Microsoft.JScript assembly. 
namespace CodeDOMExample
{
    class CodeDomExample
    {
        // Build a Hello World program graph using 
        // System.CodeDom types.
        public static CodeCompileUnit BuildHelloWorldGraph()
        {
            // Create a new CodeCompileUnit to contain 
            // the program graph.
            CodeCompileUnit compileUnit = new CodeCompileUnit();

            // Declare a new namespace called Samples.
            CodeNamespace samples = new CodeNamespace("Samples");
            // Add the new namespace to the compile unit.
            compileUnit.Namespaces.Add(samples);

            // Add the new namespace import for the System namespace.
            samples.Imports.Add(new CodeNamespaceImport("System"));

            // Declare a new type called Class1.
            CodeTypeDeclaration class1 = new CodeTypeDeclaration("Class1");
            // Add the new type to the namespace type collection.
            samples.Types.Add(class1);

            // Declare a new code entry point method.
            CodeEntryPointMethod start = new CodeEntryPointMethod();

            // Create a type reference for the System.Console class.
            CodeTypeReferenceExpression csSystemConsoleType = new CodeTypeReferenceExpression("System.Console");

            // Build a Console.WriteLine statement.
            CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(
                csSystemConsoleType, "WriteLine",
                new CodePrimitiveExpression("Hello World!"));

            // Add the WriteLine call to the statement collection.
            start.Statements.Add(cs1);

            // Build another Console.WriteLine statement.
            CodeMethodInvokeExpression cs2 = new CodeMethodInvokeExpression(
                csSystemConsoleType, "WriteLine",
                new CodePrimitiveExpression("Press the Enter key to continue."));

            // Add the WriteLine call to the statement collection.
            start.Statements.Add(cs2);

            // Build a call to System.Console.ReadLine.
            CodeMethodInvokeExpression csReadLine = new CodeMethodInvokeExpression(
                csSystemConsoleType, "ReadLine");

            // Add the ReadLine statement.
            start.Statements.Add(csReadLine);

            // Add the code entry point method to
            // the Members collection of the type.
            class1.Members.Add(start);

            return compileUnit;
        }

        public static void GenerateCode(CodeDomProvider provider,
            CodeCompileUnit compileunit)
        {
            // Build the source file name with the appropriate
            // language extension.
            String sourceFile;
            if (provider.FileExtension[0] == '.')
            {
                sourceFile = "TestGraph" + provider.FileExtension;
            }
            else
            {
                sourceFile = "TestGraph." + provider.FileExtension;
            }

            // Create an IndentedTextWriter, constructed with
            // a StreamWriter to the source file.
            IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(sourceFile, false), "    ");
            // Generate source code using the code generator.
            provider.GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions());
            // Close the output file.
            tw.Close();
        }

        public static CompilerResults CompileCode(CodeDomProvider provider,
                                                  String sourceFile,
                                                  String exeFile)
        {
            // Configure a CompilerParameters that links System.dll
            // and produces the specified executable file.
            String[] referenceAssemblies = { "System.dll" };
            CompilerParameters cp = new CompilerParameters(referenceAssemblies,
                                                           exeFile, false);
            // Generate an executable rather than a DLL file.
            cp.GenerateExecutable = true;

            // Invoke compilation.
            CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourceFile);
            // Return the results of compilation.
            return cr;
        }
    }

    public class CodeDomExampleForm : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Button run_button = new System.Windows.Forms.Button();
        private System.Windows.Forms.Button compile_button = new System.Windows.Forms.Button();
        private System.Windows.Forms.Button generate_button = new System.Windows.Forms.Button();
        private System.Windows.Forms.TextBox textBox1 = new System.Windows.Forms.TextBox();
        private System.Windows.Forms.ComboBox comboBox1 = new System.Windows.Forms.ComboBox();
        private System.Windows.Forms.Label label1 = new System.Windows.Forms.Label();

        private void generate_button_Click(object sender, System.EventArgs e)
        {
            CodeDomProvider provider = GetCurrentProvider();
            CodeDomExample.GenerateCode(provider, CodeDomExample.BuildHelloWorldGraph());

            // Build the source file name with the appropriate
            // language extension.
            String sourceFile;
            if (provider.FileExtension[0] == '.')
            {
                sourceFile = "TestGraph" + provider.FileExtension;
            }
            else
            {
                sourceFile = "TestGraph." + provider.FileExtension;
            }

            // Read in the generated source file and
            // display the source text.
            StreamReader sr = new StreamReader(sourceFile);
            textBox1.Text = sr.ReadToEnd();
            sr.Close();
        }

        private void compile_button_Click(object sender, System.EventArgs e)
        {
            CodeDomProvider provider = GetCurrentProvider();

            // Build the source file name with the appropriate
            // language extension.
            String sourceFile;
            if (provider.FileExtension[0] == '.')
            {
                sourceFile = "TestGraph" + provider.FileExtension;
            }
            else
            {
                sourceFile = "TestGraph." + provider.FileExtension;
            }

            // Compile the source file into an executable output file.
            CompilerResults cr = CodeDomExample.CompileCode(provider,
                                                            sourceFile,
                                                            "TestGraph.exe");

            if (cr.Errors.Count > 0)
            {
                // Display compilation errors.
                textBox1.Text = "Errors encountered while building " +
                                sourceFile + " into " + cr.PathToAssembly + ": \r\n\n";
                foreach (CompilerError ce in cr.Errors)
                    textBox1.AppendText(ce.ToString() + "\r\n");
                run_button.Enabled = false;
            }
            else
            {
                textBox1.Text = "Source " + sourceFile + " built into " +
                                cr.PathToAssembly + " with no errors.";
                run_button.Enabled = true;
            }
        }

        private void run_button_Click(object sender,
            System.EventArgs e)
        {
            Process.Start("TestGraph.exe");
        }

        private CodeDomProvider GetCurrentProvider()
        {
            CodeDomProvider provider;
            switch ((string)this.comboBox1.SelectedItem)
            {
                case "CSharp":
                    provider = CodeDomProvider.CreateProvider("CSharp");
                    break;
                case "Visual Basic":
                    provider = CodeDomProvider.CreateProvider("VisualBasic");
                    break;
                case "JScript":
                    provider = CodeDomProvider.CreateProvider("JScript");
                    break;
                default:
                    provider = CodeDomProvider.CreateProvider("CSharp");
                    break;
            }
            return provider;
        }

        public CodeDomExampleForm()
        {
            this.SuspendLayout();
            // Set properties for label1
            this.label1.Location = new System.Drawing.Point(395, 20);
            this.label1.Size = new Size(180, 22);
            this.label1.Text = "Select a programming language:";
            // Set properties for comboBox1
            this.comboBox1.Location = new System.Drawing.Point(560, 16);
            this.comboBox1.Size = new Size(190, 23);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Items.AddRange(new string[] { "CSharp", "Visual Basic", "JScript" });
            this.comboBox1.Anchor = System.Windows.Forms.AnchorStyles.Left
                                    | System.Windows.Forms.AnchorStyles.Right
                                    | System.Windows.Forms.AnchorStyles.Top;
            this.comboBox1.SelectedIndex = 0;
            // Set properties for generate_button. 
            this.generate_button.Location = new System.Drawing.Point(8, 16);
            this.generate_button.Name = "generate_button";
            this.generate_button.Size = new System.Drawing.Size(120, 23);
            this.generate_button.Text = "Generate Code";
            this.generate_button.Click += new System.EventHandler(this.generate_button_Click);
            // Set properties for compile_button.
            this.compile_button.Location = new System.Drawing.Point(136, 16);
            this.compile_button.Name = "compile_button";
            this.compile_button.Size = new System.Drawing.Size(120, 23);
            this.compile_button.Text = "Compile";
            this.compile_button.Click += new System.EventHandler(this.compile_button_Click);
            // Set properties for run_button.
            this.run_button.Enabled = false;
            this.run_button.Location = new System.Drawing.Point(264, 16);
            this.run_button.Name = "run_button";
            this.run_button.Size = new System.Drawing.Size(120, 23);
            this.run_button.Text = "Run";
            this.run_button.Click += new System.EventHandler(this.run_button_Click);
            // Set properties for textBox1.        
            this.textBox1.Anchor = (System.Windows.Forms.AnchorStyles.Top
                                     | System.Windows.Forms.AnchorStyles.Bottom
                                     | System.Windows.Forms.AnchorStyles.Left
                                     | System.Windows.Forms.AnchorStyles.Right);
            this.textBox1.Location = new System.Drawing.Point(8, 48);
            this.textBox1.Multiline = true;
            this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(744, 280);
            this.textBox1.Text = "";
            // Set properties for the CodeDomExampleForm.
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(768, 340);
            this.MinimumSize = new System.Drawing.Size(750, 340);
            this.Controls.AddRange(new System.Windows.Forms.Control[] {this.textBox1,
                this.run_button, this.compile_button, this.generate_button,
                this.comboBox1, this.label1 });
            this.Name = "CodeDomExampleForm";
            this.Text = "CodeDom Hello World Example";
            this.ResumeLayout(false);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        [STAThread]
        static void Main()
        {
            Application.Run(new CodeDomExampleForm());
        }
    }
}

 

'.Net > .Net' 카테고리의 다른 글

[.Net] NuGet  (0) 2013.01.23
[.Net] AOP 프로그래밍  (0) 2013.01.23
[.Net] Aspect Oriented Programming With .Net  (0) 2013.01.14
[.Net] 웹 자동화 구현 클래스  (0) 2013.01.09
[.Net] pinvoke.net  (0) 2013.01.07
posted by 뚱2

링크 : http://msdn.microsoft.com/ko-kr/library/k9x6w0hc(v=vs.80).aspx 

 

public class Bus
{
    // Static constructor:
    static Bus()
    {
        System.Console.WriteLine("The static constructor invoked.");
    }

    public static void Drive()
    {
        System.Console.WriteLine("The Drive method invoked.");
    }
}

class TestBus
{
    static void Main()
    {
        Bus.Drive();
    }
}

'.Net > C#' 카테고리의 다른 글

[C#] Visual Studio TODO 만들기  (0) 2013.01.30
[C#] Form close와 Dispose  (0) 2013.01.28
[C#] Assemble.load  (0) 2013.01.15
[Visual Studio 2010] C# 선언할때 공백 제거 문제  (0) 2013.01.14
[C#] C# as 연산자  (0) 2013.01.09
posted by 뚱2

[C#] Assemble.load

.Net/C# 2013. 1. 15. 12:22

링크 : http://msdn.microsoft.com/ko-kr/library/system.reflection.assembly.load.aspx

 

어셈블리를 로드합니다.

이 멤버는 오버로드됩니다. 구문, 사용법 및 예제를 비롯하여 이 멤버에 대한 자세한 내용을 보려면 오버로드 목록에서 이름을 클릭합니다.

이름 설명
Public 메서드정적 멤버 Load(AssemblyName) 해당 AssemblyName이 지정된 어셈블리를 로드합니다.
Public 메서드정적 멤버 Load(Byte[]) 내보낸 어셈블리가 포함된 COFF(Common Object File Format) 기반 이미지를 사용하여 어셈블리를 로드합니다.이 어셈블리는 호출자의 응용 프로그램 도메인에 로드됩니다.
Public 메서드정적 멤버 Load(String) 긴 형식의 이름으로 지정된 어셈블리를 로드합니다.
Public 메서드정적 멤버 Load(AssemblyName, Evidence) 사용되지 않습니다. 해당 AssemblyName이 지정된 어셈블리를 로드합니다.어셈블리는 제공된 증명을 사용하여 호출자의 도메인에 로드됩니다.
Public 메서드정적 멤버 Load(Byte[], Byte[]) 생성된 어셈블리가 들어 있고 경우에 따라 어셈블리에 대한 기호도 포함하는 COFF(공용 개체 파일 형식) 기반 이미지를 사용하여 어셈블리를 로드합니다.이 어셈블리는 호출자의 응용 프로그램 도메인에 로드됩니다.
Public 메서드정적 멤버 Load(String, Evidence) 사용되지 않습니다. 해당 표시 이름이 지정된 어셈블리를 로드한 다음 제공된 증명을 사용하여 이 어셈블리를 호출자의 도메인에 로드합니다.
Public 메서드정적 멤버 Load(Byte[], Byte[], Evidence) 사용되지 않습니다. 생성된 어셈블리가 들어 있고 경우에 따라 어셈블리에 대한 기호 및 증명 정보도 포함하는 COFF(공용 개체 파일 형식) 기반 이미지를 사용하여 어셈블리를 로드합니다.이 어셈블리는 호출자의 응용 프로그램 도메인에 로드됩니다.
Public 메서드정적 멤버 Load(Byte[], Byte[], SecurityContextSource) 생성된 어셈블리가 들어 있고 경우에 따라 기호도 포함하고 보안 컨텍스트의 소스도 지정하는 COFF(공용 개체 파일 형식) 기반 이미지를 사용하여 어셈블리를 로드합니다.이 어셈블리는 호출자의 응용 프로그램 도메인에 로드됩니다.
위쪽

'.Net > C#' 카테고리의 다른 글

[C#] Form close와 Dispose  (0) 2013.01.28
[C#] 정적 생성자 (static 멤버 초기화)  (0) 2013.01.15
[Visual Studio 2010] C# 선언할때 공백 제거 문제  (0) 2013.01.14
[C#] C# as 연산자  (0) 2013.01.09
[C#] StackTrace  (0) 2013.01.08
posted by 뚱2

선언할때 줄 잘 맞춰서 코딩해 놨는데 공백이 사라지는 억울함이....

 

도구 -> 옵션 -> 텍스트 편집기 -> C# -> 서식 -> 간격 -> 기타 간격 옵션을 설정합니다. -> 선언문의 공백을 무시합니다.

' 체크 '

 

 

'.Net > C#' 카테고리의 다른 글

[C#] 정적 생성자 (static 멤버 초기화)  (0) 2013.01.15
[C#] Assemble.load  (0) 2013.01.15
[C#] C# as 연산자  (0) 2013.01.09
[C#] StackTrace  (0) 2013.01.08
[C#] DllImportAttribut  멤버  (0) 2013.01.04
posted by 뚱2

링크 : http://www.devpia.com/NET2/EvaCast/Lecture/?fCode=2&sCode=5&c=9 

링크 : http://aspectdotnet.codeplex.com/

링크 : https://www.facultyresourcecenter.com/curriculum/pfv.aspx?ID=8658&c1=en-us&c2=0



자바에서 정말 편하게 사용했던 AspectJ 


닷넷에 와서 비슷하걸 찾고 있는데 내 입맛에 맞는걸 아직 찾지 못했다.


시간을 두고 좀더 찾아봐야 할 것 같다.

'.Net > .Net' 카테고리의 다른 글

[.Net] AOP 프로그래밍  (0) 2013.01.23
[.Net] CodeDomProvider  (0) 2013.01.15
[.Net] 웹 자동화 구현 클래스  (0) 2013.01.09
[.Net] pinvoke.net  (0) 2013.01.07
[.Net] Windows 서비스 응용 프로그램  (0) 2012.12.07
posted by 뚱2

.Net에서 WebBrowser 컨트롤을 임포트 해서 해결할수도 있고

 

좀더 아래 로우레벨로 내려가서 해결할수도 있습니다.

 

링크 (WebClient) : http://msdn.microsoft.com/ko-kr/library/system.net.webclient(v=vs.80).aspx

using System;
using System.Net;
using System.IO;

public class Test
{
    public static void Main (string[] args)
    {
        if (args == null || args.Length == 0)
        {
            throw new ApplicationException ("Specify the URI of the resource to retrieve.");
        }
        WebClient client = new WebClient ();

        // Add a user agent header in case the
        // requested URI contains a query.

        client.Headers.Add ("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

        Stream data = client.OpenRead (args[0]);
        StreamReader reader = new StreamReader (data);
        string s = reader.ReadToEnd ();
        Console.WriteLine (s);
        data.Close ();
        reader.Close ();
    }
}

 

이벤트 (DownloadProgressChanged) : http://msdn.microsoft.com/ko-kr/library/system.net.webclient.downloadprogresschanged(v=vs.80).aspx 


링크 (WebRequest) : http://msdn.microsoft.com/ko-kr/library/system.net.webrequest(v=vs.95).aspx 

 

'.Net > .Net' 카테고리의 다른 글

[.Net] CodeDomProvider  (0) 2013.01.15
[.Net] Aspect Oriented Programming With .Net  (0) 2013.01.14
[.Net] pinvoke.net  (0) 2013.01.07
[.Net] Windows 서비스 응용 프로그램  (0) 2012.12.07
[.Net] Zlib Wrapper  (0) 2012.05.22
posted by 뚱2

[C#] StackTrace

.Net/C# 2013. 1. 8. 15:36

아는 동생이 알려주길래 얼른 사용해봄 나중을 위해서 기록...

private void GetDebugInfo()
{
    string szDebugInfo = string.Empty;

    System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(true);

    System.Diagnostics.Debug.Write("*******************************");
    for(int i=1; i<trace.FrameCount; i++)
    {
        if(trace.GetFrame(i).GetFileName() != null)
        {
            if(trace.GetFrame(i).GetFileName() != String.Empty)
                 System.Diagnostics.Debug.Write(trace.GetFrame(i).ToString());
        }
    }
    System.Diagnostics.Debug.Write("*******************************");
}

 

posted by 뚱2

[.Net] pinvoke.net

.Net/.Net 2013. 1. 7. 16:46

링크 : http://www.pinvoke.net/index.aspx 

 

win32 API 를 .Net에서 사용할때 .Net에 맞게 DllImport를 해야 하는데 그 변환 문법이 만만치 않다.

각 함수별로 잘 정리되어 있는 사이트

 

* 사이트 메인 화면을 보면 Visual Studio 2010, 2012 Add-In 프로그램도 존재한다. Winform으로 개발시 유용할듯

'.Net > .Net' 카테고리의 다른 글

[.Net] Aspect Oriented Programming With .Net  (0) 2013.01.14
[.Net] 웹 자동화 구현 클래스  (0) 2013.01.09
[.Net] Windows 서비스 응용 프로그램  (0) 2012.12.07
[.Net] Zlib Wrapper  (0) 2012.05.22
[.Net] Inter-Process Communication  (0) 2012.04.17
posted by 뚱2

[C#] DllImportAttribut  멤버

.Net/C# 2013. 1. 4. 14:03

링크 : http://msdn.microsoft.com/ko-kr/library/system.runtime.interopservices.dllimportattribute_members(v=vs.90).aspx 

참조 : http://blog.naver.com/luvinuns?Redirect=Log&logNo=90051065276


'.Net > C#' 카테고리의 다른 글

[C#] C# as 연산자  (0) 2013.01.09
[C#] StackTrace  (0) 2013.01.08
[C#] Visual C# 메소드를 비동기로 호출하는 방법  (0) 2012.04.14
[C#] Assembly Version Loading  (0) 2012.04.06
[C#] VS 2008 서식 자동 해제  (0) 2012.04.06
posted by 뚱2

링크 : 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