수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1620555
  • Today | 417
  • Yesterday | 670

2 Articles, Search for 'Excel'

  1. 2007/07/10 ADO Connection Strings
  2. 2007/02/05 Office 2003 : Visual Studio Tools for Office System을 활용한 Excel 게임
Programming/Database Programming2007/07/10 09:57

ADO Connection Strings

출처 : http://www.codeproject.com/database/connectionstrings.asp?print=true

Overview

Generally, one of the first steps when you are trying to work with databases is open it. You can find several types of those, and each have a different mode of connection. When you try to connect with your database sometimes, you don't know the correct connection string that you must use. It is for that I wrote this article. I wanted to compile the connection strings to the majority of known databases...

ODBC DSN Less Connection

ODBC Driver for dBASE

strConnection = _T("Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;"
"Dbq=c:\\DatabasePath;");

Note: You must specify the filename in the SQL statement... For example:

CString strQuery = _T("Select Name, Address From Clients.dbf");

ODBC Driver for Excel

strConnection = _T("Driver={Microsoft Excel Driver (*.xls)};DriverId=790;"
bq=C:\\DatabasePath\\DBSpreadSheet.xls;DefaultDir=c:\\databasepath;");

ODBC Driver for Text

strConnection = _T("Driver={Microsoft Text Driver (*.txt; *.csv)};"
"Dbq=C:\\DatabasePath\\;Extensions=asc,csv,tab,txt;");

If you are using tab delimited files, you must create the schema.ini file, and you must inform the Format=TabDelimited option in your connection string.

Note: You must specify the filename in the SQL statement... For example:

CString strQuery = _T("Select Name, Address From Clients.csv");

Visual FoxPro

If you are using a database container, the connection string is the following:

strConnection = _T("Driver={Microsoft Visual Foxpro Driver};UID=;"
ourceType=DBC;SourceDB=C:\\DatabasePath\\MyDatabase.dbc;Exclusive=No");

If you are working without a database container, you must change the SourceType parameter by DBF as in the following connection string:

strConnection = _T("Driver={Microsoft Visual Foxpro Driver};UID=;"
"SourceType=DBF;SourceDB=C:\\DatabasePath\\MyDatabase.dbc;Exclusive=No");

ODBC Driver for Access

strConnection = _T("Driver={Microsoft Access Driver (*.mdb)};"
"Dbq=c:\\DatabasePath\\dbaccess.mdb;Uid=;Pwd=;");

If you are using a Workgroup (System database): you need to inform the SystemDB Path, the User Name and its password. For that, you have two solutions: inform the user and password in the connection string or in the moment of the open operation. For example:

strConnection = _T("Driver={Microsoft Access Driver (*.mdb)};"
"Dbq=C:\\VC Projects\\ADO\\Samples\\AdoTest\\dbTestSecurity.mdb;"
"SystemDB=C:\\Program Files\\Microsoft Office\\Office\\SYSTEM.mdw;"
"Uid=Carlos Antollini;Pwd=carlos");

or may be:

strConnection = _T("Driver={Microsoft Access Driver (*.mdb)};"
"Dbq=C:\\VC Projects\\ADO\\Samples\\AdoTest\\dbTestSecurity.mdb;"
"SystemDB=C:\\Program Files\\Microsoft Office\\Office\\SYSTEM.mdw;");
if(pDB.Open(strConnection, "DatabaseUser", "DatabasePass"))
{
DoSomething();
pDB.Close();
}

If you want to open in Exclusive mode:

strConnection = _T("Driver={Microsoft Access Driver (*.mdb)};"
"Dbq=c:\\DatabasePath\dbaccess.mdb;Exclusive=1;");

ODBC Driver for SQL Server

For Standard security:

strConnection = _T("Driver={SQL Server};Server=MyServerName;"
"Trusted_Connection=no;"
"Database=MyDatabaseName;Uid=MyUserName;Pwd=MyPassword;");

For Trusted Connection security (Microsoft Windows NT integrated security):

strConnection = _T("Driver={SQL Server};Server=MyServerName;"
"Database=myDatabaseName;Uid=;Pwd=;");

Also, you can use the parameter Trusted_Connection that indicates that you are using the Microsoft Windows NT Authentication Mode to authorize user access to the SQL Server database. For example:

strConnection = _T("Driver={SQL Server};Server=MyServerName;"
"Database=MyDatabaseName;Trusted_Connection=yes;");

If the SQL Server is running in the same computer, you can replace the name of the server by the word (local) like in the following sample:

strConnection = _T("Driver={SQL Server};Server=(local);"
"Database=MyDatabaseName;Uid=MyUserName;Pwd=MyPassword;");

If you want to connect with a remote SQL Server, you must inform the address, the port, and the Network Library to use:

The Address parameter must be an IP address and must include the port. The Network parameter can be one of the following:

  • dbnmpntw Win32 Named Pipes
  • dbmssocn Win32 Winsock TCP/IP
  • dbmsspxn Win32 SPX/IPX
  • dbmsvinn Win32 Banyan Vines
  • dbmsrpcn Win32 Multi-Protocol (Windows RPC)

For more information, see Q238949.

strConnection = _T("Driver={SQL Server};Server=130.120.110.001;"
"Address=130.120.110.001,1052;Network=dbmssocn;Database=MyDatabaseName;"
"Uid=myUsername;Pwd=myPassword;");

ODBC Driver for Oracle

For the current Oracle ODBC driver from Microsoft:

strConnect = _T("Driver={Microsoft ODBC for Oracle};Server=OracleServer.world;"
"Uid=MyUsername;Pwd=MyPassword;");

For the older Oracle ODBC driver from Microsoft:

strConnect = _T("Driver={Microsoft ODBC Driver for Oracle};"
"ConnectString=OracleServer.world;Uid=myUsername;Pwd=myPassword;");

ODBC Driver for MySQL

If you want to connect to a local database, you can use a connection string like the following:

strConnect = _T("Driver={MySQL ODBC 3.51 Driver};Server=localhost;"
"Database=MyDatabase;User=MyUserName;Password=MyPassword;Option=4;");

If you want to connect with a remote database, you need to specify the name of the server or its IP in the Server parameter. If the Port is distinct to 3306 (default port), you must specify it.

strConnect = _T("Driver={mySQL ODBC 3.51 Driver};Server=MyRemoteHost;"
"Port=3306;Option=4;Database=MyDatabase;Uid=MyUsername;Pwd=MyPassword;");

The parameter Option can be one or more of the following values:

  • 1 - The client can't handle that MyODBC returns the real width of a column.
  • 2 - The client can't handle that MySQL returns the true value of affected rows. If this flag is set then MySQL returns 'found rows' instead. One must have MySQL 3.21.14 or newer to get this to work.
  • 4 - Make a debug log in c:\myodbc.log. This is the same as putting MYSQL_DEBUG=d:t:O,c::\myodbc.log in AUTOEXEC.BAT.
  • 8 - Don't set any packet limit for results and parameters.
  • 16 - Don't prompt for questions even if driver would like to prompt.
  • 32 - Enable or disable the dynamic cursor support. This is not allowed in MyODBC 2.50.
  • 64 - Ignore use of database name in 'database.table.column'.
  • 128 - Force use of ODBC manager cursors (experimental).
  • 256 - Disable the use of extended fetch (experimental).
  • 512 - Pad CHAR fields to full column length.
  • 1024 - SQLDescribeCol() will return fully qualified column names.
  • 2048 - Use the compressed server/client protocol.
  • 4096 - Tell server to ignore space after function name and before '(' (needed by PowerBuilder). This will make all function names keywords!
  • 8192 - Connect with named pipes to a MySQLd server running on NT.
  • 16384 - Change LONGLONG columns to INT columns (some applications can't handle LONGLONG).
  • 32768 - Return 'user' as Table_qualifier and Table_owner from SQLTables (experimental).
  • 65536 - Read parameters from the client and ODBC groups from my.cnf.
  • 131072 - Add some extra safety checks (should not be needed but...).

If you want to have multiple options, you should add the above flags! For example: 16 + 1024 = 1030 and use Option= 1030;.

For more information, go to MyODBC Reference Manual.

ODBC Driver for AS400

strConnect = _T("Driver={Client Access ODBC Driver (32-bit)};System=myAS400;"
"Uid=myUsername;Pwd=myPassword;");

ODBC Driver for SyBase

strConnect = _T("Driver={Sybase System 10};Srvr=MyServerName;Uid=MyUsername;"
"Pwd=myPassword;");

ODBC Driver for Sybase SQL AnyWhere

strConnect = _T("ODBC;Driver=Sybase SQL Anywhere 5.0;"
"DefaultDir=C:\\DatabasePath\;Dbf=C:\\SqlAnyWhere50\\MyDatabase.db;"
"Uid=MyUsername;Pwd=MyPassword;Dsn=\"\";");

DSN Connection

ODBC DSN

strConnect = _T("DSN=MyDSN;Uid=MyUsername;Pwd=MyPassword;");

OLE DB Provider

OLE DB Provider for SQL Server

For Standard security:

strConnect = _T("Provider=sqloledb;Data Source=MyServerName;"
"Initial Catalog=MyDatabaseName;"
"User Id=MyUsername;Password=MyPassword;");

For Trusted Connection security (Microsoft Windows NT integrated security):

strConnect = _T("Provider=sqloledb;Data Source=MyServerName;"
"Initial Catalog=MyDatabaseName;"
"Integrated Security=SSPI;");

If you want to connect to a "Named Instance" (SQL Server 2000), you must to specify Data Source=Servere Name\Instance Name like in the following example:

strConnect = _T("Provider=sqloledb;Data Source=MyServerName\MyInstanceName;"
"Initial Catalog=MyDatabaseName;User Id=MyUsername;Password=MyPassword;");

If you want to connect with a SQL Server running on the same computer, you must specify the keyword (local) in the Data Source like in the following example:

strConnect = _T("Provider=sqloledb;Data Source=(local);"
"Initial Catalog=myDatabaseName;"
"User ID=myUsername;Password=myPassword;");

To connect to SQL Server running on a remote computer (via an IP address):

strConnect = _T("Provider=sqloledb;Network Library=DBMSSOCN;"
"Data Source=130.120.110.001,1433;"
"Initial Catalog=MyDatabaseName;User ID=MyUsername;"
"Password=MyPassword;");

OLE DB Provider for MySQL (By Todd Smith)

strConnection = _T("Provider=MySQLProv;Data Source=test");

Where test is the name of MySQL database. Also, you can replace the name of the database by the following connection string: server=localhost;DB=test.

OLE DB Provider for AS400

strConnect = _T("Provider=IBMDA400;Data source=myAS400;User Id=myUsername;"
"Password=myPassword;");

For more information, see: Using the OLE DB Provider for AS/400 and VSAM.

OLE DB Provider for Active Directory

strConnect = _T("Provider=ADSDSOObject;User Id=myUsername;Password=myPassword;");

For more information, see: Microsoft OLE DB Provider for Microsoft Active Directory Service.

OLE DB Provider for DB2

If you are using a TCP/IP connection:

strConnect = _T("Provider=DB2OLEDB;Network Transport Library=TCPIP;"
"Network Address=130.120.110.001;"
"Initial Catalog=MyCatalog;Package Collection=MyPackageCollection;"
"Default Schema=MySchema;User ID=MyUsername;Password=MyPassword;");

If you are using APPC connection:

strConnect = _T("Provider=DB2OLEDB;APPC Local LU Alias=MyLocalLUAlias;"
"APPC Remote LU Alias=MyRemoteLUAlias;Initial Catalog=MyCatalog;"
"Package Collection=MyPackageCollection;Default Schema=MySchema;"
"User ID=MyUsername;Password=MyPassword;");

For more information, see: Using the OLE DB Provider for DB2.

OLE DB Provider for Microsoft Jet

  • Connecting to an Access file using the JET OLE DB Provider:

    Using Standard security:

    strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;"
    "Data Source=C:\\DatabasePath\\MmDatabase.mdb;"
    "User Id=admin;Password=;");

    If you are using a Workgroup (System database):

    strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;"
    "Data Source=C:\\DataBasePath\\mydb.mdb;"
    "Jet OLEDB:System Database=MySystem.mdw;");
    pRs.Open(strConnect, "MyUsername", "MyPassword");
  • Connecting to an Excel Spreadsheet using the JET OLE DB Provider:
    strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;"
    "Data Source=C:\\DatabasePath\\DBSpreadSheet.xls;"
    "Extended Properties=\"\"Excel 8.0;HDR=Yes;\"\";");

    Note: If "HDR=Yes", the provider will not include the first row of the selection into the recordset. If "HDR=No", the provider will include the first row of the cell range (or named ranged) into the recordset.

    For more information, see: Q278973.

  • Connecting to a Text file using the JET OLE DB Provider:
    strConnect = 
    _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\DatabasePath\\;"
    "Extended Properties=\"\"text;"
    "HDR=Yes;FMT=Delimited;\"\";");

    Note: You must specify the filename in the SQL statement... For example:

    CString strQuery = _T("Select Name, Address From Clients.txt");

    For more information, see: Q262537.

  • Connecting to an Outlook 2000 personal mail box using the JET OLE DB Provider: (By J. Cardinal)
    strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;Outlook 9.0;"
    "MAPILEVEL=;DATABASE=C:\\Temp\\;")

    Replace c:\temp with any temporary folder. It will create a schema file in that folder when you open it which shows all the fields available. Blank MAPILEVEL indicates top level of folders).

  • Connecting to an Exchange mail box through JET: (By J. Cardinal)
    strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;Exchange 4.0;"
    "MAPILEVEL=Mailbox - Pat Smith|;DATABASE=C:\\Temp\\;")

    You must replace c:\temp with any temporary folder.

    Replace Pat Smith with the name of the mail box and you must keep vertical pipe character | to indicate top level of folders. Place sub folder after vertical pipe if accessing specific folder.

    Note: you can enter queries against the mail store just like a database... For example:

    CString strQuery = _T("SQL "SELECT Contacts.* FROM Contacts;");

    For more information, see: The Jet 4.0 Exchange/Outlook IISAM.

OLE DB Provider for ODBC Databases

If you want to connect with a Microsoft Access database:

strConnect = _T("Provider=MSDASQL;Driver={Microsoft Access Driver (*.mdb)};"
"Dbq=c:\\DatabasePath\\MyDatabase.mdb;Uid=MyUsername;Pwd=MyPassword;");

If you want to connect with a SQL Server database:

strConnect = _T("Provider=MSDASQL;Driver={SQL Server};Server=MyServerName;"
"Database=MyDatabaseName;Uid=MyUsername;Pwd=MyPassword;");

If you want to use DSN:

strConnect = _T("Provider=MSDASQL;PersistSecurityInfo=False;"
"Trusted_Connection=Yes;"
"Data Source=MyDSN;catalog=MyDatabase;");

For more information, see: Microsoft OLE DB Provider for ODBC.

OLE DB Provider for OLAP

Microsoft® OLE DB for Online Analytical Processing (OLAP) is a set of objects and interfaces that extends the ability of OLE DB to provide access to multidimensional data stores.

strConnect = _T("Provider=MSOLAP;Data Source=MyOLAPServerName;"
"Initial Catalog=MyOLAPDatabaseName;");
Connection using HTTP:

This feature enables a client application to connect to an Analysis server through Microsoft® Internet Information Services (IIS) by specifying a URL in the Data Source property in the client application's connection string. This connection method allows PivotTable® Service to tunnel through firewalls or proxy servers to the Analysis server. A special Active Server Pages (ASP) page, Msolap.asp, enables the connection through IIS. The directory in which this file resides must be included as part of the URL when connecting to the server (for example, http://www.myserver.com/myolap/).

Using a URL
strConnect = _T("Provider=MSOLAP;Data Source=http://MyOLAPServerName/;"
"Initial Catalog=MyOLAPDatabaseName;");
Using SSL
strConnect = _T("Provider=MSOLAP;Data Source=https://MyOLAPServerName/;"
"Initial Catalog=MyOLAPDatabaseName;");

For more information, see: OLE DB for OLAP, Connecting Using HTTP.

OLE DB Provider for Oracle

OLE DB Provider for Oracle (from Microsoft)

The Microsoft OLE DB Provider for Oracle allows ADO to access Oracle databases.

strConnect = _T("Provider=MSDAORA;Data Source=MyOracleDB;User Id=myUsername;"
"Password=myPassword;");

For more information, see: Microsoft OLE DB Provider for Oracle.

OLE DB Provider for Oracle (from Oracle).

For Standard security:

strConnect = _T("Provider=OraOLEDB.Oracle;Data Source=MyOracleDB;"
"User Id=myUsername;Password=myPassword;");

For a Trusted connection:

  • OS Authenticated connect setting user ID to "/":
    strConnect = _T("Provider=OraOLEDB.Oracle;Data Source=MyOracleDB;"
    "User Id=/;Password=;");
  • OS Authenticated connect using OSAuthent:
    strConnect = _T("Provider=OraOLEDB.Oracle;Data Source=MyOracleDB;OSAuthent=1;")

    Note: "Data Source=" must be set to the appropriate Net8 name which is known to the naming method in use. For example, for Local Naming, it is the alias in the tnsnames.ora file; for Oracle Names, it is the Net8 Service Name.

For more information, see: Oracle Provider for OLE DB Developer's Guide.

OLE DB Provider for Visual FoxPro

strConnect = _T("Provider=vfpoledb;"
"Data Source=C:\\DatabasePath\\MyDatabase.dbc;");

For more information, see: Microsoft OLE DB Provider for Visual FoxPro.

OLE DB Provider for Index Server (By Chris Maunder)

strConnect = _T("provider=msidxs;Data Source=MyCatalog;");

For more information, see: Microsoft OLE DB Provider for Microsoft Indexing Service.

OLE DB Data Link Connections

Data Link File - UDL

strConnection = _T("File Name=c:\\DataBasePath\\DatabaseName.udl;");

If you want to create a Data Link File, you can make a new empty text file, change its extension by .udl, then double click over the file, and the operating system calls for you the Data Link app.

[ Data Link Application ]

About Carlos Antollini


Carlos Antollini is a software engineer working on Object Oriented, Visual C++, MFC, COM, ATL, ADO, Internet technologies, OLAP, MS-SQLServer and Cyrillic Languages.
Carlos is originally from Argentina, he was living by a couple of years in Fort Lauderdale, Florida, working for Citibank. Then he started his own business.
Carlos is the creator of piFive[^], a family of BI Analytic Platform software, that it deals next to, latinsys[^], his partner in businesses...

Click here to view Carlos Antollini's online profile.

"Database Programming" 카테고리의 다른 글
  • Database Development using Visual C++ and OLE D... (0)2007/07/10
  • ADO Connection Strings (0)2007/07/10
  • 고급 DAO 프로그래밍 (0)2007/05/18
  • Using ADO.NET with SQL Server (0)2007/04/29
  • [Class] ADODB Connect (0)2007/04/03
2007/07/10 09:57 2007/07/10 09:57
Posted by webdizen
Tags Access, ADO, AS400, DB2, dBASE, Excel, FoxPro, MySQL, ODBC, OLAP, Oracle, SQL Server, SyBase, Text
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3069

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C#2007/02/05 17:36

Office 2003 : Visual Studio Tools for Office System을 활용한 Excel 게임

고수닷넷 - 디버깅전문가님

요약

Microsoft에서는 Microsoft Office Word 2003과 Excel 2003을 새로운 개발 플랫폼으로 사용하기 위한 작업을 진행 중이다. Office 2003을 개발 플랫폼으로 사용한다는 의미는 과연 무엇일까? 이제 우리는 Office 2003에서 Windows Forms를 다루듯이 버튼과 메뉴를 추가하는 것과 같이 Windows Forms가 제공하는 거의 모든 기능을 사용할 수 있을 뿐만 아니라 Office 2003(특히, Excel과 Word)에서 제공하는 거의 모든 기능을 프로그래밍 적으로 다룰 수 있다는 것을 의미한다. Microsoft에서는 Visual Studio Tools for the Microsoft Office System(줄여서 VSTO)이라고 불리는 이 기술을 통해서 데스크 톱의 기능성과 생산성을 높이고자 한다.

이 글에서는 엑셀에서 작동하는 게임을 작성하는 방법에 대해서 소개하고 Excel 문서를 다루는 방법을 소개한다.

Microsoft는 Office 2003을 출시한 이후로 VSTO에 대한 많은 내용들을 소개하였다. 필자는 지난 1월에 있었던 Office 기술 관련 세미나에 참석하여 처음으로 VSTO에 대해서 알게 되었다. 그 세미나에서 여러 가지 데모를 볼 수 있었는데, 대부분은 주로 엔터프라이즈 환경을 위한 예제였다. 하지만 아이러니 하게도 필자는 그 데모를 보면서 90년대 초반 인기를 끌었던 Tetris라는 게임이 머리를 스쳤다. ‘아! 딱딱하지 않으면서도 사용자들에게 VSTO의 가능성을 보여주기에는 게임만큼 쉬운 방법이 없지!’ 그렇게 해서 Excel Tetris 게임을 만들게 되었다.

사용자 삽입 이미지

<Figure 1 - Tetris>

정보 구하기

여러분도 알고 있듯이, VSTO는 Visual Studio .NET 2003과 함께 설치되지 않는다. VSTO는 Microsoft Office System을 위한 확장 기능일 뿐, .NET Platform을 위한 개발 도구는 아니기 때문이다. VSTO는 현재 일반 유저에게 공개되어 있지 않으며, MSDN Subscription Download 사이트에서 다운로드할 수 있다. VSTO를 설치하기 위해서는 다음과 같은 프로그램들이 여러분의 운영체제에 미리 설치되어 있어야 한다.

- Visual Studio .NET 2003

- Microsoft Office System

또한 Office System의 경우, 전체 설치를 하거나 custom 설치를 눌러서 Excel이나 Word에서 .NET Programmability Support를 선택하도록 한다. 또한 VSTO를 사용하여 개발한 솔루션을 배포하기 위해서는 다음과 같은 프로그램들을 순서대로 설치해야 한다.

- Microsoft .NET Framework 1.1

- Microsoft Office Excel 2003(앞서 말한 것과 같이 필요한 요소들을 설치해야 한다)

현재 VSTO는 언어별로 제공하고 있기 때문에, 만약 여러분이 영문 운영 체제에서 작업하는 것이 아니라면 반드시 언어 환경과(Culture) 일치하는 VSTO를 설치해야 한다. VSTO에 대한 개괄적인 정보들을 얻기 위해서 다음 사이트를 방문해 보는 것이 좋을 것이다. http://msdn.microsoft.com/office/unders ··· ntro.asp 또한 Office 홈페이지(http://msdn.microsoft.com/office/)에 가면 간단한 예제부터 시작해서 VSTO에 대한 최근의 정보를 확인할 수 있다. 만약 여러분이 Visual Studio .NET 2003 을 설치했다면(이에 대한 정보는 VSTO 설치 파일의 도움말 html에서 확인 해야 함), 로컬 컴퓨터에 설치되어 있는 MSDN 도움말에서 간단한 정보를 얻을 수 있다. 하지만 충분한 내용을 제공한다고 볼 수 없기 때문에 여러분이 구체적이고 실질적인 작업을 진행하기에는 다소 어려움이 따를 것으로 보인다. 그리고 이 글에서 설치나 배포, 그리고 VSTO의 작동 방법에 대해서는 자세하게 소개하고 싶지만, 이 글은 VSTO를 활용하는 실질적인 예를 보여주기 위한 것이기 때문에 관련 정보에 대한 링크를 걸어두는 것으로 만족하도록 하겠다.

시작은 Windows Forms부터

이번 글에서 소개하는 Excel Tetris를 작성하기 전에 필자는 다음과 같은 기능들을 보여주고자 했다.

- 기본적인 Tetris 게임 진행

- 타이머 객체를 활용

- 엑셀의 Row와 Column을 다루는 방법

- 이벤트 처리

이 예제를 작성하기 전에 필자도 여러분처럼 VSTO에 익숙하지 않았기 때문에 VSTO를 기본 테스트 프로젝트로 사용하고 싶지는 않았다. 코드의 작성도 용이하고 디버깅도 쉬워야 했기 때문에 VSTO를 사용하는 대신 Windows Forms에서 간단한 게임을 구현하고 난 후, 이를 VSTO에 적용하는 방법을 선택하기로 하였다. 여러분이 Tetris를 한번이라도 해 본적이 있다면, 이 게임이 굉장히 직관적이며 아주 간단한 조작만이 요구되기 때문에 누구나 쉽게 접근할 수 있다는 것을 알고 있을 것이다. 그렇기 때문에 프로그래밍을 시작하는 많은 사람들이 이 게임을 통해서 알고리즘에 대해서 이해하고 코드를 작성하는 방법에 대해서 이해하려고 한다. 사실 Tetris를 구현하는 소스는 이미 일반에게 많이 공개되었으며, 굉장히 많은 알고리즘이 사용되고 있다. 이 책에서 제공하는 소스도 필자가 나름대로 고민하고 생각하여 구현한 소스로 최고의 알고리즘을 사용했다고 단언할 수는 없다. 또한 필자는 이 소스를 구현함에 있어서 어느 누구의 소스도 참고하지 않았다는 점을 미리 말해둔다.

Tetris 게임을 한번도 해보지 않은 사람을 위해서 게임 진행 방법에 대해서 간단히 소개하자면, 이 게임은 총 4개의 블록으로 구성된 7가지 형태의 바(bar)가 존재한다. 각 바들은 각자의 특성에 따라서 여러 가지 형태로 변형이 되며 여러분이 Row에 빈 곳이 없도록 Bar를 채우는 것이 목표이다. 이번 글이 테트리스 소스 자체에 설명하기 위함이 아니기 때문에 보다 자세한 내용은 소스 코드를 참고하도록 한다. 필자가 이 소스를 구현하면서 가장 중점적으로 생각했던 부분은 이 소스를 VSTO 프로젝트에서 사용해야 하기 때문에 각 블록을 화면에 그리기 위한 코드를 VSTO에 적용하기 쉽도록 정리하는 부분이었다. 이 소스 코드는 FillCell 이라는 이름으로 정리하였다. 이 함수에 대해서는 뒤에서 자세하게 소개할 것이다. Windows Forms에서 만든 예제의 실행 화면은 다음과 같으며, 뒤에서 이 화면에 어떻게 Excel에서 표현되는지 확인할 수 있을 것이다.

사용자 삽입 이미지

<Figure 2 - Windows Forms 예제 파일>

간단한 예제 만들기

이 글은 VSTO에 이미 익숙한 개발자보다는 VSTO에 익숙하지 않은 개발자 혹은 VSTO에 대해서 들어본 적이 없는 개발자를 위한 글이기 때문에 VSTO가 무엇인지에 대한 기본적인 개념을 얻을 수 있도록 간단한 프로젝트를 만드는 것부터 시작하겠다. VSTO를 설치하고 난 후 새 프로젝트를 생성하면 다음 <Figure 3>과 같이 새로운 템플릿이 추가된 것을 확인할 수 있다. 여러분은 사용하는 언어에 따라서 C# 또는 VB.NET으로 프로젝트를 생성할 수 있다. 우선 C# 프로젝트를 선택하고 엑셀 문서를 선택한 다음 프로젝트 이름을 입력하면 프로젝트 이름의 엑셀 문서와 엑셀 문서에서 사용할 DLL을 만들기 위한 C# 프로젝트가 생성된다.

사용자 삽입 이미지

<Figure 3 - 추가된 프로젝트 템플릿>

프로젝트를 생성하자 마자, 디버그-시작 메뉴를 클릭하거나 Ctrl+F5를 클릭하여 프로젝트를 시작해보자. 여러분이 그 동안 접해왔던 Windows Forms나 익스플로어(Web Forms를 위한 호스트) 창이 아니라 엑셀 문서가 열린다. 프로젝트 마법사가 생성한 코드에는 여러분이 아무것도 추가한 내용이 없기 때문에 여러분이 앞으로 어떻게 해야 할지 전혀 알 수 없는 백지 상태의 엑셀 문서가 열린다. 여러분이 무엇을 해도 상관없지만 엑셀 문서를 닫기 전에 저장하지는 않도록 한다. 나중에 이 문서는 C# 프로젝트에서 다루기 위해서 수정할 것이다. 일단 문서를 닫고 이번에는 디버그-디버깅 모드로 시작 또는 F5를 클릭하여 디버깅 모드로 프로젝트를 시작해보자. Visual Studio .NET이 디버깅 모드로 전환되고 이전과 마찬가지로 엑셀 파일이 열린다. 이번에는 엑셀 파일을 닫지 말고 Visual Studio .NET에서 디버그-디버깅 중지를 눌러보자. 기존에 여러분이 작업했던 것과 마찬가지로 엑셀 문서가 닫힐 것이다.

이번에는 백지 상태의 엑셀 문서에 버튼을 추가해보자. 지금까지 살펴본 모든 작동 방법들이 Windows Forms과 거의 동일하기 때문에 여러분은 엑셀에서 버튼을 추가하고 이벤트 핸들러는 연결하는 작업이 Windows Forms의 작업에서처럼 간단할 것이라고 예상하겠지만, 불행하게도 VSTO는 이러한 작업을 위한 마법사 기능을 제공하고 있지 않다. 결론적으로 볼 때 C# 소스에 추가되는 코드는 동일하지만, 여러분이 이 모든 작업을 수작업을 해야 한다는 사실이 불편할 뿐이다. VSTO 팀에서 다음 버전에서는 엑셀을 Windows Forms처럼 사용할 수 있는 마법사를 제공해주기를 바란다.

UI 구현하기

엑셀에서 버튼을 추가하기 위해서는 Toolbar에서 Control Toolbox를 선택하여 Control Toolbox를 띄우도록 한다. 다음 <Figure 4>에서 확인할 수 있듯이 엑셀 문서에 여러 가지 컨트롤들을 추가할 수 있으며, More Controls를 클릭하여 <Figure 4>에 있는 컨트롤 이외에도 훨씬 많은 컨트롤들을 포함시킬 수 있다. <Figure 5>와 같이 버튼을 추가하고 속성 버튼을 눌러 Text에 Click Here를 입력하고 ID는 btnClick으로 설정한다. 이 버튼을 누르면 Sheet1의 A1에 ‘Hello, Excel’이라는 텍스트를 입력하도록 할 것이다. 버튼을 추가하였다면 Control Toolbox와 Property 창을 닫고 엑셀 문서를 저장하도록 한다.

사용자 삽입 이미지

< Figure 4 - Control Toolbox>

사용자 삽입 이미지

< Figure 5 - Command Button 추가>

이번에는 C# 프로젝트로 돌아와서 엑셀 문서에 추가한 버튼 객체를 찾고 이벤트 핸들러를 설치해야 한다. 여러분이 C# 소스 코드를 살펴보았다면 이미 알고 있겠지만, 엑셀 문서에 있는 컨트롤을 찾기 위하여 FindControl 이라는 함수를 사용할 것이다. 이 함수는 다음 <Figure 6>과 같이 구현되어 있다.

/// <summary>

/// Finds the control with the specified name in the active worksheet.

/// </summary>

/// <param name='name'>Name of the control to find.</param>

/// <returns>

/// Returns the specified control, or null if it is not found.

/// </returns>

object FindControl(string name )

{

  return FindControl(name, (Excel.Worksheet) ThisWorkbook.ActiveSheet);

}

/// <summary>

/// Returns the control with the specified name in the specified worksheet.

/// </summary>

/// <param name='name'>Name of the control to find.</param>

/// <param name='sheet'>Worksheet object that contains the control.</param>

/// <returns>

/// Returns the specified control, or null if it is not found.

/// </returns>

object FindControl(string name, Excel.Worksheet sheet )

{

  Excel.OLEObject theObject;

  try

  {

       theObject = (Excel.OLEObject) sheet.OLEObjects(name);

       return theObject.Object;

  }

  catch

  {

       // Returns null if the control is not found.

  }

  return null;

}

사용자 삽입 이미지

<Figure 6 ? Overloaded FindControl methods>

기본적으로 생성되는 C# 소스 코드에는 <Figure 6>과 같이 두개의 오버로드된 FindControl 메서드가 존재한다. 매개 변수가 2개인 메서드는 찾고자하는 컨트롤의 ID 이름과 컨트롤이 있는 Sheet를 매개 변수로 넘긴다. 매개 변수가 1개인 메서드는 현재 작업 중인 Sheet를 구해서 또 다른 FindControl 메서드를 호출한다. FindControl 메서드에서는 Sheet의 OLEObjects 메서드를 호출하여 OLE 객체를 구한 다음 반환하고 있다. FindControl이 반환하는 OLEObjects는 MSForms에서 제공하는 컨트롤과 1:1 매칭이 되는데, 예를 들면 Command Button은 CommandButton과 Text Control은 TextBox와 매칭된다. 또한 소스 코드에서 확인할 수 있는 것처럼 FindControl이 컨트롤을 찾지 못할 경우 null을 반환하기 때문에, FindControl을 사용할 때에는 항상 리턴값이 null인지를 비교한 다음 리턴된 객체를 사용하도록 해야 한다. 따라서 다음과 같이 코드를 작성하여 엑셀 문서에 추가한 컨트롤을 찾을 수 있다.

MSForms.CommandButton btnClick = (MSForms.CommandButton) FindControl("btnClick");

그리고 다음과 같이 이벤트 핸들러를 생성할 수 있다. 이벤트 핸들러를 작성하는 방법은 Wizard가 제공되지 않을 뿐, 여러분이 지금까지 Windows Forms이나 Web Forms에서 해왔던것과 동일한 방법으로 추가한다.

btnClick.Click += new Microsoft.Vbe.Interop.Forms.CommandButtonEvents_ClickEventHandler(btnClick_Click);

하지만 다행스럽게도 Visual Studio .NET의 IntelliSense 덕분에 이 긴 코드를 직접 입력하지 않고 <Tab> 키만 눌러도 자동으로 코드가 추가된다. 이제 앞서 말했듯이, 버튼을 클릭했을 때 발생하는 핸들러에서 엑셀 영역에 텍스트를 설정하는 코드를 작성해야 한다. 코드를 작성하기 전에 Excel의 구조에 대해서 간단히 살펴보도록 하자. 엑셀의 구조는 크게 Application과 Worksheet, Sheet 그리고 Range(또는 Cell)로 나눌 수 있다. Application은 Excel 응용 프로그램 자체를 나타내며, Worksheet는 여러분이 작성하거나 다루고 있는 문서를 나타낸다. Sheet는 하나의 문서 안에 들어 있는 각 페이지를 나타내고 마지막으로 Range는 각 페이지에 있는 Cell의 영역을 나타낸다. Range의 범위가 하나일 경우를 Cell이라고 한다. 이러한 구조 때문에 각 단위들은 자신들에게 고유한 이벤트와 속성들을 제공한다. 예를 들어, 여러분이 프로그램을 실행할 때 발생하는 이벤트들은 모두 Application에 존재하며, 문서를 열거나 닫을 때 발생하는 이벤트들은 모두 Worksheet에 존재한다. 따라서 Excel을 제대로 다루기 위해서는 이러한 구조를 이해하고 여러분이 원하는 이벤트와 속성이 어떤 범위에 속할 것인지를 판단할 필요가 있다.

사용자 삽입 이미지

< Figure 7 ? Excel의 구조>

< Figure 7>에서와 같이 Range는 하나 이상의 Cell을 포함하고 있다. 하지만 Excel에서는 주요작업 단위를 Range로 사용하기 때문에 A1에 문자열을 출력하기 위해서 A1에 해당하는 Range를 구하도록 한다. Cell 이름을 통해서 Range를 얻는 방법은 간단한다. 다음 코드와 같이 원하는 영역의 이름을 매개 변수로 전달하면 해당 영역 만큼 Range를 반환한다.

private void btnClick_Click ( )

{

       Excel.Worksheet sheet = (Excel.Worksheet) ThisApplication.Sheets.get_Item(1);

       Excel.Range rng = sheet.get_Range ( "A1", "A1" );

       rng.Value2 = "Hello, Excel";

}

필자가 이 예제를 작성할 때 텍스트를 설정하기 위한 메서드나 속성이 당연히 Text와 같은 이름의 이름을 갖고 있을 것이라고 생각했지만, 결국 Value2라는 속성이었다(Text 속성은 읽기 전용이다). VSTO 팀에서 Value2를 지원하는지 모르겠지만, 다음 버전에서는 이러한 부분들이 수정되었으면 하는 바람이다.

작성된 코드를 테스트하기 위해서 디버그-시작 메뉴를 클릭하여 엑셀 문서를 띄운 후, 버튼을 클릭보자. A1에 ‘Hello, Excel’이라는 문자열이 출력될 것이다. 이제 여러분은 내가 더 이상 설명할 필요도 없이 엑셀 문서를 다루는 방법을 알고 있을 것이다.

Let’s get started!

이미 VSTO에 대해서 알고 있는 독자라면 지루한 설명을 보느라 수고가 많았다. 이제부터 본격적으로 엑셀에서 작동하는 게임을 만들어보도록 하겠다. 가장 먼저 앞서 설명한 것과 같은 방법으로 ExcelTetris라는 이름으로 C#용 엑셀 프로젝트를 생성하도록 한다. 그리고 앞에서 작성한 예제의 테트리스 클래스(MyTetris)를 새로운 프로젝트에 추가하도록 한다. 아무런 문제없이 빌드되는지 확인하도록 한다. 앞으로 추가해야 하는 부분들이 다소 복잡할 수 있기 때문에 많은 문제점들이 한꺼번에 발생하지 않도록 틈틈히 빌드 오류를 수정하면 진행하도록 하겠다. 아마도 몇몇 독자들은 Windows Forms에서 사용한 코드를 그대로 사용할 수 있는가에 대해서 의문을 갖고 있을것이다. 물론, 코드를 전혀 수정하지 않고 MyTetris 클래스를 그대로 사용할 수는 없다. 가장 큰 이유는 MyTetris 클래스가 화면을 그리는 부분과 관련된 코드를 포함하고 있기 때문에 엑셀 문서에 블록을 그려주기 위해서는 이 부분을 수정해야 한다. 하지만 크게 걱정할 필요는 없다. 여러분이 해야 할 일은 FillCell 이라는 메서드를 Graphics 객체를 사용하지 않고 Excel.Sheet를 사용하도록 수정하는 것이다. 물론 여러분은 MyTetris 클래스를 상속 받은 새로운 ExcelTetris 클래스를 구현한 후, FillCell 메서드를 오버라이드 할 수 있다. 하지만 필자는 될 수 있으면 예제를 간단하게 유지하기 위해서 MyTetris 클래스의 FillCell 부분과 UI에 관련된 몇몇 코드를 수정하였다. 몇가지 코드를 수정한다면, 이 클래스를 Excel 프로젝트와 Windows Forms 프로젝트 모두에서 쉽게 사용할 수 있을 것이다. 또한 이 글이 테트리스 게임 자체가 아니라, VSTO의 활용법에 중점을 두고 있기 때문에 MyTetris 클래스에 몇가지 논리적인 오류가 있음을 알아두도록 한다.

MyTetris 클래스를 사용하는 외부에서는 MyTetris 클래스가 화면에 게임의 진행 상태를 그릴 수 있도록 Draw 메서드를 호출하며, Draw 메서드는 다음과 같이 구현되어 있다.

/// <summary>

/// This is the only drawing method that client can call.

/// </summary>

/// <param name="sheet">Excel.Sheet object that will be used to draw</param>

public void Draw ( ref Excel.Worksheet sheet )

{

       // Game over?

       //

       if ( IsEnd == false )

       {

              DrawBlock ( ref sheet );

              DrawNextBlock ( ref sheet  );

       }

}

Draw 메서드는 게임의 진행 상태를 그려야 하는 Sheet에 대한 reference 객체를 매개 변수로 받고, 게임이 종료되지 않았다면 전달받은 매개 변수를 이용하여 DrawBlock과 DrawNextBlock 메서드를 호출한다. DrawBlock과 DrawNextBlock 메서드의 코드는 첨부된 파일에서 확인할 수 있다. 이 두 메서드는 좌표를 계산한 후, FillCell 메서드를 호출한다. FillCell 메서드에 대해서는 뒤에서 보다 자세하게 살펴보도록 한다.

ExcelTestris 프로젝트를 생성한 후, 가장 먼저 해야 할 일은 엑셀 문서를 열고 UI를 구현하는 것이다. 엑셀 문서를 여는 방법은 ExcelTetris를 실행시키거나 프로젝트 폴더에 가서 직접 열 수 있다. 어느 방법을 통해서든지 문서를 수정하고 저장하면 된다. 엑셀 문서를 다음 < Figure 8>과 같이 같이 버튼들을 배치하도록 하고, 각 버튼들의 속성은 다음 <표 1>과 같다. 컨트롤들을 배치할 때 버튼의 위치가 Cell의 크기를 조절하는 부분(InitSheet 메서드)과 겹치게 되면 컨트롤의 크기가 변경되기 때문에 적절하게 배치해야 한다.

사용자 삽입 이미지

<그림 8 ? UI 구성>

컨트롤 클래스

ID

Text

Enable

CommandButton

btnGameStart

Start

Yes

CommandButton

btnGameStop

Stop

No

CommandButton

btnGamePause

Pause

No

CommandButton

btnAbout

About?

Yes

TextBox

txtInput

No

TextBox

txtClearLined

Yes

<표 1 ? UI 컨트롤 속성>

나중에 이 버튼들에 대한 Click 이벤트를 C# 소스에 추가할 것이다. < Figure 8>에서 확인할 수 있는 것처럼 밑 부분에는 텍스트 박스가 위치하고 있다. 여러분이 게임을 진행하기 위해서는 키보드 조작을 위한 입력을 받아야 하는데, Sheet 자체 내에서 키보드 입력을 직접 받을 수 없었기 때문에, 아무 내용도 출력하지 않는 텍스트 상자를 만든 후 이 텍스트 상자를 통해서 입력을 받을 수 있도록 처리하였다. 또한 < Figure 8>에는 프로그램 정보를 출력하는 텍스트 상자들이 있다. <Figure 8>과 같은 형태로 UI를 구성한 후에 엑셀 문서를 닫고 C# 프로젝트로 이동한다. 우선 이 컨트롤들을 사용하기 위해서 변수를 다음과 같이 선언해야 한다.

// Excel controls...

//

private MSForms.CommandButton btnGameStart;

private MSForms.CommandButton btnGameStop;

private MSForms.CommandButton btnGamePause;

private MSForms.CommandButton btnAbout;

private MSForms.TextBox txtInput;

private MSForms.TextBox txtClearLined;

변수를 선언하고 난 후, 엑셀 문서에서 해당 컨트롤들을 찾아서 할당해야 한다. 이와 같은초기화 작업은 엑셀 문서가 열리는 시점에서 수행해야 하며 이에 대한 이벤트 핸들러가ThisWorkbook_Open 이벤트 핸들러이다. 이 핸들러는 엑셀 프로젝트 생성시 자동으로 생성되며 문서에 대한 초기화 작업을 수행하기에 적당한 곳이다. 초기화에 관련된 모든 코드가 ThisWorkbook_Open 이벤트 핸들러에 있지만, 뒤에서 소개할 Sheet 자체에 대한 초기화나 Style에 관련된 내용은 별도의 메서드로 분리하였다. ThisWorkbook_Open 이벤트 핸들러의 전체 소스는 <Figure 9>와 같다.

/// <summary>

/// Called when the workbook is opened.

/// </summary>

protected void ThisWorkbook_Open()

{

  sheet = (Excel.Worksheet)this.ThisApplication.Sheets.get_Item(1);

       // Find controls...

       // Because there are no wizard to connect excel's controls

       // with code-behind's variables, you should connect these

       // controls manually like following code.

       //

       this.btnGameStart = (MSForms.CommandButton)this.FindControl(

              "btnGameStart");

       this.btnGameStop = (MSForms.CommandButton)this.FindControl(

              "btnGameStop");

       this.btnGamePause = (MSForms.CommandButton)this.FindControl(

              "btnGamePause");

       this.btnAbout = (MSForms.CommandButton)this.FindControl(

              "btnAbout");

       this.txtInput = (MSForms.TextBox)this.FindControl(

              "txtInput");

       this.txtClearLined = (MSForms.TextBox)this.FindControl(

              "txtClearLined");

       // Craete event handler...

       //

       if (this.btnGameStart != null &&

              this.btnGameStop != null &&

              this.btnGamePause != null &&

              this.btnAbout != null &&

              this.txtInput != null)

       {

              this.btnGameStart.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGameStart_Click);

             

              this.btnGameStop.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGameStop_Click);

              this.btnGamePause.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnGamePause_Click);

              this.btnAbout.Click += new

                      MSForms.CommandButtonEvents_ClickEventHandler(

                      btnAbout_Click);

              this.txtInput.KeyDown += new                                                    MSForms.MdcTextEvents_KeyDownEventHandler (

                      txtInput_KeyDown );

             

       }

       // Create new styles...

       //

       CreateStyle ( );

       // Create new tetris object & set properties...

       //

       tetris = new MyTetris ();

       tetris.OffSetX = 5;

       tetris.OffSetY = 5;

      

       tetris.GameEnded += new MyTetris.GameEndedEventHandler ( tetris_GameEnded );

       tetris.GameStarted += new

              MyTetris.GameStartedEventHandler ( tetris_GameStarted );

       tetris.ClearLine += new MyTetris.ClearLineEventHandler ( tetris_ClearLine );

       // Initialize current sheet to show tetris game properly

       //

       InitSheet ( );

       // Create new timer

       //

       timer = new System.Windows.Forms.Timer();

       timer.Interval = 1000;

       timer.Tick += new System.EventHandler(timer_Tick);

}

