프로그래밍을 하다보면

부팅할때 프로그램을 같이 시작해야 할 때가 있습니다

 

이럴때 가장 확실하고 간단한 방법은 레지스트리에 등록하는 겁니다

당연히 아무 레지스트리에나 등록한다고 되는 것은 아니고

부팅시 시작 프로그램을 등록하는 레지스트리 경로에 등록해줘야 하겠죠

 

이제 실제 구현 하면서 알아보도록 합시다 ㅇ_ㅇ

 

 

1. 먼저 Main Form(여기서는 AutoStartTestForm)에 버튼 두개를 등록하고 각각 Click이벤트를 만들어 줍니다

 

2. 아래와 같이 코드를 작성해 줍니다

namespace BlogTest
{
    public partial class AutoStartTestForm : Form
    {
        public AutoStartTestForm()
        {
            InitializeComponent();
        }


        private void BtnRegStart_Click(object sender, EventArgs e)
        {
            AddStartupProgram("AutoStartTestForm", Application.ExecutablePath);
        }

        private void BtnUnRegStart_Click(object sender, EventArgs e)
        {
            RemoveStartupProgram("AutoStartTestForm");
        }


        // 부팅시 시작 프로그램을 등록하는 레지스트리 경로
        private static readonly string _startupRegPath =
            @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";

        private Microsoft.Win32.RegistryKey GetRegKey(string regPath, bool writable)
        {
            return Microsoft.Win32.Registry.CurrentUser.OpenSubKey(regPath, writable);
        }

