https://learn.microsoft.com/ko-kr/dotnet/csharp/advanced-topics/interop/using-type-dynamic

 

유형 동적 사용 - C#

동적 형식을 사용하는 방법을 알아봅니다. 동적 형식은 정적 형식이지만 동적 개체는 정적 형식 검사를 무시합니다.

learn.microsoft.com

클래스든 변수든 dynamic 하나로 이용가능.

Object 형식의 경우 Cast 따위 필요없다.

https://learn.microsoft.com/ko-kr/dotnet/api/system.tuple-3?view=net-7.0

 

Tuple<T1,T2,T3> Class (System)

Represents a 3-tuple, or triple.

learn.microsoft.com

이벤트 매개변수로 등 다수의 서로 다른 자료형의 데이터를  매개변수로 전달 할 때 유용.

따로 클래스나 구조체를 정의하지 않고도 사용할 수 있기에 편하다.

 

https://learn.microsoft.com/ko-kr/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=net-8.0

 

ConcurrentQueue<T> 클래스 (System.Collections.Concurrent)

스레드로부터 안전한 FIFO(첫 번째 출력) 컬렉션을 나타냅니다.

learn.microsoft.com

여러 스레드에서 접근시 충돌방지.

프로그램 제작 후 방화벽 때문에 허용되는 앱을 등록 할 떄가 있다.

방화벽에서 앱 허용.. 귀찮다.

매번 하기 귀찮아 방법을 찾아보았다.

코드로 가능한가 해서 찾아보니 요기잉네

https://csharp.hotexamples.com/examples/-/INetFwMgr/-/php-inetfwmgr-class-examples.html

 

아래 올려둔 코드는 해당 클래스를 static으로 변경해 놓은거다. 

AddProgram 함수도 쪼끔 바꿔놨으니 아래코드 가져다 쓰시면 된다.

 

일단 참조에 NetFwTypeLib을 추가해준다. 

참조추가

더보기

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NetFwTypeLib;

public static class FirewallHandler
{
    #region Fields

        // ProgID for the AuthorizedApplication object
        private const string PROGID_AUTHORIZED_APPLICATION = "HNetCfg.FwAuthorizedApplication";
        private const string PROGID_OPEN_PORT = "HNetCfg.FWOpenPort";

        private static INetFwMgr _fwMgr;

        #endregion Fields

        #region Constructors


        #endregion Constructors

        #region Methods