사용자 삽입 이미지


<Figure 9 ? ThisWorkbook_Open Event handler>

FindControl 메서를 이용하여 컨트롤을 얻고 각각의 Command Button에 대해서 Click 이벤트를 Text Box에 대해서 KeyDown 이벤트 핸들러를 추가한다. 이벤트 핸들러를 추가할 때 어려웠던 점은 핸들러의 매개 변수가 무엇을 뜻하고 어떤 타입으로 매개 변수를 입력받는지를 확인할 수 있는 정보가 없었기 때문에, 매개 변수가 없는 이벤트 핸들러를 작성한 후, 컴파일하여 컴파일 오류를 통해서 매개 변수를 추가하는 방법을 사용하였다. 특히 txtInput 컨트롤로부터 키보드 입력을 받는 부분에서는 두개의 매개 변수의 값이 무엇을 의미하는지 확인하기 위해서 간단한 테스트 코드를 작성하여 확인하는 방법으로 접근하였다. 확인한 결과 첫번째 매개 변수가 키 코드 값을 의미한다는 것을 알게 되었다.

각 이벤트 핸들러의 소스 코드는 MyTetris 클래스의 메서드를 호출하는 것이외에는 그다지하는 일이 없기 때문에 어렵지 않다. <Figure 9>에 보면 InitSheet 라는 메서드를 호출하고 있는 것을 확인할 수 있는데, 이 메서드는 Excel에서 제공하는 기본 Cell의 간격이 상당히 넓기 때문에 블록을 그리기 위한 부분에 대해서 Cell의 높이와 너비를 조절하기 위함이다.