        // 부팅시 시작 프로그램 등록
        public void AddStartupProgram(string programName, string executablePath)
        {
            using (var regKey = GetRegKey(_startupRegPath, true))
            {
                try
                {
                    // 키가 이미 등록돼 있지 않을때만 등록
                    if (regKey.GetValue(programName) == null)
                        regKey.SetValue(programName, executablePath);

                    regKey.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        // 등록된 프로그램 제거
        public void RemoveStartupProgram(string programName)
        {
            using (var regKey = GetRegKey(_startupRegPath, true))
            {
                try
                {
                    // 키가 이미 존재할때만 제거
                    if (regKey.GetValue(programName) != null)
                        regKey.DeleteValue(programName, false);

                    regKey.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }
}

 

3. 레지스트리에 등록 되는지 확인!

Before
After (AutoStartTestForm로 등록된걸 알 수 있다)

 

4. 컴퓨터 재시작해서 확인

 

코드 설명을 자세히 하려고 했습니다만...

코드도 짧고 메서드명도 직관적이라 간단한 주석만 달고 생략 했습니다

사실 레지스트리 경로만 알면 등록, 해제가 끝이거든요

 

혹시라도 질문 있으시면 댓글달아주세요~

윈폼을 이용해 프로그램을 만들었을때, 프로그램을 한개만 실행되도록 해야 할때가 있습니다

이런 경우 C#의 뮤텍스(mutex) 클래스를 이용해 프로그램의 중복 실행을 막을 수 있습니다

 

뮤텍스(mutex)는 상호 배제(mutual exclusion)의 약자로 프로세스나 스레드등이 동시에 접근해서는 안되는 공유영역을 뜻합니다

C#에서는 Mutex클래스를 제공해서 비교적 쉽게 구현할수 있죠

 

그럼 바로 구현해보도록 하겠습니다

 

먼저 중복실행을 방지할 프로젝트의 Program.cs를 엽니다

 

그 다음 Program.cs의 소스코드를 아래와 같이 바꿔줍니다

namespace TestFrm
{
    static class Program
    {
        /// <summary>
        /// 해당 애플리케이션의 주 진입점입니다.
        /// </summary>
        [STAThread]
        static void Main()
        {
            bool flagMutex;

            System.Threading.Mutex m_hMutex = new System.Threading.Mutex(true, "TestFrm", out flagMutex);
            if (flagMutex)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                // 실행할 Form 클래스
                Application.Run(new Form1());
                m_hMutex.ReleaseMutex();
            }
            else
            {
                // 여러개 실행시켰을때 띄울 메시지
                MessageBox.Show("프로그램이 이미 실행 중입니다", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

 

프로그램 중복 실행시 아래와 같이 나온다면 성공입니다

오래전 숫자가 포함된 문자 배열을 정렬 할때 1다음에 10이오는걸 보고 당황한 적이 있습니다.

 

 

Array.Sort를 했음에도 제대로 정렬되지 않는다

 

바로 위 그림처럼 말이죠.

Array.Sort메서드로 정렬을 했음에도 불구하고 1다음에 10이오고, 2다음에 20이 오는등 원하던대로 정렬 되지 않습니다.

 

 

이건 프로그램 입장에선 당연한 겁니다. 왜냐하면 '문자열'을 기준으로 비교하는 거니까요.

사람이야 저게 숫자니까 1다음에 2가 오는게 맞다고 생각하지만, 프로그램은 저걸 '문자'로 인식하고 정렬 합니다.

 

 

사실 기존 Array.Sort를 사용하면서 숫자 정렬 하는 방법이 있긴합니다.

그냥 숫자 앞에 0을 붙여줘서 01, 02 ··· 이런식으로 자리수를 맞춰주면 되죠.

하지만 이 방법은 파일이름을 일일이 바꿔줘야 하고

파일이 수백, 수천개가 되면 자리수를 맞추기위해 앞에 0을 계속 붙여줘야 하는 문제가 있습니다.

따라서 이 글에서는 다른 방식으로 해결해 보겠습니다.

 

 

이 문제를 해결하기 위해서는 ArraySort메서드를 좀더 살펴봐야 합니다.

Array.Sort 메서드는 IComparer 인터페이스를 구현한 클래스를 대상으로, IComparer의 구현대로 정렬을 시행합니다.

하지만 기존 String클래스의 IComparer로는 우리가 원하는대로 정렬할수 없습니다.

 

 

그렇다면 숫자를 고려하여 정렬 하기 위해서는 어떻게 해야 할까요?

사실 Array.Sort 메서드에는 직접 구현한 IComparer을 사용할수 있게 오버로드가 되어 있습니다.

그렇다면 숫자까지 고려한 IComparer을 직접 만들어서 정렬 하면 문제는 해결 됩니다.

 

 

다행히도 구글링을 통해 스택 오버플로에서 구현한 걸 찾았습니다. 스택 오버플로에 웬만한 건 다 있네요

해당 링크는 본문 하단에 첨부했으니 관심있으시면 방문해 보세요.

 

 

아래는 구글링한 소스를 바탕으로 구현한 StringAsNumericComparer 클래스입니다.

public class StringAsNumericComparer : IComparer<string>
{
    public StringAsNumericComparer()
    { }

    public int Compare(object x, object y)
    {
        if ((x is string) && (y is string))
        {
            return StringLogicalComparer.Compare((string)x, (string)y);
        }

        return -1;
    }

    public int Compare(string x, string y)
    {
        return StringLogicalComparer.Compare(x, y);
    }

    private class StringLogicalComparer
    {
        public static int Compare(string s1, string s2)
        {
            //get rid of special cases
            if ((s1 == null) && (s2 == null)) return 0;
            else if (s1 == null) return -1;
            else if (s2 == null) return 1;

            if ((s1.Equals(string.Empty) && (s2.Equals(string.Empty)))) return 0;
            else if (s1.Equals(string.Empty)) return -1;
            else if (s2.Equals(string.Empty)) return -1;

            //WE style, special case
            bool sp1 = Char.IsLetterOrDigit(s1, 0);
            bool sp2 = Char.IsLetterOrDigit(s2, 0);
            if (sp1 && !sp2) return 1;
            if (!sp1 && sp2) return -1;

            int i1 = 0, i2 = 0; //current index
            int r = 0; //temp result
            while (true)
            {
                bool c1 = Char.IsDigit(s1, i1);
                bool c2 = Char.IsDigit(s2, i2);
                if (!c1 && !c2)
                {
                    bool letter1 = Char.IsLetter(s1, i1);
                    bool letter2 = Char.IsLetter(s2, i2);
                    if ((letter1 && letter2) || (!letter1 && !letter2))
                    {
                        if (letter1 && letter2)
                        {
                            r = Char.ToLower(s1[i1]).CompareTo(Char.ToLower(s2[i2]));
                        }
                        else
                        {
                            r = s1[i1].CompareTo(s2[i2]);
                        }
                        if (r != 0) return r;
                    }
                    else if (!letter1 && letter2) return -1;
                    else if (letter1 && !letter2) return 1;
                }
                else if (c1 && c2)
                {
                    r = CompareNum(s1, ref i1, s2, ref i2);
                    if (r != 0) return r;
                }
                else if (c1)
                {
                    return -1;
                }
                else if (c2)
                {
                    return 1;
                }
                i1++;
                i2++;
                if ((i1 >= s1.Length) && (i2 >= s2.Length))
                {
                    return 0;
                }
                else if (i1 >= s1.Length)
                {
                    return -1;
                }
                else if (i2 >= s2.Length)
                {
                    return -1;
                }
            }
        }

        private static int CompareNum(string s1, ref int i1, string s2, ref int i2)
        {
            int nzStart1 = i1, nzStart2 = i2; // nz = non zero
            int end1 = i1, end2 = i2;

            ScanNumEnd(s1, i1, ref end1, ref nzStart1);
            ScanNumEnd(s2, i2, ref end2, ref nzStart2);
            int start1 = i1; i1 = end1 - 1;
            int start2 = i2; i2 = end2 - 1;

            int nzLength1 = end1 - nzStart1;
            int nzLength2 = end2 - nzStart2;

            if (nzLength1 < nzLength2) return -1;
            else if (nzLength1 > nzLength2) return 1;

            for (int j1 = nzStart1, j2 = nzStart2; j1 <= i1; j1++, j2++)
            {
                int r = s1[j1].CompareTo(s2[j2]);
                if (r != 0) return r;
            }
            // the nz parts are equal
            int length1 = end1 - start1;
            int length2 = end2 - start2;
            if (length1 == length2) return 0;
            if (length1 > length2) return -1;
            return 1;
        }

        private static void ScanNumEnd(string s, int start, ref int end, ref int nzStart)
        {
            nzStart = start;
            end = start;
            bool countZeros = true;
            while (Char.IsDigit(s, end))
            {
                if (countZeros && s[end].Equals('0'))
                {
                    nzStart++;
                }
                else countZeros = false;
                end++;
                if (end >= s.Length) break;
            }
        }
    }
}

 

이제 힘들게 구현했으니 사용해봐야겠죠?

사용할때는 Array.Sort(strArray new StringAsNumericComparer()); <- 이런식으로 사용하면 됩니다.

 

 

아래 코드는 실제 사용예 입니다.

static void Main(string[] args)
{
    string _dirPath = @"D:\Test";

    var dirInfo = new DirectoryInfo(_dirPath);
    var dirName = dirInfo.FullName;

    // 파일명 배열
    string[] fileArr = dirInfo.GetFiles().Select(o => o.Name).ToArray();

    Array.Sort(fileArr, new StringAsNumericComparer());
}

 

 

이제 결과를 살펴봅시다.

정렬 결과 확인

 

 

이제 우리가 원하던 대로 정렬 됐습니다

저같은 경우는 꽤나 쓸일이 많은지라, 따로 보관해 필요할때마다 쓰고있습니다.

이글이 도움이 됐으면 좋겠네요

 

 

 

 

 

https://stackoverflow.com/questions/23114201/sorting-issue-in-net

 

Sorting issue in .NET

I am using this linq query to sort a string column but the results I am getting does not seems to be in right order? Query: userList = users.OrderBy(u => u.FirstName) .Skip(off...

stackoverflow.com

 

+ Recent posts