2007/05/24 00:18

일전에 자바스크립트 코드로 한글의 초성, 중성, 종성을 알아내는 코드를 본적이 있습니다.

 

어떤 공식에 의한 것이긴 한데 암튼 신기하기도 하고 C#으로도 해보고 싶기도 하고 하여...

 

그 자바스크립트 코드를 나름 C#으로 변경해봤습니다.

 

더 좋은 코드 있음 알려주세요...

 

     staticvoidMain(string[] args)

      {

          stringorigString =Console.ReadLine();

          for(inti = 0 ; i < origString.Length; i++)

               Make(origString.Substring(i, 1));

      }

     staticvoidMake(stringorigString)

      {

          charorigChar = origString.ToCharArray(0, 1)[0];

          intunicode =Convert.ToInt32(origChar);

 

          uintjongCode = 0;

          uintjungCode = 0;

          uintchoCode = 0;

 

          if(unicode < 44032 || unicode > 55203)

              Console.WriteLine("{0}의 초성 : {1}, 중성 : {2}, 종성 : {3}", origString, origString,"","");

          else

           {

              uintuCode =Convert.ToUInt32(origChar -'\xAC00');

               jongCode = uCode % 28;

               jungCode = ((uCode - jongCode) / 28) % 21;

               choCode = ((uCode - jongCode) / 28) / 21;

           }

          char[] choChar =newchar[] {'ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ','ㅆ','ㅇ','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'};

          char[] jungChar =newchar[]{'ㅏ','ㅐ','ㅑ','ㅒ','ㅓ','ㅔ','ㅕ','ㅖ','ㅗ','ㅘ','ㅙ','ㅚ','ㅛ','ㅜ','ㅝ','ㅞ','ㅟ','ㅠ','ㅡ','ㅢ','ㅣ'};

          char[] jongChar =newchar[]{' ','ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄼ','ㄽ','ㄾ','ㄿ','ㅀ','ㅁ','ㅂ','ㅄ','ㅅ','ㅆ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'};

 

          Console.WriteLine("{0}의 초성 : {1}, 중성 : {2}, 종성 : {3}", origString, choChar[choCode].ToString(), jungChar[jungCode].ToString(), jongChar[jongCode].ToString());

      }

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2007/02/08 04:24

XML에는 DTD(Document Type Definition)라는 태그 정의 문서? 가 있다.

 

이 DTD에 정의 되어있는데로 XML은 구성되어야 한다. 그런데... 브라우져로 보면 해당 XML문서가 DTD에 맞게 작성되었는지를

 

판단할 수 있지만 일반 텍스트로 본다면 하나하나 맞춰보지 않는 이상 알기 쉽지 않다.

 

그럼.. XML문서가 DTD에 맞게 작성되었는지를 프로그램으로 어떻게 작성해야 될까...

 

DTD를 한줄한줄, 아니면 개발자 맘대로 두,세줄씩 읽어서 각각의 Eelement나 Attribute를 찾아 XML문서를 뒤져야 하는가??

 

아...진짜 무식한 방법밖에 생각이 안난다. 무식하면 MSDN이나 웹을 검색하자.

 

MSDN에서 XML관련 클래스들을 보니 유독... 눈에 띄는XmlValidatingReader클래스가 있었다.

 

이 클래스의 설명은DTD(Document Type Definition), XDR(XML-Data Reduced) 스키마 및 XSD(XML 스키마 정의 언어)의 유효

 

성 검사를 제공하는 판독기를 나타냅니다.

 

XML파일을XmlTextReader로 읽고 ValidationType 지정후 XML파일을 읽기만 하면 OK~!!

 

이렇게 간단 할 수가... 일단 예제 소스를 간단히 살펴보면...

 

textreader =newXmlTextReader("파일명");

validatingreader =newXmlValidatingReader(textreader);

validatingreader.ValidationType = ValidationType.DTD;

result =true;

 

validatingreader.ValidationEventHandler +=newValidationEventHandler(ValidationError);

 

while(validatingreader.Read()) { }

returnresult;

 

XmlTextReader 객체를 생성하고 XmlValidatingReader객체를 생성할 때 인자로 넣어준다.

 

그리고 XmlValidatingReader객체의 ValidationType을 ValidationType.DTD로 설정해준다.

 

ValidationType의 속성은 Auto, DTD, None, Schema, XDR이 있다.

 

다음은 ValidationEventHandler를 등록해준다. 이 이벤트 발생은 XML문서를 검사할 때 DTD규격에 맞지 않으면...

 

즉, 오류가 있으면 발생이 된다. 이벤트 발생시 실행되는 메소드는 단순히... 오류에 대한 메세지를 문자열 변수나 기타 저장소에

 

저장하고 result 변수를 false로 변경해준다. 그래야 검사가 실패했다는걸 알 수 있으니까...

 

이벤트 등록까지 했으면 XmlValidatingReader객체의 Read()메소드를 수행하면... 끝~

 

정말 간단하다. 이제 XML문서의 각 Element, Attribute의 이름, 값등을 추출하는 코드를 작성해야 하는데...

 

쉽지만은 않을듯 하다. 그래도 DTD검사를 쉽게 한 것만으로도 만족... 친절한 .NET 씨...ㅋ

 

참고로 여기서 사용한 XmlValidatingReader클래스는 .NET 2.0부터 없어졌다. 불쌍해...

 

MSDN에 보면 이 클래스는 .NET 2.0에서 사용되지 않으며 XmlReaderSettings클래스와 Create 메소드를 사용하여 유효성 검사

 

XmlReader 인스턴스를 만들 수 있다고 나온다.

 

MSDN의 예제소스는 다음과 같이 나온다.

 

// Set the validation settings.

XmlReaderSettings settings =newXmlReaderSettings();

settings.ProhibitDtd =false;

settings.ValidationType = ValidationType.DTD;

settings.ValidationEventHandler +=newValidationEventHandler(ValidationCallBack);

 

// Create the XmlReader object.

XmlReader reader = XmlReader.Create("itemDTD.xml", settings);

 

// Parse the file.

while(reader.Read()) ;

 

// Display any validation errors.

privatestaticvoidValidationCallBack(objectsender, ValidationEventArgs e)

{

  Console.WriteLine("Validation Error: {0}", e.Message);

}

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/05/18 21:41

출처 :DOTNETJUNKIES

 

Windows Services Simplified in .NET - Part 2
사용자 삽입 이미지
ByKeyuan Jiang
Published:6/5/2002
Reader Level:Intermediate
Rated:
This article has not yet been rated.
Be the first to rate it!
Tell a Friend
사용자 삽입 이미지
Rate this Article
사용자 삽입 이미지
Printable Version
사용자 삽입 이미지
Discuss in the Forums
사용자 삽입 이미지


InPart 1, I discussed how to create service applications and how to install the service apps using the project installer class. In this part, I will cover how to create a Service Control Program (SCP), a separate executable, that provides an UI to communicate with and control the service app.

Service Control Program

Windows provides two SCPs to allow a user to interact with the service apps. One is the Services MMC Snapin and the other is a set of NET commands that can be issued in a Command Prompt window. For example, if we have a service app named Service1, we can issue "NET START Service1" in the command window to start it and "NET STOP Service1" to stop it.  

However, many times using the built-in SCPs are not sufficient to control the operations of the service app. For instance, the simple date/time service implemented using myTcp class (see Part 1) requires a way to control which time zone date/time to be returned to the client. It is impossible to do this through the built-in SCPs. A custom SCP is needed.

ServiceController Class

In .NET'sSystem.ServiceProcessnamespace, theServiceControllerclass exposes several useful functions to interact with service apps: to monitor the status of a specified service app (Status property), to retrieve a list of service apps installed on a specified computer (GetServices() method), to start and stop service apps (Start() and Stop() methods), and so forth. 

Once instantiated, aServiceControllerobject is associated with a single service app. To associate the object to a different service, one sets the ServiceName and/or MachineName instance properties. TheServiceControllerclass exposes a static method of GetServices() (see discussion below) that is not associated with any specific service app.

Starting and Stopping Service Apps 

Through the use ofSystem.ServiceProcess.ServiceController, creating a custom SCP is quite easy. When aServiceControllerobject is instantiated, the name of the machine where the service resides and the name of the service can be specified (both properties can be set after the object is instantiated). If the machine name is not specified, it defaults to the local machine (where the SCP runs). Once aServiceControllerobject is instantiated, invoking calls to Start() and Stop() methods starts and stops the specified service.

// instantiates a ServiceController object
private const stringSERVICENAME = "Service1";
privateServiceController controller =newServiceController();
controller.ServiceName = SERVICENAME;

// starts/stops the ServiceController object
controller.Start();
controller.Stop();

Similarly, one can suspend and continue a service app by calling Pause() and Continue() methods. 

Monitoring Service App Status 

A service app can have different status such as Running and Stopped. The status of a service can be retrieved from the Status property of ServiceController object. One important usage of the Status property is to enable/disable issuing corresponding commands. For example, one may want to enable the Stop button, which in turn calls ServiceController's Stop() method, on a form only when the Status is Running.

In addition to the status of a service app, the SCP can query ServiceController's CanX properties (X is PauseAndContinue, Stop or Shutdown) to determine if the service app allows to Pause, Continue, Stop or Shutdown. This is equally useful to enable/disable issuing corresponding commands. If a service app does not support Pause and Continue - the value of CanPauseAndContinue is false, the Pause and Continue buttons on a UI should be disabled.

Issuing Custom Commands to Service Apps 

In addition to standard Start()/Stop() and Pause()/Continue() commands, theServiceControllerclass provides a way to send custom commands to the service app by ExecuteCommand() method. Inside a service application, the ServiceBase class needs to implement an OnCustomCommand() method which accepts custom commands sent by the SCP via SCM.  

사용자 삽입 이미지

Both ExecuteCommand() methods and OnCustomCommand() take an integer argument as the custom command, and this integer must be between 128 to 256 - the integers below 128 are reserved for system use. Given the data type of command, an enum can be constructed for different custom commands. For example,

enum DateType {Local = 128, UTC = 129}

To use it, one can use the following statement in SCP, which tells the service app which date/time should be returned:

controller.ExecuteCommand(DateType.Local)
controller.ExecuteCommand(DateType.UTC)

Inside the service app's ServiceBase class, the code needs to determine which command was accepted and decides what action to take for the command. In my simple date/time service app, the code decides whether the accepted command is DateType.Local or DateType.UTC so that it can set the Type property of the myTcp object accordingly.  

// this method is for handling custom commands issued from SCP
protected override voidOnCustomCommand(intcommand)
{
  switch(command)
  {
     case(int) DateType.Local:
        tcp.Type = "LOCAL";// set Type to "LOCAL"
        break;
     case(int) DateType.UTC:
        tcp.Type = "UTC";// set Type to "UTC"
        break;
  }
}

Retrieving Service App List 

While an instance of ServiceController object is associated with a specified service app, to retrieve a list of installed service apps, a call must be made to the GetServices() static method of ServiceController class. A call to this method returns an array of ServiceController objects, each of which represents a service app. We can manipulate the service apps via the returned ServiceController objects as we do on a single service app. 

// retrieves a list of installed service apps
ServiceController[] services = ServiceController.GetServices();

// loops through the ServiceController object array
foreach(ServiceController scinservices)
{
    // displays name and status of the service
    txtMessage.AppendText(sc.ServiceName + ": " + sc.Status + "\n");
}

Conclusion 

Thanks to the .NET BCL, which provides a unified approach to accessing Winodws APIs, creating Windows Services is much easier than it used to be. Using several classes in System.ServiceProces namespace, a Windows service app can be developed in a short period of time.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/05/18 21:36

출처 :DOTNETJUNKIES

 

Windows Services Simplified in .NET - Part 1
사용자 삽입 이미지
ByKeyuan Jiang
Published:5/17/2002
Reader Level:Intermediate
Rated:4.00 by 1 member(s).
Tell a Friend
사용자 삽입 이미지
Rate this Article
사용자 삽입 이미지
Printable Version
사용자 삽입 이미지
Discuss in the Forums
사용자 삽입 이미지

Introduction

A Windows Service, or Win32 Service, is a long running executable that typically waits to respond to some system events, similar to that of the daemon process in the Unix environment. It usually starts when the operating system starts and stops when the operating shuts down. There is no lack of Windows Services in Microsoft business Windows operating systems. Examples inlcudeEvent Log,Net Logon,World Wide Web Publishing ServiceandMSSQLSERVER.

A Windows Service is made up of three components. Service Application (SA) is an executable that provides the desired service. Service Control Manager (SCM) is a utility that, built into the OS, communicates to and controls the SAs. Service Control Program (SCP) provides the UI to intervene the operation and monitor the status of the SAs via SCM.



사용자 삽입 이미지
 

The Service Applications are the main part on which developers will spend a majority of their time to write the service functionality. Depending upon the needs, the developers may need to create their own SCP if the SCPs built into Windows is not sufficient to interact with the SA. The SCM is already built into Windows and there will never be a need for developers to create one.

Once a Service Application is developed, its installation requires registering it with the system. The registered services can be found in the Windows registry under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services. For example, the key name forWorld Wide Web Publishing Serviceis W3SVC and that ofNet Logis Netlogon.

System.ServiceProcess Namespace

In the pre-.NET days, creating Windows services using a high level language like VB6 could be very difficult if not impossible. I remember that I had to resort to a third party tool from Desaware (NT Service Toolkit) to accomplish my task. With .NET Base Class Library, the task of writing Windows services becomes easier and can be done using any .NET language. TheSystem.ServiceProcessnamespace contains several classes required to implement, install and control the Windows services.

A Service Application is the app that provides the desired service. In .NET, the service application inherits fromSystem.ServiceProcess.ServiceBaseclass and overrides the class methods to provided desired behaviors of the service.

A class namedServiceControlleris the base class to implemente SCP that allows end users to monitor and communicate with the service applications.

TheServiceInstallerandServiceProcessInstallerare two classes used for installing services. They are usually called by the installation utilities.

Creating Service Application

Creatation of a Service Application usually involves creating (1) a clase that inherits from theServiceBaseclass (I call it service base class) and (2) one or more classes that provide the service functionality (I call them service function classes). This partition provides several advantages. It follows OOP practices. Many times the service function classes may lack responsiveness while waiting for the system events. It is more appropriate to invoke method calls to the service function classes on different threads (for discussions on multi-thread programming, refer to Ben Hinton's multi-part tutorial at this website). Also, it helps in debugging the service function because a WinForm test harnes can be easily created to test the service function classes.

The service base class can be created by creating a new Windows Service project in Visual Studio.NET. The project, by default, includes a class named Service1. Service1 is inherited fromServiceBaseclass and has some machine generated code snippet in Main method to provide the entry point for the service.

static voidMain()
{
  System.ServiceProcess.ServiceBase[] ServicesToRun;
  ServicesToRun =newSystem.ServiceProcess.ServiceBase[] {newService1() };
  System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

Service1 class also has two overriden, empty methods: OnStart() and OnStop(), where the code to start and stop the service can be placed. This is where the service function classes are instantiated and method calls to the objects are made. In the OnStart() method below, a myTcp object (the myTcp class will be discussed later). And the reader can simply replace it with his/her own object here and makes method calls) is instantiated and a method call is made on a new thread, and in the OnStop() method, the service is stopped (through a method call) and the thread is aborted.

protected override voidOnStart(string[] args)
{
  tcp =newmyTcp(8080);// instantiates myTcp object
  tcp.Type = "LOCAL";// sets the date/time time zone format
  t =newThread(new ThreadStart(tcp.Start));
  t.Start();// invokes the method call on a different thread
}

protected override voidOnStop()
{
  tcp.Stop();// stops the tcp listener in myTcp object
  t.Abort();// aborts the thread
  t.Join();
}

While the Main(), OnStart() and OnStop() are all one needs to implement a simplest Service Application, optionally, one can add other methods such as OnPause, OnContinue and OnCustomCommand to implement different behaviors of the service.

The service application I included in the download provides a simple date/time service. The date/time returned is either the local date/time of the machine hosting the windows service app or the UTC date/time. To invoke the service, telnet to the service machine's port 8080 (which can be altered in the code or via SCP if implemented) by issuing telnet localhost 8080 in the command window. Once connected, any keystroke except carriage return (Enter key) will be responded with either local or UTC date/time. When the Enter key is pressed, the connection to the service is disconnected. A user can keep using the service as long as the service is not stopped.

사용자 삽입 이미지

To create a service application with the function described, I created a service function class named myTcp that provides the data/time service. It listens to to incoming TCP message at a specified port and decides how to process. The myTcp class exposes two methods, Start() and Stop(), which starts and stops the tcp listener. The class also has a Type property that determines whether the returned date/time should be "Local" or "UTC".

To the client, the myTcp class looks as the following.

public classmyTcp
{
  publicmyTcp(intport) {}
  public voidStart() {}
  public voidStop() {}
  public stringType
  {
    get{}
    set{}
  }
}

Installing/Uninstalling Service Application

The installation of a Windows service app is usually done using either InstallUtil.exe tool or Windows Installer, but both approaches require a project installer class in the service app project. The project installer class inherits fromSystem.Configuration.Install.Installerand uses bothSystem.ServiceProcess.ServiceInstallerandSystem.ServiceProcess.ServiceProcessInstallerto complete registering the Windows service app.

TheServiceInstalleris specific to each service (there can be more than one service in the solution). In other words, each service app will have its own instance ofServiceInstallerobject. The Name property is for the name of the service (usually the service base class name) and will be listed in the Name column of the Services MMC Snapin. If one needs a more descriptive name, one can set the DisplayName property. This will change the text in the Name column of the Services MMC Snapin but the service name remains unchanged. In other words, when issuing .NET START/STOP commands, the same service name should be used. The StartType property determines how the service should get started. The property has one of these values: "Automatic", "Disabled" and "Manual". These values correspond to the choices in the Startup type combobox on the service app's property page.

TheServiceProcessInstalleris common to all services. The services may run in different security context and the account associated with the security context can be set by setting Account property, which is "LocalService", "LocalSystem", "NetworkService" or "User". If the Account is set to User, the credentials of the user can be set by assigning Username and Password property values.

The project installer class can be manually or by adding a Deployment project to the same solution in VS.NET. After the server app project is built, one can install/uninstall the service app using InstallUtil.exe along with the.exe which is located in bin\debug if the Configuration is set to Debug. The syntax for installing and uninstalling service app is as follows.

InstallUtil <project>.exe// install service app
InstallUtil /u <project>.exe// uninstall service app

With VS.NET Deployment project template, it is easy to create Windows Installer package (.msi), but every time there is any change in the service app project, the service app project has to be re-built before the Deployment project is re-built. Installing the service app using Windows Installer package is straightforward: execute the .msi file. One can use Control Panel's Add/Remove Program utility to uninstall the service app.

There is a way to install and uninstall the service app from within the VS.NET IDE. Right click on the Deployment project and choose either Install or Uninstall. This is very handy for developers who need to make changes and test the service app frequently without using other tools.

The three methods of installing/uninstalling service apps are used for different situations. If the development environment only has the .NET SDK without VS.NET, InstallUtil.exe command line tool is preferred. Running .msi outside VS.NET is the standard way to install/uninstall service apps on the same development box or on a different machine such as QA/production box. VS.NET is well integrated with Windows Installer and creating .msi package is very efficient using VS.NET. When in development, it is more productive using the shortcut menu on the Deployment Project because there is no need to leave the VS.NET IDE.

Refer the .NET SDK for details on how to use InstallUtil.exe and refer toWalkthrough: Creating a Windows Service Application in the Component Designeron how to use Windows Installer to install the service app.

Once the service app is installed successfully, it should be listed in the Services MMC Snapin and one can start and stop the service using the shortcut menu. The example code included in the download has a descriptive name called "Simple Date/Time Windows Service".

In Part 2, I will cover how to create a Service Control Program (SCP) to communicate and control the service application (for example, how to control whether to return local date/time or UTC date/time).

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/04/20 21:45

출처 -MSDN

즉석으로 UI 만들기

.NET Framework를 사용하여 런타임에 사용자 지정 컨트롤 생성 및 실행



이 문서 내용:
  • 런타임 코드 생성
  • Reflection.Emit을 사용하여 클래스 생성
  • System.CodeDom을 사용하여 클래스 생성
  • 런타임에 Windows Forms 및 ASP.NET 컨트롤 만들기
이 문서에는 다음 기술이 사용됩니다:
Windows Forms, ASP.NET, .NET Framework, C#


Code download available at:
CodeGeneration.exe(146KB)

사용자 삽입 이미지
icrosoft .NET Framework에서 간과하기 쉬운 기능 중 하나는 런타임에 사용자 지정 코드를 생성, 컴파일 및 실행할 수 있는 기능입니다. 예를 들어, 이 기능은 XML 데이터 serialization을 수행하는 동안이나 정규식을 사용하는 과정에 사용할 수 있습니다. 정규식을 사용할 때는 식 계산 기능이 런타임에 실행됩니다.

이 기사에서는 런타임 코드 생성 기능을 사용할 수 있는 또 다른 분야인 UI 컨트롤 만들기에 대해 설명합니다. 이러한 코드를 한 번 생성해 둔 다음 필요할 때 다시 사용하면 폼이나 페이지가 요청될 때 매번 컨트롤을 생성하는 것보다 훨씬 효율적입니다.

이 기능은 사용자 구성이 가능한 필드가 있는 모든 응용 프로그램에 적용할 수 있습니다. 사용자 구성 가능한 필드의 예로는 최종 사용자가 화면에 표시할 데이터 항목을 선택할 수 있는 필드를 들 수 있습니다. 일반적으로 사용자 지정 폼은 XML을 사용하여 정의합니다. 사용자 지정된 폼은 런타임에 구문 분석되어 페이지가 로드될 때 UI를 동적으로 생성합니다. 그러나 폼이 표시될 때마다(서버 시나리오에서는 각 사용자에 대해) 구문 분석이 빈번하게 수행되기 때문에 응용 프로그램에 대한 불필요한 로드가 발생합니다.

이 기사에서는 런타임 코드 생성 기능을 사용하여 런타임에 컨트롤을 생성, 로드 및 실행하는 방법을 자세한 예제를 통해 설명하겠습니다. 여기서 설명하는 예제는 .NET Framework 1.x 및 .NET Framework 2.0에도 동일하게 적용할 수 있습니다. Framework 2.0에는 Reflection 네임스페이스에 대한 중요한 추가 사항이 있지만 이러한 변경 내용으로 인해 이 기사에서 설명하는 솔루션의 기능을 사용할 수 없는 것은 아닙니다.


코드 생성 기본 사항

대부분의 응용 프로그램에서는 필드를 추가하거나 기존 필드의 순서 및 위치를 변경하는 방식으로 사용자 지정할 수 있는 사용자 인터페이스(UI)를 제공합니다. 이 작업은 보통 다음 두 가지 방법 중 하나를 사용하여 수행합니다.

  • 사용자가 Visual Studio에서 사용자 인터페이스를 편집하여 변경합니다
  • 응용 프로그램이 보통 XML 파일에 저장되어 있는 구성 데이터의 일부 폼을 기준으로 런타임에 컨트롤을 생성합니다.

그러나 이 두 솔루션은 모두 이상적이지 못합니다. 실제로 필요한 것은 사용자가 직접 만든 솔루션의 뛰어난 성능과 런타임에 컨트롤을 생성하는 응용 프로그램의 융통성을 제공하는 위의 두 방법을 조합한 형태이기 때문입니다.

런타임에 컨트롤을 생성하는 데는 Reflection.Emit 및 System.CodeDom의 두 가지 메서드가 지원됩니다. IL(Intermediate Language)에 익숙해야 사용할 수 있는 Reflection.Emit은 명시적으로 지정된 IL 명령을 사용하여 관리되는 어셈블리를 생성합니다. System.CodeDom은 코드의 개체 모델을 사용하여 소스 코드를 내보냅니다. 그러면 소스 코드는 IL로 컴파일되어 실행됩니다. 이 기사에서는 이 두 메서드를 모두 다룹니다.

사실 CodeDOM 방식의 변형인 다른 메서드도 있습니다. 이 메서드는 개체 모델을 사용하여 소스 코드를 생성하는 대신 사용자가 수동으로 소스 코드를 생성(.cs 또는 .vb 파일을 직접 작성)한 다음 런타임에 해당 소스 코드를 컴파일하는 데 사용됩니다. 이 메서드를 사용해도 되지만 수동 솔루션보다는 앞서 언급한 메커니즘을 사용하는 것이 좋습니다.


Reflection.Emit을 사용하여 클래스 생성

Reflection.Emit 네임스페이스를 사용하면 완전히 일시적인 어셈블리를 생성할 수 있습니다. 즉, 이러한 어셈블리는 디스크에 저장하지 않아도 메모리에서 생성하여 실행할 수 있습니다. 그러나 필요한 경우에는 디스크에 쓰는 옵션도 사용할 수 있습니다. 이에 대해서는 이 기사 뒷부분에서 간략하게 설명합니다.

Reflection.Emit을 사용하여 클래스를 생성하려면 다음 단계를 수행해야 합니다.

  • 동적 어셈블리를 정의합니다.
  • 해당 어셈블리 내에 모듈을 만듭니다.
  • 해당 기본 클래스 및 인터페이스에서 형식을 파생시켜 해당 모듈에 형식을 정의하고, 해당 형식에 메서드를 만들고, 각 메서드에 대해 ILGenerator를 가져오고, 적절한 IL opcode를 내보내고, 형식을 보존합니다.
  • 나중에 사용하기 위해 어셈블리를 저장합니다(옵션).

Reflection.Emit을 사용하여 클래스를 생성하는 작업은 복잡하며, 지정된 소스 코드 작업(예: 메서드 호출)에 대해 보통 IL 코드를 여러 줄 생성해야 합니다. 여기에 대해서는 이 기사 뒷부분에서 다시 설명하겠습니다.


System.CodeDom을 사용하여 클래스 생성

System.CodeDom 네임스페이스는 형식 정의를 위한 언어 독립적인 방식을 제공합니다. 코드의 메모리 내 모델을 생성한 다음 코드 생성기를 사용하여 해당 모델의 소스 코드를 내보냅니다. 적절한 코드 생성기가 있으면 모든 언어에서 코드를 내보낼 수 있습니다. .NET Framework 재배포 가능 패키지에는 Visual Basic, C# 및 JScript용 코드 생성기가 포함되어 있습니다. Visual Studio가 설치되어 있으면 C++ 및 J#용 생성기도 사용할 수 있습니다.

CodeDOM을 사용하여 클래스를 생성하려면

  • 새 CodeCompileUnit을 만듭니다.
  • CodeCompileUnit에 네임스페이스를 추가합니다.
  • import 문을 적절하게 추가합니다.
  • 생성되는 클래스에 대해 CodeTypeDeclaration을 추가합니다.
  • 각 메서드에 CodeExpressions를 추가합니다.
  • 코드 컴파일러를 가져와 CodeCompileUnit을 컴파일합니다.
이 방법을 사용하면 Reflection.Emit을 사용할 때보다 수준 높은 개념을 사용할 수 있다는 이점이 있습니다.

클래스를 정의한 후에는 런타임에 해당 클래스의 인스턴스를 생성해야 합니다. 가장 일반적인 방법은 다음 코드 조각과 같이 Activator 클래스를 사용하여 형식을 로드하는 것입니다. public Control LoadControl ( string typeName ){ return LoadControl(Type.GetType(typeName));}public Control LoadControl ( Type controlType ){ return Activator.CreateInstance(controlType) as Control;}

여기서는 Type 클래스의 정적 GetType 멤버를 호출합니다. GetType은 해당 인수로 지정된 형식에 대한 .NET 형식 정보를 반환합니다. 그러면 Activator 클래스를 사용하여 해당 형식의 인스턴스가 생성됩니다. 지정된 형식이 mscorlib 또는 현재 실행 중인 어셈블리가 아닌 다른 어셈블리에 있는 경우에는 형식 이름을 어셈블리 이름으로 정규화해야 합니다. 관리되는 형식 이름을 문자열로 저장하는 경우에는 형식 네임스페이스로 부분 정규화할 수도 있고 형식 네임스페이스 및 형식이 저장된 어셈블리 이름으로 전체 정규화할 수도 있습니다. 다음은 어셈블리 정규화 형식 이름의 예제입니다. TestControls.TestControl, TestAssembly

이 형식 이름은 TestAssembly라는 어셈블리로 컴파일되는 다음 코드의 형식에 해당합니다. using System.Web.UI;namespace TestControls{ public class TestControl : Control { ... }}

컨트롤을 생성할 때는 보통 이 형식 이름을 SQL Server 데이터베이스 같은 영구적 저장소 매체에 저장합니다. 그러면 폼 또는 페이지를 렌더링할 때 Activator.CreateInstance를 사용하여 형식을 로드한 다음 해당 개체를 사용자에게 표시합니다.


생성된 어셈블리 저장

Reflection.Emit 또는 System.CodeDom을 사용하여 만든 어셈블리는 완전한 임시 어셈블리일 수도 있고, 나중에 사용하기 위해 디스크에서 생성할 수도 있습니다. 임시 어셈블리는 만들어지는 응용 프로그램 도메인의 수명이 유지될 동안만 존재합니다. 응용 프로그램 도메인이 언로드되면, 즉 응용 프로그램이 언로드되어 기본 응용 프로그램 도메인이 언로드되면 이러한 어셈블리도 메모리에서 언로드되어 더 이상 존재하지 않게 됩니다. 런타임에 이러한 어셈블리를 생성하려면 비용이 들기 때문에 생성된 어셈블리를 파일 시스템에 보관하고 필요할 때는 먼저 파일 시스템을 찾아보는 것이 좋습니다. 런타임에 파일 시스템에서 어셈블리를 찾을 수 없는 경우 다시 생성하면 됩니다. 물론 이 방법을 선택하는 경우에는 컨트롤의 정의 및 생성된 어셈블리를 동기화된 상태로 유지해야 합니다. 상용 응용 프로그램의 경우에는 생성해야 하는 모든 컨트롤에 대해 실행할 수 있는 관리 도구를 사용자에게 제공하고, 디스크 또는 SQL Server 내에 유지되는 단일 어셈블리에 이러한 컨트롤을 저장하도록 하는 것이 좋습니다.

또한 동적으로 생성되는 어셈블리 수를 가능한 한 최소(1개 권장)로 제한하는 것이 좋습니다. 즉, 컨트롤이 변경될 때마다 전체 컨트롤 집합을 생성해야 합니다. 그러나 이는 보통 자주 수행되지 않는 관리 작업이므로 성능 문제는 없습니다.


생성된 어셈블리 보안

또한 생성된 어셈블리와 관련된 보안 문제를 이해해야 합니다. 생성된 어셈블리는 독특한 방식으로 응용 프로그램에 위협을 줄 수 있기 때문에 이는 매우 중요합니다. 즉, 응용 프로그램이 동적으로 일부 형식을 로드하려는 경우 이론적으로는 이러한 형식이 임의의 코드를 실행할 수 있습니다.

생성된 코드를 보호하는 데 권장되는 방법은 생성된 모든 코드에 대해 제한적인 권한 집합을 할당하는 것입니다. .NET 구성 도구인 mscorcfg.msc 내에서 코드 그룹을 생성하여 이 작업을 수행할 수 있습니다.

특정 멤버 조건 및 권한 집합으로 사용자 지정 코드 그룹을 정의할 수 있습니다. 예를 들어, 동적으로 생성된 모든 코드에 대해 어셈블리를 실행할 수는 있지만 코드가 수행할 수 있는 작업은 엄격하게 제한하는 Execute 등의 매우 제한적인 권한 집합을 할당하려는 경우가 있습니다. 코드 그룹은 멤버 조건을 통해 어셈블리에 적용되며, 결과적으로 이는 그룹에 포함할 내용을 정의하는 데 사용됩니다.

멤버 조건에는 여러 가지 형식이 있습니다. 그 중 이 예제에서 가장 유용하게 사용되는 형식은 디스크의 특정 디렉터리(예: "file://E:/Code Generation/bin/Debug/*"를 URL로 사용)를 기반으로 하여 멤버를 정의하는 데 사용할 수 있는 URL 권한입니다. 도구 내에서 정의된 권한 집합은 지정된 폴더 내에 저장된 모든 생성된 어셈블리에 적용됩니다.

컨트롤을 한 번 생성해 두었다가 런타임에 로드하면 폼의 XML 표현을 사용하고 런타임에 해당 표현을 평가하는 것보다 성능 면에서 이점이 있습니다. XML 표현을 로드하려면 일반적으로 XmlDocument를 인스턴스화 및 로드해야 하는데, 이 작업은 Activator.CreateInstance를 사용하더라도 일반 컨트롤을 인스턴스화하는 작업보다 속도가 느립니다.


Windows Forms 예제

이 섹션에서는 Reflection.Emit 및 System.CodeDom을 사용하여 런타임에 Windows Forms를 생성하는 방법을 보여 주는 예제를 제공합니다. 생성되는 컨트롤은 그림 1과 같이 정적 텍스트 상자로만 구성된 간단한 사용자 인터페이스를 표시합니다.

사용자 삽입 이미지

그림 1 컨트롤 생성 샘플 응용 프로그램

생성된 컨트롤은 빨간 선 안에 표시되는 부분입니다. 간단한 예제이기는 하지만 보다 완벽한 구현을 제공하기 위해 확장할 수 있는 필요한 개념은 모두 제공하고 있습니다.

이 기사에서는그림 2의 코드와 비슷한 소스 코드를 가진 컨트롤을 생성합니다. 이 코드는 내보내는 코드의 양에 관계없이 사용할 수 있는 다음과 같은 여러 가지 개념을 보여 줍니다.

  • 클래스가 기본 클래스에서 파생됩니다.
  • 클래스에 개인 필드가 있습니다.
  • 클래스에 공용 기본 생성자가 있습니다.
  • 클래스에 개인 메서드가 있습니다.
  • 클래스가 속성 사용 방법을 보여 줍니다.
  • 클래스가 메서드 호출 방법을 보여 줍니다.
  • 클래스가 새 개체 인스턴스 생성 방법을 보여 줍니다.
위의 목록에 내보낼 수 있는 모든 기능이 나열되어 있는 것은 아니지만, 이를 기반으로 하여 원하는 작업을 수행할 수 있습니다.


Reflection.Emit을 사용하여 Windows Forms 컨트롤 생성

Reflection.Emit 네임스페이스는 적은 수의 클래스만을 포함하는 매우 낮은 수준의 API이므로 대부분의 까다로운 작업은 사용자가 직접 코드를 작성하여 수행해야 합니다. 코드를 섹션별로 살펴보고 각 섹션을 자세히 설명하겠습니다.

Reflection.Emit을 사용하는 작업은 까다롭습니다. 정확한 코드를 생성할 수 있는 실제적인 방법은 테스트 코드를 일부 작성하고 컴파일한 다음 ildasm 또는 Lutz Roeder의 .NET Reflector 같은 도구를 사용하여 생성된 IL을 검사하는 것뿐입니다. 다음 IL은 이 방법으로 생성한 것입니다. 일부 다른 코드를 리버스 엔지니어링하지 않고 사용자의 고유한 IL을 생성할 수 없다는 의미는 아닙니다. 그러나 IL을 기본 언어로 사용하는 개발자가 많지 않으므로 이 분야에 사용할 수 있는 기술은 거의 없고 기술 간에도 격차가 큽니다. IL에 대한 고급 지식이 필요한 경우에는 Serge Lidin의 저서 Inside Microsoft .NET IL Assembler(Microsoft Press, 2002)에 참조할 만한 좋은 내용이 있습니다. 이에 대한 내용을 설명하기 위해 코드를 어셈블리 내보내기, 형식 정의, 생성자 정의 및 InitializeComponent 메서드 정의의 네 가지 논리 섹션으로 구분했습니다.


어셈블리 내보내기

그림 3의 코드에서는 동적 어셈블리를 생성하고 해당 어셈블리에 모듈을 추가하며 어셈블리의 고유한 이름을 만듭니다. 먼저 어셈블리 이름이 생성됩니다. 이 예제에서는 GUID를 사용하여 어셈블리의 고유한 이름을 지정합니다.

그런 다음 AppDomain 클래스의 정적 DefineDynamicAssembly 메서드를 호출해야 합니다. DefineDynamicAssembly 함수에는 여러 가지 오버로드 버전이 있지만 여기서는 가장 간단한 재정의를 호출합니다. 다음으로는 모듈 이름 및 파일 이름을 정의하여 모듈 작성기 개체를 만듭니다. 마지막으로 다음 섹션에 있는 코드가 실행된 후 AssemblyBuilder.Save 메서드를 사용하여 어셈블리가 만들어집니다.


형식 정의

Reflection.Emit을 사용하여 형식을 만들려면 ModuleBuilder 클래스에서 DefineType 메서드 중 하나를 호출해야 합니다. 클래스, 해당 클래스의 기본 클래스 및 해당 클래스가 명시적으로 구현하는 인터페이스를 정의할 수 있는 여러 가지 메서드가 있습니다. 다음 코드 세그먼트에서는 System.Windows.Forms.UserControl에서 파생된 클래스를 정의하는 방법을 볼 수 있습니다. Type baseClass = typeof ( System.Windows.Forms.UserControl ) ;TypeBuilder typeBuilder = moduleBuilder.DefineType( "MyControls.MyControl", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.BeforeFieldInit, baseClass ) ;

DefineType을 사용하면 생성된 형식의 형식 특성을 지정할 수 있습니다. 이 경우 새 형식은 클래스입니다. 이는 공용으로 사용할 수 있도록 표시되며 런타임에 클래스를 초기화하도록 하지 않고도 정적 멤버를 호출할 수 있습니다.

그런 다음 레이블 컨트롤을 보관할 개인 필드를 만듭니다. 이 필드는 이름, 형식 및 특성이 단일 호출로 지정된다는 점에서 앞서 나왔던 코드와 비슷한 패턴을 따릅니다. 이 필드를 만드는 코드는 다음과 같습니다. // 레이블 컨트롤을 위한 개인 필드를 만듭니다.FieldBuilder labelField = typeBuilder.DefineField( "_label", typeof( System.Windows.Forms.Label ) , FieldAttributes.Private ); (참고: 프로그래머 코멘트는 샘플 프로그램 파일에는
영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)