/// <summary>

/// This method will modify the current sheet.

/// Current sheet's RowHeight and ColumnWidth will be modified

/// to show blocks properly

/// </summary>

private void InitSheet ( )

{

       Excel.Range rng;

       for ( int i = 1 ; i < 30; i ++ )

       {

              rng = (Excel.Range)sheet.Rows[i,Type.Missing];

              rng.RowHeight = 10;

       }

       for ( int j = 1 ; j < 30; j ++ )

       {

              rng = (Excel.Range)sheet.Columns[j,Type.Missing];

              rng.ColumnWidth = 1;

       }

}

RowHeight와 ColumnWidth 속성을 통해서 Cell의 너비와 높이를 조절할 수 있다. 이 때 이들 속성에 설정하는 값은 픽셀을 나타내는 단위가 아님을 알아두도록 한다. 또한 MyTetris 클래스를 사용할 때 한가지 주의할 점은 MyTetris 클래스 자체가 타이머를 포함하고 있지 않기 때문에 MyTetris에는 pause라는 메서드가 없다. 만약 여러분이 Timer 이벤트에서 MyTetris 객체의 메서드를 호출하지 않는다면 MyTetris는 언제나 일시 정지된 상태로 있을 것이다.

마지막으로 txtInput 컨트롤에 대한 이벤트 핸들러를 추가해야 하는데, 이 컨트롤은 앞서 설명했던 것처럼 사용자가 게임을 조작하기 위해서 필요한 입력을 처리하기 위한 것이다. 만약 Excel.Sheet로부터 직접 키보드를 입력받을 수 있는 방법이 있다면 txtInput 컨트롤의 키보드 처리 부분을 옮기는 것이 좋을 것이다. txtInput 핸들러 소스 코드에서는 이벤트 핸들러의 매개 변수로 넘어온 키 값을 MyTetris 객체에 넘기는 코드만 실행하고 있음을 알 수 있다.

