[C#] 중복된 Key를 허용하며, Key로 정렬 된 해쉬맵(Dictionary)

C#의 자료구조 중 Key-Value 컨테이너는 Dictionary입니다. 경우에 따라 Key로 정렬된 Dictionary는 SortedDictionary 클래스로써 C#에서 기본적으로 제공하고 있습니다. 그런데, Dictionary이므로 Key가 중복될 수 없습니다. 중복된 값을 정렬해서 저장하기 위해서는 Set 자료 구조를 이용해야 합니다. 그런데, Set는 Key만을 저장할 수 있고 Value는 저장할 수 없습니다. 중복된 Key를 허용하면서, Key로 정렬된 컨테이너는 기본적으로 C#에서 제공하지 않습니다. 하지만 두개의 클래스를 조합하면 가능한데, SortedSet과 Tuple입니다. 이 두개의 클래스를 통해 중복된 Key를 허용하면서, Key로 정렬된 컨테이너에 대한 클래스는 아래와 같습니다.

public class SortedTupleBag<TKey, TValue> : SortedSet<Tuple<TKey, TValue>>
where TKey : IComparable
{
    private class TupleComparer : Comparer<Tuple<TKey, TValue>>
    {
        public override int Compare(Tuple<TKey, TValue> x, Tuple<TKey, TValue> y)
        {
            if (x == null || y == null) return 0;

            return x.Item1.Equals(y.Item1) ? 1 : Comparer<TKey>.Default.Compare(x.Item1, y.Item1);
        }
    }

    public SortedTupleBag() : base(new TupleComparer()) { }

    public void Add(TKey key, TValue value)
    {
        Add(new Tuple<TKey, TValue>(key, value));
    }
}

이 클래스의 활용 예는 아래의 코드와 같습니다.

var tuples = new SortedTupleBag<double, double>();

tuples.Add(10.0, 100.1);
tuples.Add(40.0, 100.4);
tuples.Add(10.0, 1000.1);
tuples.Add(80.0, 20.8);
tuples.Add(70.0, 2100.7);
tuples.Add(50.0, 200.5);

Add 함수의 첫번째는 Key이고 두번째는 Value입니다. Key의 순서대로 정렬되어 있는지 확인하기 위해 아래의 코드를 통해 살펴볼 수 있습니다.

Console.WriteLine("Sorted List");
foreach (var tuple in tuples)
{
    Console.WriteLine("{0} {1}", tuple.Item1, tuple.Item2);
}

Console.WriteLine("");

Console.WriteLine("Min: {0}, Max: {1}", tuples.Min, tuples.Max);

위 코드의 실행 결과는 아래와 같습니다.

Sorted List
10 100.1
10 1000.1
40 100.4
50 200.5
70 2100.7
80 20.8

Min: (10, 100.1), Max: (80, 20.8)

[C#] ActiveX 객체가 담긴 파일의 경로 얻기

ActiveX 객체는 ocx나 dll 파일에 담겨 있는데요. 이 ActiveX 객체를 등록(regsvr32.exe를 통해 직접 등록되거나 설치 파일 등을 통해 등록)될 경우 객체 ID를 통해 해당 ActiveX 파일의 전체 경로를 파악해야 할 필요가 있습니다. 이때 사용하는 함수입니다.

private static string GetFilePathOfActiveX(string comName)
{
    RegistryKey comKey = Registry.ClassesRoot.OpenSubKey(comName + "\\CLSID");
    if (comKey == null) return null;

    string clsid = (string)comKey.GetValue("");
    RegistryKey subKey = Registry.ClassesRoot.OpenSubKey("CLSID\\" + clsid + "\\LocalServer32");

    if (subKey == null) {
        subKey = Registry.ClassesRoot.OpenSubKey("CLSID\\" + clsid + "\\InprocServer32");
    }

    if (subKey == null)
    {
        subKey = Registry.ClassesRoot.OpenSubKey("WOW6432Node\\CLSID\\" + clsid + "\\InprocServer32");
    }

    if (subKey == null) return null;

    return (string)subKey.GetValue("");
}

위의 방식은 현재 Windows 10 64Bits 환경에서 작동하는 것을 확인했습니다. 다른 환경에서도 정상적으로 작동하는지 확인이 필요한데요. 혹 Win10 64Bits 환경 이외에서도 어떻게 작동하는지 댓글을 통해 언급해 주시면 좋겠습니다.

위의 함수는 아래처럼 사용할 수 있습니다.

MessageBox.Show(GetFilePathOfActiveX("XrMap.XrMapControl"));

[C#] 관리자 권한으로 상승하여 프로그램 실행

Windows 운영체제는 시스템의 몇가지 중요한 정보를 변경을 수행하기 위해서 관리자 권한으로 실행되어져야 합니다. 예를 들어 COM 기반의 컴포넌트를 등록하기 위한 경우 관리자 권한이 아닌 경우 등록이 실패합니다. 아래의 코드는 C#으로 개발된 프로그램을 실행할 때 관리자 권한으로 프로그램이 실행될 수 있도록 하는데, Program.cs의 Main() 함수에 대한 전체 코드입니다.

[STAThread]
static void Main()
{
    if (IsAdministrator() == false) // 관리자 권한으로 실행되지 않는 경우라면 ..
    {
        try
        {
            ProcessStartInfo procInfo = new ProcessStartInfo();
            procInfo.UseShellExecute = true;
            procInfo.FileName = Application.ExecutablePath;
            procInfo.WorkingDirectory = Environment.CurrentDirectory;
            procInfo.Verb = "runas";
            Process.Start(procInfo);
        }
        catch (Exception ex)
        {
            // 사용자가 프로그램을 관리자 권한으로 실행하기를 원하지 않을 경우에 대한 처리
            MessageBox.Show(ex.Message);
            return;
        }
    } else { // 처음부터 프로그램은 관리자 권한으로 실행되고 있는 경우라면 ..
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

위의 코드에서 IsAdministrator 라는 함수가 보이는데요. 이 함수는 아래와 같습니다.

public static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();
            
    if(identity != null)
    {
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }

    return false;
}

위의 코드에서 참조하는 클래스를 인식하기 위해서는 다음을 import 문이 필요합니다.

using System.Security.Principal;
using System.Diagnostics;

[C#] 관리자 권한으로 실행하기

관리자 권한이 없는 상태에서 시스템의 정보를 읽거나 쓰려고 할때 프로그램이 올바르게 작동하지 않습니다. 이럴때 프로그램이 실행되면서, 관리자 권한으로 상승시키는 것이 필요한데요. C#에서 이러한 내용에 대한 코드를 정리합니다. 모든 코드는 Program.cs에서 이루어집니다. 먼저 using 절에 다음과 같은 네임스페이스를 추가합니다.

using System.Security.Principal;
using System.Diagnostics;

그리고 현재 관리자 권한 상태인지의 여부를 파악하는 함수를 추가합니다.

public static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();

    if (null != identity)
    {
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }

    return false;
}

Program.cs 파일의 Main 함수의 시작 부분에 다음의 코드를 추가합니다.

if (IsAdministrator() == false)
{
    try
    {
        ProcessStartInfo procInfo = new ProcessStartInfo();
        procInfo.UseShellExecute = true;
        procInfo.FileName = Application.ExecutablePath;
        procInfo.WorkingDirectory = Environment.CurrentDirectory;
        procInfo.Verb = "runas";
        Process.Start(procInfo);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString());
    }

    return;
}