VB.NET에서 엑셀을 다룰 때 보통은 Microsoft.Office.Interop.Excel 참조를 추가합니다. 그런데 이러면 개발 PC의 오피스 버전에 묶여서, 버전이 다른 PC에 배포하면 참조 오류가 나기도 합니다. CreateObject 로 엑셀을 만들고 변수를 전부 Object 로 두면(늦은 바인딩) 참조 없이 설치된 엑셀이 무엇이든 그대로 씁니다. 자동 완성이 안 되는 대신 배포가 편해지죠. 프로젝트 설정에서 Option Strict 가 꺼져 있어야 컴파일됩니다.

아래는 프로그램 폴더의 csv.csv 를 엑셀로 열어서, 첫 행을 컬럼 이름으로 보고 둘째 행부터 DataTable(D)에 옮기는 코드입니다.

        Dim Excel As Object = CreateObject("Excel.Application")
        Excel.Visible = True
        Dim WB As Object = Excel.Workbooks.Open(Application.StartupPath & "\csv.csv")
        Dim WS As Object = WB.Worksheets(1)
        Dim C As Object = WS.UsedRange
        Dim R As DataRow

        For i As Integer = 2 To C.rows.count
            R = D.NewRow
            For n As Integer = 1 To C.columns.count
                R("ck") = D.Rows.Count + 1
                R(C(1, n).Value.Replace(Space(1), "_")) = C(i, n).Value
            Next
        Next

UsedRange 는 데이터가 들어 있는 영역 전체라, 행·열 개수를 따로 계산하지 않아도 됩니다. C(1, n) 은 첫 행의 n번째 셀, 즉 헤더입니다. 헤더의 공백을 밑줄로 바꿔서 같은 이름의 DataTable 컬럼을 찾아 값을 넣는 구조라, D 에는 미리 그 이름들의 컬럼과 번호용 ck 컬럼이 만들어져 있어야 합니다. 이름이 하나라도 안 맞으면 "열이 이 테이블에 속하지 않습니다" 예외가 납니다.

빠진 부분 - 행 추가와 엑셀 종료

위 코드에는 채운 행을 테이블에 넣는 D.Rows.Add(R) 가 없어서, 실행해도 D 에 아무것도 늘지 않습니다. 그리고 엑셀을 닫지 않으니 실행할 때마다 엑셀 창과 프로세스가 남습니다. 두 가지를 넣은 버전입니다.

        Dim Excel As Object = CreateObject("Excel.Application")
        Excel.Visible = True
        Dim WB As Object = Excel.Workbooks.Open(Application.StartupPath & "\csv.csv")
        Dim WS As Object = WB.Worksheets(1)
        Dim C As Object = WS.UsedRange
        Dim R As DataRow

        For i As Integer = 2 To C.rows.count
            R = D.NewRow
            For n As Integer = 1 To C.columns.count
                R("ck") = D.Rows.Count + 1
                R(C(1, n).Value.Replace(Space(1), "_")) = C(i, n).Value
            Next
            D.Rows.Add(R)
        Next

        WB.Close(False)
        Excel.Quit()
        System.Runtime.InteropServices.Marshal.ReleaseComObject(C)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(WS)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(WB)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(Excel)

Quit 만 하고 COM 객체를 해제하지 않으면 작업 관리자에 EXCEL.EXE 가 계속 남는 경우가 많습니다. 엑셀 자동화에서 제일 자주 듣는 불만이 이것이라, 쓴 객체는 안쪽부터 ReleaseComObject 로 풀어 주는 습관을 들이는 게 좋습니다.

속도와 대안

셀을 하나씩 C(i, n).Value 로 읽으면 COM 호출이 셀 개수만큼 일어나서 수천 행만 돼도 꽤 느립니다. Dim arr = C.Value 로 영역 전체를 2차원 배열로 한 번에 받은 다음 arr(i, n) 으로 읽으면 훨씬 빨라집니다(배열은 1부터 시작). 작업하는 동안 화면을 볼 필요가 없으면 Excel.Visible = False 로 두는 것도 좋고요.

애초에 파일이 CSV라면 엑셀 없이 TextFieldParser 나 File.ReadAllLines 로 직접 읽는 방법도 있습니다. 엑셀이 설치되지 않은 PC에서도 돌고 훨씬 가볍습니다. 다만 엑셀로 열면 따옴표, 쉼표가 들어간 값, 날짜 형식을 엑셀이 알아서 해석해 주기 때문에, 형식이 지저분한 CSV를 다룰 때는 이 방식이 편한 경우도 있습니다. 반대로 엑셀이 001 같은 값을 숫자 1로 바꿔 버리는 일도 있으니, 앞자리 0이 중요한 데이터라면 직접 파싱하는 쪽을 고르세요.