/// <summary>

/// This method is used to move the block.

/// </summary>

/// <param name="ri">Key value</param>

/// <param name="n"></param>

private void txtInput_KeyDown (Microsoft.Vbe.Interop.Forms.ReturnInteger ri, short n)

{

       if ( tetris.IsEnd == false )

              tetris.KeyDown ( (Keys)ri.Value );

       tetris.Draw( ref sheet );

}

사용자가 Start 버튼을 클릭하면 현재 출력된 바를 직접 아래로 이동하거나 시간이 흐름에 따라 블록을 강제로 아래로 이동시켜야 한다. 사용자가 블록을 직접 아래로 이동시키는 방법은 txtInput 컨트롤의 입력 핸들러에서 처리했으며, 시간이 흐름에 따라서 강제로 이동시키는 기능은 timer를 사용해야 한다. Timer를 추가하는 방법도 지금까지 사용했던 컨트롤들을 추가하는 방법과 상당히 유사하다. 일단 타이머를 생성하고 적절한 속성과 메서드를 호출하면 된다. 이 글에서 제공하는 소스에서는 타이머가 호출되는 간격을 1000ms(1초)로 설정했다. Timer 객체를 생성하고 이에 대한 속성을 설정하는 코드는 <Figure 9>에서 확인할 수 있으며, Timer에 의해서 주기적으로 호출되는 이벤트 핸들러의 소스 코드는 다음과 같다.