        public static bool AddPort(string title, int portNo, bool isUDP)
        {
            bool result = false;
            INetFwOpenPort port = _GetPort(title, portNo, isUDP);
            INetFwMgr manager = _GetFirewallManager();
            try
            {
                manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Add(port);
                result = true;
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return result;
        }
         /// <summary>
        /// 방화벽에 프로그램을 추가합니다.
        /// </summary>
        /// <param name="title">Application.ProductName + ".exe"</param>
        /// <param name="applicationPath">Application.ExecutablePath</param>
        /// <returns></returns>
        public static bool AddProgram(string title, string applicationPath)
        {
            bool exist = false;
            INetFwAuthorizedApplication auth = _GetAuth(title, applicationPath);

            _fwMgr = _GetFirewallManager();

            try
            {                               
                //           
                foreach (INetFwAuthorizedApplication mApp in _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications)
                {
                    if (auth == mApp)
                    {
                        exist = true;
                        break;
                    }
                }
                if (!exist)
                {                    
                    _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(auth);
                    foreach (INetFwAuthorizedApplication mApp in _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications)
                    {
                        if (auth == mApp)
                        {
                            exist = true;
                            break;
                        }
                    }
                }
                if (!exist)
                {
                    _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(auth);
                    foreach (INetFwAuthorizedApplication mApp in _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications)
                    {
                        if (auth == mApp)
                        {
                            exist = true;
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return exist;
        }

        public static bool DisableFirewall()
        {
            return _SetEnableFirewall(false);
        }

        public static bool EnableFirewall()
        {
            return _SetEnableFirewall(true);
        }

        public static string GetByPort(int _port)
        {
            string portName = null;
            try
            {
                _fwMgr = _GetFirewallManager();
                foreach (INetFwOpenPort port in _fwMgr.LocalPolicy.CurrentProfile.GloballyOpenPorts)
                {
                    if (port.Port == _port)
                    {
                        portName = port.Name;
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return portName;
        }

        public static string GetByProgramPath(string fileName)
        {
            string programName = null;
            try
            {
                _fwMgr = _GetFirewallManager();
                foreach (INetFwAuthorizedApplication app in _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications)
                {
                    if (fileName.ToLower().Equals(app.ProcessImageFileName.ToLower()))
                    {
                        programName = string.Format("{0}[{1}]", app.Name, app.ProcessImageFileName);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return programName;
        }

        public static List GetPortList()
        {
            List aList = new List();
            try
            {
                _fwMgr = _GetFirewallManager();
                foreach (INetFwOpenPort port in _fwMgr.LocalPolicy.CurrentProfile.GloballyOpenPorts)
                {
                    aList.Add(port.Name + ":" + port.Port);
                }
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return aList;
        }

        public static List GetProgramList()
        {
            List aList = new List();
            try
            {
                _fwMgr = _GetFirewallManager();
                foreach (INetFwAuthorizedApplication app in _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications)
                {
                    aList.Add(app.Name + ":" + app.ProcessImageFileName);
                }
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return aList;
        }

        public static bool IsFirewallEnabled()
        {
            _fwMgr = _GetFirewallManager();

            return _fwMgr.LocalPolicy.CurrentProfile.FirewallEnabled;
        }

        public static bool RemovePort(int portNo, bool isUDP)
        {
            bool result = false;
            INetFwMgr manager = _GetFirewallManager();
            NET_FW_IP_PROTOCOL_ protocol = isUDP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
            try
            {
                manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(portNo, protocol);
                result = true;
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return result;
        }

        public static bool RemoveProgram(string applicationPath)
        {
            bool result = false;
            _fwMgr = _GetFirewallManager();

            try
            {
                _fwMgr.LocalPolicy.CurrentProfile.AuthorizedApplications.Remove(applicationPath);
                result = true;
            }
            catch (Exception ex)
            {
                //MsgrLogger.WriteLog(ex.ToString());
            }
            return result;
        }

        private static INetFwAuthorizedApplication _GetAuth(string title, string applicationPath)
        {
            // Create the type from prog id
            Type type = Type.GetTypeFromProgID(PROGID_AUTHORIZED_APPLICATION);
            INetFwAuthorizedApplication auth = Activator.CreateInstance(type)
                as INetFwAuthorizedApplication;
            auth.Name = title;
            auth.ProcessImageFileName = applicationPath;
            auth.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
            auth.IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY;
            auth.Enabled = true;

            return auth;
        }

        private static INetFwMgr _GetFirewallManager()
        {
            Type NetFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
            return (INetFwMgr)Activator.CreateInstance(NetFwMgrType);
        }

        private static INetFwOpenPort _GetPort(string title, int portNo, bool isUDP)
        {
            Type type = Type.GetTypeFromProgID(PROGID_OPEN_PORT);
            INetFwOpenPort port = Activator.CreateInstance(type)
                as INetFwOpenPort;

            port.Name = title;
            port.Port = portNo;
            port.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
            port.Protocol = isUDP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
            port.IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY;

            return port;
        }

        private static bool _SetEnableFirewall(bool enable)
        {
            _fwMgr = _GetFirewallManager();

            _fwMgr.LocalPolicy.CurrentProfile.FirewallEnabled = enable;

            return _fwMgr.LocalPolicy.CurrentProfile.FirewallEnabled;
        }

        #endregion Methods
}

 

 

AddProgram 함수 사용하시면 된다.

다중 스레드를 사용하다보면 자원을 공유하게 되는 상황이 있을 수 있다.

이러한 상황에서 일정 영역을 한 번에 한 스레드만 접근 할 수 있는 영역(Critical Section)

으로 만들어 스레드간의 간섭을 없애기 위해 lock문을 사용하고는 한다.

 

하지만 간혹 lock 영역에서의 작업시간이 길어져 다른 스레드들의 대기 시간이 너무 길어지는 상황이 생길 수 있으며,

스레드간 교착상태인 Dead lock이 발생 할 수도 있으며,  

lock 영역에서 외부 라이브러리를 사용하는 경우(왠만해서는 피해야 하지만;) 해당 라이브러리 오류로 반환이 되지 않고

라이브러리 서브루틴에서 무한 루프라도 돌면 해당 모든 스레드가 대기 상태가 되어 프로그램이 맛이 가버리는 골때리는 상황이 오기도 한다.

 

이럴경우 Monitor.TryEnter (System.Threading.Monitor.TryEnter) 메소드를 사용하여 해결 할 수 있다.

 

다음 예제는 lock으로 인한 스레드들이 대기 시간을 확인해보자.

작업 시간에 5초가 소요되는 함수를 실행하는 예제이다.

 

더보기
namespace threadTest
{
    public partial class Form1 : Form
    {
        
        public Form1()
        {
            InitializeComponent();
            Thread t1 = new Thread(() => DoConsoleWirte(1));
            Thread t2 = new Thread(() => DoConsoleWirte(2));
            Thread t3 = new Thread(() => DoConsoleWirte(3));
            Thread t4 = new Thread(() => DoConsoleWirte(4));
            t1.IsBackground = true;
            t2.IsBackground = true;
            t3.IsBackground = true;
            t4.IsBackground = true;
            t1.Start();
            Thread.Sleep(10);
            t2.Start();
            Thread.Sleep(10);
            t3.Start();
            Thread.Sleep(10);
            t4.Start();
        }
        
        static readonly object lockobj = new object();
        public void DoConsoleWirte(int id)
        {
            Console.WriteLine(id + " : START " + DateTime.Now.ToString("HH:mm:ss.fff"));
            lock(lockobj)
            {
            	Console.WriteLine(id + " : NOW WORKING "  + DateTime.Now.ToString("HH:mm:ss.fff"));
          	Thread.Sleep(5000); //작업시간 5초(5000 ms)
            }
            Console.WriteLine(id + " : WORK DONE "  + DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}
실행결과 
1 : START 17:16:07.439
1 : NOW WORKING 17:16:07.442
2 : START 17:16:07.452
3 : START 17:16:07.467
4 : START 17:16:07.483
1 : WORK DONE 17:16:12.444
2 : NOW WORKING 17:16:12.444
2 : WORK DONE 17:16:17.446
3 : NOW WORKING 17:16:17.446
3 : WORK DONE 17:16:22.451
4 : NOW WORKING 17:16:22.451
4 : WORK DONE 17:16:27.468

 

t1 - t4 총 4개 스레드가 순차적으로 DoConsoleWirte 함수를 실행 할 때 Critical Section에 접근 할 수 있는 스레드는

한 번에 하나이기 때문에 먼저 실행된 t1 스레드 부터 순차적으로 스레드가 실행 및 종료된다.

 

위 테스트 에서는 lock문의 동작을 확인하기 위한 것이기에 작업시간을 5초로 설정 하였지만 Critical Section 에서 무한루프에 빠지게 된다면 t2 , t3 , t4 스레드는 무한 대기상태가 된다.

 

 

Monitor.TryEnter 함수는 다음과 같이 사용한다.

 

더보기
bool lockTaken = false; //단독잠금 설정 여부 True 일 때 해당영역에 접근 가능. False 일때는 대기.
Monitor.TryEnter(lockobj, 2000, ref lockTaken); //대기시간 2초(2000ms) 설정
try
{
	if (lockTaken)
	{
		//작업 
	}
	else
	{
		//시간 초과시 작업        
	}
}
catch(Exception error) { throw error }
finally { if (lockTaken) Monitor.Exit(lockobj); }

 

 

Monitor.TryEnter 를 사용한 예제는 다음과 같다. 작업시간 5초, 대기시간 2초로 설정한 예제이다. 

 

더보기
public partial class Form1 : Form
    {
        
        public Form1()
        {
            InitializeComponent();
            Thread t1 = new Thread(() => DoConsoleWirte(1));
            Thread t2 = new Thread(() => DoConsoleWirte(2));
            Thread t3 = new Thread(() => DoConsoleWirte(3));
            Thread t4 = new Thread(() => DoConsoleWirte(4));
            t1.IsBackground = true;
            t2.IsBackground = true;
            t3.IsBackground = true;
            t4.IsBackground = true;
            t1.Start();
            t2.Start();
            t3.Start();
            t4.Start();
        }
     
        static readonly object lockobj = new object();
        public void DoConsoleWirte(int id)
        {
        	Console.WriteLine(id + " : START " + DateTime.Now.ToString("HH:mm:ss.fff"));
            bool lockTaken = false; //단독잠금 설정 여부 True 일 때 해당영역에 접근 가능. False 일때는 대기.
            Monitor.TryEnter(lockobj, 2000, ref lockTaken); //대기시간 2초(2000ms) 설정
            if (lockTaken)
            {
            	Console.WriteLine(id + " : NOW WORKING "  + DateTime.Now.ToString("HH:mm:ss.fff"));
                Thread.Sleep(5000); //작업시간 5초(5000ms)
            }
            else
            {
                Console.WriteLine(id + " : TIME OVER " + DateTime.Now.ToString("HH:mm:ss.fff"));
                return;
            }
            Console.WriteLine(id + " : WORK DONE " + DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
실행결과
1 : START 17:26:37.527
1 : NOW WORKING 17:26:37.530
2 : START 17:26:37.558
3 : START 17:26:37.564
4 : START 17:26:37.582
2 : TIME OVER 17:26:39.569
3 : TIME OVER 17:26:39.569
4 : TIME OVER 17:26:39.599
1 : WORK DONE 17:26:42.531

 

t1 스레드가 작업을 시작하고 t2 , t3 , t4 스레드는 대기상태에 들어간다. 

설정된 2초가 지난 뒤에도 t1 스레드가 해당 Critical Section을 점유하고 잠금상태에 있기 때문에 

t2 , t3 , t4 스레드는 TIME OVER 메세지를 표시하고 작업을 끝마치는 모습을 볼 수 있다.

 

 

위 예제에서는 try catch 문을 사용하지 않았지만, 일부 상황에서 잠금이 해제되지 않을 수 있기 때문에 꼭! 꼮!! try catch finally 사용하시길 바란다.

+ Recent posts