@서론:
안녕하세요. 엉스데브 입니다.
엑셀을 사용하다 보면 가끔 엑셀의 기본 함수가 다소 아쉽게 느껴지는 경우가 있습니다. 이러이러한 함수가 있으면 참 좋을텐데.. 싶을 때가 간혹 있지요.
그래서 엑셀은 사용자가 직접 함수를 만들어서 사용 할 수 있는 기능을 지원하며, 이렇게 사용자가 직접 만든 함수는 "사용자 정의 함수(UDF, User-Defined Function)" 라고 불립니다.
사용자 정의 함수는 여러가지 다양한 방법으로 정의 할 수 있는데, 그 중 제가 가장 익숙한 .NET/C# 으로 함수를 직접 만들어 사용하는 방법을 찾아 봤습니다.
.NET/C# 으로 UDF 를 만드는 방법은 크게 3가지 정도가 있는데,
1. COM Interop 을 이용하는 방법, 2. Sharepoint Server 제품에 포함되어 제공되는 Microsoft.office.Excel.Server.Udf.dll 을 이용하는 방법, 3. Excel-DNA 라이브러리를 이용하는 방법이 있습니다.
이 중 오늘은 COM Interop 을 활용하는 방식에 대해 정리 해 보겠습니다.
@C#.NET 으로 엑셀 UDF 만들기 (COM Interop):
* OS 환경: Windows 7 Enterprise K (Service Pack 1) 64bit
상세사양보기(이미지) 접기
접기
* 사용도구: Visual Studio 2010 Premium Microsoft Excel 2010
상세사양보기(이미지) 접기
접기
단계1: 클래스 라이브러리 형식의 프로젝트 생성 비주얼 스튜디오를 켜고, 클래스 라이브러리 형식의 새 프로젝트를 만듭니다.
단계2: 엑셀 UDF 용 클래스 생성 엑셀 UDF 용으로 클래스를 만들어 줍니다. 클래스 이름을 적당히 지어주고, 엑셀에서 함수로 사용 될 메소드를 만들어 줍니다. 이번에 필요한 기능은 지정된 도메인의 연결 IP 를 가져오는 기능이기에, 다음과 같이 소스 코드를 작성 했습니다.
using System; using System.Linq;
namespace InfradevExcelUDF { public class DNSUtil { public string GetIPAddress(string domain) { if (string.IsNullOrEmpty(domain) == true) { return string.Empty; }
try { if (System.Net.Dns.GetHostAddresses(domain).Length > 0) { return System.Net.Dns.GetHostAddresses(domain).First().ToString(); } else { return string.Empty; } } catch (Exception) { return "#ERROR: 예상하지 못한 오류 입니다."; } }
} }
단계3 : System.Runtime.InteropService 네임스페이스 추가(using)
using System; using System.Linq;using System.Runtime.InteropServices;
namespace InfradevExcelUDF { public class DNSUtil { public string GetIPAddress(string domain) { if (string.IsNullOrEmpty(domain) == true) { return string.Empty; }
try { if (System.Net.Dns.GetHostAddresses(domain).Length > 0) { return System.Net.Dns.GetHostAddresses(domain).First().ToString(); } else { return string.Empty; } } catch (Exception) { return "#ERROR: 예상하지 못한 오류 입니다."; } }
} }
단계4 : 클래스에 ClassInterface 속성 설정 클래스가 COM 에 노출 될 때 사용 될 인터페이스 속성을 설정 해 줍니다.
using System; using System.Linq; using System.Runtime.InteropServices;
namespace InfradevExcelUDF { [ClassInterface(ClassInterfaceType.AutoDual)] public class DNSUtil { public string GetIPAddress(string domain) { if (string.IsNullOrEmpty(domain) == true) { return string.Empty; }
try { if (System.Net.Dns.GetHostAddresses(domain).Length > 0) { return System.Net.Dns.GetHostAddresses(domain).First().ToString(); } else { return string.Empty; } } catch (Exception) { return "#ERROR: 예상하지 못한 오류 입니다."; } }
} }
단계5 : COM 자동 등록/삭제 용 메소드 추가 COM 에 자동 등록/삭제 될 때 사용될 메소드를 추가 해 줍니다.
using System; using System.Linq; using System.Runtime.InteropServices;using Microsoft.Win32;
namespace InfradevExcelUDF { [ClassInterface(ClassInterfaceType.AutoDual)] public class DNSUtil { public string GetIPAddress(string domain) { if (string.IsNullOrEmpty(domain) == true) { return string.Empty; }
try { if (System.Net.Dns.GetHostAddresses(domain).Length > 0) { return System.Net.Dns.GetHostAddresses(domain).First().ToString(); } else { return string.Empty; } } catch (Exception) { return "#ERROR: 예상하지 못한 오류 입니다."; } }
[ComRegisterFunctionAttribute] public static void RegisterFunction(System.Type type) { Registry.ClassesRoot.CreateSubKey( GetSubKeyName(type, "Programmable") );
RegistryKey key = Registry.ClassesRoot.OpenSubKey( GetSubKeyName(type, "InprocServer32"), true );
key.SetValue( "", System.Environment.SystemDirectory + @"\mscoree.dll", RegistryValueKind.String ); }
[ComUnregisterFunctionAttribute] public static void UnregisterFunction(System.Type type) { Registry.ClassesRoot.DeleteSubKey( GetSubKeyName(type, "Programmable"), false ); }
private static string GetSubKeyName(System.Type type, string subKeyName) { System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(@"CLSID\{"); s.Append(type.GUID.ToString().ToUpper()); s.Append(@"}\"); s.Append(subKeyName);
return s.ToString(); } } }
단계6: COM Interop 관련 설정. 이제 작성한 클래스를 COM Interop 에 노출시키기 위해 아래와 같이 프로젝트 속성을 설정 해 줍니다.
1. [프로젝트 속성 > 응용프로그램 > 어셈블리 정보 > 어셈블리를 COM에 노출] 옵션 체크
2. [프로젝트 속성 > 빌드 > COM Interop 등록] 옵션 체크
단계7 : 빌드 클래스 라이브러리를 빌드하면 자동으로 COM Interop 에 빌드 된 어셈블리가 등록 됩니다. COM Interop 에 등록 된 어셈블리를 엑셀에서 불러와 사용이 가능 합니다.
단계8 : 엑셀에서 실제 사용 해 보기
1. [개발도구 > 추가기능 > 자동화] 버튼을 클릭하면 "자동화 서버" 창이 표시 됩니다.
2. 사용 가능한 자동화 서버 목록에서 방금 만든 라이브러리를 찾아서 선택 후 "확인" 버튼을 누릅니다.
3. 방금 찾은 라이브러리를 체크 해 주고 확인 버튼을 누릅니다.
4. 계산된 값이 입력 될 셀을 선택 하고 "함수 삽입" 버튼을 누릅니다.
5. "범주선택" 메뉴에서 방금 추가 한 라이브러리를 선택 해 줍니다.
6. 함수 목록 중 사용하고자 하는 함수를 선택 후 확인 버튼을 누릅니다.
7. 대상 값을 지정(마우스로 셀 클릭) 해 주고 확인 버튼을 누릅니다.
8. 직접 만든 UDF 로 원하는 값을 얻어냈습니다. ^^
@트러블 슈팅:
1. "올바른 추가 기능이 아닙니다 " 오류 빌드까지 마친 후 엑셀에서 추가기능 메뉴를 통해 불러오려 할 때 다음의 메시지가 나오는 경우가 있습니다.
확인 결과 원인은 클래스 이름에 언더바('_') 가 들어가 있어서 인 것으로 확인 되었습니다. 언더바를 제거 해 주면 정상적으로 불러와 집니다.
이상입니다. 감사합니다.