/// <summary>

/// This event handler will be used to move block down or

/// determine whether the game is over or not.

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void timer_Tick(object sender, System.EventArgs e)

{

       // game over?

       //

       if ( true == tetris.IsEnd )

       {

              timer.Stop();

       }

       else

       {

              tetris.Down ();

              tetris.Draw ( ref sheet );

       }

}

Sheet 다루기

지금까지 UI를 구현하고 작동시키는 방법까지 설명하였다. 그렇다면 Excel에 블록을 표시하기 위해서 어떤 방법을 사용했을까? Excel을 다루는 C# 프로젝트를 생성하면 크게 2개의 객체가 자동으로 생성된다. ThisApplication 이라는 이름의 속성으로 제공되는 Application 객체는 Excel 응용 프로그램 자체를 나타내며, ThisWorkSheet라는 이름의 속성으로 제공되는 Worksheet 객체는 Excel에서 연 파일 자체를 가리킨다. 또한 이 Worksheet 내에는 기본적으로 Sheet1, Sheet2, 그리고 Sheet3라는 세개의 sheet가 기본적으로 제공된다. 따라서 Windows Forms과 같이 컨트롤을 포함하는 호스트는 Sheet라고 보면 된다. 그리고 하나 이상의 Cell을 Range라고 표현한다. 결국 프로그래머가 다룰 수 있는 가장 단위는 Cell이지만, 실제로는 Range라고 말할 수 있다. 이 글에서 제공하는 소스 역시 Range를 가장 작은 단위로 사용하고 있다. Range는 상당히 여러 가지 방법을 통해서 생성할 수 있다. 광범위한 영역을 지정하고 싶다면 Range(“A1”, “F28”)과 같은 방법으로 엑셀에 표현되는 각 행과 열의 이름을 직접 지정하여 영역을 생성할 수 있다. 또는 Sheet를 Cell의 2차원 집합이라고 생각하고 배열을 다루듯이 Cell 배열로부터 Range를 구할 수 있다. 이 글에서 소개하는 소스에서는 두 가지 방법을 적절학게 혼합하여 사용하고 있다.

필자가 만들고자 했던 목표가 게임이었기 때문에, Range 객체의 존재를 알고 나서 곧바로 BackgroundColor 또는 BgColor과 같은 속성이 제공되는지 확인해 보았다. 하지만 각 Range(또는 Cell)는 여러분이 생각하는 것 이상의 많은 속성들을 제공하고 있다. 또한 각 속성들이 또 다른 속성 그룹으로 나뉘어 있기 때문에 세부적인 속성을 설정하기 위해서는 거의 대부분 여러 속성 그룹을 거쳐야 할 것이다. 이들 속성 중 특정한 Range에서 화면에 표시되는 부분에 대한 속성은 Style를 통해서 설정할 수 있다.

결국 Range에 색상을 적용하기 위해서는 Style이 필요하며, 예제에서는 다음 <Figure 10>에서와 같이 총 9개의 Style들을 생성하고 있다. Style을 생성하는 방법은 간단하다. Style 객체를 생성하고 배경색을 지정한 후, Style의 이름을 지정한다. 소스에서와 같이 style 객체의 이름과 style의 이름을 동일하게 명명할 필요는 없지만, 소스를 직관적으로 유지하기 위해서 그렇게 했을 뿐이다. 실제로 사용되는 부분은 style 객체의 이름이 아니라 Style 자체의 이름임을 명심하도록 한다. Style 이름은 Range의 Style 속성에 할당한다. Style 속성은 읽고 쓰기가 가능한 속성으로 테트리스의 성능을 향상시키기 위해서 각 range에 style을 적용할 때, 해당 range의 style이 새롭게 적용하려는 style로 이미 설정되어 있는 상태라면 style을 적용하지 않도록 코드를 작성하였다. 화면에 그리는 작업보다 논리적으로 range의 상태를 판단하는 작업이 CPU 속도에 더 적은 영향을 준다는 사실은 자명한 일이다.