생성자 정의

형식에 내용을 추가하려면 앞에서 만든 개체인 TypeBuilder 클래스의 Define* 메서드 중 하나를 호출해야 합니다. 생성자, 이벤트, 필드, 메서드 및 기타 구문을 정의하기 위한 이러한 메서드에는 여러 가지가 있습니다.

그림 4에서는 DefineConstructor 메서드를 사용하여 매개 변수가 없는 공용 생성자를 만듭니다. DefineConstructor 메서드에 적절한 메서드 특성을 제공하고 이 인스턴스에서 매개 변수 배열이 비어 있도록 정의합니다. 생성자 코드에서 생성된 IL은 다음과 같습니다. .method public hidebysig specialname rtspecialname instance void .ctor()cil managed{ .maxstack 3 L_0000: ldarg.0 L_0001: call instance void System.Windows.Forms.UserControl::.ctor() L_0006: ldarg.0 L_0007: call instance void MyControls.MyControl::InitializeComponent() L_000c: ret }생성된 IL과 C# 코드의 해당 IL을 내보낸 명령은 거의 일치합니다.

ConstructorBuilder 개체를 만든 후에는 해당 개체에서 ILGenerator를 가져올 수 있습니다. 이 개체를 사용하여 IL을 어셈블리로 내보낼 수 있습니다.

BeginScope 및 EndScope 메서드는 여기서 반드시 필요하지 않으며, 기본적으로 C#의 { 및 }와 같습니다. 이 범위 내에서 IL opcode를 내보냅니다.

CLR은 스택 기반 아키텍처이므로 첫 번째 opcode(Ldarg_0)는 인수(이 경우 현재 개체의 this 포인터)를 계산 스택으로 전달합니다. 두 번째 명령은 기본 클래스 생성자에 대한 명령입니다. 이 명령으로 인해 this 포인터가 스택 밖으로 이동하고 생성자가 호출되므로 다음 opcode는 this 포인터를 스택으로 로드하여 다음 호출을 준비합니다.

끝에서 두 번째 opcode는 InitializeComponent 메서드 호출이며 마지막 opcode는 생성자에서 반환된 것입니다.


InitializeComponent 메서드 정의

이 메서드는 다음과 같은 작업을 수행하므로 다른 메서드보다 사용 빈도가 높습니다.

  • 새 레이블 컨트롤 만들기
  • SuspendLayout 호출
  • 레이블 컨트롤의 모든 속성 설정
  • 사용자 컨트롤의 Control 컬렉션에 레이블 컨트롤 추가
  • 사용자 컨트롤의 초기 크기 설정
  • ResumeLayout 호출
이러한 작업은그림 5에 요약되어 있습니다. 컨트롤을 생성하려면 많은 양의 코드를 작성해야 하며 오류가 발생하기 쉽습니다. 코드를 각각의 함수로 구분하면 보다 읽기 쉽도록 구성할 수 있습니다.

그림 5에 있는 대부분의 코드 섹션은 비슷한 작업을 수행합니다. 즉, Ldarg_0 opcode를 사용하여 this 포인터를 스택에 로드한 다음 매개 변수를 스택에 로드합니다. 그런 다음 속성 setter 호출 또는 일부 경우 메서드 호출에 매개 변수를 사용합니다. 다음은 컨트롤 크기를 설정하는 코드의 예제입니다. initIL.Emit ( OpCodes.Ldarg_0 ) ;initIL.Emit ( OpCodes.Ldc_I4, 328 ) ;initIL.Emit ( OpCodes.Ldc_I4, 200 ) ;initIL.Emit ( OpCodes.Newobj , typeof ( Size ).GetConstructor( new Type[] { typeof ( int ) , typeof ( int ) } ) ) ;initIL.Emit ( OpCodes.Callvirt , controlClass.GetProperty("Size", typeof(Size)).GetSetMethod ( ) ) ;

this 포인터 뒤에 328 및 200의 두 정수를 붙여 스택에 로드한 다음 이 두 정수를 사용해 크기 개체를 생성하는 Size 생성자를 호출합니다. 이 크기 개체는 스택 맨 위에 반환됩니다. 그런 다음 컨트롤의 Size 속성을 가져오고 해당 속성의 setter를 검색합니다. 이 setter는 현재 스택에 있는 인수로 호출합니다. 그러면 다음 호출이 생성됩니다. this.Size = new Size ( 328, 200 ) ;

이 솔루션은 상속 과정이 복잡하기 때문에 광범위하게는 사용하지 않는 것이 좋습니다. 다음 솔루션에서는 코드를 작성하는 사용자가 보다 쉽게 배울 수 있는 융통성 있는 IL 생성 방법을 제공합니다. 다른 CLR 대상 언어로 수행할 수 없는 까다로운 작업은 IL을 사용하여 수행할 수 있습니다. 그러나 대부분의 응용 프로그램에서 이러한 작업은 필요하지 않습니다.


System.CodeDom을 사용하여 Windows.Forms 컨트롤 생성

CodeDOM 네임스페이스는 프로그래밍 가능한 요약된 코드 모델을 제공합니다. 이 모델은 간단한 식을 사용하여 메모리에서 구축되며, 사용 가능한 .NET 코드 생성기 중 하나를 사용하여 소스 코드로 변환할 수 있습니다. Microsoft에서는 현재 C#, Visual Basic, C++, JScript, J# 및 MSIL용 코드 생성기를 제공합니다. 또한 다른 언어를 위한 타사 생성기도 사용 가능합니다.

각 단계를 직접 비교할 수 있도록 Reflection.Emit 예제에서 사용했던 것과 동일한 섹션으로 코드를 구분했습니다. 코드를 생성하는 두 메서드는 서로 호환되지 않습니다. 즉, CodeDOM을 동일한 어셈블리에서 내보낸 코드와 혼합할 수는 없습니다.


어셈블리 정의 및 컴파일

CodeDOM을 사용하기 위한 첫 번째 단계는 System.CodeDom 네임스페이스를 참조하는 것입니다. 그런 다음 어셈블리의 기반을 형성하는 CodeCompileUnit을 만들어야 합니다. CodeCompileUnit을 사용하여 네임스페이스를 추가하고 이러한 네임스페이스에 import 문을 추가하고 형식을 정의할 수 있습니다.

다음 코드에서는 특정 어셈블리를 만드는 방법을 보여 줍니다. 필자는 CodeCompileUnit을 만들고 여기에 MyControls 네임스페이스를 추가한 다음 코드에 사용된 모든 클래스에 대해 import 문을 정의했습니다. 그런 다음 네임스페이스는 코드 컴파일 단위에 추가됩니다. // 코드 컴파일 단위 및 네임스페이스를 만듭니다.CodeCompileUnit ccu = new CodeCompileUnit ( ) ;CodeNamespace ns = new CodeNamespace ( "MyControls" ) ;// imports 문을 네임스페이스에 추가합니다.ns.Imports.Add ( new CodeNamespaceImport ( "System" ) ) ;ns.Imports.Add ( new CodeNamespaceImport ( "System.Drawing" ) ) ;ns.Imports.Add ( new CodeNamespaceImport ( "System.Windows.Forms" ) ) ;// 코드 컴파일 단위에 네임스페이스를 추가합니다.ccu.Namespaces.Add ( ns ) ;

형식 정의를 완료하고 나면 다음 코드를 통해 C# 코드 공급자를 사용하여 정의를 어셈블리로 컴파일할 수 있습니다. 이 섹션을 완료하기 위해 여기에 컴파일 단계를 추가했습니다. 그러나 논리적으로 이 작업은 어셈블리에 형식을 만든 후에 수행합니다. 이에 대해서는 이 기사 뒷부분에서 설명합니다. CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider ( ) ;ICodeCompiler compiler = provider.CreateGenerator ( ) as ICodeCompiler ;CompilerParameters cp = new CompilerParameters ( new string[] { "System.dll", "System.Windows.Forms.dll", "System.Drawing.dll" } ) ;cp.GenerateInMemory = true;cp.OutputAssembly = "AutoGenerated";CompilerResults results = compiler.CompileAssemblyFromDom ( cp, ccu ) ;여기서 CodeDomProvider가 C#용으로 만들어지며 CreateGenerator를 호출하여 ICodeCompiler 인터페이스가 반환됩니다. 그런 다음 이 어셈블리에서 참조해야 하는 라이브러리를 정의합니다. 이 CompilerParameters 클래스에서 일부 매개 변수를 설정한 다음 CompileAssemblyFromDom 메서드를 호출합니다. 이 코드에서 지정한 설정을 기반으로 하여 이름이 AutoGenerated인 어셈블리가 메모리에 생성됩니다.

CompilerResults 클래스에는 성공적으로 수행된 어셈블리 컴파일 작업에서 설정된 어셈블리 속성이 들어 있습니다. 이 어셈블리에서 형식을 로드하거나 필요한 작업을 수행할 수 있습니다.


CodeDOM으로 형식 정의

CodeDOM을 사용한 형식 만들기는 매우 쉽습니다. 다음 코드에서는 UserControl에서 파생되는 클래스를 만드는 방법을 보여 줍니다. CodeTypeDeclaration ctd = new CodeTypeDeclaration ( "MyControl" ) ;ctd.BaseTypes.Add ( typeof ( System.Windows.Forms.UserControl ) ) ;ns.Types.Add ( ctd ) ;CodeTypeDeclaration에는 형식이 파생되는 클래스를 포함하는 BaseTypes라는 컬렉션이 들어 있습니다. 또한 형식이 구현하는 인터페이스가 들어 있을 수 있습니다. 이는 임시 형식 컬렉션이지만 CLR은 단일 상속만을 지원하므로 여기에 둘 이상의 클래스를 사용할 수는 없습니다. 둘 이상의 기본 클래스를 사용하려고 하면 오류가 발생합니다.


생성자 정의

CodeTypeDeclaration이 있으면 다른 모든 코드는 이 정의에 추가됩니다. 다음 코드 조각에서는 생성자를 정의할 때 만들어진 ctd 변수를 사용합니다. CodeConstructor constructor = new CodeConstructor ( ) ;constructor.Statements.Add ( new CodeMethodInvokeExpression ( new CodeThisReferenceExpression ( ) , "InitializeComponent", emptyParams ) ) ;constructor.Attributes = MemberAttributes.Public ;ctd.Members.Add ( constructor ) ;

생성자 및 CodeMemberMethod에서 파생된 다른 형식은 Statements라는 CodeStatementCollection 속성을 포함합니다. 여기에 CodeStatement 개체 형식으로 하나 이상의 문을 추가할 수 있습니다. CodeExpression을 추가하는 경우 CodeExpressionStatement 개체에 자동으로 래핑됩니다. 이 예제에서는 this.InitializeComponent를 호출하고 매개 변수를 전달하지 않는 CodeMethodInvokeExpression을 추가했습니다. 그런 다음 생성자에 공개적으로 액세스할 수 있도록 지정하고 마지막으로 생성자를 형식 정의에 추가합니다.


InitializeComponent 정의

그림 6에 나와 있는 InitializeComponent 메서드는 만들어야 하는 문이 많기 때문에 생성자보다 훨씬 복잡합니다. 그러나 이 코드는 길이는 길지만 해당 IL 코드보다 간단하게 작성할 수 있습니다. 코드 작동 방식을 살펴볼 수 있도록 코드에 대해 좀 더 자세히 알아보겠습니다. 먼저 SuspendLayout 호출을 살펴봅니다. this.SuspendLayout ( ) ;이 식은 CodeMethodInvokeExpression을 사용하고 메서드가 호출된 개체에 대한 참조를 전달하여 생성합니다. 그 뒤에는 메서드 이름과 해당 메서드로 전달해야 하는 매개 변수가 옵니다. CodeMethodInvokeExpression은 다음과 같이 호출됩니다. CodeMethodInvokeExpression ( targetObject, methodName, parameters )이 인스턴스에서 targetObject는 this이고 methodName은 SuspendLayout이며 매개 변수는 없으므로 코드에서 다음과 같이 정의됩니다. 참고로 emptyParams는 CodeExpression 형식의 빈 배열입니다. initializeComponent.Statements.Add( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "SuspendLayout", emptyParams));