/// <summary>

/// This method will create new styles which are used to fill block.

/// You can't fill any range directly, but fill with styles.

/// </summary>

private void CreateStyle ( )

{

       object missingValue = Type.Missing;

       try

       {

              VerticleLineStyle = ThisWorkbook.Styles["VerticleLineStyle"];

              LeftThreeStyle = ThisWorkbook.Styles["LeftThreeStyle"];

              RightThreeStyle = ThisWorkbook.Styles["RightThreeStyle"];

              LeftTwoStyle = ThisWorkbook.Styles["LeftTwoStyle"];

              RightTwoStyle = ThisWorkbook.Styles["RightTwoStyle"];

              RectangleStyle = ThisWorkbook.Styles["RectangleStyle"];

              TriangleStyle = ThisWorkbook.Styles["TriangleStyle"];

              BackgroundStyle = ThisWorkbook.Styles["BackgroundStyle"];

              BoardBorderStyle = ThisWorkbook.Styles["BoardBorderStyle"];

       }

              // Style doesn't exist yet.

       catch

       {

              VerticleLineStyle = ThisWorkbook.Styles.Add("VerticleLineStyle",

                      missingValue);

              VerticleLineStyle.Interior.Color = (255 << 16) | (0 << 8) | 0;

              VerticleLineStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

             

              LeftThreeStyle = ThisWorkbook.Styles.Add("LeftThreeStyle",

                      missingValue);

              LeftThreeStyle.Interior.Color = (222 << 16) | (186 << 8) | 0;

              LeftThreeStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;       

             

              RightThreeStyle = ThisWorkbook.Styles.Add("RightThreeStyle",

                      missingValue);

              RightThreeStyle.Interior.Color = (222 << 16) | (0 << 8) | 222;

              RightThreeStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

             

              LeftTwoStyle = ThisWorkbook.Styles.Add("LeftTwoStyle",

                      missingValue);

              LeftTwoStyle.Interior.Color = (255 << 16) | (66 << 8) | 156;

              LeftTwoStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

             

              RightTwoStyle = ThisWorkbook.Styles.Add("RightTwoStyle",

                      missingValue);

              RightTwoStyle.Interior.Color = (0 << 16) | (186 << 8) | 148;

              RightTwoStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

             

              RectangleStyle = ThisWorkbook.Styles.Add("RectangleStyle",

                      missingValue);

              RectangleStyle.Interior.Color = (69 << 16) | (222 << 8) | 0;

              RectangleStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

             

              TriangleStyle = ThisWorkbook.Styles.Add("TriangleStyle",

                      missingValue);

              TriangleStyle.Interior.Color = (66 << 16) | (186 << 8) | 0;

              TriangleStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

              BoardBorderStyle = ThisWorkbook.Styles.Add("BoardBorderStyle",

                      missingValue);

              BoardBorderStyle.Interior.Color = (192 << 16) | (192 << 8) | 192;

              BoardBorderStyle.Interior.Pattern = Excel.XlPattern.xlPatternSolid;

              BackgroundStyle = ThisWorkbook.Styles.Add("BackgroundStyle",

                      missingValue);

              BackgroundStyle.Interior.Color = (255 << 16) | (255 << 8) | 255;

              BackgroundStyle.Interior.Pattern = Excel.XlPattern.xlPatternNone;

       }

}

<Figure 10 ? CreateStyle Method>

이렇게 생성한 Style을 다음 <Figure 11>의 FillCell 메서드에서 사용한다.

/// <summary>

/// Draw one block.

/// </summary>

/// <param name="sheet">Excel.Sheet object that will be used to draw</param>

/// <param name="type">kind of block</param>

/// <param name="x">Cooridnate X</param>

/// <param name="y">Cooridnate Y</param>

private void FillCell ( ref Excel.Worksheet sheet, int type, int x, int y )

{

       Excel.Range rng;

       BLOCK_TYPE bt = (BLOCK_TYPE)type;

       rng = (Excel.Range)sheet.Cells[y, x];

       string strStyleName = "";

       try

       {

              switch ( bt )

              {

                      case BLOCK_TYPE.NONE :

                             strStyleName = "BackgroundStyle";

                             break;

                      case BLOCK_TYPE.VERTICLE_LINE :

                             strStyleName = "VerticleLineStyle";

                             break;

                      case BLOCK_TYPE.LEFT_THREE :

                             strStyleName = "LeftThreeStyle";

                             break;

                      case BLOCK_TYPE.RIGHT_THREE :

                             strStyleName = "RightThreeStyle";

                             break;

                      case BLOCK_TYPE.LEFT_TWO :

                             strStyleName = "LeftTwoStyle";

                             break;

                      case BLOCK_TYPE.RIGHT_TWO :

                             strStyleName = "RightTwoStyle";

                             break;

                      case BLOCK_TYPE.RECTANGLE :

                             strStyleName = "RectangleStyle";

                             break;

                      case BLOCK_TYPE.TRIANGLE :

                             strStyleName = "TriangleStyle";

                             break;

              }

              if ( ((Excel.Style)rng.Style).Name.Equals(strStyleName) == false )

                      rng.Style = strStyleName;

       }

       catch ( Exception ex ) {}

}

<Figure 11 ? FillCell Method>

마지막으로 중요하진 않지만, About 버튼을 누르면 다음 < Figure 12>과 같이 로고를 보여주도록 하였다. 여러분도 예상할 수 있겠지만, 이 로고는 2차원 배열로 각 좌표를 계산한 후, 출력하는 방법을 사용하고 있다.


< Figure 12 ? Logo>

지금까지 살펴본 내용은 게임을 시작하는 부분에 대해서만 소개하고 있을 뿐이다. 여러분은 게임이 종료되었거나 시작된 상태, 또는 사용자가 한 줄 이상을 완성시켜 해당 행을 삭제한 정보들에 대한 이벤트를 받을 수 있어야 한다. MyTetris에서는 이와 같은 용도로 다음 <표 2>와 같이 3가지 delegate를 제공한다.

이름

설명

이벤트

GameStartedEventHandler

게임이 시작되었음을 알린다.

GameStarted

GameEndedEventHandler

게임이 종료되었음을 알린다.

GameEnded

ClearLineEventHandler

사용자가 한 줄 이상의 블록을 완성했음을 알린다.

ClearLine

<표 2 - MyTetris에서 제공하는 delegate>

이 <Figure 6>에서 이 delegate를 사용하여 이벤트 핸들러를 작성하고 있다는 것을 확인할 수 있다. 게임이 종료되면 타이머를 중단시키고 사용자가 한 줄 이상의 블록을 완성했다면 이를 이용하여 포인트를 계산할