다음 예제에서는 속성을 특정 값으로 설정하는 방법을 보여 줍니다. 이 인스턴스에서 내보내는 코드는 다음과 같습니다. _label.TabIndex = 0 ;여기서는 두 매개 변수, 즉 왼쪽(lhs, 할당 대상) 및 오른쪽(rhs, 할당되는 개체)을 취하는 CodeAssignmentStatement를 사용해야 합니다. 이 인스턴스에서 lhs는 CodePropertyReferenceExpression이고 rhs는 CodePrimitiveExpression입니다. initializeComponent.Statements.Add( new CodeAssignStatement( // 이는 lhs를 형성하며 // _label.TabIndex입니다. new CodePropertyReferenceExpression( new CodeVariableReferenceExpression ( "_label" ), "TabIndex"), // 이는 rhs입니다. new CodePrimitiveExpression ( 0 ) ) ) ;

CodePropertyReferenceExpression 자체도 속성이 정의되는 개체 및 속성 이름의 두 매개 변수를 사용합니다. 여기서는 _label 개체를 참조하는 CodeVariableReferenceExpression을 사용했으며 해당되는 속성을 TabIndex로 지정했습니다.

이를 CodeAssignStatement 호출로 둘러쌌으며, 이 호출은 해당 속성에 값 0을 할당합니다. 문자열 및 정수 등의 특정 기본 제공 형식에는 CodePrimitiveExpression을 사용할 수 있습니다.

열거된 값 예제에서 코드에 열거된 값을 사용하는 방법을 확인할 수 있습니다. 열거된 값은 열거된 형식에 필드로 저장됩니다. 그러므로 예제에서 AnchorStyles.Left 값과 같이 지정된 필드의 값을 가져오려면 CodeFieldReferenceExpression을 사용해야 합니다.

둘 이상의 필드가 조합된 값이 있는, 보다 복잡한 열거된 값의 경우에는 CodeBinaryOperatorExpression을 사용하여 이를 조합해야 합니다. 예를 들어, CodeBinaryOperatorExpression을 사용하여 필드 값을 조합할 수 있습니다. 이는 AnchorStyle 속성을 정의할 때 일반적으로 사용되는 방법입니다. 이러한 값은 보통 논리 OR을 사용하여 조합되며, field1 | field2 형식으로 코드에 내보내집니다.

생성된 코드 내에는 개체가 만들어져 적절한 속성으로 할당되는 여러 위치가 있습니다. 다음은 그 중 한 가지 예제입니다. _label.Location = new System.Drawing.Point(8, 8);그리고 다음은 이를 생성하는 코드입니다. initializeComponent.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression( // this._label에 해당함 new CodeVariableReferenceExpression ( "_label" ), "Location"), new CodeObjectCreateExpression( // new Point(8,8)에 해당함 typeof ( System.Drawing.Point ) , new CodeExpression[]{ new CodePrimitiveExpression ( 8 ) , new CodePrimitiveExpression ( 8 ) } ) ) );

일반적인 CodePropertyReferenceExpression을 사용하여 문의 lhs를 생성합니다. CodeObjectCreateExpression은 만들 개체의 형식으로 전달되며 매개 변수 컬렉션은 생성자로 전달됩니다. 레이블의 크기 또는 전체 컨트롤의 초기 크기를 설정할 때도 이와 비슷한 코드를 확인할 수 있습니다.


ASP.NET 예제

이제 런타임에 ASP.NET 컨트롤을 생성하는 방법을 살펴보겠습니다. 여기서 사용하는 코드는 Windows Forms 예제에서 사용하는 코드와 거의 비슷하므로 생략합니다. 이 코드는 이 기사에 포함된 다운로드에서 확인할 수 있습니다. 이 예제에서는 사용자가 입력한 문자열을 렌더링하는, WebControl에서 파생되는 서버 컨트롤을 만듭니다. 사이트를 실행하면그림 7과 같이 텍스트를 입력한 다음 IL 또는 CodeDOM 예제를 생성할 수 있습니다.

사용자 삽입 이미지

그림 7 ASP.NET 컨트롤 생성

단추 중 하나를 클릭하면 코드가 서버 컨트롤을 생성하여 패널 내로 로드합니다. 그러면 사이트는 그림 8과 같이 표시됩니다. 이를 가장 유용한 컨트롤이라고 할 수는 없지만, 이 컨트롤을 통해 ASP.NET 페이지에서 사용되는 컨트롤을 런타임에 생성하기 위한 원칙을 확인할 수 있습니다.

사용자 삽입 이미지

그림 8 웹 페이지에 배치된 생성된 컨트롤

ASP.NET에는 서버 컨트롤 및 사용자 컨트롤의 두 가지 컨트롤 형식이 있습니다. 서버 컨트롤은 어셈블리 내에 있으며 보통 WebControl의 하위 클래스를 지정하여 만듭니다. 이러한 컨트롤은 최종 사용자에게 가장 적합한 디자인 타임 동작을 제공합니다.

반면 사용자 컨트롤은 .ascx 파일과 관련 코드 숨김 파일로 구성됩니다. .ascx는 컨트롤의 레이아웃을 정의하고 코드 숨김 파일은 동작을 정의합니다. 이 예제의 경우에는 서버 컨트롤을 생성하는 작업이 사용자 컨트롤을 생성하는 것보다 훨씬 쉬우므로 이 기사에서는 서버 컨트롤을 생성합니다.

이 예제와 Windows Forms 예제 간의 가장 큰 차이점은 컨트롤이 렌더링되는 방식입니다. Windows Forms 사용자 지정 컨트롤을 사용하는 경우 각 구성 컨트롤은 InitializeComponent 메서드 내에 추가됩니다. ASP.NET 컨트롤을 사용하는 경우에는 HTML 출력 렌더링을 위한 두 가지 옵션이 있습니다. 둘 중 하나는 Render 메서드를 재정의하고 HTML을 출력 스트림에 명시적으로 작성하여 컨테이너 컨트롤에 대해 모든 콘텐츠를 생성하는 것입니다. 그리고 다른 하나는 CreateChildControls 메서드 내에 추가되는 컨트롤을 활용하는 것입니다.

이 예제에서는 Render 메서드를 재정의하는 첫 번째 방법을 사용합니다. 이 예제는 간단하지만 런타임에 컨트롤을 정의하는 방법을 이해하기 위한 기본 사항을 제공합니다. 원하는 경우 추가적인 속성 및 렌더링 논리를 추가하여 예제를 확장할 수도 있습니다. 컨트롤은 다음 코드를 사용하여 런타임에 로드됩니다. Control ctrl = Activator.CreateInstance ( t ) as Control ;t.GetProperty("Text").SetValue ( ctrl, caption, new object[] { } ) ;this.controlPlaceholder.Controls.Add ( ctrl ) ;t 형식은 생성된 컨트롤을 나타냅니다. 필자는 Activator.CreateInstance를 사용하여 컨트롤을 인스턴스화했습니다. 다음 줄은 Text 속성 값을 설정하며 런타임 바인딩 예제를 보여 줍니다. 이에 대해서는 잠시 후에 설명합니다. 마지막 줄은 새로 만든 컨트롤을 페이지의 자리 표시자 컨트롤에 추가합니다.

사용자 지정 서버 컨트롤 만들기에 대한 자세한 내용을 알아보려는 분께는 Nikhil Kothari와 Vandana Datye가 함께 저술한 Developing Microsoft ASP.NET Server Controls and Components(Microsoft Press, 2002)를 추천해 드립니다. 개인적으로 이 책은 컨트롤 만들기의 모든 측면에 대해 빈틈없이 다루고 있으며 직접 서버 컨트롤을 정의하려는 경우에는 꼭 참조해야 하는 자료라고 생각합니다.

이 예제에서는 생성된 컨트롤을 디스크에 보관하지 않았습니다. 이렇게 한 데는 두 가지 이유가 있습니다. 가장 큰 이유는 컨트롤을 디스크에 보관하지 않고도 생성할 수 있음을 보여 드리기 위해서입니다. 컨트롤은 완전히 임시 상태일 수 있으므로 응용 프로그램이 다시 시작되면 삭제됩니다. 두 번째 이유는 응용 프로그램이 컨트롤을 디스크에 생성하도록 허용하는 경우 해당 응용 프로그램에 보안 결함이 발생할 수 있기 때문입니다.

응용 프로그램이 생성된 어셈블리를 디스크에 저장하는 경우 ASP.NET에는 DLL 파일을 디스크에 저장할 수 있는 권한이 있어야 합니다. 이 경우 파일을 다시 로드하여 사용자에게 데이터를 표시할 수 있습니다. 침입자가 이 디렉터리에 파일을 쓸 수 있는 권한을 얻으면 응용 프로그램에 코드를 주입할 수 있으며 그러면 심각한 결과가 발생할 수 있습니다.

컨트롤 생성을 허용하도록 응용 프로그램을 수정하는 경우에는 웹 사이트 내에서 실행하고 있지 않은 관리 도구 집합 부분을 만드는 것이 좋습니다. 예를 들면 Windows Forms 응용 프로그램 등이 있습니다. 이 도구 집합은 사용자 데이터베이스에서 컨트롤 정의를 읽고 컨트롤을 생성하여 디스크 또는 SQL Server 데이터베이스 내에 저장합니다. 그러면 ASP.NET 사이트는 이렇게 생성된 컨트롤을 런타임에 로드하면 됩니다.


데이터 바인딩

이렇게 생성된 컨트롤을 페이지에 사용할 때는 백 엔드 데이터베이스 개체의 데이터를 컨트롤로 바인딩할 수 있어야 합니다. 데이터를 UI에 바인딩하기 위해 생성된 코드를 사용할 수도 있고(권장) 리플렉션을 사용할 수도 있습니다. 이 섹션에서는 두 가지 방법을 모두 설명합니다. 이 예제에서는 기본 비즈니스 개체의 속성과 사용자 인터페이스의 컨트롤이 일대일 대응한다고 가정합니다.

이 기사의 주된 목적은 고성능의 구성 가능한 솔루션을 만들기 위한 전략을 제공하는 것이므로, 데이터 바인딩 수행을 위한 코드 내보내기는 반드시 수행해야 하는 단계입니다. 컨트롤을 생성하는 동안 쉽게 데이터 바인딩 코드를 생성할 수 있습니다. 앞에서도 언급했지만 UI 컨트롤과 기본 비즈니스 개체 간에는 직접적인 일대일 대응 관계가 성립합니다. 생성되는 코드는 다음 코드 조각에서처럼 기본적으로 각 속성을 반복하며 컨트롤의 텍스트 값을 해당 속성의 값으로 설정합니다. control.Text = businessObject.Text;ASP.NET용으로 이 코드를 내보내는 적절한 방법은 Control 클래스에 정의되어 있는 표준 DataBind 메서드로 재정의하는 것입니다.

리플렉션을 사용하여 비즈니스 논리 구성 요소의 데이터를 가져와 컨트롤에 표시할 수 있습니다. Visual Studio를 사용하는 경우 컨트롤은 Bindable 특성을 속성 정의에 추가하여 데이터 바인딩에 대해 속성을 사용 가능한 것으로 정의합니다. [Bindable(true)]public string Text { get; set; }

그러므로그림 9에 나와 있는 것과 같은 코드를 사용하여 컨트롤의 바인딩 가능한 속성을 모두 가져올 수 있습니다. 첫 번째 줄은 t 형식에 정의되어 있는 모든 공용 인스턴스 속성의 컬렉션을 요청합니다. BindingFlags.DeclaredOnly를 통해 직접 형식의 속성만 사용되도록 할 수 있습니다. BindingFlags.DeclaredOnly가 없으면 해당 형식에 대한 상속 계층 구조의 모든 속성이 반환됩니다.

그런 후에 이 코드는 이러한 속성을 반복하여 Bindable 특성을 포함하는 항목을 찾습니다. 개발자가 Bindable(false)을 사용했을 수도 있기 때문에 코드는 특히 각 속성에 대해 특성이 Bindable(true)로 정의되어 있는지 여부를 확인합니다.

이 바인딩 가능 속성 컬렉션을 사용하면그림 10과 같은 코드를 작성하여 비즈니스 개체의 값을 기반으로 컨트롤 값을 설정할 수 있습니다.

이 함수는 컨트롤의 모든 바인딩 가능 속성을 반복하면서 각 속성에 대해 이름 및 반환 형식이 같은 속성이 비즈니스 개체에 있는지 확인합니다. 이러한 속성이 있는 경우 컨트롤의 속성은 비즈니스 개체의 속성 값으로 설정됩니다. 이와 유사한 코드를 사용하여 사용자가 UI에 입력하는 데이터를 읽은 다음 해당 데이터를 비즈니스 개체로 다시 보낼 수 있습니다.


코드 단순화

이제 샘플을 통해 내보내는 코드가 상당히 구체화되었습니다. 코드를 단순화하기 위해 기본 클래스(ASP.NET용 WebControl 또는 Windows Forms용 Control에서 파생됨)를 생성하고 컨트롤을 생성하는 까다로운 작업을 수행하도록 컨트롤에 함수를 포함할 수 있습니다. 예를 들어, 다음과 같이 함수 집합을 사용하여 컨트롤을 생성하는 메서드를 정의할 수 있습니다. private Label CreateLabelControl ( string ID, string caption ){ Label newLabel = new Label ( ) ; newLabel.ID = ID; newLabel.Text = caption ; this.Controls.Add ( newLabel ) ;}

모든 형식의 컨트롤을 만들기 위한 기본 클래스 함수를 만들 수 있습니다. 그러면 내보낸 코드는 컨트롤의 인스턴스 변수를 생성하고 이러한 기본 클래스 함수를 호출하여 컨트롤을 만드는 작업을 수행하면 됩니다.


결론

이 기사에서는 두 가지 스타일의 런타임 코드 생성 방법(Reflection.Emit 및 System.CodeDom)을 사용하여 Windows Forms 및 ASP.NET 응용 프로그램용 컨트롤을 생성하는 방법에 대해 설명했습니다. 또한 예제를 통해 이와 같이 서로 다른 두 방법에 대해 사용할 수 있는 기능을 일부 설명했습니다. 이제 이 기사에서 설명한 개념을 응용 프로그램에 직접 적용해 보십시오.

현재 XML 정의를 사용하여 인터페이스 컨트롤을 생성하는 경우, 이러한 방법을 사용하는 경우의 상대적인 성능에 관한 섹션 내용을 확인하여 CodeDOM 또는 Reflection.Emit 방식으로 변경하는 방안을 고려해 보시기 바랍니다. 컨트롤을 즉시 작성함으로써 얻을 수 있는 성능 향상을 경험한다면 투자할 만한 가치가 있음을 알게 될 것입니다.



Morgan Skinner영국 Microsoft에서 C#, Windows Forms 및 ASP.NET 전문 응용 프로그램 개발 컨설턴트로 근무하고 있습니다. 그는 .NET 도입 당시부터 .NET 작업을 수행해 오고 있습니다.PSfD(Premier Support for Developers) (영문)팀 블로그를 방문해 보십시오.
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/02/01 22:06

Written by 안재우(Jaewoo Ahn), 닷넷엑스퍼트(.netXpert)

 

고객사에서 Visual Studio 2005에서의 원격 디버깅 사용법에 대한 문의가 온 김에, 내용을 좀 정리해볼까 합니다. 원격 디버깅은 상당히 유용한 기능 임에도 불구하고, 대부분의 개발자들이 좀처럼 사용하지 않는 기능입니다. 간혹 여러가지 여건 때문에 쓸 수 없는 상황에 처해 있는 경우도 있겠지만, 대다수는 사실 '몰라서' 사용하지 못하고 있는 형편입니다.

 

이번 글에서는 원격 디버깅의 개념, Visual Studio 2005에서의 원격 디버깅에 대해 알아보고, 몇가지 주의할 사항도 함께 살펴보도록 하겠습니다.

 

원격 디버깅이란?

네이버 용어 사전 설명으로는 '원격 시스템에서 사용되는 원격 제어 콘솔에서 프로그램을 수정하고 시험하는 것'이라고 합니다. 사실 이걸로는 정확하게 무슨 말이 잘 와 닿지 않을 수 있으므로, 좀 풀어서 설명해보도록 하죠.

 

일반적으로 우리가 디버깅을 시작하려면 개발툴(Visual Studio)에서 디버그 모드(F5 키)로 애플리케이션을 시작하게 됩니다. 디버그 모드로 애플리케이션을 구동하면, 당연히 애플리케이션을 호스트하는 프로세스가 만들어질테고, 프로세스는 자신이 필요한 모듈(.exe, .dll)을 로드하게 됩니다. 이 때, 디버그 모드에서는 모듈에 해당하는 디버그 심볼 파일(.pdb)을 같이 로드하게 됩니다.

 

그런데, 디버그 모드로 '시작'한다는 의미는 애플리케이션을 처음부터 새로 시작한다는 의미입니다. 그리고 디버그 모드를 '종료'하면 애플리케이션도 같이 종료되어 버립니다. 당장 코드를 개발 중일 때는 이러한 방법으로 디버깅을 할 수 있겠지만, 항상 개발을 하다보면 멀쩡하게 잘 돌아가던 애플리케이션에서 말썽이 생기는 경우가 많습니다. 즉, 이미 돌아가고 있는 애플리케이션에 대해 디버깅을 할 수 있는 방법은 없느냐는 것이죠. 이 형태의 디버깅은 In-Process 모드와 유사하다고 볼 수 있습니다.

 

여기에서 먼저 도입된 개념이 '프로세스 디버깅'입니다. Visual Studio에서는 이미 실행 중인 프로세스에 연결(Attach)시켜서 디버깅을 수행할 수 있습니다. 디버깅을 종료하면 프로세스에서 분리(Detach)시키고, 애플리케이션 프로세스 자체는 여전히 구동되게 됩니다. 이 형태의 디버깅은 Out-Of-Process 모드와 유사하다고 볼 수 있습니다.

 

원격 디버깅은 프로세스 디버깅을 로컬 컴퓨터의 바운더리를 벗어나도록 확장한 개념입니다. 즉 원격 컴퓨터에서 실행되고 있는 프로세스를 내 로컬 컴퓨터의 디버거를 사용하여 디버깅할 수 있게 해줍니다.

 

참고로 약간 변칙적인 녀석 중 하나로 실행 중인 프로그램에 심각한 오류가 발생하면 Visual Studio 디버거를 자동으로 실행할 수 있게 해주는 Just-In-Time 디버깅도 있습니다.

 

원격 디버깅이 왜 필요한가?

로컬 디버깅의 경우, 로컬에 디버거(개발툴)가 설치되어 있다는 것을 전제로 합니다. 그러나 항상 모든 컴퓨터에 디버거(개발툴)이 설치되어 있지는 않겠죠?

예를 들어, 개발자 컴퓨터에서는 이상없이 잘 돌던 프로그램이 서버나 다른 PC에서 실행하면 문제가 발생하는 경우가 있습니다. 이런 경우, 디버깅을 해보기 위해 서버나 그 PC에 디버거(개발툴)를 설치해야 할까요?

실제로 제가 경험한 몇몇 회사에서는 서버에 Visual Studio를 설치해서 디버깅을 하는 웃지 못할 형태로 작업을 하기도 했습니다. 그런데 간혹 가다보면 Visual Studio를 설치하고 나면 제대로 동작해버리는 상황도 발생하더군요. :-)

 

전통적으로 애플리케이션을 배포하고 난 후에는 디버깅이 아닌 다른 방법을 사용해서 문제점을 추적해야 합니다. 문제점 추적 방법에 대한 다양한 개발자들의 행태(?)를 살펴보면...

 

1. 초급: Hello World를 사랑하는 사람들(Windows 애플리케이션에서는 메시지 박스를, Java 스크립트에서는 alert를, ASP나 ASP.NET에서는 Response.Write를 찍어대는 사람들을 의미합니다)

2. 중급: 로그 찍기. 파일, 이벤트 로그 등에 로그를 기록합니다.

3. 고급: 기본적으로는 2번과 동일하지만, 해당 PC에 로그를 기록하는 것이 아니라 원격 로그 수집기에 로그를 기록하게 합니다. DB에 기록하거나 에러 발생 시 원격 통신으로 전송한다든지.. 성능 카운터를 활용하거나 WMI를 이용하기도 합니다.

 

로그 기록 형태가 가장 일반적이지만, 이를 통해 문제를 정확하게 파악하는 것은 디버깅에 비해 상대적으로 쉽지 않습니다. 과거에 일어난 일을 '단서'를 가지고 추정해 나가야 하기 때문입니다.

 

원격 디버깅은 이러한 상황, 즉 로그 기록만으로는 정확한 내용을 알 수 없는 경우에 사용할 수 있는 해결책 중 하나입니다. 원격 컴퓨터에 개발툴을 설치하지 않고도 원격에서 디버깅을 수행할 수 있게 해줍니다.

 

Visual Studio에서 원격 디버깅의 구동 원리

Visual Studio 2005에서 원격 디버깅이 구동되는 형태를 도식화하면 대략 다음 그림과 같습니다. 그림을 보면 이해가 갈 것이라고 판단되므로 별다른 설명은 하지 않겠습니다.

 

사용자 삽입 이미지

 

위 그림처럼 디버거 역할을 하는 로컬 컴퓨터의 VS2005와 실행 중인 프로세스(App1.exe)를 중개하는 역할은 원격 디버깅 모니터(Remote Debugging Monitor)인 msvsmon.exe가 수행합니다. 과거에 일부 원격 디버깅 시나리오에서만 유사한 역할을 수행한 Machine Debug Manager(mdm.exe)가 있었지만, 이는 더이상 사용되지 않습니다. VS2005에서는 T-SQL 디버깅을 제외한 모든 원격 디버깅 시나리오에서 msvsmon.exe가 사용됩니다. 커맨드라인의 MDM에 비해 msvsmon.exe는 GUI를 제공합니다.

 

Visual Studio에서 원격 디버깅 구성

 

위 그림을 살펴보다 보면 원격 디버깅을 하기 전에 몇가지 설정이 필요할 것이라는 점을 추측할 수 있습니다.

1. 원격 디버깅 모니터(msvsmon.exe)를 원격 컴퓨터에 설치

2. 원격 디버깅을 위한 보안 권한 설정

3. 네트워크 방화벽 설정(개인 방화벽 포함, 로컬/원격 모두)

 

1, 3번에 대한 것은 다음 MSDN 문서를 참조하기 바랍니다. 아직 한글판이 나오지 않아서 임시 URL인 것 같습니다.

http://msdn2.microsoft.com/ko-kr/library/y7f5zaaa.aspx

 

2번은 상대적으로 간단해졌는데, 과거에 권한 설정 문제가 비교적 복잡했던 것에 비해서 이제는 원격 디버깅 모니터의 Tools/Permission 메뉴에서 설정할 수 있습니다.

 

원격 디버깅 사용법

구성이 끝났으면, 이제 사용법은 간단합니다.

 

1. 원격 컴퓨터에서 원격 디버깅 모니터를 실행합니다. 다음은 실행된 모습입니다.

사용자 삽입 이미지

 

2. 디버깅할 대상이 되는 프로세스가 아직 실행되어 있지 않다면, 실행합니다. 이 프로세스와 관련 DLL들은 당연히 디버그 모드로 빌드되어 있어야 하며, 디버깅이 사용가능한 상황이어야 합니다.

 

3. 로컬 컴퓨터에서 VS 2005를 실행하고, 디버깅할 프로세스 및 DLL이 포함된 솔루션 또는 프로젝트 파일을 엽니다.

 

4. Debug 메뉴에서 Attach to Process를 선택합니다. Qualifier에 msvsmon.exe를 실행하고 있는 원격 컴퓨터의 이름(또는 IP)을 입력 후, 엔터 키 또는 Refresh 버튼을 누릅니다.

사용자 삽입 이미지

 

5. Available Processes에서 디버깅할 프로세스를 선택한 후 Attach 버튼을 누르면...

이제 디버깅 시작입니다!

 

원격 디버깅 시 주의할 사항

이제 사용법은 알았고, 원격 디버깅에는 몇가지 주의할 사항이 있습니다.

 

첫째, 기본적으로 원격 디버깅은 로그 추적/ 트레이스 등으로 해결할 수 없을 때 사용되는 보조적인 방법이라는 것입니다. 무조건 모든 문제를 원격 디버깅으로 해결하려고 드는 것은 바람직한 것이 아니라는 점입니다.

 

두번째, 디버깅을 하면서 브레이크를 걸었을 때, 실행 중인 프로세스 자체도 정지되어 있는 상태가 된다는 것입니다. 일반적인 애플리케이션이라면 별 상관없지만, 웹 애플리케이션처럼 서버 애플리케이션인 경우에는 문제가 심각해질 수 있습니다.

예를 들어 ASP.NET 애플리케이션을 원격 디버깅하고 있다고 가정해 봅시다. 내가 브레이크 포인트를 걸고 디버깅을 하고 있는 도중에는 ASP.NET 웹 애플리케이션이 다른 사용자의 요청에 응답할 수 없게 됩니다.

실제로 있었던 웃지못할 사건 중 하나는.. 모 고객사에서 실행 중인 서버에서 이상한 문제점이 발견되었는데, 로그나 트레이스를 하는 것이 아무것도 없어서 결국 원격 디버깅을 걸어서 문제점을 찾아 해결했습니다. 그러면서 원격 디버깅 사용법을 가르쳐 주고 나왔는데.. 그 담부터 그 개발자가 원격 디버깅을 지나치게 남발하면서 문제가 생겼습니다. 툭하면 서버가 먹통이 되어 버린다고 사용자들이 난리가 난거죠. 알고보니 개발자가 원격 디버깅을 걸어 둔 채 점심식사를 하러 갔다고 합니다. -_-;;

 

결론적으로 Visual Studio 2005에서 원격 디버깅은 보다 설정 및 사용법이 간편해지긴 했지만, 원격 디버깅은 어쩔 수 없는 경우에 사용하는 히든 카드(?)라는 점이며, 사용 시에는 가급적 빠른 시간 내에 작업을 마치는 것이 바람직하다는 것입니다.

마지막으로 원격 디버깅은 .NET으로 작성된 프로그램뿐만 아니라 Native, Script, T-SQL 등에 모두 사용이 가능하다는 점을 유념하시기 바랍니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2005/07/25 19:46

 

출처 :MSDN

.NET 개발자의 관점에서 파악한 Excel 개체 모델

Ken Getz
MCW Technologies, LLC

적용 대상:
    Microsoft Visual Studio Tools for the Microsoft Office System
    Microsoft Office Excel 2003
    Microsoft Visual Studio .NET 2003

요약:Microsoft Office Excel 2003에서 제공되는 개체 중 일부를 설명하고 이를 사용하여 Microsoft Visual Studio Tools for the Microsoft Office System(VSTO)와 함께 관리되는 코드 솔루션을 만드는 방법을 설명합니다. 주로 Application, Workbook, Worksheet, Range 개체를 중점적으로 다루며, Visual Basic .NET과 Visual C# 코드 샘플을 통해 각 개체의 속성, 메서드, 이벤트에 대해 설명합니다.

Microsoft 다운로드 센터 

사용자 삽입 이미지
에서 ExcelObj.exe를 다운로드하십시오.

목차

소개
Application 개체
   Excel의 상태와 표시를 제어하는 구성원
   개체를 반환하는 구성원
   작업을 실행하는 구성원
   파일 조작을 처리하는 구성원
   기타 유용한 구성원
   Application 이벤트
Workbook 클래스
   Workbook 클래스의 속성
   Document 속성 사용
   스타일 사용
   시트 사용
   Workbook 클래스의 메서드
Worksheet 클래스
   Sheet 클래스 없음
   보호 작업
   개체 속성
Range 개체
   선택 대상 관리
   코드에서 범위 참조
   기술 사용
   범위 사용
   범위 내의 데이터 정렬
다음 단계

소개

Microsoft Visual Studio Tools for the Microsoft Office System(VSTO)을 활용하거나 간단히 COM Automation을 이용하여 Microsoft Office Excel 2003 응용 프로그램을 제어하고자 하는 개발자들은 Excel 개체 모델에서 제공되는 개체들을 다룰 수 있어야 합니다. Excel은 수백 가지의 개체를 제공하고 있지만 그 중 극히 일부만 집중적으로 익혀도 개체 모델을 시작하는 데에는 충분합니다. 중요한 개체는 다음과 같습니다.

  • Application
  • Workbook
  • Worksheet
  • Range

구체적인 수치를 제시할 수는 없지만 Excel을 통해 수행하는 작업의 상당 부분은 이 4개의 클래스와 그 구성원들을 중심으로 이루어집니다. 이 문서에서는 이 4개의 클래스를 사용하는 방법을 설명하고 각각의 속성, 메서드 및 이벤트를 소개하도록 하겠습니다. 또한 각 개체의 특징 중 일부를 보여주는 예제도 제시되어 있습니다.

:   일반적으로 Microsoft Visual Basic .NET을 사용하는 개발자는 Microsoft Visual C#을 사용하는 개발자보다 Microsoft Office 개체를 쉽게 다룰 수 있는데 그 중요한 이유 중 하나는, Visual Basic forApplications(VBA) 메서드는 선택적 매개 변수를 포함하고 있는 경우가 많으며 Visual Basic .NET에서는 선택적 매개 변수를 지원한다는 점입니다. C# 개발자는 모든 선택적 메서드 매개 변수마다 반드시 값을 제공해야 하는 반면, Visual Basic .NET 개발자는 이름이 지정된 매개 변수를 사용하여 필요한 값만 제공하면 됩니다. 또한 C#에서는 인덱서가 아닌 매개 변수가 있는 속성을 지원하지 않지만 많은 Excel 속성들은 매개 변수를 허용합니다. VBA와 Visual Basic .NET에서 제공되는Application.Range등과 같은 속성들은 별도의 접근자 메서드가 있어야 C# 개발자들이 이용할 수 있습니다(get_Range메서드는Range속성을 대체합니다). 이 문서의 전반에 걸쳐 사용되는 이와 같은 두 언어 간의 차이점에 주목하시기 바랍니다.

대부분의 경우, Excel 개체 모델은 그 사용자 인터페이스를 직접 에뮬레이트합니다.Application개체는 전체 응용 프로그램을 둘러싸는 래퍼를 제공하고 각Workbook개체에는Worksheet개체의 컬렉션이 포함되어 있다는 것을 쉽게 짐작할 수 있을 것입니다. 마찬가지로 셀을 표현하는 것은 기본적으로Range개체이며 이를 통해 개별 셀 또는 셀 그룹을 다룰 수 있습니다.

아래의 각 섹션에서는 주요 Excel 개체를 하나씩, 해당 개체의 특정 구성원들을 예시하여, 설명하고 있습니다. 다룰 수 있는 개체가 수백 개에 이르므로 이 문서에서 모두를 자세히 다루는 것은 불가능합니다. 그러나 개체 모델을 시작하기에는 충분한 내용이 설명될 것이며 이 내용을 숙지한다면 더 자세한 내용은 Excel의 온라인 도움말을 통해 스스로 파악할 수 있을 것입니다.

:   이 문서 전체에서DirectCastCType메서드가 자주 사용될 것입니다. 그 이유는 이 문서에서 사용된 샘플 프로젝트에Option Strict설정이 활성화되어 있기 때문입니다. 이것은 Visual Basic .NET이 엄격한 형식 변환을 요구한다는 것을 의미합니다. 많은 Excel 메서드 및 속성은Object유형을 반환하거나 늦은 바인딩에 의존합니다. 예를 들어Application.ActiveSheet속성은 예상할 수 있듯이Worksheet와는 반대로Object를 반환합니다. 따라서 이 예제에서는 가능한한 엄격한 변환을 위해Option Strict를 활성화하고 각 유형 변환을 명시적으로 처리합니다. (Visual Basic .NET에서Option Strict를 사용하지 않고 코드를 작성해도 컴파일은 정상적으로 이루어지지만 런타임에서는 문제를 일으킬 것입니다.Option Strict를 활성화하는 것은 바로 이 점, 즉 실행 시의 잘못된 변환이 예외를 일으킬 가능성을 크게 낮춰 주기 때문입니다.) C# 개발자라면 이러한 결정을 높이 평가할 것입니다.

이 백서에서는 ExcelObjectModel.sln이라는 샘플 프로젝트를 참조하고 있습니다. 이 프로젝트에는 Excel 통합 문서 및 관련 Visual Basic .NET 코드를 포함하고 있습니다. 이 문서에 사용된 모든 예제가 샘플 프로젝트에 나와 있는 것은 아니지만 한 행 이상의 코드를 포함하고 모든 예제는 코드를 호출하도록 설정된 프로젝트 내부의 하이퍼링크와 함께 통합 문서에 들어 있습니다.

:   이 문서의 분량은 제한되어 있으므로 각각의 개체 또는 구성원을 모두 설명하는 것은 불가능합니다. 클래스들의 극히 일부분만 다루는 것조차 불가능합니다. 대규모 개체 모델을 살펴 보는 데 가장 좋은 도구는 Object Browser 창입니다. 이 창을 통해 각 클래스의 목록과 함께 해당 클래스의 구성원을 찾을 수 있습니다. 이 문서에서 다루는 구성원들 중 다수가 각기 다른 여러 클래스에 적용됩니다. 예를 들어Sheets컬렉션의 맥락에서 다루는PrintOut메서드는Chart,Worksheet,Range및 기타 개체에도 잘 적용됩니다. 이 문서의 의도는 이용할 수 있는 도구와 방법에 대한 기본 인식을 제공하고 나머지는 독자의 탐구에 맡기는 것입니다.

Application개체

ExcelApplication개체는 Excel 응용 프로그램 자체를 나타냅니다. 이것은 너무 뻔한 설명처럼 들릴 것입니다. 그러나Application개체는 실행 중인 응용 프로그램과 해당 인스턴스에 적용되는 옵션, 그리고 인스턴스에서 열려 있는 현재 사용자 개체에 관한 수많은 정보를 제공하고 있습니다.Application개체는 수많은 구성원을 제공하는데 이들 중 다수는 일일이 살펴볼 필요가 없지만 나머지 다른 구성원들은 응용 프로그램의 정확한 동작에 핵심적으로 작용할 것입니다. 이러한 구성원들을 다음과 같이 분류할 수 있습니다.

  • Excel의 상태와 표시를 제어하는 구성원
  • 개체를 반환하는 구성원
  • 작업을 실행하는 구성원
  • 파일 조작을 처리하는 구성원
  • 기타 유용한 구성원

아래 섹션에는 위 그룹들 각각에 대해 설명되어 있으며 그 일부 구성원들을 보여주는 코드 샘플이 제시되어 있습니다.

Excel의 상태와 표시를 제어하는 구성원

Application개체는 Excel의 일반적 상태를 제어하는 수많은 속성을 제공합니다.표 1에는 상태 관련Application개체 속성들 중 일부가 나열되어 있습니다.

표 1. Excel의 상태를 제어하는Application속성의 일부

속성유형설명
CursorXlMousePointer (xlDefault, xlIBeam, xlNorthwestArrow, xlWait)마우스 포인터의 모양을 가져오거나 설정합니다.
EditDirectlyInCellBoolean셀을 직접 편집할 수 있도록 가져오거나 설정합니다. False로 설정되면 수식 입력줄에서만 셀을 편집할 수 있습니다.
FixedDecimalBooleanTrue로 설정되면 모든 수치 값이FixedDecimalPlaces속성을 이용하여 소수점 자릿수를 결정하고, True로 설정되지 않으면FixedDecimalPlaces속성은 무시됩니다(기본값은 False입니다).
FixedDecimalPlacesLongFixedDecimal속성이 True인 경우 수치 데이터에 사용될 소수점 자리 수를 결정합니다.
InteractiveBoolean사용자가 키보드와 마우스를 통해 Excel을 다룰 수 있는 기능을 가져오거나 설정합니다. 이 속성을 False로 설정했다면 예외 처리기에서 반드시 True로 되돌려 놓아야 합니다. Excel에서 자동으로 재설정되지 않습니다.
MoveAfterReturnBooleanTrue로 설정된 경우 Enter를 누를 때 선택 영역이 다음 셀로 이동합니다. 기본값은 True입니다.
MoveAfterReturnDirectionxlDirection (xlDown, xlToLeft, xlToRight, xlUp)MoveAfterReturn속성이 True인 경우 Enter를 눌렀을 때 이동할 방향을 나타냅니다. 기본값은xlDown입니다.
ScreenUpdatingBooleanTrue로 설정되면 각 메서드 호출 이후 Excel에서 화면이 업데이트됩니다. 시간을 절약하고 응용 프로그램이 보다 전문적으로 보이게 하려면 코드가 실행되는 동안 표시를 끄는 것이 좋습니다. 작업이 완료되면 이 속성을 다시 True로 재설정하십시오. Excel에서 자동으로 재설정되지 않습니다.
SheetsInNewWorkbookLongExcel에서 새 통합 문서에 자동으로 배치하는 시트 수를 가져오거나 설정합니다.
StandardFontStringExcel의 기본 글꼴 이름을 가져오거나 설정합니다. Excel을 다시 시작해야 적용됩니다.
StandardFontSizeLongExcel의 기본 문서꼴 크기를 가져오거나 설정합니다. Excel을 다시 시작해야 적용됩니다.
StartupPath (read-onlyStringExcel 시작 추가 기능이 들어 있는 폴더의 전체 경로를 반환합니다.
TemplatesPath (read-only)String서식 파일이 들어 있는 폴더의 전체 경로를 반환합니다. 이 값은 Windows 특수 폴더 중 하나를 나타냅니다.

표 1에 제시된 속성 가운데 가장 많이 사용하게 될 속성은ScreenUpdating속성일 것입니다. 이 속성을 사용하면 Excel 응용 프로그램을 더 전문적으로 보이게 할 수 있을 뿐만 아니라 더 빨리 실행되도록 할 수 있습니다. 매번 수정할 때마다 표시를 업데이트하는 것은 특히 프로그래밍 방식으로 큰 범위를 채울 때 코드에 큰 부담을 줄 수 있습니다. 그러나 Excel에서 자동으로 이 속성을 재설정하지 않으므로 작업이 끝나면 항상 이 속성을 설정해야 합니다. 따라서ScreenUpdating속성을 사용할 때는 .NET 예외 처리 기능을 활용하여 화면 업데이트가 재수행될 수 있도록 항상 아래 코드와 유사한 코드를 사용해야 합니다.

' Visual BasicTry    ThisApplication.ScreenUpdating = False    ' Do your work that updates the screen.Finally    ThisApplication.ScreenUpdating = TrueEnd Try// C#try{    ThisApplication.ScreenUpdating = false;    // Do your work that updates the screen.}finally{    ThisApplication.ScreenUpdating = true;}

Application 개체는 또한 Excel의 표시를 제어하는 여러 속성을 제공합니다. 이 속성들을 수정하여 사용자들이 화면에서 보는 내용을 변경할 수 있습니다.표 2에는 사용할 수 있는 표시 옵션의 일부가 나열되어 있습니다.

표 2. Excel의 모양을 제어하는 Application 속성의 일부

속성유형설명
DisplayAlertsBooleanTrue(기본값)로 설정되면 필요한 경우(예: 시트를 삭제하는 경우) 코드가 실행되는 중에 경고 메시지가 표시됩니다. False로 설정하면 경고가 표시되지 않습니다. Excel은 각 경고에 기본값이 선택된 것처럼 동작합니다.
DisplayFormulaBarBooleanTrue(기본값)로 설정되면, 셀 편집을 위한 일반적인 수식 입력줄이 표시되고 False로 설정되면 수식 입력줄이 사라집니다.
DisplayFullScreenBooleanTrue로 설정되면 Excel은 전체 화면 모드(단순히 Excel 창을 극대화하는 것과는 다른 효과)로 실행됩니다. 기본값은 False입니다.
:   ScreenUpdating속성과 마찬가지로DisplayAlerts속성도 작업 후 반드시 다시 설정해야 합니다. Excel에서 자동적으로 재설정되지 않으므로 이 속성이 False로 설정되어 있으면 통합 문서를 닫을 때 저장하라는 메시지가 표시되지 않기 때문에 반드시DisplayAlerts속성을 재설정해야 합니다. 그렇지 않으면 데이터를 잃게 될 수도 있습니다.

개체를 반환하는 구성원

많은Application개체의 속성들이 다른 개체를 반환합니다. Visual Studio .NET에서 제공하는 표준 Microsoft Office 프로젝트 서식 파일에는ThisApplicationThisWorkbook개체만 들어 있으므로 일반적으로Application클래스의 개체 구성원을 활용하여 Excel이 제공하는 다른 개체를 참조해야 할 것입니다.Chart와 같은 속성을 사용하는 개체의 컬렉션 또는ActiveWindow와 같은 속성을 사용하는 특정 자식 개체에 대한 참조를 검색하는 데 이러한 구성원들을 이용할 수 있습니다.표 3에는Application개체의 개체 반환 속성의 일부가 나열되어 있습니다.

표 3.Application개체의 사용 가능한 개체 반환 속성 집합

속성유형설명
ActiveCellRange활성 창(맨 위에 있는 창)의 현재 활성 셀에 대한 참조를 반환합니다. 활성 창이 없으면 이 속성은 오류를 일으킵니다.
ActiveChartChart현재 활성 차트에 대한 참조를 반환합니다. 포함된 차트는 선택되거나 활성화되는 경우에만 활성 차트로 간주됩니다.
ActiveSheetObject활성 통합 문서의 활성 시트에 대한 참조를 반환합니다.
ActiveWindowWindow활성 창(맨 위에 있는 창)에 대한 참조를 반환하며 활성 창이 없는 경우에는 아무 것도 반환하지 않습니다.
ChartsSheets활성 창의 각 차트에 대한 참조가 들어 있는Sheet개체(ChartWorksheet개체의 부모 개체)의 컬렉션을 반환합니다.
SelectionObject응용 프로그램 내에서 선택된 개체를 반환합니다. 대체로Range,Worksheet또는 다른 개체가 되며Window클래스에도 적용됩니다. 이 경우에는 일반적으로Range개체가 됩니다. 현재 선택된 개체가 없으면 아무 것도 반환하지 않습니다.
SheetsSheets활성 통합 문서의 각 시트에 대한 참조가 들어 있는Sheet개체의 컬렉션을 반환합니다.
WorkbooksWorkbooks열려 있는 모든 통합 문서에 대한 참조가 들어 있는Workbook개체의 컬렉션을 반환합니다.

실제 사용에서는,Application클래스의Workbooks속성을 가장 자주 다루게 될 것입니다. 이 속성을 이용하면 열려 있는 문서를 여러 번 반복하거나 기존 통합 문서를 열거나 새로 만들 수 있습니다. 아래 섹션에는 이 속성의 동작이 설명되어 있습니다.

Workbooks 컬렉션

Workbooks컬렉션은 열려 있는 모든 통합 문서를 다루고, 새 통합 문서를 만들고 새 통합 문서에 데이터를 가져올 수 있게 합니다. 아래 목록은Workbooks컬렉션의 주요 사용 방법 중 일부를 보여줍니다.

  • 새 통합 문서 만들기: 아래와 같은 코드를 이용합니다(Add메서드에 대한 매개 변수로서 통합 문서 서식 파일의 이름을 지정할 수도 있습니다).
    ' Visual BasicDim wb As Excel.Workbook = ThisApplication.Workbooks.Add()// C#Excel.Workbook wb = ThisApplication.Workbooks.Add(Type.Missing);
  • 열려 있는 모든 통합 문서 닫기: 대부분의 컬렉션과 달리, 이 컬렉션을 이용하면 그 구성원을 모두 한 번에 닫을 수 있습니다. 아래의 메서드 호출은 열려 있는 모든 통합 문서를 닫습니다.
    ' Visual BasicThisApplication.Workbooks.Close()// C#ThisApplication.Workbooks.Close();
  • 기존 통합 문서 열기:Workbooks컬렉션의Open메서드를 사용합니다. 가장 단순한 형태로는 아래 코드에서와 같이Open메서드를 사용합니다. Open 메서드는 특정 상황에서 그 동작에 영향을 미치는 수많은 선택적 매개 변수를 제공하지만 일반적으로는 선택적 매개 변수가 필요하지 않을 것입니다.
    ' Visual BasicDim wb As Excel.Workbook = _  ThisApplication.Workbooks.Open("C:\YourPath\YourWorkbook.xls")// C#Excel.Workbook wb = ThisApplication.Workbooks.Open(     "C:\\YourPath\\Yourworkbook.xls",     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing);
    :   C# 개발자라면 메서드 호출에서 Type.Missing 값에 대한 참조를 보는 데 익숙해져야 합니다. Excel 개체 모델은 VBA를 고려하여 작성되었으므로 그 메서드 중 다수가 최대 30개까지의 선택적 매개 변수를 허용합니다. C# 개발자는 Type.Missing 값의 인스턴스를 많이 사용하거나 각 개별 매개 변수에 특정 기본값을 제공해야 합니다.
  • 텍스트 파일, 데이터베이스 또는 XML 파일을 통합 문서로 열기:OpenText,OpenDatabase또는OpenXml메서드를 사용합니다. 이 메서드들은 상당한 융통성을 제공하므로, 간단한 적용 예만 다루더라도 이 문서에서 할당할 수 있는 분량을 넘을 것입니다. 현재로서는 이러한 메서드들이 있다는 사실을 아는 것으로 충분합니다. 이러한 항목을 Excel에 로딩해야 하는 경우가 발생했을 때 더 자세히 알아 볼 수 있을 것입니다. 아래 코드에서는 텍스트 파일을 통합 문서로 로딩하면서 세 번째 행부터 쉼표를 구분자로 사용하고 있습니다.
    ' Visual BasicDim wb as Excel.Workbook = _    ThisApplication.Workbooks.OpenText("C:\Test.txt", StartRow:=3, _    DataType:=xlDelimited, Comma:=True)// C#Excel.Workbook wb =     ThisApplication.Workbooks.OpenText("C:\\Test.txt",     Type.Missing, 3, Excel.XlTextParsingType.xlDelimited,     Excel.XlTextQualifier.xlTextQualifierDoubleQuote,     Type.Missing, Type.Missing, Type.Missing, True,     Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing);
  • 개별 통합 문서 참조:정수(컬렉션 내부의 위치를 나타냄) 또는 통합 문서 이름을 사용하여Workbooks컬렉션에 인덱스를 넣을 수 있습니다. 그러나 이름을 기준으로 통합 문서를 참조하고자 한다면 참조 방법에 유의해야 합니다. 파일을 저장할 때까지는 반드시 제목 표시줄의 ".xls" 확장명을 포함하지 않는 이름을 사용해야 합니다.
    ' Visual BasicDim wb As Excel.Workbook = ThisApplication.Workbooks(1)' Before Book1 is saved:wb = ThisApplication.Workbooks("Book1")' After Book1 is saved:wb = ThisApplication.Workbooks("Book1.xls")// C#Excel.Workbook wb = ThisApplication.Workbooks[1];// Before Book1 is saved:wb = ThisApplication.Workbooks["Book1"];// After Book1 is saved:wb = ThisApplication.Workbooks["Book1.xls"];
    :   특정 통합 문서를 참조할 때는 기본 인덱서인Item속성을 이용하게 됩니다. Item 속성 외에도Workbooks컬렉션은, Microsoft Office에서 제공되는 모든 컬렉션과 마찬가지로 컬렉션에 있는 항목(이 경우에는Workbooks)의 수를 반환하는Count속성을 포함하고 있습니다.

작업을 실행하는 구성원

Application개체는 현재 데이터 재계산에서부터 데이터에 대한 변경 취소에 이르는 작업을 수행할 수 있도록 해주는 여러 메서드를 제공합니다. 아래 목록에는Application개체의 메서드 중 일부를 각각 예제를 통해 설명하고 있습니다. 이 섹션의 예제는 샘플 통합 문서의Application Object시트에 나와 있습니다.

  • Calculate: 열려 있는 모든 통합 문서, 특정 통합 문서, 또는 특정 범위의 재계산을 강제합니다.
    ' Visual BasicThisApplication.Calculate' Or...ThisWorkbook.Calculate' Or...ThisApplication.Range("SomeNamedRange").Calculate// C#ThisApplication.Calculate();// Or...ThisWorkbook.Calculate();// Or...ThisApplication.get_Range("A1", "B12").Calculate();
    참고:   샘플 코드에 나와 있는 것처럼RangeWorksheet개체도Calculate메서드를 제공합니다. 다시 계산하려는 가장 작은 수의 셀로 계산 범위를 제한하는 개체의 메서드를 사용하십시오. Excel의 재계산 엔진은 매우 빠르지만 포함되는 셀의 수를 제한하면 작업을 더욱 최적화할 수 있습니다. 열려 있는 모든 통합 문서에서 대기 중인 모든 변화를 다시 계산하려는 경우에만Application.Calculate를 사용하십시오.
    :   Visual Basic .NET과 C#에서 Excel 구성원을 처리하는 방식은 서로 약간 다른 점이 있습니다. 예를 들어 Excel, VBA 및 Visual Basic .NET의Range속성은 C#에서는get_Range메서드를 이용해야만 액세스할 수 있습니다. 이 문서에는 이에 대한 몇 개의 예제와 접근자 구성원에 대한 몇 개의 예제가 제시되어 있습니다.
  • CheckSpelling: 제공된 매개 변수의 맞춤법이 정확한지 나타내는Boolean을 반환합니다. 대/소문자 무시 여부를 지시하는Boolean과 사용자 지정 사전의 이름을 선택적으로 제공할 수도 있습니다. 아래 코드는 제공된 값의 맞춤법을 확인하고 그 결과를 시트에 나타냅니다.
    ' Visual BasicPrivate Sub TestSpelling()    Dim rng As Excel.Range = _      ThisApplication.Range("CheckSpelling")    Dim strOut As String    If ThisApplication.CheckSpelling( _      rng.Offset(0, 1).Value.ToString) Then        strOut = "Spelled correctly"    Else        strOut = "Spelled incorrectly"    End If    rng.Offset(0, 2).Value = strOutEnd Sub// C#private void TestSpelling(){    // If you specify only a named range in the call     // to get_Range, use Type.Missing for the second parameter.    Excel.Range rng = ThisApplication.    get_Range("CheckSpelling", Type.Missing);    // 참고: that C# requires you to retrieve and set    // the Value2 property of the Range, rather than     // the Value property, because the Value property     // is parameterized, making it unavailable to C# code:    rng.get_Offset(0, 2).Value2 =         (ThisApplication.CheckSpelling(    rng.get_Offset(0, 1).Value2.ToString(),         Type.Missing, Type.Missing)     ? "Spelled correctly"     : "Spelled incorrectly");}
    :   위 코드에서는Range개체의Offset메서드를 사용했는데, 둘 중 어느 것도 아직 실제로는 접해보지 못했을 것입니다. 이 둘 모두 이 문서의 후반부에Range개체를 설명한 섹션에 설명되어 있습니다.Range클래스의 사용 방법은 이해하기 쉽습니다.Range개체는 셀 또는 셀 그룹을 나타냅니다. 이 경우Range개체는 이름이 지정된 범위CheckSpelling을 참조합니다.Offset속성은 연결된Range의 왼쪽 맨 위부터 지정된 수의 행과 열에 있는Range개체를 반환하며 알려진 위치와 관련된 셀을 작업할 수 있게 해줍니다.
  • Evaluate: Excel 이름을 실제 참조 또는 값으로 변환합니다. 이 메서드를 이용하면 문자열로 참조를 만든 다음 필요에 따라 이를 실제 개체 참조로 변환하거나 해당 표현식의 값을 계산합니다. 아래 샘플 코드에서는 셀 주소를 샘플 시트에 입력하고 지정한 주소의 셀에 텍스트를 입력합니다.
    ' Visual BasicPrivate Sub TestEvaluate()    Dim rng As Excel.Range = _      ThisApplication.Range("Evaluate")    Try        Dim rngNew As Excel.Range = _          ThisApplication.Evaluate( _          DirectCast(rng.Offset(0, 1).Value), Excel.Range)        rngNew.Value = "Hello, World!"    Catch ex As Exception        MessageBox.Show(ex.Message, ThisApplication.Name)    End TryEnd Sub// C#private void TestEvaluate(){    Excel.Range rng = ThisApplication.        get_Range("Evaluate", Type.Missing);    try     {        Excel.Range rngNew =             (Excel.Range) ThisApplication.Evaluate(            rng.get_Offset(0, 1).Value2);        rngNew.Value2 = "Hello, World!";    }     catch (Exception ex)    {        MessageBox.Show(ex.Message, ThisApplication.Name);    }}
  • MailSystem, MailSession, MailLogoff, MailLogon, SendMail: 이 구성원들을 이용하면 설치된 전자 메일 시스템에 로그온하여 현재 통합 문서를 첨부 파일로 전송하고 로그오프할 수 있습니다.MailSystem속성은 설치된 전자 메일 시스템을 나타내며MailSession속성은 현재 전자 메일 세션에 대한 참조를 반환합니다(활성 세션이 있는 경우에는 로그온할 필요가 없습니다). 아래 예제는 샘플 통합 문서를 간단한 전자 메일 메시지의 파일로 첨부하여 전송합니다.
    ' Visual BasicPrivate Sub TestEmail()    If ThisApplication.MailSystem = Excel.XlMailSystem.xlMAPI Then        If ThisApplication.MailSession Is Nothing Then            Dim frm As New SendMail            If frm.ShowDialog = DialogResult.OK Then              ThisApplication.MailLogon( _              frm.EmailName, frm.EmailPassword, frm.DownloadNewMail)            End If        End If        Dim strEmail As String = _         ThisApplication.Range("SendMail").Offset(0, 1). _         Value.ToString        ThisWorkbook.SendMail(strEmail, "Sample Excel Email")        ThisApplication.MailLogoff()    Else        MessageBox.Show( _         "This demonstration works only if MAPI is installed.")    End IfEnd Sub// C#private void TestEmail(){    if (ThisApplication.MailSystem ==         Excel.XlMailSystem.xlMAPI )     {        if ( ThisApplication.MailSession == null )         {            SendMail frm = new SendMail();            if (frm.ShowDialog() == DialogResult.OK )            {                ThisApplication.MailLogon(frm.EmailName,                     frm.EmailPassword, frm.DownloadNewMail);            }        }        string  strEmail = ThisApplication.            get_Range("SendMail", Type.Missing).            get_Offset(0, 1).Value2.ToString();        ThisWorkbook.SendMail(strEmail,             "Sample Excel Email", Type.Missing);        ThisApplication.MailLogoff();    }     else     {        MessageBox.Show("This demonstration works only if " +             "MAPI is installed.");    }}
    참고:   Workbook클래스는SendMail메서드를 제공합니다. 전자 메일로 보낼 수 있는 가장 세부적 단위의 개체가 통합 문서 자체이기 때문입니다.SendMail메서드는 발송되는 메시지에 텍스트를 첨부할 수 있는 방법을 제공하지 않으며 주소 지정 방식에도 큰 유연성이 없다는 것을 알게 될 것입니다. 이 구성원들은 통합 문서를 전자 메일을 통해 간단히 전송하려는 목적으로만 제공됩니다. 모든 기능을 지원 받으려면 전자 메일을 다른 방식으로 이용할 수 있는 수단을 찾아야 합니다. 또한 현재 전자 메일 시스템에 온라인으로 연결되어 있지 않으면 위의 샘플 코드는 올바로 작동하지 않습니다.MailSession속성이 '없음(Nothing)'을 반환하면 이러한 오류를 피해 메일을 발송하지 않도록 할 수 있습니다.
  • Quit:프로그래밍 방식으로 Excel을 종료할 수 있습니다.DisplayAlerts속성을 False로 설정했다면 저장되지 않은 데이터를 저장하라는 메시지가 표시되지 않습니다. 또한WorkbookSaved속성을 True로 설정하면 실제 변경 여부에 관계 없이, 변경 내용을 저장하라는 메시지가 표시되지 않습니다.
    ' Visual BasicThisApplication.Quit// C#ThisApplication.Quit();
  • Undo: 사용자 인터페이스에서 사용자가 수행한 마지막 작업을 취소합니다. 이 메서드는 코드에서 수행된 작업에는 아무 효과가 없으며 1회의 작업만 취소할 수 있습니다. 굉장히 기능적인 메서드는 아니지만 코드를 실행하기 전에 사용자가 수행한 마지막 작업을 취소할 수는 있습니다.
    ' Visual BasicThisApplication.Undo// C#ThisApplication.Undo();

파일 조작을 처리하는 구성원

Application개체는 Excel 응용 프로그램의 맥락에서 파일 시스템을 다룰 수 있게 해주는 몇 가지 구성원을 제공합니다. 아래 섹션에는 자주 사용하게 될 구성원 일부가 설명되어 있습니다. (이 섹션에서 설명된 예제는 샘플 통합 문서의Application File Handling시트에 있습니다.)sheet of the sample workbook.)

DefaultFilePath 속성

아래의 간단한 속성은 Excel에서 파일 로딩 및 저장에 사용되는 경로를 가져오거나 설정합니다.

' Visual Basic' When the workbook opens:ThisApplication.Range("DefaultFilePath").Value = _  ThisApplication.DefaultFilePath' When you save the DefaultFilePath property:ThisApplication.DefaultFilePath = _  ThisApplication.Range("DefaultFilePath"). _  Value.ToString// C#// When the workbook opens:ThisApplication.get_Range("DefaultFilePath", Type.Missing).    Value2 = ThisApplication.DefaultFilePath;// When you save the DefaultFilePath property:ThisApplication.DefaultFilePath =     ThisApplication.get_Range("DefaultFilePath", Type.Missing).    Value2.ToString();

DefaultSaveFormat 속성

이 속성은 통합 문서를 저장하는 기본 서식을 가져오거나 설정합니다. Excel은 이 속성에 많은 옵션을 제공하는데 모두XlFileFormat열거의 구성원입니다. 이 샘플 통합 문서에서는그림 1과 같이 제공된 항목들을 선택할 수 있습니다. 아래 코드는 해당 예제가 속성의 값을 어떻게 로딩하고 저장하는지 보여줍니다.

이 예제에서 샘플 시트의 열 E에는XlFileFormat열거에 가능한 모든 값의 이름 목록이 들어 있으며(이름이 "XlFileFormat"인 범위) 열 F에는 그에 해당하는 정수 값이 들어 있습니다.그림 2에는 이 두 열의 일부가 나와 있습니다. DefaultSaveFormat이라고 이름이 지정된 범위(그림 1)에는 XlFileFormat 범위에 대한 참조가 들어 있어 목록에서 선택할 수 있습니다. 선택을 하고 그 값을 저장하기로 결정했으면 코드에서는Range.Find메서드를 사용하여 선택된 문자열을 찾은 다음,Range.Offset메서드를 사용하여 찾은 값으로부터 지정된 오프셋의 값을 반환해야 합니다. (Range.Find메서드에 대한 자세한 내용은 이 문서 뒤쪽의 "범위 내의 검색"이라는 섹션을 참조하십시오.) 마지막으로 이 코드에서는 정수 값(적절한 열거 유형으로 변환)을 다시DefaultSaveFormat속성으로 저장합니다.

DefaultSaveFormat의 현재 값은 쉽게 검색할 수 있습니다. 아래 코드는 그 값을 텍스트로 변환하고 샘플 시트의 정확한Range에 표시합니다.

' Visual Basic' When the workbook opens, convert the enumerated value ' into a string:ThisApplication.Range("DefaultSaveFormat").Value = _  ThisApplication.DefaultSaveFormat.ToString// C#// When the workbook opens, convert the enumerated value // into a string:ThisApplication.get_Range("DefaultSaveFormat", Type.Missing).    Value2 = ThisApplication.DefaultSaveFormat.ToString();

선택된 값을 다시 할당하는 것은 좀 더 어렵습니다. 이 과정은 세 단계로 구성됩니다. 코드에서는 다음과 같은 작업을 처리해야 합니다.

시트의 DefaultSaveFormat 범위에서 선택된 저장 형식의 이름을 검색합니다.

' Visual Basic' Retrieve the name of the new save format, as a string:Dim strSaveFormat As String = _  ThisApplication.Range("DefaultSaveFormat"). _  Value.ToString()// C#// Retrieve the name of the new save format,// as a string:string strSaveFormat = ThisApplication.  get_Range("DefaultSaveFormat", Type.Missing).  Value2.ToString();

시트의 XlFileFormat 범위에 인접한 열에서 일치하는 정수 값을 찾아 Range 클래스의 Find 메서드를 호출합니다. 그런 다음Range.Offset속성을 사용하여 한 열 오른쪽에 있는 값을 조회합니다.

' Visual BasicDim intSaveFormat As Integer = _  CType(ThisApplication.Range("XlFileFormat"). _  Find(strSaveFormat).Offset(0, 1).Value, Integer)// C#Excel.Range rng = ThisApplication.    get_Range("xlFileFormat", Type.Missing);Excel.Range rngFind = rng.Find(strSaveFormat,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Excel.XlSearchDirection.xlNext, Type.Missing, Type.Missing,     Type.Missing);// In C#, use the get_Offset method instead of the Offset property:int intSaveFormat =     Convert.ToInt32(rngFind.get_Offset(0, 1).Value2);

정수 값을 DefaultSaveFormat 속성으로 다시 할당합니다.

' Visual BasicThisApplication.DefaultSaveFormat = _  CType(intSaveFormat, Excel.XlFileFormat)// C#ThisApplication.DefaultSaveFormat =   Excel.XlFileFormat) intSaveFormat;

사용자 삽입 이미지

그림 1. 제공된 유형 목록에서 파일 형식을 선택합니다.

사용자 삽입 이미지

그림 2. 샘플 워크시트의 XlFileFormat 범위의 일부

RecentFiles 속성

RecentFiles속성은 '파일' 메뉴의 최근 사용된 파일 목록에 표시되는 파일의 이름이 모두 들어 있는 문자열 컬렉션을 반환합니다. 사용자가 보유하기로 선택한 파일 수에 따라 목록 길이는 달라집니다. 샘플 통합 문서는 열릴 때 이 프로시저를 호출하여 샘플 통합 문서에 있는 RecentFiles라는 이름의 범위로 최근 파일 목록을 복사합니다.

' Visual BasicPrivate Sub ListRecentFiles()    Dim i As Integer    Dim rng As Excel.Range = DirectCast( _      ThisApplication.Range("RecentFiles"). _      Cells(1, 1), Excel.Range)    For i = 1 To ThisApplication.RecentFiles.Count        rng.Offset(i - 1, 0).Value = _          ThisApplication.RecentFiles(i).Name    NextEnd Sub// C#private void ListRecentFiles(){    Excel.Range rng = (Excel.Range)ThisApplication.        get_Range("RecentFiles", Type.Missing).Cells[1, 1];    for (int i = 1; i <= ThisApplication.RecentFiles.Count; i++)    {        rng.get_Offset(i - 1, 0).Value2 =             ThisApplication.RecentFiles[i].Name;    } }

FileDialog 속성

FileDialog속성은 4가지 유형으로 파일 조작을 다루는FileDialog개체를 반환합니다. 이 속성이 반환하는FileDialog개체를 이용하면 다음과 같은 작업을 할 수 있습니다.

  • 파일을 선택하고 엽니다.
  • 파일 위치를 선택하고 현재 통합 문서를 저장합니다.
  • 폴더를 선택합니다.
  • 파일 이름을 선택합니다.

이 대화 상자를 이용하면 Microsoft Office에서 제공되는 모든 파일 처리 기능을 사용할 수 있습니다.FileDialog속성을 이용하려면msoFileDialogType열거값인msoFileDialogFilePicker, msoFileDialogFolderPicker, msoFileDialogOpen또는msoFileDialogSaveAs중 하나를 전달하여 이 대화 상자의 특정 용도를 선택해야 합니다. 그러면 이 속성이 반환하는FileDialog개체를 다룰 수 있습니다.

FileDialog개체는 다른 많은 개체와 마찬가지로Microsoft.Office.Core네임스페이스에서 제공됩니다. 각 Office 개체마다 전체 경로를 입력하지 않도록, 샘플 프로젝트에서는 Imports 또는 using 문으로 이 네임스페이스를 가져옵니다. 이 문서의 코드에서는 파일에 적절한 네임스페이스 참조를 추가한 것으로 가정합니다.

' Visual Basic Imports Office = Microsoft.Office.Core// C#using Office = Microsoft.Office.Core;

FileDialog개체의Show메서드는 대화 상자를 표시하며 '확인'을 누른 경우에는 -1을, '취소'를 누른 경우에는 0을 반환합니다.msoFileDialogOpen또는msoFileDialogSaveAs열거값을 사용했다면 실제로 파일을 열거나 저장하기 위해Execute메서드를 사용할 수 있습니다.SelectedItems속성에는 문자열 컬렉션이 들어 있는데 각각이 선택된 파일 이름을 나타냅니다.

예를 들어, 샘플 통합 문서에 있는 아래와 같은 코드에서는 새 통합 문서를 열라는 메시지를 표시합니다. 이 코드는 다중 선택을 허용하고, 제공되는 필터 목록을 지우며, 2개의 새로운 필터를 추가하고, 대화 상자를 표시합니다(그림 3참조). 파일을 선택하면,FileDialog개체의Execute메서드가 호출되어 요청된 파일을 엽니다.

' Visual BasicWith ThisApplication.FileDialog( _  Office.MsoFileDialogType.msoFileDialogOpen)    .AllowMultiSelect = True    .Filters.Clear    .Filters.Add "Excel Files", "*.xls;*.xlw"    .Filters.Add "All Files", "*.*"    If .Show <> 0 Then        .Execute    End IfEnd With// C#dlg = ThisApplication.get_FileDialog(    Office.MsoFileDialogType.msoFileDialogOpen);dlg.Filters.Clear();dlg.Filters.Add("Excel Files", "*.xls;*.xlw", Type.Missing);dlg.Filters.Add("All Files", "*.*", Type.Missing);if(dlg.Show() != 0)    dlg.Execute();

사용자 삽입 이미지

그림 3. FileDialog 클래스를 이용하여 일반적인 '파일 열기' 대화 상자 표시하기

예제에 포함된 아래의 코드는 대화 상자를 이용하여 폴더를 선택하는 방법을 보여줍니다.

' Visual BasicWith ThisApplication.FileDialog( _  Office.MsoFileDialogType.msoFileDialogFolderPicker)    If .Show <> 0 Then        ThisApplication.Range("FolderPickerResults"). _          Value = .SelectedItems.Item(1)    End IfEnd With// C#dlg = ThisApplication.get_FileDialog(    Office.MsoFileDialogType.msoFileDialogFolderPicker);if (dlg.Show() != 0){    ThisApplication.get_Range("FolderPickerResults", Type.Missing).        Value2 = dlg.SelectedItems.Item(1);}
참고:   Application개체는 열려는 파일 이름을 선택할 수 있는GetOpenFileNameGetSaveAsFileName메서드도 제공합니다. 이 메서드를 이용할 수도 있으나 Microsoft .NET Framework에서 제공되는OpenFileDialogSaveFileDialog컨트롤이 더 사용하기 쉽고 기능도 풍부합니다.

기타 유용한 구성원

Application개체는WorksheetFunction속성,Names컬렉션 및Windows컬렉션과 같이 다른 범주에 포함되지 않는 일부 구성원을 제공합니다. 다음 섹션은 이러한 구성원에 대한 설명입니다.

WorksheetFunction 클래스

Application개체는WorksheetFunction클래스의 인스턴스를 반환하는WorksheetFunction속성을 포함합니다. 이 클래스는 각각 Excel 워크시트 함수를 래핑하는 여러 공유된/정적 메서드를 제공합니다. 이 각각의 메서드는 VBA에서는 따로 제공되지 않는 여러 Excel 스프레드시트 계산 함수 중 하나를 제공합니다. 일부 구성원은 Visual Basic .NET 및 C# 연산자 및 메서드에서 중복됩니다. 따라서And메서드와 같이 중복되는 구성원은 거의 사용되지 않습니다.

WorksheetFunction클래스의 메서드에는 흥미롭고 유용한 기능이 많이 있습니다. 다음 목록의 요약 내용을 참조하십시오.

  • Acos, Acosh, Asin, Asinh, Cosh, Degrees, Ln, Log, Median, Max, Min, Mode, Radians등의수치 연산 함수
  • DAverage, DCount, DCountA, DGet, DMax, DMin, DProduct, DSum등의 범위 계산을 수행하는도메인 함수
  • IsErr, IsError, IsLogical, IsNA, IsNonText, IsNumber, IsText등의논리 함수
  • BetaDist, BinomDist, ChiTest, ChiInv, LogNormDist, NegBinomDist, Pearson, SumProduct, SumSq, TDist, TTest, Var, VarP등의통계 함수
  • And, Or, Choose와 같이 .NET Framework에서 거의 사용되지 않는스프레드시트 함수
  • 태국어 관련 함수: BahtText, IsThaiDigit, ThaiDayOfWeek, ThaiDigit, ThaiMonthOfYear, ThaiNumSound, ThaiNumString, ThaiStringLength, ThaiYear, RoundBahtDownRoundBahtUp과 같이 태국 숫자, 달력 및 통화를 조작하는 함수(일부 소문에는 Excel 팀이 태국 음식을 좋아하여 태국 음식점에서 음식 값을 계산하기 쉽도록 이런 함수를 추가했다는 소문이 있지만, 이는 사실이 아닙니다.)

Visual Studio .NET 프로젝트에서는WorksheetFunction클래스를 쉽게 활용할 수 있습니다. 프로젝트 서식 파일은ThisApplication개체를 제공하기 때문에 사용자는 해당 개체의WorksheetFunction속성을 쉽게 참조할 수 있습니다. 샘플 응용 프로그램에는그림 4와 같이 클래스의 일부 구성원을 테스트하는Other Application Members라는 시트가 있습니다.

참고:   WorksheetFunction클래스 및 그 구성원은 대표적으로 Visual Basic에서 Excel 개체를 사용하는 것이 C#에서 동일한 코드를 사용하는 것보다 쉬운 이유를 설명해 줍니다. 여러WorksheetFunction클래스 메서드에서 C# 개발자는 30개의 매개 변수를 대부분 빈 값으로 전달해야 합니다. 여러 메서드 그룹에서 필요한 매개 변수 개수별로 각 메서드 그룹을 구분하는 래퍼를 작성하면 이러한 부담을 크게 줄일 수 있습니다. 이 문서의 코드에서는 래퍼 메서드 없이 "naked" 메서드를 호출합니다. 어찌되었건 간에 C# 코드는 확실히 효율이 떨어집니다.

Demonstrate WorksheetFunction링크를 클릭하면 다음 코드가 실행됩니다. (Sort메서드에 대한 자세한 내용은 "범위 내의 데이터 정렬" 섹션을 참조하십시오.)

' Visual BasicPrivate Sub TestWorksheetFunction()    Dim ws As Excel.Worksheet = _      DirectCast(ThisWorkbook.ActiveSheet, Excel.Worksheet)    Dim rng As Excel.Range = ws.Range("RandomNumbers")    Dim rnd As New System.Random    Dim i As Integer    For i = 1 To 20        ws.Cells(i, 2) = rnd.Next(100)    Next i    rng.Sort(rng, _      Orientation:=Excel.XlSortOrientation.xlSortColumns)    With ThisApplication.WorksheetFunction        ws.Range("Min").Value = .Min(rng)        ws.Range("Max").Value = .Max(rng)        ws.Range("Median").Value = .Median(rng)        ws.Range("Average").Value = .Average(rng)        ws.Range("StDev").Value = .StDev(rng)    End WithEnd Sub// C#private void TestWorksheetFunction() {    Excel.Worksheet ws = (Excel.Worksheet) ThisWorkbook.ActiveSheet;    Excel.Range rng = ws.get_Range("RandomNumbers", Type.Missing);    System.Random rnd = new System.Random();    for ( int i = 1 ; i <= 20; i++)        ws.Cells[i, 2] = rnd.Next(100);    rng.Sort(rng, Excel.XlSortOrder.xlAscending,         Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending,         Type.Missing, Excel.XlSortOrder.xlAscending,         Excel.XlYesNoGuess.xlNo, Type.Missing,Type.Missing,         Excel.XlSortOrientation.xlSortColumns,         Excel.XlSortMethod.xlPinYin,         Excel.XlSortDataOption.xlSortNormal,         Excel.XlSortDataOption.xlSortNormal,         Excel.XlSortDataOption.xlSortNormal);    Excel.WorksheetFunction wsf = ThisApplication.WorksheetFunction;    ws.get_Range("Min", Type.Missing).Value2 = wsf.Min(rng,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing);    ws.get_Range("Max", Type.Missing).Value2 = wsf.Max(rng,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing);    ws.get_Range("Median", Type.Missing).Value2 = wsf.Median(rng,        Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing);    ws.get_Range("Average", Type.Missing).Value2 = wsf.Average(rng,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing);    ws.get_Range("StDev", Type.Missing).Value2 = wsf.StDev(rng,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing);}

사용자 삽입 이미지

그림 4. WorksheetFunction 시트를 선택하여 WorksheetFunction 클래스 및 그 유용한 메서드를 테스트합니다.

샘플 코드에서와 같이Range개체를 매개 변수로WorksheetFunction메서드에 전달할 수 있습니다. 또한 단일 값이나 값 목록을 매개 변수로 전달할 수 있습니다. 이 메서드는 일반적으로 최대 32개의 매개 변수를 허용할 수 있습니다. 따라서 고정된 수 목록의 평균을 계산하려면 다음과 같이 코드를 사용할 수 있습니다.

' Visual BasicdblAverage = ThisApplication.WorksheetFunction.Average( _ 12, 14, 13, 19, 21)// C#// 참고: the number of Type.Missing values--the method accepts// 30 parameters.dblAverage = ThisApplication.WorksheetFunction.Average(    12, 14, 13, 19, 21,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing);

Window 클래스 및 Windows 컬렉션

일반적으로Application개체는 Excel 응용 프로그램 내에서 표시되는 창을 제어하며, 사용자는Application개체의Windows속성을 사용하여 Excel 개체 창을 열거나 닫고 배열할 수 있습니다.

Windows속성은Window개체의 컬렉션을 반환하고, 사용자는Arrange메서드를 호출하여 열려 있는 모든 창(또는 볼 수 있는 창만)을 배열할 수 있습니다.XlArrangeStyle열거값 중 하나를 지정하여 창 배열 방식을 지정하고, 선택적으로 볼 수 있는 창만 배열하고 창 스크롤을 동기화하는 방법을 지정할 수 있습니다. 예를 들어 Excel 작업 공간 내에서 창을 배열하려면 다음과 같은 코드를 사용합니다.

' Visual BasicThisApplication.Windows.Arrange( _  Excel.XlArrangeStyle.xlArrangeStyleTiled)// C#ThisApplication.Windows.Arrange(   Excel.XlArrangeStyle.xlArrangeStyleTiled,   Type.Missing, Type.Missing, Type.Missing);

프로그래밍 방식으로 새 창을 만들려면 통합 문서의NewWindow메서드를 다음과 같이 호출할 수 있습니다.

' Visual BasicThisWorkbook.NewWindow()// C#ThisWorkbook.NewWindow();

Because theNewWindowmethod returns aNewWindow메서드는Window개체를 반환하기 때문에, 사용자는 다음과 같이 새 창에 대한 캡션을 설정하고 이를 활성화하는 코드를 작성할 수 있습니다.

' Visual BasicWith ThisWorkbook.NewWindow()    .Caption = "New Window"    .Activate()End With// C#Excel.Window wnd = ThisWorkbook.NewWindow();wnd.Caption = "New Window";wnd.Activate();

Windows클래스는 색상, 캡션, 창 기능의 시각화 및 스크롤 동작과 같이 관련 창의 모양과 동작을 제어하는 속성 및 메서드를 제공합니다. 사용자는 특정 창의 속성을 사용하기 위해 다음과 같이 코드를 작성할 수 있습니다.

' Visual BasicWith ThisApplication.Windows(3)    .GridlineColor = ColorTranslator.ToOle(Color.Red)    .Caption = "A New Window"    .DisplayHeadings = False    .DisplayFormulas = False    .DisplayWorkbookTabs = False    .SplitColumn = 1End With// C#wnd = ThisApplication.Windows[3];wnd.GridlineColor = ColorTranslator.ToOle(Color.Red);wnd.Caption = "A New Window";wnd.DisplayHeadings = false;wnd.DisplayFormulas = false;wnd.DisplayWorkbookTabs = false;wnd.SplitColumn = 1;
:   VBA 및 .NET은 비슷한 방식(각각은 32비트 정수의 하위 3바이트로 인코딩된 색의 구성 요소인 빨간색, 녹색 및 파란색을 포함하는 3바이트를 사용)으로 색상을 사용하지만 색상을 서로 다르게 처리합니다. 사용자는System.Drawing.ColorTranslator.ToOle메서드를 사용하여 .NET 색상을 VBA에서 필요한 OLE 색상으로 변환할 수 있습니다.

Other Application Members시트에서Work with Windows를 클릭하면 이 섹션에서 제공된 모든 코드가 포함된 TestWindows 샘플 프로시저를 실행합니다. 같은 시트에서Reset Windows를 클릭하면 첫 번째 창을 제외한 모든 창을 닫고 첫 번째 창을 최대화하는 다음 프로시저가 실행됩니다.

' Visual BasicPrivate Sub ResetWindows()    Dim i As Integer    For i = ThisApplication.Windows.Count To 2 Step -1        ThisApplication.Windows(i).Close()    Next    ThisApplication.Windows(1).WindowState = _      Excel.XlWindowState.xlMaximizedEnd Sub// C#private void ResetWindows(){    for (int i = ThisApplication.Windows.Count; i >= 2; i--)        ThisApplication.Windows[i].Close(          false, Type.Missing, Type.Missing);    ThisApplication.Windows[1].WindowState =         Excel.XlWindowState.xlMaximized;}

Name 클래스 및 Names 컬렉션

Application개체는Name개체의 컬렉션을 반환하는Names속성을 제공합니다. 각 Name 개체는 Excel 응용 프로그램에서 이름이 지정된 범위에 해당합니다. 이름이 지정된 범위에 대한 참조를 검색하는 방법은 여러 가지입니다. 사용자는WorkbookNames속성을 사용하거나Worksheet개체의Names속성을 사용할 수 있습니다.

새로운 이름이 지정된 범위를 만들려면Names개체의Add메서드를 다음 코드에서와 같이 사용합니다.Add메서드는 두 개의 필수 매개 변수 외에도 여러 가지 옵션 매개 변수를 허용합니다.

' Visual BasicDim nm As Excel.Namenm = ThisApplication.Names.Add( _  "NewName", "='Other Application Members'!$A$6")// C#Excel.Name nm;nm = ThisApplication.Names.Add(    "NewName", @"='Other Application Members'!$A$6",     Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing);

옵션 매개 변수와 함께 이름 및 위치를 지정한 다음 코드에서 범위를 참조할 수 있습니다.

' Visual BasicThisApplication.Range("NewName").Value = "Hello, World!"// C#ThisApplication.get_Range(    "NewName", Type.Missing).Value2 = "Hello, World!";

이름이 지정된 범위에 대한 정보를 검색하려면Name클래스의 여러 속성을 사용할 수 있습니다. 다음 목록은 가장 일반적으로 사용되는 몇 가지 구성원에 대한 설명입니다.

  • Name은 이름이 지정된 범위에 할당된 이름을 반환합니다.
  • RefersTo는 표준 형식("=SheetName!$B$25")으로 실제 대상 주소를 포함하는 문자열을 반환합니다.
  • RefersToR1C1은 "R1C1" 형식("=SheetName!R25C2")으로 대상 주소를 반환합니다.
  • Value는 해당 범위의 내용을 확인하는(resolve) 이름이 지정된 범위에 대한 참조를 반환합니다.

예제에서Work with Names링크를 클릭하면 시트의 한 영역에 모든 이름이 지정된 범위에 대한 정보를 채우는 다음 코드를 실행합니다.

' Visual BasicDim nm As Excel.NameDim rng As Excel.Range = ThisApplication.Range("Names")Dim i As IntegerFor i = 0 To ThisApplication.Names.Count ? 1    nm = ThisApplication.Names.Item(i + 1)    rng.Offset(i, 0).Value = nm.Name    ' Without the leading "'", these references    ' get evaluated, rather than displayed directly.    rng.Offset(i, 1).Value = "'" & nm.RefersTo.ToString    rng.Offset(i, 2).Value = "'" & nm.RefersToR1C1.ToString    rng.Offset(i, 3).Value = nm.ValueNext i// C#Excel.Range rng = ThisApplication.get_Range("Names", Type.Missing);for ( int i = 0 ; i <= ThisApplication.Names.Count - 1; i++){    nm = ThisApplication.Names.Item(i + 1,         Type.Missing, Type.Missing);    rng.get_Offset(i, 0).Value2 = nm.Name;    // Without the leading "'", these references    // get evaluated, rather than displayed directly.    rng.get_Offset(i, 1).Value2 = "'" + nm.RefersTo.ToString();    rng.get_Offset(i, 2).Value2 = "'" + nm.RefersToR1C1.ToString();    rng.get_Offset(i, 3).Value2 = nm.Value;}

Application 이벤트

Application클래스에서 제공되는 다른 모든 메서드 외에도 사용할 수 있는 여러 이벤트가 있습니다. 이런 이벤트를 모두 설명할 수는 없지만, 이벤트의 이름만으로도 이벤트의 용도를 쉽게 확인할 수 있습니다. 다음 섹션에서는 응용 프로그램에서 사용할 가능성이 높은 일부 이벤트에 대해 설명합니다.

:   Office 응용 프로그램에서 이벤트 처리기로 전달되는 매개 변수는 네이티브 .NET 이벤트에서 사용되는 매개 변수와는 다르게 느껴질 수 있습니다. 일반적으로 .NET 이벤트 처리기는 항상 이벤트를 발생시킨 개체를 참조하는Object변수를 받고, 또한 이벤트에 대한 추가 정보를 포함하는EventArgs기본 클래스에서 상속된 두 번째 매개 변수를 받습니다. Office 응용 프로그램에는 잘 정의된 이벤트 디자인 패턴이 없습니다. 따라서 각 이벤트 처리기는 원래 개발자가 정의한 임의의 매개 변수 개수를 허용합니다.

시트 동작

Application 개체는 시트(차트 및 워크시트)와 관련된 일련의 이벤트를 제공합니다. 다음 목록은 이러한 이벤트에 대한 정보입니다.

  • SheetActivate는 시트가 활성화될 때 발생합니다. Excel은 이벤트 처리기에 활성화된 시트에 대한 참조를 포함하는 Object 변수를 전달합니다.
    :   Excel에서 시트를 참조하는 Object를 전달하는 모든 경우, 사용자는 참조를 사용하기 전에 참조를 올바른 유형의 변수(상황에 따라Worksheet또는Chart)로 캐스트해야 합니다. 하지만 Visual Basic .NET에서Option Strict설정을 비활성화한 경우에는 늦은 바인딩을 사용할 수 있습니다. 이 경우에는 여전히 입력 시 IntelliSense를 사용할 수 없어 코드 작성이 더 어려워집니다.Sheets컬렉션 내의 항목을 사용하는 이 문서의 모든 예제는 결과를Worksheet또는Chart와 같은 필요한 시트 유형으로 명시적으로 캐스트합니다.
  • SheetBeforeDoubleClick은Excel이 기본 두 번 클릭 처리를 제공하기 전에 시트를 두 번 클릭할 때 발생합니다. Excel은 이벤트 처리기에 시트에 대한 참조를 포함하는Object변수와 두 번 클릭의 위치와 가장 가까운 셀을 포함하는 Range 개체 및 사용자가 기본 이벤트 처리를 취소할 수 있도록 허용하는Boolean값(기본값은 False)을 전달합니다. (이 이벤트는 차트 시트에서는 발생하지 않습니다.)
    :   이름에 "Before"가 있는 모든 이벤트는 기본 이벤트 처리를 취소할 수 있게 해줍니다. 이벤트 처리기에 전달되는 매개 변수의 이름은 일반적으로 Cancel로 지정되고 기본값은 False가 사용됩니다. 이 매개 변수를 True로 설정하면 Excel이 이벤트에 대한 기본 처리를 실행하지 않습니다.
  • SheetBeforeRightClick은 Excel이 기본 오른쪽 클릭 처리를 제공하기 전에 시트를 마우스 오른쪽 단추로 클릭할 때 발생합니다. Excel은 이벤트 처리기에 시트에 대한 참조를 포함하는Object변수와 마우스 오른쪽 단추 클릭의 위치와 가장 가까운 셀을 포함하는 Range 개체 및 사용자가 기본 이벤트 처리를 취소할 수 있도록 허용하는Boolean값(기본값은 False)을 전달합니다. (이 이벤트는 차트 시트에서는 발생하지 않습니다.)
  • SheetCalculate는 시트가 다시 계산될 때 발생합니다. Excel은 이벤트 처리기에 다시 계산된 시트에 대한 참조를 포함하는Object를 전달합니다.
  • SheetChange는 사용자 또는 실행 코드에 의해 워크시트의 셀이 변경될 때 발생합니다. Excel은 이벤트 처리기에 시트에 대한 참조를 포함하는 Object 변수와 변경된 범위를 참조하는 Range 변수를 전달합니다.
  • SheetDeactivate는 시트가 비활성화될 때(즉, 더 이상 포커스를 가지고 있지 않을 때) 발생합니다. 이 이벤트 처리기는 포커스가 같은 통합 문서 내의 다른 시트로 이동할 때에만 실행됩니다. Excel은 이벤트 처리기에 비활성화된 시트에 대한 참조를 포함하는Object변수를 전달합니다.
  • SheetFollowHyperlink는 통합 문서 내의 하이퍼링크를 클릭할 때 발생합니다. Excel은 이벤트 처리기에 링크가 포함된 시트를 참조하는Object변수와 사용자가 클릭한 링크에 대한 참조를 포함하는Hyperlink개체를 전달합니다. (샘플 프로젝트는 이 이벤트를 사용하여 예제 내의 탐색을 제공합니다.)
  • SheetSelectionChange는 워크시트에서 선택이 변경될 때 발생합니다. (이 이벤트는 차트 시트에서는 발생하지 않습니다.) Excel은 이벤트 처리기에 선택이 변경된 시트를 참조하는Object변수와 새로운 선택을 참조하는 Range 변수를 전달합니다. (Excel은 변경되기 전의 원래 선택에 대한 정보는 전달하지 않음을 유의하십시오.)
    참고:   이 섹션의 각 이벤트는Workbook클래스에서 제공되는 이벤트로도 사용할 수 있습니다.Application개체에서 이벤트가 제공될 때, 이 이벤트는 Excel 내에 현재 열린 시트에 대해서 발생합니다. 이 이벤트가Workbook개체에서 제공될 때 이 이벤트는 특정 통합 문서 내의 시트에 영향을 줄 경우에만 발생합니다. 또한Worksheet클래스에서 제공되는 동일 이벤트를 찾을 수 있습니다. 이 경우 이벤트 이름에는 "Sheet"가 포함되지 않으며(예:SheetFollowHyperlink대신FollowHyperlink), 이벤트 처리기에는 이벤트를 받은 개체에 포함된 정보인 시트에 대한 참조가 전달되지 않습니다. 그렇지 않으면 이벤트, 사용 용도 및 매개 변수는 여기에 설명된 이벤트와 동일합니다.

Window 동작

Application개체(및 대응하는Workbook개체)는Window개체의 동작을 처리하는 일련의 이벤트를 제공합니다. 다음 목록은 이러한 이벤트에 대한 설명입니다.

  • WindowActivate는 창이 활성화될 때 발생합니다. Excel은 이벤트 처리기에 창을 제공한 통합 문서를 참조하는Workbook개체와 선택된 창을 참조하는Window개체를 전달합니다. 다른 활성화 이벤트와 마찬가지로, 이 이벤트는 포커스가 Excel 내에서 이동할 때만 발생합니다. 다른 응용 프로그램으로 전환한 다음 다시 Excel로 돌아오면 이 이벤트는 발생하지 않습니다.
  • WindowDeactivate는 창이 비활성화될 때 발생합니다. 자세한 내용은WindowActivate이벤트 설명을 참조하십시오.
  • WindowResize는 통합 문서 창의 크기를 조정할 때 발생합니다. Excel은 이벤트 처리기에 창을 제공한 통합 문서를 참조하는Workbook개체와 크기가 조정된 창을 참조하는Window개체를 전달합니다.
    참고:   Workbook클래스에서 제공된 이벤트에서 이벤트 처리기는 이벤트를 발생시킨 개체에 포함된 정보인Workbook에 대한 참조를 받지 않습니다.

Workbook 관리

Application개체는Workbook개체와 상호 작용할 때 발생하는 일련의 이벤트를 제공합니다. 이러한 각 이벤트 프로시저는 이벤트에 포함된 특정 통합 문서를 가리키는Workbook변수를 받습니다. 다음 목록은 사용 가능한 일부 이벤트에 대한 설명입니다.

  • NewWorkbook은 새 통합 문서를 만들 때 발생합니다. Excel은 이벤트 처리기에 새 통합 문서를 참조하는Workbook변수를 전달합니다. (이 이벤트는Application클래스에서만 제공됩니다.)
  • WorkbookActivate는 통합 문서가 활성화될 때 발생합니다. Excel은 이벤트 처리기에 활성화된 통합 문서를 참조하는Workbook변수를 전달합니다. (다른 "활성화" 이벤트와 같이 이 이벤트는 한 통합 문서에서 다른 통합 문서로 전환할 경우에만 발생합니다.)
  • WorkbookBeforeClose는 열린 통합 문서를 닫을 때 기본 이벤트 처리 전에 발생합니다. Excel은 이벤트 처리기에 닫으려는 통합 문서를 참조하는 Workbook 변수와 함께 이벤트 처리기가 기본 이벤트 처리(즉, 통합 문서를 열린 상태로 유지)를 취소할 수 있도록 허용하는Boolean값(기본값은 False)을 전달합니다.
    경고:   다른 경우를 고려하지 않고 Cancel 매개 변수를 총체적으로 True로 설정한 경우 통합 문서를 닫을 수 없게 됩니다.
  • WorkbookBeforePrint는 통합 문서 내에서 인쇄가 시작될 때 기본 이벤트 처리 전에 발생합니다. Excel은 이벤트 처리기에 인쇄된 콘텐츠를 포함하는 통합 문서를 참조하는Workbook변수와 함께 이벤트 처리기가 기본 이벤트 처리(즉, 요청된 인쇄 건너뜀)를 취소할 수 있도록 허용하는Boolean값(기본값은 False)을 전달합니다.
  • WorkbookBeforeSave는 통합 문서가 저장될 때 기본 이벤트 처리 전에 발생합니다. Excel은 이벤트 처리기에 저장되는 통합 문서를 참조하는Workbook변수와 함께 이벤트 처리기가 기본 이벤트 처리(즉, 저장 취소)를 취소할 수 있도록 허용하는Boolean값(기본값은 False)을 전달합니다.
  • WorkbookDeactivate는 통합 문서가 비활성화될 때 발생합니다. Excel은 이벤트 처리기에 비활성화된 통합 문서를 참조하는Workbook변수를 전달합니다. (다른 "활성화" 이벤트와 같이 이 이벤트는 한 통합 문서에서 다른 통합 문서로 전환할 경우에만 발생합니다.)
  • WorkbookNewSheet는 통합 문서에 새 시트를 추가할 때 발생합니다. Excel은 이벤트 처리기에 통합 문서를 참조하는Workbook변수와 새로운 시트를 참조하는Object변수를 전달합니다.
  • WorkbookOpen은 통합 문서가 열릴 때 발생합니다. Excel은 이벤트 처리기에 새로 열린 통합 문서를 참조하는Workbook변수를 전달합니다.
    참고:   Workbook클래스는 여기에 설명된 이벤트들과 매우 유사한 자체 이벤트 집합을 제공합니다. "Workbook"으로 시작하는 모든 이벤트는 "WorkbookActivate" 대신 "Activate"와 같이 대상 지정 없이Workbook클래스의 이벤트 목록에 표시되어 있습니다.Workbook클래스 이벤트 처리기는Workbook변수를 매개 변수로 수신하지 않고 해당 정보는 이벤트를 발생시킨 개체에 의해 의미가 지정됩니다. 또한Workbook클래스는 다른Application개체 이벤트의 미러를 제공하지만, 모든 통합 문서에 트래핑하는 것과는 대조적으로 단일 통합 문서에 대해서만 트래핑합니다. 지금까지 사용할 가능성이 높은 이벤트는 모두 설명되었으므로, 본 문서의 남은 부분에서는 이벤트에 대한 설명이 제공되지 않습니다.

Workbook 클래스

일반적으로Workbook클래스는 Excel 응용 프로그램 내의 단일 통합 문서를 나타냅니다. 이 섹션에서는 이 클래스의 일부 구성원에 대해 확인하고, 가장 자주 사용되는 속성 및 메서드에 대해 설명합니다.

:   Application클래스의 구성원 중 상당 부분이Workbook클래스의 구성원과 일치합니다. 이 경우, 속성은 활성 통합 문서에 적용되는 것과는 대조적으로 특정 통합 문서에만 적용됩니다. 이들 구성원에 대해서는 이미 여러 곳에서 언급되고 있기 때문에 이 섹션에서는 이전 섹션보다 훨씬 적은 구성원에 대해서만 설명합니다.

Workbook 클래스의 속성

Workbook클래스는 90개 정도의 여러 속성을 제공하고, 대부분의 개발자가 생각해 보지 못한 특별한 경우의 해결 방법을 제공합니다. 예를 들어AutoUpdateFrequency속성은 공유된 통합 문서에 대한 자동 업데이트 사이의 분 수를 반환합니다.Date1904속성은 통합 문서가 1904 날짜 시스템(1904년 1월 2일을 값 1에 해당하는 날짜로 사용하는 날짜 연속화 체계, Macintosh 컴퓨터에서 일반적으로 사용됨)을 사용하는 경우 True를 반환합니다.PasswordEncryptionAlgorithm속성을 사용하면 암호 암호화에 사용되는 정확한 알고리즘을 설정할 수 있습니다.

Workbook개체의 여러 속성을 다루는 포괄적인 설명을 제공하는 대신, 이 섹션에서는 가장 자주 사용되는 속성에 대해서만 설명합니다. 통합 문서의 특정 동작이 필요한 경우에는 이미 다른 누군가가 이를 요청하였고, 이 동작을 가능하게 하는 속성이 있으며, 이 동작을 제공하는 메서드가 있을 가능성이 높습니다. 그러므로 통합 문서에 자신의 코드를 추가하기 전에 먼저 설명서를 자세히 확인해 보십시오.

다음 목록은 일반적으로 자주 사용되는Workbook속성 중 일부에 대해 설명합니다.Workbookproperties:

  • Name,FullName,Path(String, 읽기 전용): 이들 각 속성은 통합 문서 이름의 서로 다른 버전을 반환합니다.FullName은 통합 문서 파일 이름을 포함한 전체 경로를 반환합니다.Name은 이름 부분만 반환하고Path는 경로 부분만 반환합니다. 샘플 통합 문서에서 Name Information 링크를 클릭하면 다음 코드가 실행되고그림 5와 같은 정보가 반환됩니다.
    ' Visual BasicThisApplication.Range("WorkbookName").Value = _  ThisWorkbook.NameThisApplication.Range("WorkbookPath").Value = _  ThisWorkbook.PathThisApplication.Range("WorkbookFullName").Value = _  ThisWorkbook.FullName// C#ThisApplication.get_Range("WorkbookName", Type.Missing).    Value2 = ThisWorkbook.Name;ThisApplication.get_Range("WorkbookPath", Type.Missing).    Value2 = ThisWorkbook.Path;ThisApplication.get_Range("WorkbookFullName", Type.Missing).    Value2 = ThisWorkbook.FullName;

    사용자 삽입 이미지

    그림 5. 통합 문서 속성을 사용하여 이름에 대한 정보를 검색

  • Password(String): 통합 문서와 관련된 암호를 가져오거나 설정합니다. 비어 있지 않은 암호를 지정한 경우 통합 문서의HasPassword속성은 True를 반환합니다.Password속성을 검색할 수는 있지만 값이 항상 "********"로 표시됩니다. 샘플 통합 문서에서Set Password링크를 클릭하면 다음 코드가 실행되어, 텍스트를 제공했는지 빈 문자열을 제공했는지 여부에 따라 통합 문서의 암호를 설정하거나 지웁니다. 이 예제에서는 샘플 프로젝트에서 단일 입력란과 Password 속성을 제공하는Passoword라는 이름의 양식을 사용합니다. property:
    ' Visual BasicPrivate Sub SetPassword()    Dim frm As New Password    If frm.ShowDialog = DialogResult.OK Then        ThisWorkbook.Password = frm.Password    End If    frm.Dispose()End Sub// C#private void SetPassword(){    Password frm = new Password();    if (frm.ShowDialog() == DialogResult.OK)        ThisWorkbook.Password = frm.Value;    frm.Dispose();}
  • PrecisionAsDisplayed(Boolean): True로 설정된 경우 Excel은 표시된 소수 자릿수를 사용하여 계산을 수행합니다. False(기본값)로 설정된 경우 계산은 표시가 되어 있지 않더라도 사용 가능한 모든 소수점 기호를 사용하여 수행됩니다.그림 6은 속성이 True로 설정된 샘플 통합 문서를 보여줍니다. C 열에 있는 각 값은 B 열에 있는 값의 복사본이지만, 숫자 형식은 C 열에서 소숫점 이하 두 자리까지만 표시되도록 설정됩니다.PrecisionAsDisplayed속성을 True로 설정한 경우 반올림에 따라 실제 값이 달라져서 합계가 다르게 표시됩니다.PrecisionAsDisplayed=False링크를 클릭하면 합계는 동일하게 표시됩니다. 클릭하면 다음 프로시저를 호출하고 클릭한 링크에 따라 True 또는 False를 전달합니다.
    ' Visual BasicPrivate Sub TestPrecisionAsDisplayed( _  ByVal IsPrecisionAsDisplayedOn As Boolean)    ThisWorkbook.PrecisionAsDisplayed = IsPrecisionAsDisplayedOnEnd Sub// C#private void TestPrecisionAsDisplayed(    bool IsPrecisionAsDisplayedOn){    ThisWorkbook.PrecisionAsDisplayed =         IsPrecisionAsDisplayedOn;}

    사용자 삽입 이미지

    그림 6. PrecisionAsDisplayed 속성을 True로 설정한 경우 Excel은 표시된 소수 자릿수만 사용하여 계산을 수행합니다.

  • ReadOnly(Boolean, 읽기 전용): 통합 문서가 읽기 전용으로 열린 경우 True를 반환합니다. 통합 문서에 데이터를 저장할 수 없는 경우 응용 프로그램에서 다른 작업을 수행하도록 할 수 있습니다.
  • Saved(Boolean): 통합 문서의 저장된 상태를 가져오거나 설정합니다. 사용자가 통합 문서의 내용이나 구조를 변경한 경우Saved속성은 True입니다. 통합 문서를 닫거나 Excel을 종료하려고 시도하면Application.DisplayAlerts속성을 False로 설정하지 않은 한 통합 문서를 저장하라는 경고 메시지가 표시됩니다. 코드에서Saved속성 값을 False로 설정한 경우 Excel은 통합 문서가 이미 저장된 것과 같이 취급하고 저장하라는 메시지를 표시하지 않습니다.

Document 속성 사용

다른 Office 응용 프로그램과 마찬가지로 Excel을 사용하면 통합 문서와 함께 문서의 속성을 저장할 수 있습니다. Excel은 여러 가지 기본 제공된 속성을 제공하며 사용자만의 고유한 속성을 추가할 수도 있습니다. 파일|속성을 선택하면그림 7과 같은 대화 상자가 표시되고, 사용자는 사용자 지정 탭을 선택하여 사용자 지정 속성을 만들고 수정할 수 있습니다.

사용자 삽입 이미지

그림 7. 이 대화 상자를 사용하여 문서의 속성을 설정합니다.

Workbook클래스BuiltInDocumentProperties속성으로는 기본 제공된 속성을 사용하고,CustomDocumentProperties속성으로는 사용자 지정 속성을 사용합니다. 이들 각 속성은 DocumentProperty 개체의 컬렉션인DocumentProperties개체를 반환합니다. 컬렉션의Item속성을 사용하여 컬렉션 내의 이름 또는 인덱스별로 특정 속성을 검색할 수 있습니다. 속성 이름의 전체 목록은 Excel 설명서에서 찾을 수 있지만, 목록을 쉽게 검색할 수 있는 방법이 있습니다. 샘플 통합 문서의 Document Properties 링크를 클릭하면 다음 프로시저가 실행됩니다(그림 8참조). 이 프로시저는 기본 제공된 모든 속성과 현재 값을 나열하는DumpPropertyCollection메서드를 호출하고 사용자 지정 속성에 대해서도 같은 과정을 반복합니다. 또한 이 프로시저는 Revision Number 속성을 개별적으로 수정하고 새로운 사용자 지정 속성을 만듭니다.

' Visual BasicPrivate Sub DisplayDocumentProperties()    Dim prp As Office.DocumentProperty    Dim prps As Office.DocumentProperties    Dim rng As Excel.Range = _      ThisApplication.Range("DocumentProperties")    Dim i As Integer    Try        ThisApplication.ScreenUpdating = False        Try            prps = DirectCast( _              ThisWorkbook.BuiltinDocumentProperties, _              Office.DocumentProperties)            ' Set the Revision Number property:            prp = prps.Item("Revision Number")            prp.Value = CType(prp.Value, Integer) + 1            ' Dump contents of the collection:            DumpPropertyCollection(prps, rng, i)        Catch ex As Exception            MessageBox.Show(ex.Message)        End Try        ' Work with custom properties:        Try            prps = DirectCast( _              ThisWorkbook.CustomDocumentProperties, _             Office.DocumentProperties)            DumpPropertyCollection(prps, rng, i)        Catch ex As Exception            MessageBox.Show(ex.Message)        End Try        ' Add a custom property:        Try            ' Delete the property, if it exists.            prp = prps.Item("Project Name")            prp.Delete()        Catch            ' Do nothing if you get an exception.        End Try        Try            ' Add a new property.            prp = prps.Add("Project Name", False, _             Office.MsoDocProperties.msoPropertyTypeString, _              "White Papers")        Catch ex As Exception            MessageBox.Show(ex.Message)        End Try    Finally        ThisApplication.ScreenUpdating = True    End TryEnd SubPrivate Sub DumpPropertyCollection( _  ByVal prps As Office.DocumentProperties, _  ByVal rng As Excel.Range, ByRef i As Integer)    Dim prp As Office.DocumentProperty    For Each prp In prps        rng.Offset(i, 0).Value = prp.Name        Try            If Not prp.Value Is Nothing Then                rng.Offset(i, 1).Value = _                   prp.Value.ToString            End If        Catch            ' Do nothing at all.        End Try        i += 1    NextEnd Sub// C#private void DisplayDocumentProperties(){    Office.DocumentProperty prp = null;    Office.DocumentProperties prps =         (Office.DocumentProperties)         ThisWorkbook.BuiltinDocumentProperties;    Excel.Range rng = ThisApplication.        get_Range("DocumentProperties", Type.Missing);    int i = 0;    try     {        ThisApplication.ScreenUpdating = false;        try         {            // Set the Revision Number property:            prp = prps["Revision Number"];      prp.Value = Convert.ToInt32(prp.Value) + 1;            // Dump contents of the collection:            i = DumpPropertyCollection(prps, rng, i);        }         catch (Exception ex)         {            MessageBox.Show(ex.Message, ThisApplication.Name);        }        // Work with custom properties:        try         {      prps = (Office.DocumentProperties)        ThisWorkbook.CustomDocumentProperties;            DumpPropertyCollection(prps, rng, i);        }         catch (Exception ex)         {            MessageBox.Show(ex.Message, ThisApplication.Name);        }        // Add a custom property:        try         {            // Delete the property, if it exists.            prp = prps["Project Name"];            prp.Delete();        }         catch        {            // Do nothing if you get an exception.        }        try         {            // Add a new property.            prp = prps.Add("Project Name", false,                 Office.MsoDocProperties.msoPropertyTypeString,                         "White Papers", Type.Missing);        }         catch (Exception ex)         {            MessageBox.Show(ex.Message, ThisApplication.Name);        }    }     finally     {        ThisApplication.ScreenUpdating = true;    }}private int DumpPropertyCollection(    Office.DocumentProperties prps, Excel.Range rng, int i) {    foreach (Office.DocumentProperty prp in prps)    {        rng.get_Offset(i, 0).Value2 = prp.Name;        try         {            if (prp.Value != null )             {                rng.get_Offset(i, 1).Value2 =                     prp.Value.ToString();            }        }         catch        {            // Do nothing at all.        }        i += 1;    }    return i;}
:   이전 코드 샘플 DisplayDocumentProperties에서는 Microsoft.Office.Core 어셈블리에서 일부 열거 및 유형을 사용했습니다. 샘플 코드에는 "Excel" 축약에 사용되는 것과 같이 이 네임스페이스에 대한 축약으로 "Office" 텍스트를 설정한 Imports/using 문이 포함됩니다. 이 프로젝트 서식 파일은 "Excel" 축약을 자동으로 설정합니다. "Office" 문은 사용자가 직접 추가해야 합니다.

사용자 삽입 이미지

그림 8. 기본 제공된 문서 속성

참고:   여기에서는 Excel과 관련 개체를 사용하지만, 기본 제공된 문서 속성 목록은 Office에서 제공되며, Excel에서는 예외에서 정의되지 않은 속성 트리거에 대한Value속성에 액세스하기 위해 모든 속성을 반드시 구현할 필요는 없습니다. 샘플 프로시저에는 이러한 상황을 처리하기 위해 반드시 발생해야 하는 간단한 예외가 포함됩니다.

스타일 사용

Word 문서와 마찬가지로 Excel 통합 문서에서도 통합 문서 내의 영역에 이름이 지정된 스타일을 적용할 수 있으며, Excel은 다양한 미리 정의된 스타일을 제공합니다. 사용자는 서식|스타일 메뉴 항목을 사용하여그림 9와 같이 사용자가 스타일을 수정할 수 있는 대화 상자를 표시할 수 있습니다.

사용자 삽입 이미지

그림 9. 이 대화 상자를 사용하여 대화식으로 스타일을 수정합니다.

스타일 대화 상자에서 수정을 클릭하면그림 10과 같이 셀 서식 대화 상자가 표시됩니다.

사용자 삽입 이미지

그림 10. 셀 서식 대화 상자를 사용하여 스타일을 수정합니다.

셀 서식 대화 상자에는 셀 서식에 사용할 수 있는 모든 옵션이 표시되며, 이 대화 상자에서 사용할 수 있는 모든 옵션은 코드에서도 사용할 수 있습니다.Workbook개체의Styles속성을 사용하면 통합 문서 내의 영역에 스타일을 사용하고 지정할 수 있습니다.

Workbook개체의Styles속성을 사용하여 스타일을 만들고, 삭제하고, 수정할 수 있습니다. 샘플 통합 문서에서스타일 적용을 클릭하면 다음 프로시저가 실행되어 새 스타일을 만들고(코드를 이미 실행한 경우 기존 스타일 사용), 스타일에 대한 다양한 값을 설정하고, 이를 영역에 적용합니다.

' Visual BasicPrivate Sub ApplyStyle()    Const STYLE_NAME As String = "PropertyBorder"    Dim rng As Excel.Range    ' Get the range containing all the document properties.    rng = GetDocPropRange()    Dim sty As Excel.Style    Try        sty = ThisWorkbook.Styles(STYLE_NAME)    Catch        sty = ThisWorkbook.Styles.Add(STYLE_NAME)    End Try    sty.Font.Name = "Verdana"    sty.Font.Size = 12    sty.Font.Color = ColorTranslator.ToOle(Color.Blue)    sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray)    sty.Interior.Pattern = XlPattern.xlPatternSolid    rng.Style = STYLE_NAME    rng.Columns.AutoFit()End Sub// C#private void ApplyStyle(){    const String STYLE_NAME = "PropertyBorder";    // Get the range containing all the document properties.    Excel.Range rng = GetDocPropRange();    Excel.Style sty;    try    {        sty = ThisWorkbook.Styles[STYLE_NAME];    }    catch    {        sty = ThisWorkbook.Styles.Add(STYLE_NAME, Type.Missing);    }    sty.Font.Name = "Verdana";    sty.Font.Size = 12;    sty.Font.Color = ColorTranslator.ToOle(Color.Blue);    sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray);    sty.Interior.Pattern = Excel.XlPattern.xlPatternSolid;    rng.Style = STYLE_NAME;    rng.Columns.AutoFit();}

GetDocPropRange메서드는 문서 속성에 의해 채워진 범위를 반환합니다. 이 프로시저는Range.End메서드를 사용하여 문서 속성으로 채워진 범위의 끝을 검색한 다음 해당 범위의 왼쪽 위와 오른쪽 아래 모서리에 맞춰 새로운 범위를 만듭니다.

' Visual BasicPrivate Function GetDocPropRange() As Excel.Range    Dim rng As Excel.Range = _      ThisApplication.Range("DocumentProperties")    Dim rngStart As Excel.Range = _      DirectCast(rng.Cells(1, 1), Excel.Range)    Dim rngEnd As Excel.Range = _      rng.End(Excel.XlDirection.xlDown).Offset(0, 1)    Return ThisApplication.Range(rngStart, rngEnd)End Function// C#private Excel.Range GetDocPropRange(){    Excel.Range rng =         ThisApplication.get_Range("DocumentProperties", Type.Missing);    Excel.Range rngStart =         (Excel.Range) rng.Cells[1, 1];    Excel.Range rngEnd =         rng.get_End(Excel.XlDirection.xlDown).get_Offset(0, 1);    return ThisApplication.get_Range(rngStart, rngEnd);}
:   Range개체의 검색 및 사용 방법은 이 문서의 "범위 사용" 섹션을 참조하십시오.

코드를 실행한 다음 샘플 통합 문서에서 문서 속성이 포함된 영역은그림 11과 같이 음영 및 글꼴이 변경됩니다.

사용자 삽입 이미지

그림 11. 사용자 지정 스타일 적용 이후

스타일 지우기를 클릭하면 동일 영역에 대한 스타일을 해제하는 다음 프로시저가 실행됩니다.

' Visual BasicPrivate Sub ClearStyle()    ' Get the range containing all the document properties, and    ' clear the style.    GetDocPropRange().Style = "Normal"End Sub// C#private void ClearStyle(){    // Get the range containing all the document properties, and    // clear the style.    GetDocPropRange().Style = "Normal";}

시트 사용

Workbook클래스는Sheets개체를 반환하는Sheets속성을 제공합니다. 이 개체에는 각각Worksheet또는Chart개체일 수 있는Sheet개체의 컬렉션이 포함됩니다. 샘플 통합 문서에서List Sheets를 클릭하면 다음 프로시저가 실행되고 통합 문서에 모든 기존 시트가 나열됩니다.

' Visual BasicPrivate Sub ListSheets()    Dim sh As Excel.Worksheet    Dim rng As Excel.Range    Dim i As Integer    rng = ThisApplication.Range("Sheets")    For Each sh In ThisWorkbook.Sheets        rng.Offset(i, 0).Value = sh.Name        i = i + 1    Next shEnd Sub// C#private void ListSheets(){    int i = 0;    Excel.Range rng =         ThisApplication.get_Range("Sheets", Type.Missing);    foreach (Excel.Worksheet sh in ThisWorkbook.Sheets)    {        rng.get_Offset(i, 0).Value2 = sh.Name;        i = i + 1;    }}

또한 Sheets 클래스의 다음 구성원들을 유용하게 사용할 수 있습니다.

  • Visible속성을 사용하면 시트를 삭제하고 다시 만들지 않고 기존 시트를 표시하거나 숨길 수 있습니다.Visibility속성을XlSheetVisibility열거값(XlSheetHidden,XlSheetVeryHidden,xlSheetVisible) 중 하나로 설정합니다.XlSheetHidden을 사용하면 사용자가 Excel 인터페이스를 통해 시트의 숨김을 해제할 수 있습니다.XlSheetVeryHidden을 사용하려면 사용자가 시트의 숨김을 해제하는 코드를 실행해야 합니다.
    ' Visual BasicDirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Visible = _  Excel.XlSheetVisibility.xlSheetVeryHidden// C#((Excel.Worksheet) ThisWorkbook.Sheets[1]).Visible =     Excel.XlSheetVisibility.xlSheetVeryHidden;
  • Add메서드를 사용하면 통합 문서의 시트 컬렉션에 새 시트를 추가할 수 있으며, 시트 위치, 추가할 시트 번호 및 시트 유형(워크시트, 차트 등)을 나타내는 4개의 옵션 매개 변수를 허용합니다.
    ' Visual BasicDim sh As Excel.Sheet = ThisWorkbook.Sheets.Add()// C#Excel.Sheet sh = ThisWorkbook.Sheets.Add(    Type.Missing, Type.Missing, Type.Missing, Type.Missing);
  • Copy메서드는 시트의 복사본을 만들고 지정한 위치에 시트를 삽입합니다. 필요에 따라 기존 시트의 앞이나 뒤에 새 시트를 삽입하도록 지정할 수 있습니다. 위치를 지정하지 않으면 Excel은 새 시트가 들어 있는 새로운 통합 문서를 만듭니다. 다음 코드는 현재 통합 문서에서 첫 번째 시트를 복사하여 이를 세 번째 시트 다음에 둡니다.
    ' Visual BasicDirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _  Copy(After:=ThisWorkbook.Sheets((3)))// C#((Excel.Worksheet) ThisWorkbook.Sheets[1]).    Copy(Type.Missing, ThisWorkbook.Sheets[3]);.
  • Delete메서드는 지정된 시트를 삭제합니다.
    ' Visual BasicDirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Delete// C#((Excel.Worksheet) ThisWorkbook.Sheets[1]).Delete();
  • FillAcrossSheets메서드는 한 시트의 임의의 영역에서 데이터를 복사하여 통합 문서 내의 다른 모든 시트로 붙여 넣습니다. 사용자는 영역을 지정하고, 데이터, 서식 또는 둘 다 모두 복사할지 지정하며, 그 외 나머지 작업은 Excel이 수행합니다. 다음 코드는 한 시트의 Data라는 이름의 영역에서 데이터와 서식을 복사하여 통합 문서 내의 다른 모든 시트에 있는 동일 영역에 붙여 넣습니다.
    ' Visual BasicThisWorkbook.Sheets.FillAcrossSheets( _  ThisApplication.Range("Data"), Excel.XlFillWith.xlFillWithAll)// C#ThisWorkbook.Sheets.FillAcrossSheets(    ThisApplication.get_Range("Data", Type.Missing),     Excel.XlFillWith.xlFillWithAll);
  • Move메서드는 시트의 인스턴스 하나만 남게 되는 점을 제외하고는Copy메서드와 거의 비슷합니다. 사용자는 시트를 다른 시트의 앞이나 뒤에 두도록 지정할 수 있습니다. 여기에서도 시트의 이동 위치를 지정하지 않으면, Excel은 시트가 포함된 새 통합 문서를 만듭니다. 다음 코드는 첫 번째 워크시트를 마지막 워크시트 위치로 이동합니다.
    ' Visual BasicDim shts As Excel.Sheets = ThisWorkbook.SheetsDirectCast(shts(1), Excel.Worksheet).Move(After:=shts(shts.Count))// C#Excel.Sheets shts = ThisWorkbook.Sheets;((Excel.Worksheet)shts[1]).Move(Type.Missing, shts[shts.Count]);
    :   통합 문서 내에 있는 시트 목록을 정렬할 필요가 있는 경우에는Move메서드를 사용하여 비효율적인 글머리 정렬을 수행할 수 있습니다. 물론 시트 수가 그렇게 많을 경우는 별로 없기 때문에 정렬 속도는 문제가 되지 않을 것입니다.
  • PrintOut메서드를 사용하면 선택된 개체를 인쇄할 수 있습니다. 이 메서드는 여러 다른 개체에 적용됩니다. 사용자는 인쇄할 페이지, 인쇄 매수, 인쇄 전 미리 보기, 사용할 프린터 이름, 파일로 인쇄 여부, 순서대로 인쇄 여부, 인쇄하려는 파일 이름 등과 같은 다양한 옵션 매개 변수를 지정할 수 있습니다. 다음 예제는 인쇄하기 전에 문서를 미리 본 다음 기본 프린터를 사용하여 지정한 시트에서 첫 번째 페이지의 두 복사본만 인쇄합니다.
    ' Visual BasicDirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _  PrintOut(From:=1, To:=1, Copies:=2, Preview:=True)// C#((Excel.Worksheet)ThisApplication.Sheets[1]).    PrintOut(1, 1, 2, true, Type.Missing, Type.Missing,     Type.Missing, Type.Missing);
  • PrintPreview메서드를 사용하면 PrintPreview 창에서 지정된 개체를 표시하고 페이지 레이아웃 변경 허용을 선택적으로 해제할 수 있습니다.
    ' Visual BasicDirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _  PrintPreview(False)// C#((Excel.Worksheet)ThisApplication.Sheets[1]).PrintPreview(false);
  • Select메서드는 지정된 개체를 선택하여 사용자의 선택 대상을 이동합니다. 사용자의 선택 대상을 변경하지 않고 개체에 포커스를 두려면Activate메서드를 사용하십시오. 사용자는 현재 선택 대상에 대체될 개체에 대한 참조를 선택적으로 제공할 수 있습니다. 다음 코드는 첫 번째 워크시트를 선택합니다.
    ' Visual BasicActiveWorkbook.Sheets(1).Select()// C#((Excel.Worksheet)ThisApplication.Sheets[1]).Select(Type.Missing);
    :   이 섹션에 나열된 여러 메서드는 다른 클래스에도 적용됩니다. 예를 들어PrintOut메서드는Chart, Charts, Range, Sheets, Window, Workbook, WorksheetWorksheets클래스에서도 제공됩니다. 이러한 메서드는 각 경우에 동일하게 작동되며, 다른 개체에 대해서도 사용됩니다.Select메서드는 선택할 수 있는 모든 개체에 적용됩니다.

Workbook 클래스의 메서드

Workbook클래스는 매우 특정한 상황을 처리하는 매우 다양한 메서드를 제공합니다. 이 섹션에서는 자세한 세부 내용을 모두 설명하는 대신 모든 응용 프로그램에서 가장 자주 사용되는 일부 메서드에 대해서만 설명합니다. 특정 메서드에 대한 내용은 이후에 살펴보도록 하십시오. 다음 목록은 일반적으로 사용해야 하는 일부 메서드에 대한 설명입니다.

  • Activate메서드는 통합 문서를 활성화하고 해당 통합 문서에서 첫 번째 시트를 선택합니다.
    ' Visual BasicThisApplication.Workbooks(1).Activate// C#ThisApplication.Workbooks[1].Activate;
  • Close메서드는 지정된 통합 문서를 닫고 선택적으로 변경 내용을 저장할지 여부를 지정합니다. 통합 문서를 처음으로 저장하는 경우에는 파일 이름을 지정할 수 있습니다. 또한 통합 문서가 다른 사용자에게 게시되는 경우, 통합 문서를 다음 사용자에게 전송할지 여부를 지정할 수 있습니다. 다음 코드는 통합 문서를 닫고 변경 내용은 무시합니다.
    ' Visual BasicThisApplication.Workbooks(1).Close(SaveChanges:=False)// C#ThisApplication.Workbooks(1).Close(false,   Type.Missing, Type.Missing);
  • ProtectUnprotect메서드를 사용하면 워크시트를 추가하거나 삭제할 수 없도록 통합 문서를 보호하며, 통합 문서의 보호를 다시 해제할 수도 있습니다. 선택적으로 암호를 지정하건, 다른 사용자가 시트를 이동할 수 없도록 구조를 보호하거나 통합 문서의 창을 보호하도록 설정할지 여부를 선택적으로 표시할 수 있습니다. 통합 문서를 보호하면 다른 사용자가 셀을 편집할 수 없습니다. 데이터를 보호하려면 워크시트를 보호해야 합니다. 통합 문서의 보호를 해제하려면 필요한 경우 암호를 전달하여Unprotect메서드를 호출합니다. 다음 예제는 사용자에게 암호를 입력하도록 요청하는 GetPasswordFromUser라는 프로시저를 가정하고 입력된 값을 반환합니다.
    ' Visual BasicThisApplication.Workbooks(1).Protect(GetPasswordFromUser())// C#ThisApplication.Workbooks[1].Protect(  GetPasswordFromUser(), Type.Missing, Type.Missing);
  • Save메서드는 일반적인 방식으로 통합 문서를 저장합니다. 통합 문서를 처음으로 저장하는 경우에는SaveAs메서드를 대신 호출하여 경로를 지정해야 합니다. 통합 문서를 처음으로 저장하는 경우 Excel은 문서를 생성할 때 지정한 이름과 현재 폴더를 사용하여 문서를 저장합니다.
    ' Visual Basic' Save all open workbooks.Dim wb As Excel.WorkbookFor Each wb in ThisApplication.Workbooks    wb.SaveNext wb// C#// Save all open workbooks.foreach (Excel.Workbook wb in ThisApplication.Workbooks){    wb.Save();}
  • SaveAs메서드는Save메서드보다 좀 더 복잡합니다. 이 메서드를 사용하면 지정된 통합 문서를 저장하고, 이름, 파일 형식, 암호, 액세스 모드 등을 선택적으로 지정할 수 있습니다. 모든 옵션에 대한 목록은 온라인 도움말을 참조하십시오. 다음 코드는 현재 통합 문서를 지정된 위치에 XML 형식을 사용하여 저장합니다.
    ' Visual BasicThisApplication.ActiveWorkbook.SaveAs("C:\MyWorkbook.xml", _ FileFormat:=Excel.XlFileFormat.xlXMLSpreadsheet)// C#ThisApplication.ActiveWorkbook.SaveAs("C:\\MyWorkbook.xml",     Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing,   Type.Missing, Type.Missing, Type.Missing,   Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    :   일부 형식의 경우 파일을 저장하려면 사용자 입력이 필요하기 때문에SaveAs메서드를 호출하기 전에Application.DisplayAlerts속성을 False로 설정해야 할 수 있습니다. 예를 들어 워크시트를 XML 형식으로 저장하려면 Excel은 VBA 프로젝트를 통합 문서에 저장할 수 없다는 메시지를 표시합니다.DisplayAlerts속성을 False로 설정하면 이러한 경고 메시지가 표시되지 않습니다.
  • SaveCopyAs메서드는 통합 문서의 사본을 파일로 저장하지만, 메모리에 열려 있는 통합 문서를 수정하지 않습니다. 이 메서드는 통합 문서의 위치를 수정하지 않으면서 백업 사본을 만들고자 할 때 유용합니다.
    ' Visual BasicThisApplication.ActiveWorkbook.SaveCopyAs("C:\Test.xls")// C#ThisApplication.ActiveWorkbook.SaveCopyAs("C:\\Test.xls");
    경고:   통합 문서를 저장하거나 복사하는 메서드를 대화식으로 취소하면 코드에서 런타임 오류가 발생합니다. 예를 들어 프로시저에서SaveAs메서드를 호출했지만, Excel에서 프롬프트를 비활성화한 경우 사용자가 프롬프트 메시지에서 취소를 클릭하면 Excel은 코드에 런타임 오류를 발생시킵니다.

Worksheet 클래스

지금까지 개별 워크시트를 사용하는 데 필요한 개념은 대부분 설명되었습니다.Worksheet클래스는 매우 많은 구성원을 제공하지만, 이들 대부분의 속성, 메서드 및 이벤트는Application및/또는Workbook클래스에서 제공되는 구성원과 동일하거나 유사합니다. 이 섹션에서는 지금까지 설명되지 않은Worksheet클래스와 관련하여 중요한 구성원과 문제에 대해서만 설명합니다.Worksheet Object시트에 대한 샘플 통합 문서에서 이 섹션에 대한 예제를 찾을 수 있습니다.

Sheet 클래스 없음

Excel은Workbook개체의 속성으로Sheets컬렉션을 제공하지만 사용자는 Excel에서Sheet클래스를 찾을 수 없습니다. 대신에 Sheets 컬렉션의 각 구성원은Worksheet또는Chart개체입니다.WorksheetChart클래스는 내부Sheet클래스가 각각 특수화된 인스턴스입니다. 소스 코드에 대한 액세스 권한이 없으면 이것이 실제 구현과 일치하는지 알 수 있는 방법이 없습니다. 하지만Sheet클래스는 공개적으로 사용할 수 없습니다.

보호 작업

일반적으로 Excel에서 보호 기능은 사용자 및/또는 코드가 워크시트 내의 개체를 수정할 수 없도록 보호합니다. 워크시트에 대해 보호 기능을 설정한 다음에 다른 방식의 준비를 하지 않으면 사용자는 시트를 편집하거나 수정할 수 없습니다. 사용자 인터페이스 내에서도구|보호|시트 보호메뉴 항목을 사용하여 보호 기능을 설정할 수 있습니다. 이 항목을 선택하면그림 12와 같이 시트 보호 대화 상자가 표시됩니다. 여기에서 암호를 설정하거나 사용자가 특정 작업을 수행하도록 허용할 수 있습니다. 보호 기능을 설정하면 기본적으로 모든 셀이 잠깁니다. 또한그림 13과 같은 대화 상자를 표시하는도구|보호|범위 편집 허용메뉴 항목을 사용하여 사용자가 특정 범위를 편집하도록 허용할 수 있습니다. 이러한 두 대화 상자를 사용하여 시트를 잠근 다음 사용자가 특정 기능 또는 범위를 편집하도록 허용할 수 있습니다.

사용자 삽입 이미지

그림 12. 사용자 인터페이스에서 이 대화 상자를 사용하여 보호 기능을 제어합니다.

사용자 삽입 이미지

그림 13. 이 대화 상자를 사용하여 사용자가 특정 영역을 편집하도록 허용할 수 있습니다.

워크시트의Protect메서드를 사용하여 프로그래밍 방식으로 시트 보호를 제어할 수 있습니다. 이 메서드의 구문은 다음과 같이 보입니다. 여기에서 각 매개 변수는 옵션 매개 변수입니다.

' Visual BasicWorksheetObject.Protect(Password, DrawingObjects, Contents, _  Scenarios, UserInterfaceOnly, AllowFormattingCells, _  AllowFormattingColumns, AllowFormattingRows, _  AllowInsertingColumns, AllowInsertingRows, _  AllowInsertingHyperlinks, AllowDeletingColumns, _  AllowDeletingRows, AllowSorting, AllowFiltering, _  AllowUsingPivotTables)// C#WorksheetObject.Protect(Password, DrawingObjects, Contents,   Scenarios, UserInterfaceOnly, AllowFormattingCells,   AllowFormattingColumns, AllowFormattingRows,   AllowInsertingColumns, AllowInsertingRows,   AllowInsertingHyperlinks, AllowDeletingColumns,   AllowDeletingRows, AllowSorting, AllowFiltering,   AllowUsingPivotTables);

다음 목록은 Protect 메서드의 매개 변수를 설명합니다.

  • Password 매개 변수를 설정하여 워크시트의 보호를 해제하기 위해 입력해야 하는 대소문자로 구분되는 문자열을 지정합니다. 이 매개 변수를 지정하지 않으면 누구나 시트의 보호를 해제할 수 있습니다.
  • DrawingObjects 매개 변수를 True로 설정하여 워크시트의 모양을 보호합니다. 기본값은 False입니다.
  • Contents 매개 변수를 True로 설정하여 워크시트의 콘텐츠(셀)를 보호합니다. 기본값은 True이며, 이 값은 변경할 필요가 없습니다.
  • Scenarios 매개 변수를 True로 설정하여 워크시트의 시나리오를 보호합니다. 기본값은 True입니다.
  • UserInterfaceOnly 매개 변수를 True로 설정하여 코드로부터의 변경은 허용하고 사용자 인터페이스로부터의 변경은 허용하지 않습니다. 기본값은 False로서, 코드 또는 사용자 인터페이스 모두에서 보호되는 워크시트를 변경할 수 없습니다. 이 속성 설정은 현재 세션에만 적용됩니다. 코드에서 모든 세션의 워크시트를 조작할 수 있도록 하려면 통합 문서를 열 때마다 이 속성을 설정하는 코드를 포함시켜야 합니다.
  • AllowFormattingCells 매개 변수, AllowFormattingColumns 매개 변수 및 이전 전체 메서드 구문 목록에 있는 매개 변수들을 사용하면그림 12에 표시된 대화 상자의 옵션에 따라 특정 서식 기능을 사용할 수 있습니다. 기본적으로 모든 속성은 False입니다.

워크시트를 보호하려면 다음 코드에서 시트의Protect메서드를 호출하여 암호를 설정하고 정렬만 허용합니다.

' Visual BasicDirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _  Protect("MyPassword", AllowSorting:=True)// C#((Excel.Worksheet)ThisApplication.Sheets[1]).Protect(    "MyPassword", Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, true, Type.Missing, Type.Missing);
:   코드 내에 암호를 직접 코딩하는 것은 좋은 방법이 아닙니다. 일반적으로 사용자에게 암호를 요청하고 이를 저장하지 않고 통합 문서에 적용해야 합니다. 소스 코드에는 암호를 직접 입력하지 않습니다.

워크시트의 보호를 해제하려면 다음과 같이 코드를 사용할 수 있습니다. 다음 코드는 사용자에게 암호를 입력하도록 요청하는 GetPasswordFromUser라는 프로시저를 가정하고 입력된 값을 반환합니다.

' Visual BasicDirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _  Unprotect(GetPasswordFromUser())// C#((Excel.Worksheet)ThisApplication.Sheets[1]).  Unprotect(GetPasswordFromUser());

Unprotect메서드는 워크시트에서 보호를 제거하고 사용자가 추가 암호를 제공할 수 있도록 허용합니다.

Excel은 또한 보호를 사용할 때 유용하게 사용할 수 있는ProtectionAllowEditRange개체를 제공합니다.Protection개체는 보호되지 않는 범위에 대한 정보뿐만 아니라Protect메서드를 호출할 때 지정하는 모든 정보를 캡슐화합니다.Protect메서드를 호출하면 다음Boolean속성을 제공하고Protect메서드의 매개 변수를 따르는 공유Protection개체의 속성을 설정합니다.

  • AllowDeletingColumns, AllowDeletingRows
  • AllowFiltering
  • AllowFormattingCells, AllowFormattingColumns, AllowFormattingRows
  • AllowInsertingColumns, AllowInsertingHyperlinks, AllowInsertingRows
  • AllowSorting
  • AllowUsingPivotTables

또한Protection클래스는 사용자가그림 13에 표시된 대화 상자에서 지정되는 정보에 따라 워크시트의 편집 가능한 범위를 지정할 수 있도록 허용하는 AllowEditRanges 속성을 제공합니다.AllowEditRanges속성에는 각각 다음과 같은 유용한 속성을 제공하는AllowEditRange개체의 컬렉션이 포함됩니다.

  • Range: 편집 가능한 영역에 따라 범위를 가져오거나 설정합니다.
  • Title:그림 13과 같은 대화 상자에 표시할 편집 가능한 범위에 대한 제목을 가져오거나 설정합니다.
  • Users:UserAccess개체의 컬렉션을 가져오거나 설정합니다.UserAccess개체에 대한 자세한 내용은 온라인 설명서를 참조하십시오.

샘플 통합 문서의Worksheet Object시트에서 프로그래밍 방식의 보호 기능을 실험할 수 있습니다(그림 14참조). 음영으로 표시된 범위 내에서만 편집할 수 있도록보호를 클릭하여 시트를 보호합니다(Information 및 Date 범위). 시트 보호를 해제하려면보호 해제를 클릭합니다.

사용자 삽입 이미지

그림 14. 워크시트의 보호 기능을 테스트합니다.

샘플 시트의 링크는 다음 프로시저를 실행합니다.

' Visual BasicPrivate Sub ProtectSheet()    Dim ws As Excel.Worksheet = _      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)    With ws.Protection.AllowEditRanges        .Add("Information", ThisApplication.Range("Information"))        .Add("Date", ThisApplication.Range("Date"))    End With    ws.Protect()End SubPrivate Sub UnprotectSheet()    Dim ws As Excel.Worksheet = _      DirectCast(ThisApplication.Sheets("Worksheet Class"), _      Excel.Worksheet)    ' Unprotect the sheet.    ws.Unprotect()    ' Delete all protection ranges, just to clean up.    ' You must loop through this using the index,     ' backwards. This collection doesn't provide     ' an enumeration method, and it doesn't handle    ' being resized as you're looping in a nice way.    Dim i As Integer    With ws.Protection.AllowEditRanges        For i = .Count To 1 Step -1            .Item(i).Delete()        Next i    End WithEnd Sub// C#private void ProtectSheet(){         Excel.Worksheet ws =      (Excel.Worksheet)ThisApplication.ActiveSheet;    Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges;    ranges.Add("Information",         ThisApplication.get_Range("Information", Type.Missing),         Type.Missing);    ranges.Add("Date",         ThisApplication.get_Range("Date", Type.Missing), Type.Missing);    ws.Protect(Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing,Type.Missing, Type.Missing,         Type.Missing,Type.Missing, Type.Missing, Type.Missing,         Type.Missing,Type.Missing,Type.Missing,        Type.Missing,Type.Missing);}private void UnprotectSheet(){    Excel.Worksheet ws =         (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"];    ws.Unprotect(Type.Missing);    // Delete all protection ranges, just to clean up.    // You must loop through this using the index,     // backwards. This collection doesn't provide     // an enumeration method, and it doesn't handle    // being resized as you're looping in a nice way.    Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges;    for (int i = ranges.Count; i >= 1; i--)    {        ranges[i].Delete();    }}

개체 속성

Worksheet클래스는 개체를 반환하는 일부 속성을 제공합니다. 다음 섹션은 이러한 개체에 대해 소개하고 개체 사용 예제를 제공합니다.

메모

삽입|메모메뉴 항목을 사용하면 워크시트의 일정 범위에 연결된 텍스트 설명을 삽입할 수 있습니다(그림 15참조). 코드에서는Range개체의AddComment메서드를 사용하여 동일한 결과를 얻을 수 있습니다. 다음 코드는 Date라는 이름의 범위에 연결된 메모가 있는 경우 해당 메모를 삭제하고 새로운 메모를 만듭니다. 마지막으로 코드는 다음 코드 샘플에서 설명되는ShowOrHideComments메서드를 호출하여 시트의 모든 메모를 표시합니다(그림 16참조).

' Visual BasicDim rng As Excel.Range = ThisApplication.Range("Date")If Not rng.Comment Is Nothing Then    rng.Comment.Delete()End Ifrng.AddComment("Comment added " & DateTime.Now)' Display all the comments:ShowOrHideComments(Show:=True)// C#Excel.Range rng = ThisApplication.get_Range("Date", Type.Missing);if (rng.Comment != null ) {    rng.Comment.Delete();}rng.AddComment("Comment added " + DateTime.Now);// Display all the comments:ShowOrHideComments(true);

사용자 삽입 이미지

그림 15. 사용자 인터페이스를 사용하여 워크시트에 새로운 메모를 쉽게 삽입할 수 있습니다.

사용자 삽입 이미지

그림 16. 샘플 시트에 모든 메모를 표시한 다음

Worksheet클래스는Comments개체를 반환하는Comments속성을 제공합니다. 이Comment개체 컬렉션을 사용하면Worksheet와 연결된 모든Comment개체를 반복해서 실행할 수 있습니다.Comment클래스는 여러 구성원을 제공하지 않습니다. 대부분의 경우 사용자는Comment클래스의Visible속성을 사용하여 메모를 표시하거나 숨기고,Delete메서드를 사용하여 메모를 삭제합니다. 또한Text메서드도 유용하게 사용할 수 있습니다. 이 메서드를 사용하면 메모에 텍스트를 첨부하거나 기존 텍스트를 덮어쓰는 등 텍스트를 추가할 수 있습니다.

메모를 추가한 다음에는 워크시트에서 메모를 표시할 수 있습니다. 샘플 프로젝트에는 활성 시트의 모든 메모를 표시하거나 숨기는 ShowOrHideComments 프로시저가 포함됩니다.

' Visual BasicPrivate Sub ShowOrHideComments(ByVal Show As Boolean)    ' Show or hide all the comments:    Dim ws As Excel.Worksheet = _      DirectCast(ThisApplication.Sheets("Worksheet Class"), _      Excel.Worksheet)    Dim i As Integer    For i = 1 To ws.Comments.Count        ws.Comments(i).Visible = Show    NextEnd Sub// C#private void ShowOrHideComments(bool show){    // Show or hide all the comments:    Excel.Worksheet ws =         (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"];    for (int i = 1; i <= ws.Comments.Count; i++)    {        ws.Comments[i].Visible = show;    }}
참고:   Comments컬렉션은 Excel의 여러 하위 컬렉션과 같이 기본 표시기를 제공하지 않습니다. 다시 말해서For Each루프를 사용하여 컬렉션의 모든 요소를 방문할 수 없습니다.Comment컬렉션과 같은 컬렉션의 경우에는 컬렉션을 반복 실행하기 위해 인덱싱된 루프를 사용해야 합니다.

윤곽선

Excel은 윤곽선 기능을 사용하여 데이터 행을 그룹화하는 기능 지원을 제공합니다. 코드 내에서도 동일한 기능을 활용할 수 있습니다. 예를 들어그림 17에 표시된 행 집합에 대해 사용자는 이미 그림에 표시된 것과 같이 윤곽선을 추가하여그림 18과 같이 행을 축소하거나그림 19와 같이 그룹을 축소할 수 있습니다.

사용자 삽입 이미지

그림 17. 그룹 생성

사용자 삽입 이미지

그림 18. 축소된 그룹

사용자 삽입 이미지

그림 19. 완전하게 축소된 그룹

Worksheet클래스는 그 자체가Outline개체인Outline속성을 제공합니다. Outline 클래스는 여러 구성원을 제공하지 않으며 다음 목록은 자주 사용되는 구성원들에 대한 설명입니다.

  • AutomaticStyles(Boolean)는 윤곽선에 자동 스타일을 적용할지 여부를 Excel에 알려줍니다.
  • SummaryColumn(XlSummaryColumn)은 요약 열의 위치를 가져오거나 설정합니다.XlSummaryColumn열거는xlSummaryOnLeftxlSummaryOnRight의 두 가지 가능한 값을 갖고 있습니다.
  • SummaryRow(XlSummaryRow)는 요약 행의 위치를 가져오거나 설정합니다.XlSummaryRow열거는xlSummaryAbovexlSummaryBelow의 두 가지 가능한 값을 갖고 있습니다.
  • ShowLevels를 사용하면 윤곽선 그룹을 행 수준 및/또는 열 수준으로 필요에 따라 축소하거나 확장할 수 있습니다. 이 메서드에는 다음과 같이 두 매개 변수를 전달할 수 있습니다.
    ' Visual BasicDim ws As Excel.Worksheet = _  DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)' Specify RowLevels and/or ColumnLevels parameters:ws.Outline.ShowLevels(RowLevels:=3)// C#Excel.Worksheet ws =     (Excel.Worksheet) ThisApplication.ActiveSheet;// Specify RowLevels and/or ColumnLevels parameters:ws.Outline.ShowLevels(3, Type.Missing);

샘플 워크시트에는 years 2001(Data2001) 및 2002(Data2002)에 대한 날짜 및 전체 행 집합(AllData)에 대한 날짜에 따라 이름이 지정된 범위가 포함됩니다. 이러한 이름이 지정된 범위에는 워크시트의 전체 폭이 포함됩니다. 그룹화를 사용하려면 전체 행으로 구성되는 범위를 사용해야 합니다. 2003에 대한 데이터에는 이와 관련된 이름이 지정된 범위가 없습니다. 따라서 샘플 코드에서는 전체 행 범위로 사용할 수 있는 방법을 설명합니다.

그룹은 간단하게 만들 수 있습니다. 그룹을 만들려는 하나 이상의 행에 맞는 범위에 대해Group메서드를 호출하면 됩니다. 그룹화 매개 변수로는 그룹화할 시작 및 종료 값, 값별 그룹, 그룹화 기간을 나타내는Boolean배열 값의 네 가지 옵션 매개 변수가 있습니다. 이러한 매개 변수는 거의 필요하지 않기 때문에 예제에서는 이러한 매개 변수를 사용하지 않습니다. 그룹을 제거하려면Ungroup메서드를 호출합니다. 예를 들어 샘플 시트에서Work with Groups링크를 클릭하면 다음 코드가 실행됩니다.

' Visual BasicPrivate Sub WorkWithGroups()    Dim ws As Excel.Worksheet = _      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)    ' Set worksheet-level features for the outline.    ' In this case, summary rows are below    ' the data rows (so Excel knows where to put    ' the summary rows), and we don't want Excel    ' to format the summary rows--that's already been done.    ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow    ws.Outline.AutomaticStyles = False    ' Group the two named ranges. Each of these    ' ranges extends across entire rows.    ThisApplication.Range("Data2001").Group()    ThisApplication.Range("Data2002").Group()    ThisApplication.Range("AllData").Group()    ' The range of rows from 24 to 27 doesn't have     ' a named range, so you can work with that     ' range directly.    Dim rng As Excel.Range = _      DirectCast(ws.Rows("24:27"), Excel.Range)    rng.Group()    ' Collapse to the second group level.    ws.Outline.ShowLevels(RowLevels:=2)End Sub// C#private void WorkWithGroups(){    Excel.Worksheet ws =         (Excel.Worksheet) ThisApplication.ActiveSheet;    // Set worksheet-level features for the outline.    // In this case, summary rows are below    // the data rows (so Excel knows where to put    // the summary rows), and we don't want Excel    // to format the summary rows--that's already been done.    ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow;    ws.Outline.AutomaticStyles = false;    // Group the two named ranges. Each of these    // ranges extends across entire rows.    ThisApplication.get_Range("Data2001", Type.Missing).        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);    ThisApplication.get_Range("Data2002", Type.Missing).        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);    ThisApplication.get_Range("AllData", Type.Missing).        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);    // The range of rows from 24 to 27 doesn't have     // a named range, so you can work with that     // range directly.    Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing];    rng.Group(Type.Missing, Type.Missing, Type.Missing,     Type.Missing);    // Collapse to the second group level.    ws.Outline.ShowLevels(2, Type.Missing);}

세 개의 이름이 지정된 범위를 그룹화하기 위해 이 코드는 해당 범위의 Group 메서드를 호출합니다.

' Visual BasicThisApplication.Range("Data2001").Group()// C#ThisApplication.get_Range("Data2001", Type.Missing).    Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

이름이 지정되지 않은 범위를 그룹화하기 위해 코드에서는 일정 행 범위에 대해 워크시트의Rows속성을 사용합니다. 이 속성은 요청된 행에 따른 범위를 반환합니다.

' Visual BasicDim rng As Excel.Range = _  DirectCast(ws.Rows("24:27"), Excel.Range)rng.Group()// C#Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing];rng.Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

샘플 워크시트에서Clear Groups링크를 클릭하면 유사한 코드가 실행되어 그룹을 해제합니다.

' Visual BasicPrivate Sub ClearGroups()    Dim ws As Excel.Worksheet = _      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)    ' Specify RowLevels and/or ColumnLevels parameters:    ws.Outline.ShowLevels(RowLevels:=3)    Dim rng As Excel.Range = _      DirectCast(ws.Rows("24:27"), Excel.Range)    rng.Ungroup()    ThisApplication.Range("Data2001").Ungroup()    ThisApplication.Range("Data2002").Ungroup()    ThisApplication.Range("AllData").Ungroup()End Sub// C#private void ClearGroups(){    Excel.Worksheet ws =         (Excel.Worksheet) ThisWorkbook.Sheets["Worksheet Class"];    // Specify RowLevels and/or ColumnLevels parameters:    ws.Outline.ShowLevels(3, Type.Missing);    Excel.Range rng = (Excel.Range) ws.Rows["24:27", Type.Missing];    rng.Ungroup();    ThisApplication.get_Range("Data2001", Type.Missing).Ungroup();    ThisApplication.get_Range("Data2002", Type.Missing).Ungroup();    ThisApplication.get_Range("AllData", Type.Missing).Ungroup();}

이러한 기술을 사용하여 그룹을 생성 및 제거하고 워크시트에 표시되는 그룹화 수준을 제어할 수 있습니다.

Range 개체

Range개체는 Excel 응용 프로그램 내에서 가장 자주 사용되는 개체입니다. Excel 내의 범위를 조작하려면 먼저 해당 범위를Range개체로 표현하고 해당Range의 메서드 및 속성을 사용해야 합니다.Range클래스는 매우 중요합니다. 이 문서의 앞에서 보여진 모든 예제에도Range개체가 각각 다른 방식으로 사용되었습니다. 기본적으로 Range 개체는 셀, 행, 열, 하나 이상의 셀을 포함하는 셀 선택 범위(연속 또는 비연속적인 셀 집합) 또는 여러 시트에 있는 셀의 그룹을 나타냅니다.

Range클래스에 포함된 구성원은 너무 많아서 모두 설명할 수 없기 때문에 이 섹션에서는 다음과 같이 3개의 중요한 문제에 대해서만 설명하도록 하겠습니다.

  • 코드에서 범위 참조
  • 코드에 범위 조작
  • Range개체를 사용한 특정 목표 달성

다시 말해,Range개체는 매우 다양한 상황에서 매우 다양한 용도를 포함하기 때문에 이 섹션에서는 구성원에 대한 전체 목록을 제공하는 대신 "사용 방법" 관련 질문에 대한 답변을 제공하는 데에 중점을 둘 것입니다.

선택 대상 관리

범위의 속성 및 동작을 수정하는 방법으로 현재 선택 대상을 사용하는 것이 편하기는 하지만, 이러한 방식은 가급적 사용하지 않는 것이 좋습니다. 다른 공유 리소스와 마찬가지로 Excel 내의 선택 대상은 사용자의 선택 대상을 나타냅니다. 이를 코드에서 수정하려는 경우에는 사용자가 현재 선택 대상에 대한 제어를 잃어버리도록 만듭니다. 가장 중요한 원칙은 사용자의 선택 대상을 변경하려는 경우에만 개체의Select메서드를 호출해야 한다는 것입니다. 개발자는 단순히 사용하기 편하다는 이유만으로Select메서드를 호출해서는 안 됩니다. 단순히 범위에 대한 속성을 설정하는 것이 목표라면 다른 방법을 사용할 수 있습니다.Select메서드를 사용하지 않는다고 해서 코드 실행 속도가 더 빨라지지는 않지만, 사용자에게 더 나은 환경을 제공할 수는 있습니다.

사용자의 현재 셀에 인접한 영역을 해제하기 위해 다음과 같이 코드를 작성하는 것은 항상 너무 쉬운 방식입니다.

' Visual BasicThisApplication.ActiveCell.CurrentRegion.SelectDirectCast(ThisApplication.Selection, Excel.Range).ClearContents// C#ThisApplication.ActiveCell.CurrentRegion.Select();((Excel.Range)ThisApplication.Selection).ClearContents();

하지만 이렇게 할 경우 사용자의 선택 대상을 잃게 됩니다. 원래는 한 개의 셀만 선택되어 있는 상태에서 앞의 코드를 실행하면 연속된 전체 셀 블록이 선택되는 결과가 발생할 것입니다. 원래 프로그램 의도가 전체 셀 범위를 선택하는 것이 아닌 경우에는 다음과 같이 코드를 작성하는 것이 더 나은 방법입니다.

' Visual BasicThisApplication.ActiveCell.CurrentRegion.ClearContents// C#ThisApplication.ActiveCell.CurrentRegion.ClearContents();

하지만 왜 모든 사람들이 첫 번째 코드를 사용하려고 할까요? 이런 종류의 코드가 자주 작성되는 이유는 처음 시작하는 Excel 개발자의 경우 여러 개체 및 메서드를 Excel에서 실험해 볼 때 Excel 매크로 기록을 사용하는 경향이 크기 때문입니다. 이 방식은 훌륭한 방법이기는 하지만, 매크로 기록에서 작성되는 코드는 실제로는 거의 쓸모가 없습니다. 일반적으로 매크로 기록은 선택 대상을 사용하고 작업을 기록할 때 해당 선택 대상을 수정합니다.

팁:   셀이나 셀 그룹을 사용할 경우에는 가능한 모든 경우에 선택 대상을 수정하는 대신 사용하려는 셀을 설명하는 범위를 사용하십시오. 개발자의 의도가 사용자의 선택 대상을 수정하는 경우에는Range.Select메서드를 사용하십시오.

코드에서 범위 참조

Range클래스는 유연성이 매우 뛰어나기 때문에 프로그래밍 방식으로 범위를 사용할 때 매우 다양한 옵션을 사용할 수 있습니다. 일부 경우에Range개체는 단일 개체이고 다른 경우에는 개체의 컬렉션을 나타냅니다.Range개체는 주로 단일 개체를 참조하지만, 여기에는ItemCount구성원이 있어서 일부 경우에는Range개체의 사용 방법이 혼동될 수도 있습니다.

팁:   다음 예제 중 일부는 범위의Address속성을 검색합니다. 이 속성은 "$A$1"(A1 위치의 셀), "$1"(워크시트의 첫 번째 행) 및 "$A$1:$C$5"(A1과 C5를 경계로 사각형 안에 포함된 모든 셀로 구성되는 범위)와 같은 여러 형식 중 하나로 범위의 좌표를 나타내는 문자열을 반환합니다. "$"는 상대 좌표와는 반대 의미인 절대 좌표를 나타냅니다.Address속성을 사용하면 검색한 범위의 정확한 위치를 가장 간단하게 찾을 수 있습니다. 범위를 참조하는 다양한 방법들에 대한 자세한 내용은 Excel 온라인 도움말을 참조하십시오.

Range개체가 단일 셀 또는 셀 그룹을 참조하도록 하는 코드를 작성하는 가장 간단한 방법은 아래 목록에 설명되어 있습니다. 각 예제는 다음의 설정 코드를 기본으로 사용합니다.

' Visual BasicDim ws As Excel.Worksheet = _  DirectCast(ThisWorkbook.Worksheets(1), Excel.Worksheet)Dim rng, rng1, rng2 As Excel.Range// C#Excel.Worksheet ws = (Excel.Worksheet)ThisWorkbook.Worksheets[1];Excel.Range rng, rng1, rng2;

다음 기술 중 하나를 사용하여 특정 범위를 참조할 수 있습니다. Range 개체에 대한 참조를 얻기 위한 방법은 이외에도 많이 있습니다.

  • Application개체의ActiveCell속성을 참조합니다.
    ' Visual Basicrng = ThisApplication.ActiveCell// C#rng = ThisApplication.ActiveCell;
  • 한 개체의Range속성을 사용하여 범위를 지정합니다. C#은 매개 변수가 있는 비인덱스형 속성을 지원하지 않기 때문에 그 대신 두 개의 매개 변수를 필요로 하는get_Range메서드를 호출해야 합니다.
    ' Visual Basicrng = ws.Range("A1")rng = ws.Range("A1:B12")// C#rng = ws.get_Range("A1", Type.Missing);rng = ws.get_Range("A1:B12", Type.Missing);
  • 워크시트의Cells속성을 사용하여 단일 행 및 열 값을 지정합니다.
    ' Visual Basic' The Cells collection returns an Object--' Convert it to a Range object explicitly:rng = DirectCast(ws.Cells(1, 1), Excel.Range)// C#rng = (Excel.Range)ws.Cells[1, 1];
  • 범위의 "모서리"를 지정합니다. 또한 범위에 대한Cells, Rows또는Columns속성을 직접 참조할 수도 있습니다. 모든 경우에 속성은 범위를 반환합니다.
    ' Visual Basicrng = ws.Range("A1", "C5")rng = ws.Range("A1", "C5").Cellsrng = ws.Range("A1", "C5").Rowsrng = ws.Range("A1", "C5").Columns// C#rng = ws.get_Range("A1", "C5");rng = ws.get_Range("A1", "C5").Cells;rng = ws.get_Range("A1", "C5").Rows;rng = ws.get_Range("A1", "C5").Columns;
  • 이름이 지정된 범위를 참조합니다. 이 기술은 본 문서 전체에서 찾아볼 수 있습니다. C#get_Range메서드는 두 개의 매개 변수가 필요하고 범위 이름에는 이들 매개 변수 중 하나만 필요하기 때문에 사용자는 두 번째 매개 변수에 대해Type.Missing을 지정해야 합니다.
    ' Visual Basicrng = ThisApplication.Range("SomeRangeName")// C#rng = ThisApplication.Range("SomeRangeName", Type.Missing);
  • 특정 행이나 열 또는 행 및 열 범위를 참조하십시오.RowsColumns 속성Option StrictOn으로 설정한 경우 변환이 필요한Object를 각각 반환합니다.
    ' Visual Basicrng = DirectCast(ws.Rows(1), Excel.Range)rng = DirectCast(ws.Rows("1:3"), Excel.Range)rng = DirectCast(ws.Columns(3), Excel.Range)// C#rng = (Excel.Range)ws.Rows[1, Type.Missing];rng = (Excel.Range)ws.Rows["1:3", Type.Missing];rng = (Excel.Range)ws.Columns[3, Type.Missing];
    경고:   Columns속성의IntelliSense에 대한 설명이 잘못되었습니다. 설명에 따르면 이 속성에서는 사용자가 행을 지정한 다음 열 값을 지정해야 합니다. 하지만 실제로는Columns속성의 값 순서가 반대로 되어 있습니다.RowsColumns속성 모두의 경우 두 번째 매개 변수는 사용되지 않습니다.
  • Application개체의Selection속성을 사용하여 선택한 셀에 따른 범위를 반환합니다.그림 20에 표시된 상황의 경우, 다음 코드는 문자열 "$C$3"(절대 좌표를 나타내려면 "$"를 사용)을 반환합니다.
    ' Visual BasicDebug.WriteLine( _  DirectCast(ThisApplication.Selection, Excel.Range).Address)// C#System.Diagnostics.Debug.WriteLine(    ((Excel.Range)ThisApplication.Selection).    get_Address(Type.Missing, Type.Missing,     Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));
    :   Address속성은 C#에서 직접 처리할 수 없는 이러한 매개 변수가 있는 속성 중 하나입니다.get_Address메서드를 호출하여 Range 개체에 해당하는 주소를 검색합니다.Address속성의 모든 매개 변수는 옵션이지만get_Address메서드는 5개의 매개 변수를 검색합니다. 사용자는 이 중에서 주소 서식을 지정하는 세 번째 매개 변수만 사용하면 됩니다.
  • Create a range that contains the union of two other ranges (specify two ranges within the quotes, separated with a comma):
    ' Visual Basicrng = ThisApplication.Range("A1:D4, F2:G5")' You can also use the Application object's Union' method to retrieve the intersection of two ranges:rng1 = ThisApplication.Range("A1:D4")rng2 = ThisApplication.Range("F2:G5")rng = ThisApplication.Union(rng1, rng2)// C#rng = ThisApplication.get_Range("A1:D4, F2:G5", Type.Missing);// You can also use the Application object's Union// method to retrieve the intersection of two ranges, but this// is far more effort in C#:rng1 = ThisApplication.get_Range("A1", "D4");rng2 = ThisApplication.get_Range("F2", "G5");// 참고: that the Union method requires you to supply thirty// parameters: rng = ThisApplication.Union(rng1, rng2,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing);
  • 다른 두 범위의 통합을 포함하는 범위를 만듭니다. 큰따옴표 내의 두 범위를 쉼표로 구분하여 지정합니다.
    ' Visual Basicrng = ThisApplication.Range("A1:D16 B2:F14")' You can also use the Application object's Intersect' method to retrieve the intersection of two ranges:rng1 = ThisApplication.Range("A1:D16")rng2 = ThisApplication.Range("B2:F14")rng = ThisApplication.Intersect(rng1, rng2)// C#rng = ThisApplication.get_Range("A1:D16 B2:F14", Type.Missing);// You can also use the Application object's Intersect// method to retrieve the intersection of two ranges. 참고:// that the Intersect method requires you to pass 30 parameters:rng1 = ThisApplication.get_Range("A1", "D16");rng2 = ThisApplication.get_Range("B2", "F14");rng = ThisApplication.Intersect(rng1, rng2,     Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,    Type.Missing, Type.Missing, Type.Missing, Type.Missing,     Type.Missing, Type.Missing, Type.Missing, Type.Missing);
  • 한 범위의Offset속성을 사용하여 원래 범위에 상대적인 범위를 검색합니다. 다음 예제는 행 1, 열 1의 범위에 콘텐츠를 추가합니다.
    ' Visual Basicrng = DirectCast(ws.Cells(1, 1), Excel.Range)Dim i As IntegerFor i = 1 To 5    rng.Offset(i, 0).Value = i.ToStringNext// C#rng = (Excel.Range) ws.Cells[1, 1];for (int i = 1; i <= 5; i++){    rng.get_Offset(i, 0).Value2 = i.ToString();}
    :   Range.Offset속성은 매개 변수가 있는 속성입니다. 따라서 C# 코드는 해당 값을 직접 검색할 수 없습니다. C# 개발자는 대신에get_Offset메서드를 호출해야 합니다.
  • 한 범위의CurrentRegion속성을 사용하여 가장 가까운 빈 행과 열의 경계 안에 있는 현재 영역을 나타내는 범위를 검색합니다. 예를 들어그림 20에서 다음 표현식은 현재 영역의 글꼴을 굵게 설정합니다.
    ' Visual BasicThisApplication.Range("C3").CurrentRegion.Font.Bold = True// C#ThisApplication.get_Range("C3", Type.Missing).    CurrentRegion.Font.Bold = True;

    사용자 삽입 이미지

    그림 20. C3 셀의 CurrentRegion 속성을 요청하면 A1:E5 범위를 반환합니다.

  • 한 범위의Areas속성을 사용하여 범위의 콘텐츠 중 한 영역에 해당하는 범위 컬렉션을 검색합니다. 예를 들어 다음 코드는그림 21에 표시된 이름이 지정된 범위 Test 내의 두 영역 모두에 대한 주소를 "$B$1:$E$5" 및 "$C$7:$G$11"로 표시합니다. "$"는 절대 좌표를 나타냅니다.
    ' Visual Basicrng = ThisApplication.Range("Test")Dim i As IntegerFor i = 1 To rng.Areas.Count  Debug.WriteLine(rng.Areas(i).Address)Next// C#rng = ThisApplication.get_Range("Test", Type.Missing);for (int i = 1; i <= rng.Areas.Count; i++){     System.Diagnostics.Debug.WriteLine(        rng.Areas[i].get_Address(Type.Missing, Type.Missing,         Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));}

    사용자 삽입 이미지

    그림 21. 범위에는 연속되지 않은 영역이 포함될 수 있으며, 사용자는 Areas 속성을 사용하여 각각을 개별적으로 검색할 수 있습니다.

  • XlDirection열거(xlUp,xlToRight,xlToLeft, xlDown)의 값과 함께End 속성을 사용하여 사용자가 열거값으로 기술되는 키를 누른 것과 같이 영역의 끝에 있는 셀을 나타내는 범위를 검색합니다. 그림 22에서 표시된 선택된 셀을 사용하여 다음 코드는 코드의 주석에 표시된 대로 정의된 네 개의 범위로 종료됩니다.
    ' Visual BasicDim rngLeft, rngRight, rngUp, rngDown as Excel.Rangerng = DirectCast(ThisApplication.Selection, Excel.Range)' E3rngRight = rng.End(Excel.XlDirection.xlToRight)' A3rngLeft = rng.End(Excel.XlDirection.xlToLeft)' C1rngUp = rng.End(Excel.XlDirection.xlUp)' C5rngDown = rng.End(Excel.XlDirection.xlDown)// C#Excel.Range rngLeft, rngRight, rngUp, rngDown;rng = (Excel.Range) ThisApplication.Selection;// 참고: that the Range.End property is parameterized, so //C# developers cannot retrieve it. You must call the // get_End method, instead:// E3rngRight = rng.get_End(Excel.XlDirection.xlToRight);// A3rngLeft = rng.get_End(Excel.XlDirection.xlToLeft);// C1rngUp = rng.get_End(Excel.XlDirection.xlUp);// C5rngDown = rng.get_Down(Excel.XlDirection.xlDown);

    사용자 삽입 이미지

    그림 22. End 속성을 사용하여 범위에 해당하는 여러 범위를 반환합니다.

  • EntireRow또는EntireColumn속성을 사용하여 지정된 범위를 포함하는 행 또는 열을 참조합니다. 예를 들어 다음 코드는 행 7에서 11까지를그림 21에 표시된 예제를 사용하여 글꼴을 굵게 설정합니다.
    ' Visual Basicrng = ThisApplication.Range("Test")rng.Areas(2).EntireRow.Font.Bold = True// C#rng = ThisApplication.get_Range("Test", Type.Missing);rng.Areas[2].EntireRow.Font.Bold = true;

기술 사용

개발자는 텍스트를 굵게 표시하도록 선택된 셀을 포함하는 전체 행의 글꼴을 변경하는 기능을 공통적으로 요청합니다. Excel에는 이 기능이 포함되어 있지 않지만, 새로 기능을 추가하는 것이 그리 어렵지는 않습니다. 샘플 통합 문서의Range클래스에는 특별히 처리되는 범위가 포함되어 있습니다. 이 항목을 선택하면 해당 행이 굵게 표시됩니다.그림 23은 작업으로 이 동작을 표시합니다.

사용자 삽입 이미지

그림 23. 항목을 선택하면 전체 행을 굵게 표시합니다.

샘플 통합 문서에는 서식을 처리하는 다음 프로시저가 포함됩니다.

' Visual BasicPrivate Sub BoldCurrentRow(ByVal ws As Excel.Worksheet)    ' Keep track of the previously bolded row.    Static intRow As Integer    ' Work with the current active cell.    Dim rngCell As Excel.Range = _      ThisApplication.ActiveCell    ' Bold the current row.    rngCell.EntireRow.Font.Bold = True    ' Make sure intRow isn't 0 (meaning that     ' this is your first pass through here).    If intRow <> 0 Then        ' If you're on a different        ' row than the last time through here,        ' make the old row not bold.        If rngCell.Row <> intRow Then            Dim rng As Excel.Range = _              DirectCast(ws.Rows(intRow), Excel.Range)            rng.EntireRow.Font.Bold = False        End If    End If    ' Store away the new row number     ' for next time.    intRow = rngCell.RowEnd Sub// C#private int LastBoldedRow = 0;private void BoldCurrentRow(Excel.Worksheet ws) {    // Keep track of the previously bolded row.    // Work with the current active cell.    Excel.Range rngCell = ThisApplication.ActiveCell;    // Bold the current row.    rngCell.EntireRow.Font.Bold = true;    // Make sure intRow isn't 0 (meaning that     // this is your first pass through here).    if (LastBoldedRow != 0)     {        // If you're on a different        // row than the last time through here,        // make the old row not bold.        if (rngCell.Row != LastBoldedRow)         {            Excel.Range rng =                 (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing];            rng.Font.Bold = false;        }    }    // Store away the new row number     // for next time.    LastBoldedRow = rngCell.Row;}

이 예제는 다음 작업을 수행하여 현재 행을 굵게 표시하고 이전에 굵게 표시된 행은 정상으로 표시합니다.

  • 이전에 선택한 행을 추적할 수 있도록 변수(Visual Basic의 경우 static)를 선언합니다.
    ' Visual BasicStatic intRow As Integer// C#private int LastBoldedRow = 0;
  • Application.ActiveCell속성을 사용하여 현재 셀에 대한 참조를 검색합니다.
    ' Visual Basicprivate int LastBoldedRow = 0;Dim rngCell As Excel.Range = ThisApplication.ActiveCell// C#Excel.Range rngCell = ThisApplication.ActiveCell;
  • 활성 셀의EntireRow속성을 사용하여 현재 행을 굵게 표시합니다.
    ' Visual BasicrngCell.EntireRow.Font.Bold = True// C#rngCell.EntireRow.Font.Bold = true;
  • intRow의 현재 값이 0이 아닌지 확인하십시오. 이 코드를 처음 실행한 경우 값이 0입니다.
    ' Visual BasicIf intRow <> 0 Then    ' Code removed here...End If// C#if (LastBoldedRow != 0){    // Code removed here...}
  • 현재 행이 이전 행과 다른지 확인하십시오. 행이 이전 행과 다른 경우에만 코드에서 행의 상태를 수정해야 합니다.Row속성은 범위에 따라 행을 표시하는 정수를 반환합니다.
    ' Visual BasicIf rngCell.Row <> intRow Then    ' Code removed here...End If// C#if (rngCell.Row != LastBoldedRow){    // Code removed here...}
  • 이전에 선택한 행을 나타내는 범위에 대한 참조를 검색하고 해당 행을 굵게 표시되지 않도록 설정합니다.
    ' Visual BasicDim rng As Excel.Range = _  DirectCast(ws.Rows(intRow), Excel.Range)rng.Font.Bold = False// C#Excel.Range rng =     (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing];rng.Font.Bold = false;

샘플 통합 문서는SheetSelectionChange이벤트 처리기에서BoldCurrentRow프로시저를 호출합니다. 이 프로시저에서 코드는Application개체의 Intersect 메서드를 사용하여 새로운 선택 대상이 올바른 범위 내에 있는지 확인하고 범위가 올바르면BoldCurrentRow프로시저를 호출합니다.

' Visual BasicPrivate Sub ThisWorkbook_SheetSelectionChange( _  ByVal Sh As Object, ByVal Target As Excel.Range) _  Handles ThisWorkbook.SheetSelectionChange    If Not ThisApplication.Intersect(Target, _      ThisApplication.Range("BoldSelectedRow")) Is Nothing Then        ' The selection is within the range where you're making        ' the selected row bold.        BoldCurrentRow(DirectCast(Sh, Excel.Worksheet))    End IfEnd Sub// C#protected void ThisWorkbook_SheetSelectionChange(  System.Object sh, Excel.Range Target){    // Don't forget that the Intersect method requires    // thirty parameters.    if (ThisApplication.Intersect(Target,         ThisApplication.get_Range("BoldSelectedRow", Type.Missing),         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing,         Type.Missing, Type.Missing, Type.Missing, Type.Missing)         != null)    {        // The selection is within the range where you're making        //the selected row bold.        BoldCurrentRow((Excel.Worksheet) sh);    }}

범위 사용

범위에 대한 참조를 얻은 다음 이를 사용하는 방법에 대해 설명하도록 하겠습니다. 범위를 사용하여 할 수 있는 작업은 무제한적입니다. 이 섹션에서는Range개체를 사용하기 위한 일부 기술을 설명하고 각 기술에 대한 간단한 예제를 제공합니다. 이 섹션에서 사용되는 모든 예제는 샘플 통합 문서의Range Class시트에 있습니다.

범위 자동 채우기

Range클래스의AutoFill메서드를 사용하면 범위를 특정 값으로 자동으로 채울 수 있습니다. 대부분의 경우AutoFill메서드는 범위에 점차적으로 증가하는 값이나 감소하는 값을 저장할 때 사용됩니다. 사용자는XlAutoFillType열거값(xlFillDays,xlFillFormats,xlFillSeries,xlFillWeekdays,xlGrowthTrend,xlFillCopy,xlFillDefault,xlFillMonths,xlFillValues,xlFillYears또는xlLinearTrend)에서 상수(옵션)를 제공하여 이러한 동작을 지정할 수 있습니다. 채우기 유형을 지정하지 않을 경우 Excel은 기본 채우기 유형(xlFillDefault)을 사용할 것으로 가정하고 적합한 경우 지정된 범위를 채웁니다.

그림 24와 같이 샘플 워크시트에는 자동 채우기를 수행할 네 개의 범위가 있습니다. B열에는 5개의 평일이 있고 C열에는 5개의 월이 있으며, D열에는 5년 동안 해마다 증가하는 날짜가 있고, E열에는 행별로 2씩 증가하는 일련의 숫자가 있습니다.그림 25는 샘플 코드를 실행한 후의 동일 영역을 나타냅니다.

사용자 삽입 이미지

그림 24. 4개의 샘플 범위에 대해 AutoFill 메서드를 호출하기 전.

사용자 삽입 이미지

그림 25. 범위에 대해 자동 채우기를 실행한 후

AutoFill 링크를 클릭하면 다음 프로시저가 실행됩니다.

' Visual BasicPrivate Sub AutoFill()    Dim rng As Excel.Range = ThisApplication.Range("B1")    rng.AutoFill(ThisApplication.Range("B1:B5"), _      Excel.XlAutoFillType.xlFillDays)    rng = ThisApplication.Range("C1")    rng.AutoFill(ThisApplication.Range("C1:C5"), _      Excel.XlAutoFillType.xlFillMonths)    rng = ThisApplication.Range("D1")    rng.AutoFill(ThisApplication.Range("D1:D5"), _      Excel.XlAutoFillType.xlFillYears)    rng = ThisApplication.Range("E1:E2")    rng.AutoFill(ThisApplication.Range("E1:E5"), _      Excel.XlAutoFillType.xlFillSeries)End Sub// C#private void AutoFill(){    Excel.Range rng = ThisApplication.get_Range("B1", Type.Missing);    rng.AutoFill(ThisApplication.get_Range("B1:B5", Type.Missing),         Excel.XlAutoFillType.xlFillDays);    rng = ThisApplication.get_Range("C1", Type.Missing);    rng.AutoFill(ThisApplication.get_Range("C1:C5", Type.Missing),         Excel.XlAutoFillType.xlFillMonths);    rng = ThisApplication.get_Range("D1", Type.Missing);    rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing),         Excel.XlAutoFillType.xlFillYears);    rng = ThisApplication.get_Range("E1:E2", Type.Missing);    rng.AutoFill(ThisApplication.get_Range("E1:E5", Type.Missing),         Excel.XlAutoFillType.xlFillSeries);}

각 경우에 사용자는 다음 두 범위를 지정해야 합니다.

  • 채우기의 "시작 지점"을 지정하는AutoFill메서드를 호출하는 범위
  • AutoFill메서드의 매개 변수로 전달된 채우기를 수행할 범위. 이 대상 범위에는 소스 범위가 포함되어야 합니다.

AutoFill메서드에 대한 두 번째 매개 변수인XlAutoFillType열거값은 옵션입니다. 일반적으로 필요한 동작을 얻으려면 이 값을 제공해야 합니다. 예를 들어 다음 코드를 변경해 보십시오.

' Visual Basicrng.AutoFill(ThisApplication.Range("D1:D5"), _  Excel.XlAutoFillType.xlFillYears)// C#rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing),     Excel.XlAutoFillType.xlFillYears);

위 코드를 다음과 같이 변경하십시오.

' Visual Basicrng.AutoFill(ThisApplication.Range("D1:D5"))// C#rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing),     Excel.XlAutoFillType.xlFillDefault);

연도별로 증가하는 날짜 대신 일자별로 증가하는 날짜를 얻게 됩니다.

범위 내의 검색

Range클래스의 Find 메서드를 사용하면 범위 내에서 텍스트를 검색할 수 있습니다. 이 유용한 메서드는그림 26에 표시된 Excel의 찾기 및 바꾸기 대화 상자의 동작을 모방합니다. 실제로 이 메서드는 이 대화 상자와 직접 상호 작용합니다. 즉,Range.Find메서드는 사용자가 전달하는 매개 변수를 사용하여 검색 동작을 결정하거나, 사용자가 매개 변수를 전달하지 않은 경우 찾기 및 바꾸기 대화 상자에서 찾은 값을 사용합니다.표 4Range.Find메서드의 매개 변수를 보여주며, 이 중 첫 번째를 제외한 모든 매개 변수는 옵션입니다.

사용자 삽입 이미지

그림 26. Find 메서드의 동작에 영향을 주는 이 대화 상자의 선택 항목들

경고:   Range.Find의 거의 대부분의 매개 변수는 옵션이고, 사용자는 찾기 및 바꾸기 대화 상자에서 값을 변경할 수 있기 때문에, 사용자의 선택 항목을 고려해야 하는 경우가 아니라면 Find 메서드에 모든 값을 제대로 전달했는지 직접 확인해야 합니다. 물론 C# 개발자는 각 메서드 호출에 대해 모든 매개 변수를 제공해야 하기 때문에 이 문제를 걱정할 필요가 없습니다.

표 4. Range.Find 메서드의 매개 변수

매개 변수유형설명
What (필수)Object검색할 데이터입니다. 문자열이나 모든 Excel 데이터 형식일 수 있습니다.
AfterRange검색을 시작하려는 범위(이 셀은 검색 대상에 포함되지 않음)입니다. 이 셀을 지정하지 않으면 검색은 해당 범위의 왼쪽 위 모서리에서부터 시작됩니다.
LookInXlFindLookin (xlValue, xlComments, xlFormulas)검색할 정보의 유형입니다. 이 정보는 Or 연산자를 사용하여 조합될 수 없습니다.
LookAtXlLookAt (xlWhole, xlPart)검색이 전체 셀 또는 부분 셀에 해당하는지 여부를 결정합니다.
SearchOrderXlSearchOrder (xlByRows, xlByColumns)검색 순서를 결정합니다. xlByRows(기본값)를 사용하면 검색이 수평 방향으로 진행된 다음 수직 방향으로 진행됩니다. xlByColumns를 사용하면 검색이 수직 방향으로 진행된 다음 수평 방향으로 진행됩니다.
SearchDirectionXlSearchDirection (xlNext, xlPrevious)검색의 방향을 결정합니다. 기본값은 xlNext입니다.
MatchCaseBoolean검색의 대소문자 구분 여부를 결정합니다.
MatchByteBoolean2바이트 문자끼리 일치하도록 하거나(True), 상응하는 1바이트 문자와도 일치하도록 할지(False) 여부를 결정합니다. 2바이트 지원을 설치한 경우에만 적용됩니다.

샘플 통합 문서의 다음 예제는 "Fruits"라는 범위를 검색하고 "apples" 단어가 포함된 셀의 글꼴을 수정합니다.그림 27은 검색 결과를 보여줍니다. 이 프로시저는 또한 이전에 설정된 검색 설정을 사용하여 검색을 반복하는FindNext메서드를 사용합니다.Range.FindPrevious메서드는Range.FindNext메서드와 거의 동일하게 작동하지만, 이 예제에서는 사용되지 않습니다. 검색을 수행할 셀을 지정하면 나머지는FindNext메서드가 처리합니다.

사용자 삽입 이미지

그림 27. "apples" 단어가 포함된 셀의 검색 결과

:   FindNextFindPrevious메서드는 범위 끝까지 검색을 한 다음에는 다시 검색 범위의 시작 부분으로 돌아갑니다. 무한 루프로 계속해서 검색이 반복 수행되지 않도록 코드를 작성해야 합니다. 샘플 프로시저는 이를 처리하기 위한 한 가지 방법을 보여줍니다. 이러한 무한 반복이 이뤄지지 않도록 하거나Find/FindNext/FindPrevious메서드로는 너무 복잡한 검색을 수행하려는 경우For Each루프를 사용하여 범위 내의 모든 셀을 반복적으로 검색할 수 있습니다.

샘플 통합 문서의Range Class시트에 있는Find링크를 클릭하면 다음 프로시저가 실행됩니다.

' Visual BasicPrivate Sub DemoFind()    Dim rng As Excel.Range = ThisApplication.Range("Fruits")    Dim rngFound As Excel.Range    ' Keep track of the first range you find.    Dim rngFoundFirst As Excel.Range    ' You should specify all these parameters    ' every time you call this method, since they    ' can be overriden in the user interface.    rngFound = rng.Find( _      "apples", , _      Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _      Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext,       False)    While Not rngFound Is Nothing        If rngFoundFirst Is Nothing Then            rngFoundFirst = rngFound        ElseIf rngFound.Address = rngFoundFirst.Address Then            Exit While        End If        With rngFound.Font            .Color = ColorTranslator.ToOle(Color.Red)            .Bold = True        End With        rngFound = rng.FindNext(rngFound)    End WhileEnd Sub// C#private void DemoFind(){    Excel.Range rng = ThisApplication.        get_Range("Fruits", Type.Missing);    Excel.Range rngFound;    // Keep track of the first range you find.    Excel.Range rngFoundFirst = null;    // You should specify all these parameters    // every time you call this method, since they    // can be overriden in the user interface.    rngFound = rng.Find("apples", Type.Missing,         Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart,         Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext,         false, Type.Missing, Type.Missing);    while (rngFound != null)    {        if (rngFoundFirst == null )         {            rngFoundFirst = rngFound;        }        else if (GetAddress(rngFound) == GetAddress(rngFoundFirst))        {            break;        }        rngFound.Font.Color = ColorTranslator.ToOle(Color.Red);        rngFound.Font.Bold = true;        rngFound = rng.FindNext(rngFound);    }}

코드는 목적을 달성하기 위해 다음 작업을 수행합니다.

  • 전체 범위, 첫 번째 찾은 범위 및 현재 찾은 범위를 추적하기 위한Excel.Range변수를 선언합니다.
    ' Visual BasicDim rng As Excel.Range = ThisApplication.Range("Fruits")Dim rngFound As Excel.RangeDim rngFoundFirst As Excel.Range// C#Excel.Range rng = ThisApplication.    get_Range("Fruits", Type.Missing);Excel.Range rngFound;Excel.Range rngFoundFirst = null;
  • 다음에 찾을 셀을 제외한 모든 매개 변수를 지정하여 첫 번째 일치되는 범위를 찾은 다음(기본적으로 검색은 범위의 왼쪽 위에 있는 셀부터 시작됨) 부분 값을 찾으면서 셀 값에서 "apples"를 찾습니다. 이 때 검색은 대소문자를 구분하지 않으며 진행 방향으로 열 순서대로 진행됩니다.
    ' Visual BasicrngFound = rng.Find( _  "apples", , _  Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _  Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, _  False)// C#rngFound = rng.Find("apples", Type.Missing,     Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart,     Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext,     false, Type.Missing, Type.Missing);
  • 일치 대상을 찾는 동안 검색이 계속됩니다.
    ' Visual BasicWhile Not rngFound Is Nothing    ' Code removed here...End While// C#while (rngFound != null){    // Code removed here...}
  • 첫 번째 찾은 범위(rngFoundFirst)를 Nothing과 비교합니다. 이는 코드에서 첫 번째 일치 대상을 찾은 경우에만 가능합니다. 이 경우 코드는 찾은 범위를 저장합니다. 그렇지 않으면 찾은 범위의 주소가 첫 번째 찾은 범위의 주소와 일치하는 경우 코드에서 루프가 종료됩니다.
    ' Visual BasicIf rngFoundFirst Is Nothing Then    rngFoundFirst = rngFoundElseIf rngFound.Address = rngFoundFirst.Address Then    Exit WhileEnd If// C#if (rngFoundFirst == null ) {    rngFoundFirst = rngFound;}else if (GetAddress(rngFound) == GetAddress(rngFoundFirst)){    break;}
  • 찾은 범위의 모양을 설정합니다.
    ' Visual BasicWith rngFound.Font    .Color = ColorTranslator.ToOle(Color.Red)    .Bold = TrueEnd With// C#rngFound.Font.Color = ColorTranslator.ToOle(Color.Red);rngFound.Font.Bold = true;
  • 다른 검색을 수행합니다.
    ' Visual BasicrngFound = rng.FindNext(rngFound)// C#rngFound = rng.FindNext(rngFound);

샘플 시트에서Reset Find링크를 클릭하면 범위를 시작 상태로 되돌리는 아래와 같은 간단한 프로시저가 실행됩니다.

' Visual BasicPrivate Sub ResetFind()    Dim rng As Excel.Range = ThisApplication.Range("Fruits")    With rng.Font        .Color = ColorTranslator.ToOle(Color.Black)        .Bold = False    End WithEnd Sub// C#private void ResetFind(){    Excel.Range rng = ThisApplication.        get_Range("Fruits", Type.Missing);    rng.Font.Color = ColorTranslator.ToOle(Color.Black);    rng.Font.Bold = false;}
:   범위 내에서 찾기와 바꾸기를 모두 수행하려는 경우Range.Replace메서드를 확인하십시오. 이 메서드는Find메서드와 거의 비슷하게 작동하지만 바꿀 값을 지정할 수 있습니다.Replace메서드는 바꾸기를 수행했는지 여부를 나타내는Boolean값을 반환합니다. 한 개의 값이라도 바뀐 경우 결과는 True로 반환됩니다.

범위 내의 데이터 정렬

Excel 사용자 인터페이스에서 범위 내의 데이터를 정렬할 수 있는 것과 동일하게 프로그래밍 방식으로도Range.Sort메서드를 사용하여 데이터를 정렬할 수 있습니다. 정렬할 범위를 나타내고, 선택적으로 최대 3개의 행 또는 열을 정렬하도록 지정한 다음 일부 옵션 매개 변수를 지정하면 나머지 작업은 Excel이 처리합니다.표 5Sort메서드의 모든 매개 변수를 보여줍니다. Visual Basic .NET 개발자는 이들 매개 변수 중에서 일부만 사용할 수 있지만, C# 개발자는 각 매개 변수에 대한 값을 제공해야 합니다.

표 5. Sort 메서드의 매개 변수

매개 변수유형설명
Key1Object (String 또는 Range)범위 이름(String) 또는 Range 개체의 첫 번째 정렬 필드로, 정렬할 값을 결정합니다.
Order1XlSortOrder (xlAscending, xlDescending)Key1에서 지정한 값에 대한 정렬 순서를 결정합니다.
Key2Object (String or Range)두 번째 정렬 필드로서 피벗 테이블을 정렬할 때는 사용할 수 없습니다.
TypeObject피벗 테이블을 정렬할 때 정렬될 요소를 지정하며 일반 범위에는 영향을 주지 않습니다.
Order2XlSortOrderKey2에서 지정한 값에 대한 정렬 순서를 결정합니다.
Key3Object (String or Range)세 번째 정렬 필드로서 피벗 테이블을 정렬할 때는 사용할 수 없습니다.
Order3XlSortOrderKey3에서 지정한 값에 대한 정렬 순서를 결정합니다.
HeaderXlYesNoGuess (xlGuess, xlNo, xlYes)첫 번째 행에 헤더 정보가 포함되는지 여부를 지정합니다. 기본값은 xlNo입니다. Excel이 무작위로 선택하도록 하려면 xlGuess를 선택합니다.
OrderCustomInteger사용자 지정 정렬 목록으로 1기준 인덱스를 지정합니다. 이 매개 변수를 빈 값으로 두면 기본 정렬 순서를 사용합니다. 그림 28은 사용자 지정 정렬 순서 생성을 위한 기술을 보여줍니다. 이 예제의 경우 이 매개 변수에 6을 지정하면 "fruits" 사용자 지정 순서에 따라 정렬을 수행합니다.
MatchCaseBoolean정렬 시 대소문자를 구분하려면 True로 설정하고 구분하지 않으려면 False로 설정합니다. 피벗 테이블에는 사용할 수 없습니다.
OrientationXlSortOrientation (xlSortRows, xlSortColumns)정렬 방향입니다.
SortMethodXlSortMethod (xlStroke, xlPinYin)정렬 방법을 지정합니다. 현재 값은 중국어 정렬에 사용되기 때문에 다른 언어에는 아무런 영향을 주지 않습니다. 이 매개 변수는 일부 언어에 대해서는 적용되지 않습니다.
DataOption1XlSortDataOption (xlSortTextAsNumbers, xlSortNormal)Key1에서 지정된 범위에서 텍스트를 정렬하는 방법을 지정합니다. 피벗 테이블 정렬에는 적용되지 않습니다.
DataOption2XlSortDataOptionKey2에서 지정된 범위에서 텍스트를 정렬하는 방법을 지정합니다. 피벗 테이블 정렬에는 적용되지 않습니다.
DataOption3XlSortDataOptionKey3에서 지정된 범위에서 텍스트를 정렬하는 방법을 지정합니다. 피벗 테이블 정렬에는 적용되지 않습니다.
:   Visual Basic .NET 개발자는 C# 개발자에 비해 이와 같은 메서드를 호출할 경우 얻을 수 있는 뚜렷한 이점이 있습니다. 모든 매개 변수를 사용할 가능성은 거의 없기 때문에 Visual Basic .NET 개발자는 필요한 매개 변수만 지정하여 이름이 지정된 매개 변수를 사용할 수 있습니다. 그에 반해 C# 개발자는 기본 동작을 허용하기 위해 사용되지 않는 모든 매개 변수에도 Null 값을 전달해야 합니다.

사용자 삽입 이미지

그림 28. 사용자는 자신의 사용자 지정 정렬 목록을 만든 다음 코드에서 특정 정렬 순서를 참조할 수 있습니다.

Range Class샘플 시트에서Sort링크를 클릭하면 다음과 같은 프로시저가 실행되어 첫 번째 열의 데이터에 따라 Fruits 범위를 정렬하고 그 다음에는 두 번째 열의 데이터에 따라 정렬을 수행합니다.

' Visual BasicPrivate Sub DemoSort()    Dim rng As Excel.Range = ThisApplication.Range("Fruits")    rng.Sort( _      Key1:=rng.Columns(1), Order1:=Excel.XlSortOrder.xlAscending, _      Key2:=rng.Columns(2), Order2:=Excel.XlSortOrder.xlAscending, _      Orientation:=Excel.XlSortOrientation.xlSortColumns, _      Header:=Excel.XlYesNoGuess.xlNo)End Sub// C#private void DemoSort(){        Excel.Range rng = ThisApplication.            get_Range("Fruits", Type.Missing);        rng.Sort(rng.Columns[1, Type.Missing],             Excel.XlSortOrder.xlAscending,             rng.Columns[2, Type.Missing],Type.Missing,             Excel.XlSortOrder.xlAscending,            Type.Missing, Excel.XlSortOrder.xlAscending,            Excel.XlYesNoGuess.xlNo, Type.Missing, Type.Missing,            Excel.XlSortOrientation.xlSortColumns,             Excel.XlSortMethod.xlPinYin,            Excel.XlSortDataOption.xlSortNormal,             Excel.XlSortDataOption.xlSortNormal,            Excel.XlSortDataOption.xlSortNormal);}

같은 시트에서Reset Sort링크를 클릭하면 다음 프로시저가 실행되어그림 28에 표시된 사용자 지정 정렬에 따라 두 번째 행을 정렬합니다.

' Visual BasicPrivate Sub ResetSort()    Dim rng As Excel.Range = ThisApplication.Range("Fruits")    rng.Sort(rng.Columns(2), OrderCustom:=6, _    Orientation:=Excel.XlSortOrientation.xlSortColumns, _    Header:=Excel.XlYesNoGuess.xlNo)End Sub// C#private void ResetSort(){    Excel.Range rng = ThisApplication.        get_Range("Fruits", Type.Missing);        rng.Sort(rng.Columns[2, Type.Missing],            Excel.XlSortOrder.xlAscending,             Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending,            Type.Missing, Excel.XlSortOrder.xlAscending,            Excel.XlYesNoGuess.xlNo, 6, Type.Missing,            Excel.XlSortOrientation.xlSortColumns,             Excel.XlSortMethod.xlPinYin,            Excel.XlSortDataOption.xlSortNormal,             Excel.XlSortDataOption.xlSortNormal,            Excel.XlSortDataOption.xlSortNormal);}

다음 단계

이 문서의 내용이 길게 느껴질 수도 있지만, 본 문서의 내용은 Excel 개체 모델에서 제공되는 매우 다양한 기능을 간략하게 다룬 것에 지나지 않습니다. 본 문서에서는Application,Workbook,WorksheetRange와 같은 가장 중요한 클래스들에 대해 소개했지만, 그 외 유용한 많은 클래스들에 대해서는 다루지 않았습니다. 예를 들어PivotTableChart와 같이 Excel 개체 모델에서 제공되는 두 번째 클래스 "층"에 대한 자세한 조사가 더 필요할 수도 있습니다. Excel 개체 모델을 완벽하게 이해하면 자신에게 필요한 클래스를 찾기 위해 노력한다면 어떠한 자동화된 작업이든 쉽게 수행할 수 있을 것입니다. 본 문서의 내용과 개체 찾아보기 및 Excel VBA 온라인 도움말을 참조하여 Excel에서 수행할 수 있는 모든 작업들을 찾아 보시기 바랍니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari