728x90

VBA 매크로를 Word 추가 기능으로
Robert Bogue

코드 다운로드 위치: OfficeSpace2008_05.exe (166 KB)
Browse the Code Online
문서 자동화는 매크로가 처음 등장한 시점부터 사용되고 있는 전혀 새로울 것이 없는 기능으로, 1990년대 초반부터 Microsoft® Office 응용 프로그램에서 프로그래밍 모델이 폭넓게 개발되었습니다. 또한 Office 도구는 수년 전부터 키 입력을 재생할 뿐만 아니라 즉석에서 코드를 작성할 수 있는 매크로 레코더를 제공했습니다. 매크로의 기능은 바이러스를 작성하는 데 이용할 수 있을 정도입니다. Word 매크로를 사용한 바이러스 작성은 그 의도는 잘못된 것이지만 어쨌든 뛰어난 기술입니다.
그러나 VBA(Visual Basic® for Applications)의 모든 기능을 활용하더라도 잘 되지 않는 부분이 있습니다. VBA의 초창기는 XML이 아직 개발되지 않았고 인터넷은 갓 태동기에 있었으며 최초의 HTML 페이지가 막 나타나기 시작했던 때였습니다. 당연히 Visual Basic for Applications의 컨텍스트에서 웹 서비스 호출이 제대로 처리되지도 않았습니다.
반면 Microsoft .NET Framework CLR은 이러한 기술을 상당히 잘 처리합니다. 웹 서비스를 호출하는 이러한 기능은 VBA가 아니라 .NET을 대상으로 코드를 작성하게 되는 많은 이유 중 하나일 뿐입니다. 그리고 .NET 대상 코드를 작성하는 것은 곧 VSTO(Visual Studio® Tools for Office)를 사용함을 의미합니다. 그러나 VBA에서 VSTO로 전환한다고 해서 유용한 도구를 버리게 되는 것은 아닙니다. 단지 Office에서 이미 개발 중인 솔루션을 확장하는 자연스러운 방법이라고 할 수 있습니다.
이 칼럼에서는 Word를 사용하여 기본적인 문제를 해결하는 VBA 코드를 캡처하는 방법에 대해 알아봅니다. 그리고 Visual Studio 2008에 포함된 VSTO의 최신 버전을 사용하여 이 코드를 배포 가능한 Word 추가 기능으로 래핑하겠습니다. 또한 매크로 레코더로는 전혀 기록할 수 없거나 최적의 방식으로 기록되지 않는 몇 가지 작업을 실행하는 간단한 코드도 작성합니다.

책을 통한 학습
최근 필자는 이러한 VBA에서 VSTO로의 변환이 당면한 문제에 완벽히 들어맞았던 경우를 경험했습니다. The SharePoint Shepherd's Guide for End Users라는 책을 탈고하는 중이었습니다. 직접 출판하는 책이었기 때문에 Word로 작성한 원고를 프린터에서 사용 가능한 PDF 형식으로 출력해야 했습니다. 이 작업을 위해서는 먼저 원고를 준비하는 몇 가지 단계를 수행해야 했습니다.
우선 여러 파일을 하나의 큰 문서로 만들어야 했습니다. 13개의 단원과 116개의 작업으로 구성된 140개 이상의 개별 파일을 통합하는 작업을 수동으로 처리하기란 어려웠습니다. Microsoft SharePoint®에서 작업할 때는 편집 워크플로를 통해 각 작업을 개별적으로 추적할 수 있으므로 많은 개별 파일을 사용한 작업이 편리했지만 수가 워낙 많다 보니 통합 작업은 자동화하는 편이 효과적이었습니다.
다음으로, 모든 문서에서 추적된 변경 내용이 모두 적용되도록 해야 했습니다. 편집 과정에서 필자는 Word의 변경 내용 추적 기능을 통해 수정 내용 표시를 사용하여 원고의 편집 및 기타 변경 내용을 추적했습니다. 수정 내용 표시는 내용을 최종 감수하는 과정에서 모두 적용해야 하지만 누락되는 항목이 있을 경우 완성된 서적에 눈에 띄는 수정 내용 표시 서식이 보기 싫게 남게 될 것입니다.
셋째로, 문서에 남아 있는 메모를 모두 제거해야 했습니다. 문서에는 여러 편집자들 간에 작동 원리와 용어 표준화에 대해 주고받은 메모가 있습니다. 이러한 메모 역시 검토 초반에 제거되어야 하는 부분이지만 이를 확실히 확인할 수 있는 자동화된 방법이 필요했습니다. 수정 내용 표시와 마찬가지로 메모도 최종 출판본에 남아 있어서는 안 되니까요.
이러한 각 항목을 제작하는 프로세스, 그리고 각 기능에 필요한 코드를 캡처하는 매크로 레코더의 관련 기능을 살펴보겠습니다. 먼저 레코더를 사용하여 기본 자동화 코드를 생성하는 방법부터 시작해서 실행하는 데 문제는 없지만 최적화된 수준으로 생성되지는 않은 코드를 살펴보고 마지막으로 코드를 전혀 생성하지 않는 레코더를 살펴보겠습니다. 그리고 VBA 코드를 VSTO 코드로 변환하여 최종 출판본을 구성하는 데 사용할 Word 추가 기능으로 만들어 보겠습니다. 프로세스의 난이도를 더하기 위해 VSTO로 변환하는 중간에 VBA 코드를 C#으로 변환할 것입니다.

매크로의 기본
이 프로세스의 첫 단계에서는 매크로를 기록할 수 있도록 Word에 개발 도구 탭을 표시해야 합니다. Office 단추를 클릭한 다음 메뉴 아래쪽에 있는 Word 옵션 단추를 클릭합니다. Word 옵션 대화 상자에서 리본 메뉴에 개발 도구 탭 표시(그림 1 참조) 옆에 있는 확인란을 선택하고 확인을 클릭합니다. 그러면 리본 메뉴에 개발 도구 탭이 표시됩니다.
그림 1 리본 메뉴에 개발 도구 탭 표시 (더 크게 보려면 이미지를 클릭하십시오.)
이제 파일을 열고 해당 문서의 내용을 새 파일에 추가하는 과정을 기록해 보겠습니다. 이렇게 하려면 개발 도구 탭의 매크로 기록 단추(그림 2 참조)를 클릭합니다. 그림 3의 대화 상자가 나타나면 AddFiles라는 이름을 입력하고 확인을 클릭합니다. 커서 아이콘이 옆에 작은 카세트 테이프가 있는 포인터로 바뀝니다.
그림 2 개발 도구 탭에서 매크로 기록 (더 크게 보려면 이미지를 클릭하십시오.)
그림 3 매크로 이름 지정 
이제 평소와 마찬가지로 파일을 열고 문서의 내용을 복사한 다음, 빈 문서를 새로 만들어 복사한 텍스트를 새 문서에 붙여 넣습니다. 개발 도구 탭의 매크로 기록 단추가 있던 위치에서 기록 중지 단추를 클릭합니다. 이 모든 작업이 끝나면 개발 도구 탭의 맨 왼쪽에 있는 Visual Basic 단추를 클릭하여 Visual Basic for Applications 편집기를 실행할 수 있습니다. 그림 4와 같은 새 AddFiles 함수가 나타납니다.
다음 파일 열기 디렉터리를 다시 설정하는 불필요한 코드 줄이 있는 것을 알 수 있습니다. 그 뒤에는 Open 명령과 텍스트를 복사하고 새 문서를 만들고 새 문서에 텍스트를 붙여 넣는 일련의 명령이 있습니다. 매크로 기록을 통해 생성된 코드는 완벽하지는 않지만 나쁘지도 않습니다. 따라서 이 코드를 보강하여 VSTO로 변환하도록 하겠습니다.

매크로를 VSTO로 변환
VSTO를 시작하기 위해 먼저 Word 2007 추가 기능 프로젝트를 만듭니다. Visual Studio를 열고 새 프로젝트를 시작하고 Word 2007 추가 기능 템플릿을 프로젝트에 사용하고 프로젝트 이름을 PublishPrep으로 지정합니다. 새 VSTO 프로젝트가 성공적으로 만들어지면 그림 5와 같은 모습의 Visual Studio 2008 환경이 구성됩니다.
그림 5 새 Word 추가 기능 프로젝트 (더 크게 보려면 이미지를 클릭하십시오.)
프로젝트를 만든 후에는 사용자가 추가 기능에 액세스할 수 있는 수단을 만들어야 합니다. 2007 Office system 응용 프로그램의 경우 이는 리본 탭과 단추를 만드는 것을 의미합니다. 먼저 프로젝트에 새 항목을 추가하고 새 항목 추가 대화 상자에서 리본(비주얼 디자이너) 템플릿을 선택합니다. 새 리본의 이름을 PublishPrep으로 지정합니다.
다음 단계로 리본을 사용자 지정합니다. 여기서는 리본에 고유 탭을 추가하는 대신 추가 기능 탭에 표시되는 그룹을 만들겠습니다.. 이 그룹에는 3개의 단추가 포함됩니다.
리본에서 group1 그룹을 클릭한 다음 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 속성 창에서 그림 6과 같이 그룹 이름을 grpPublishPrep으로 변경하고 레이블을 Publishing Preparation으로 변경합니다.
그림 6 PublishPrep 리본 구성 (더 크게 보려면 이미지를 클릭하십시오.)
도구 상자를 열고 Office Ribbon Controls(Office 리본 컨트롤) 그룹으로 스크롤한 다음 3개의 단추 컨트롤을 Publishing Preparation 그룹으로 끌어 놓습니다. 단추는 기본적으로 세로로 쌓입니다.
첫 번째 단추를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 이름 속성을 btnCreateMaster로, 레이블을 Create Master로 설정합니다. 두 번째 단추의 경우 이름을 btnAcceptRevisions로, 레이블을 Accept Revisions로 설정합니다. 마지막 세 번째 단추는 이름 속성을 btnRemoveComments로, 레이블을 Remove Comments로 설정합니다. 그룹이 그림 7과 같이 구성됩니다.
그림 7 추가 기능을 위해 구성된 단추와 그룹 (더 크게 보려면 이미지를 클릭하십시오.)

새 추가 기능 코드 작성
매크로 레코더는 개별 파일을 책으로 통합하는 데 필요한 단계를 기록하는 작업을 훌률히 수행합니다. 그러나 개별 파일을 선택하는 데 필요한 코드는 여기에 포함되지 않습니다. 이를 위해 표준 파일 열기 대화 상자를 사용합니다. 통합할 모든 파일의 이름이 들어 있는 단일 텍스트 파일을 사용자가 선택할 수 있도록 할 것입니다. 이 코드는 크게 두 부분으로 구성됩니다. 첫 번째 부분에서는 실제로 파일을 첨부하고 두 번째 부분에서는 첨부할 파일의 목록을 가져옵니다.
코드의 첫 번째 부분인 AppendFile 함수(그림 8 참조)는 파일 이름을 단일 매개 변수로 받습니다. 얼핏 보기에는 매크로 레코더에서 생성한 코드와 달라 보이지만 그렇지 않습니다.
처음 4줄은 활성 문서를 가져와 오류 검사를 수행합니다. 활성 문서가 없으면 문서에 정확하게 첨부할 수 없고, 첨부할 파일 이름이 없으면 더 이상 할 수 있는 작업이 거의 없습니다. 이 코드 줄에서는 단추 메서드가 호출될 때 VBA에서 가정된 응용 프로그램과 활성 문서에 대한 참조를 가져오는 작업도 실행됩니다. 이는 기록된 매크로 버전에서는 할 필요가 없는 작업입니다.
다음 일련의 줄(개체 변수 선언)은 C#에서 Word가 제공하는 COM 구성 요소를 호출하는 방식 때문에 필요한 코드입니다. 누락된 값을 지정해야 하며, 값이 모두 참조로 전달되므로 값을 보유할 변수가 필요합니다.
그런 다음 코드에서는 매크로 레코더에서 생성한 것과 동일한 복사 작업을 수행합니다. 실질적인 차이점은 추가 기능 코드는 커서의 위치(활성 문서의 끝으로 설정)를 포함한 활성 문서 관리를 담당한다는 것입니다.
마지막 코드 블록은 열었던 문서를 닫고 변경 내용이 저장되지 않도록 합니다. 또한 개별 파일의 내용이 서로 섞이지 않도록 삽입된 내용의 뒤에 페이지 나누기를 추가합니다.
코드의 두 번째 부분에서는 통합할 파일의 목록을 실제로 가져오고 AppendFile을 호출합니다(그림 9 참조). 이 코드는 매크로 레코더로 캡처한 코드가 아니지만 모든 .NET 구문을 활용할 수 있으므로 VSTO의 성능을 확인할 수 있는 부분입니다. 여기서는 OpenFileDialog 컨트롤, 텍스트 파일을 열고 읽는 기능, 제네릭을 사용하여 파일 목록을 만드는 기능, 목록에서 반복 실행되는 다른 작은 메서드를 호출하는 기능 등을 활용합니다.
이 코드의 구조에 대해서는 "파일에 순서대로 나열되어 있고 파일을 읽는 루프에서 바로 AppendFile을 호출하면 간단할 텐데 왜 굳이 파일의 목록을 만드는가"라는 의문이 생길 수 있습니다. 그 이유는 이후에 유연성을 제공하기 위해서입니다. 눈에 띄는 도구 향상 효과 중 하나는 첨부할 파일을 나열하는 제어 파일을 사용하는 대신 사용자가 첨부할 파일을 선택할 수 있다는 점입니다. 이는 VSTO가 VBA보다 뛰어난 부분 중 하나입니다. VSTO는 완전히 .NET Framework를 기반으로 하기 때문에 제네릭, 강력한 형식의 매개 변수 만들기와 같은 기능을 이용할 수 있습니다.
이러한 코드를 이용하려면 몇 가지 단계를 더 수행해야 합니다. 즉, OpenFileDialog를 추가하고 솔루션에 필요한 여러 네임스페이스 모두에 대해 using 문을 추가해야 합니다. PublishPrep.cs 파일의 디자인 모드에서 도구 상자로 이동하고 대화 상자 그룹에서 리본으로 OpenFileDialog를 끌어 놓습니다. 그러면 Visual Studio에서 OpenFileDialog가 아래쪽에 있는 도크로 이동됩니다. 대화 상자의 이름을 dlgOpenFile로 바꾼 다음 FileName을 지웁니다. 앞서 만든 Create Master 단추를 두 번 클릭합니다. 다음과 같이 파일의 맨 위에 using 문을 추가합니다.
using Microsoft.Office.Interop.Word;
using System.IO;
using System.Reflection;
마지막으로 Visual Studio에서 생성된 메서드 스텁을 그림 9의 코드로 대체합니다.
이제 F5 키를 누르면 추가 기능이 설치된 Word 복사본이 생성됩니다. Create Master 단추를 클릭하여 코드에서 추가할 파일을 가져오는 텍스트 파일을 선택할 수 있습니다. 텍스트가 끝나면 Word를 끝내거나 Visual Studio에서 Shift+F5를 눌러 디버깅을 중지할 수 있습니다.

추가 단추
복잡한 과정은 대부분 끝났고 이제 Accept Revisions 단추를 위한 코드를 추가할 차례입니다. Word 리본 메뉴의 개발 도구 탭으로 돌아가 매크로 레코더를 다시 시작하여 모든 수정 내용을 적용하는 단계를 기록할 수 있습니다. 실제 작업은 검토 탭에서 적용 단추 메뉴를 클릭하고 문서에서 변경 내용 모두 적용을 클릭하여 수행합니다(그림 10 참조). 그러면 다음과 같은 매크로가 기록됩니다.
그림 10 리본 메뉴에 개발 도구 탭 표시 (더 크게 보려면 이미지를 클릭하십시오.)
Sub AcceptAllChanges()
'
' AcceptAllChanges Macro
'
    WordBasic.AcceptAllChangesInDoc
End Sub
매크로 레코더는 WordBasic 개체를 사용하기 위해 이 작업을 기록했습니다. WordBasic 개체는 Word 매크로가 VBA를 사용하도록 수정되기 전의 항목으로, 문제 접근을 위한 최선의 방법은 아닙니다. ActiveDocument를 사용하는 것이 훨씬 더 좋습니다. AcceptAllRevisions 메서드나 Revision 속성의 AcceptAll 메서드를 사용할 수 있습니다. 필자는 Revisions.AcceptAll이라는 명칭이 더 마음에 들지만 어떤 메서드를 사용하더라도 관계없습니다.
VSTO 코드에서 수정 내용을 적용하는 데 필요한 코드는 다음과 같습니다.
private void btnAcceptRevisions_Click(
    object sender, RibbonControlEventArgs e)
{
    Application app = Globals.ThisAddIn.Application;
    Document activeDoc = app.ActiveDocument;
    if (activeDoc == null) return;

    // 변경 내용 모두 적용    activeDoc.Revisions.AcceptAll();
}
메서드를 한 줄로 작성할 수도 있지만 활성 문서가 있는지 확인하고 디버그를 대비해 개재 변수(intervening variable)를 제공하는 것이 좋습니다. 이 코드를 삽입하려면 Visual Studio의 디자인 모드에서 Publish Prep.cs를 열고 Accept Revisions 단추를 두 번 클릭한 다음 Visual Studio에서 자동으로 추가된 btnAcceptRevisions_Click 메서드 프로토타입을 이 새 코드로 바꾸기만 하면 됩니다. F5 키를 눌러 디버깅을 시작하고 추가 기능 탭으로 이동합니다. 단추를 클릭하여 문서의 모든 수정 내용을 적용할 수 있습니다.
Remove Comments 단추는 매크로 레코더에서 기록되지 않는 항목이기 때문에 조금 더 까다롭습니다. 매크로 레코더를 시작한 후 메모를 마우스 오른쪽 단추로 클릭한 다음 삭제 옵션을 선택하면 제대로 작동하지 않습니다. 이는 제거할 메모를 선택할 수 없음을 의미하므로 직접 VSTO 코드를 새로 작성해야 합니다. 다행히 이 코드는 별로 어렵지 않습니다.
Word의 활성 문서에 대한 참조는 이미 있습니다. 해당 개체에는 메모 컬렉션 속성이 있습니다. 이 컬렉션에서 모든 메모가 제거될 때까지 첫 번째 메모를 계속 삭제할 수 있습니다.
private void btnRemoveComments_Click(
    object sender, RibbonControlEventArgs e)
{
    Application app = Globals.ThisAddIn.Application;
    Document activeDoc = app.ActiveDocument;
    if ((activeDoc == null)) return;
    // 메모 지우기    while ((activeDoc.Comments.Count > 0))
    {
        app.Selection.Comments[1].Delete();
    }

}
여기에서 한 가지 흥미로운 사실은 인덱서가 C#의 0이 아니라 1부터 시작한다는 점입니다.
이 기능은 Remove Comments 단추를 두 번 클릭하고 Visual Studio에서 추가된 btnRemoveComments_Click 메서드 프로토타입을 위의 코드로 바꾸면 간단히 솔루션에 추가됩니다.
이제 F5 키를 다시 눌러 계획했던 작업, 즉 파일을 통합하고, 수정 내용을 적용하고, 메모를 제거하는 작업을 모두 실행할 수 있습니다. 놀라운 기능이라고 할 수는 없지만 완성된 추가 기능은 Visual Basic 또는 C#에서 VSTO를 사용하여 더 완성도 높고 정교한 솔루션을 작성하기 위한 프로토타입 VBA 코드를 매크로 레코더로 얼마나 신속하게 생성할 수 있는지 잘 보여 줍니다. 매크로 레코더로 모든 시나리오를 해결할 수는 없지만 지원되는 작업의 경우에는 부담을 크게 덜어 줍니다.

질문이나 의견이 있으면 다음 전자 메일 주소로 보내시기 바랍니다: mmoffice@microsoft.com.


Robert Bogue는 Microsoft Office SharePoint Server 부문의 Microsoft MVP이자 MCSE 및 MCSA:Security이며 100권 이상의 서적 출판과 다른 많은 출판 프로젝트에 참여했습니다. Robert의 블로그 주소는 www.thorprojects.com/blog이며, Rob.Bogue@thorprojects.com을 통해 연락할 수 있습니다. Robert의 최신 서적인 The SharePoint Shepherd's Guide for End Users 프로젝트에 대해 자세히 알아보려면 sharepointshepherd.com을 참조하십시오.
728x90

<코드사인 하시기 전에 아래의 timestamp 거는 방법을 먼저 읽어 주세요>

참고)  pfx 파일로 인증서 파일을 받으신 경우, 아래 5번 이후부터 진행하시면 됩니다.

1. 다음 경로에서 pvkimprt 를 다운 받는다.
   http://office.microsoft.com/downloads/2000/pvkimprt.aspx

2. pvkimprt 를 통해 받은 spc , pvk 파일을 레지스트리로 import 시킨다.
   > pvkimprt -import nine4u.spc nine4u.pvk
   a. 비밀번호를 물어보는 창에서 개인키 비밀번호를 입력한다.
   b. 다음
   c. 인증서 종류 기준으로 인증서 저장소를 자동으로 선택 (default) 에 체크 -> 다음
   d. 마침.
   e. 이상으로 레지스트리에 인증서가 import 된다.
   확인: IE(브라우저) 에서 도구>인터넷옵션>내용>인증서 에서 개인탭을 보시면 발급대상에 추가된 인증서 확인가능

3. pvkimprt 를 통해 pfx 파일로 export 시킨다.
   > pvkimprt -pfx nine4u.spc nine4u.pvk
   a. 비밀번호를 물어보는 창에서 개인키 비밀번호를 입력한다.
   b. 다음
   c. 예, 개인키를 내보냅니다. (default) 에 체크 -> 다음
   d. 가능하면 인증 경로에 있는 인증서 모두 포함 (체크) 그외 체크 모두 없애고 다음
   e. 암호 , 암호확인 입력 후 다음
   f. 파일이름은 저장할 pfx 파일 이름을 입력합니다. (예: nine4u.pfx) 다음.
   g. 마침.


4. 레지스트리의 인증서 삭제
   a. IE(브라우저) 에서 도구 > 인터넷옵션 > 내용 > 인증서에서 개인탭의 인증서를 선택후 제거 합니다.

5. 3에서 pfx 로 export 시킨 파일을 다시 import
   a. IE(브라우저) 에서 도구> 인터넷옵션> 내용> 인증서 에서 가져오기
   b. 다음
   c. 저장된 pfx 파일을 찾아 입력 후 다음
   d. 암호 입력
      개인 키를 내보낼 수 있는 것으로 표시 에 체크
      다음
   e. 인증서 종류 기준으로 인증서 저장소를 자동으로 선택 체크 (default)
      다음
   f. 마침


6. 이제 VBA 프로젝트를 코드사인 합니다.
   a. Visual Basic Editor 를 open (엑셀에서 도구> 매크로)
   b. sign 하실 프로젝트를 선택합니다.
   c. 도구 > 디지털 서명
   d. 선택 버튼 누르시고 좀전에 import 하신 인증서를 찾아서 선택 후 확인.
   e. 확인
   f. vb editor 닫기 전에 저장.
   g. excel 닫기 전에 저장.

(꼭!!!! 참고) VBA signing 시 timestamp 걸기

코드사인 인증서는  인증서 발급시 해당 인증서의 유효기간이 있습니다.

코드사인시에 timestamp를 등록해 주지 않은 경우에 인증서 유효기간 만료후에 signing 된 매크로 등이 invalid 하게 표시됩니다.

이를 방지하기 위해 코드사인시에 반드시 timestamp를 등록할 수 있도록 아래의 작업을 먼저 해 주셔야 합니다.

VBeditor 로 매크로 사이닝시에.. 별도의 timestamp 를 등록하는 UI가 없습니다.
아래와 같이 직접 레지스트리를 수정하셔서 등록해 주셔야 합니다.

VBA Project를 사인시 아래 3개의 레지스트리값을 먼저 등록하시고, 사이닝을 하시면 timestamp가 등록됩니다.

HKEY_Current_User\Software\Microsoft\VBA\Security\TimeStampURL
HKEY_Current_User\Software\Microsoft\VBA\Security\TimeStampRetryCount
HKEY_Current_User\Software\Microsoft\VBA\Security\TimeStampRetryDelay

TimeStampURL : String 값으로 timestamp 서버 URL을 입력합니다.
                        TimeStampURL 은  다음 URL을 사용하시면 됩니다.
                        http://timestamp.verisign.com/scripts/timstamp.dll
                        (timstamp.dll <- time에 e 가 없습니다. 주의!!!)
TimeStampRetryCount : DWord 값으로 VB editor가 timestampurl 접속에 실패하는 경우 재시도 횟수를 입력합니다.
TimeStampRetryDelay : Dword VB editor 가 timestampUrl 접속에 retury 할때 delay 하는 시간을 입력합니다.

- 써트코리아 (http://www.certkorea.co.kr )

728x90

엑셀개체를 생성 후 프로세스가 종료되지 않고 작업관리자에 그대로 남아 있는 경우가 있을 것입니다.

틀림없이 워크북은 닫혔는데도  excel 자체는 종료하지 않고 남아 있는 경우가 있습니다.

엑셀개체가 종료되지 않아 자유게시판에도 질문을 드렸고 비슷한 내용의 글도 참고로 했지만 닫히지 않더군요

작업관리자에서 엑셀과 관련된 프로세서를 모두 종료시키는 코드를 api로 만들어서 하는 방법이 있지만 그것은 어떤 때는 문제를 일으키더군요

또한,워크시트를 닫은 후 워크북과 엑셀자체를 set ~ nothing 해주면 된다고 하던데 그것도 어떻게 코딩을 했느냐에 따라 다르더군요

자료와 도움을 주신분들의 글을 참고해서 몇 시간이 걸려서 해결했습니다.

엑셀개체가 종료안되는 이유는 의외로 간단한 곳에 있더군요

밑의 것을 실행했을 때 엑셀개체가 종료된다는 분도 있는데 저는 안되던데요


문제가 있는 부분을 파란색으로 다시 코딩하니 잘 되더군요

밑의 처럼 코딩하는 분들은 드물수 있지만 엑셀의 매크로 코드를 참고할 때는 가끔 실수를 할 때가 있으므로 한 번쯤 살펴보세요


대부분은 잘 아시는 부분이겠지만,모르시는 분들은 이런 경우가 있으면 참고하세요

너무나 생고생을 해서 찾은 것이라 두번 다시 이런 것으로 고생하는 분들이 없었으면 합니다.



set으로 정의한 것은 반드시 set으로 사용하고 set을 종료시켜주면 됩니다.

밑에서 파란색 부분을 잘 보면 아시겠지만,set로 정의해준대로 사용을 해야 됩니다.

엑셀에서 매크로 코드를 만들어서 vb에 다시 붙여넣어 사용하다 보면 어떤 부분은 set으로 정의해준대로 하지 않아도 정상적으로 작동하는 것을 볼 수 있습니다.

그래서 ActiveSheet.Name = "자료" 것처럼 set에서 정의해준 xlwb를 사용하지 않아도 작동은 합니다.

하지만,이런 것이 엑셀개체를 정상적으로 종료시켜주지 못하는 원인이 됩니다.

당연히 엑셀개체를 로드한 후므로 ActiveSheet.Name = "자료" 것을 사용하면 엑셀에서는 또다른 것을 생성한 것으로 인식하는 것 같습니다.

특히,차트의 제목 위치를 이동하는 부분에서 Selection.Left = 26을 그대로 사용하면 엑셀개체는 닫히지 않습니다.

이것도 set으로 정의해 준대로 하거나 제거를 해야합니다.


set으로 정의한 xlwb나 xlsh으로 코딩을 하지 않은 부분이 한 군데라도 있는지 찾아서 xlwb나 xlsh을 사용해서 코딩하는 것으로 바꾸면 해결됩니다.



Dim xl As Excel.Application

Dim xlwb As Excel.Workbook

Dim xlsh As Excel.Worksheet


On Error Resume Next ' 오류를 무시합니다.


Set xl = GetObject(, "Excel.Application") ' 실행된 엑셀이 있으면 가져온다


If Err.Number <> 0 Then ' Excel가 실행되고 있지 않았다면

Set xl = CreateObject("Excel.Application") ' Excel 객체를 로드한다.

ExcelWasNotRunning = True

End If


On Error GoTo 0 ' 정상적인 오류 처리


Set xlwb = xl.Workbooks.Add '// 워크북 하나를 추가한다.


Sheets.Add '기본양식 입력할 시트 추가(데이터 자료 입력할 시트)=>xlwb.Sheets.Add

ActiveSheet.Name = "자료" '/시트이름 변경=>xlwb.ActiveSheet.Name = "자료"


Set xlsh = xlwb.Sheets.Add '/ 시트를 하나 추가한다.(그래프 그릴 시트)

ActiveSheet.Name = "그래프" '/시트이름 변경=>xlwb.ActiveSheet.Name = "그래프"



'차트를 그릴 데이터 범위를 선택한다.


Sheets("그래프").Select '그래프 시트 선택=>xlwb.Sheets("그래프").Select

xlwb.Charts.Add '차트를 추가한다.

xlwb.ActiveChart.ChartType = xlLineMarkers '차트 종류

xlwb.ActiveChart.SetSourceData Source:=Sheets("데이터자료").Range("A" & i & ":" & colstr & i), PlotBy:=xlRows


xlwb.ActiveChart.Location Where:=xlLocationAsObject, Name:=xlsh.Name

xlwb.ActiveChart.ApplyDataLabels Type:=xlDataLabelsShowValue, LegendKey:=False


xlwb.ActiveChart.HasLegend = False


'차트 제목 입력부분

With xlwb.ActiveChart

 .HasTitle = True

     .ChartTitle.Characters.Text = grpstr(i)

     .ChartTitle.Select

     Selection.Left = 26 '차트 제목 위치=>Selection.Left = 26=>이런 경우도 엑셀개체가 종료가 안되는 원인입니다.(코딩을 안함? 다른 방법?)

     .Axes(xlValue).HasTitle = True

     .Axes(xlValue).AxisTitle.Characters.Text = "정보"


     With .Axes(xlCategory) 'x축 눈금선 조정

         .CrossesAt = 1

         .TickLabelSpacing = 1

         .TickMarkSpacing = 1

         .AxisBetweenCategories = True

         .ReversePlotOrder = False

     End With

   

   

     With .Axes(xlValue) 'y축 눈금선 조정

         .MinimumScale = 0

         .MaximumScale = sty

         .MajorUnit = 1

     End With


End With


xlwb.ActiveSheet.Shapes("Chart " & i).ScaleWidth linewidth, msofalse, msoScaleFromTopLeft '차트 크기

xlwb.ActiveSheet.Shapes("Chart " & i).IncrementLeft -99.6 '차트 왼쪽 위치

xlwb.ActiveSheet.Shapes("Chart " & i).IncrementTop i * y


'workbooks(1).saveas "c:\테스트.xls"=>이렇게 코딩하는 것도 엑셀개체를 정상적으로 닫히지 않게 하는 이유가 됩니다.(set으로 정의해준대로 코딩해야 합니다.)

xlwb.SaveAs "c:\테스트.xls"


xl.Quit

Set xlsh = Nothing

'xlwb.Close

Set xlwb = Nothing





Excel report하다보니.. 별일이 다 있더군뇨..
말씀하신데루 실행해두 어떤때는 되구 아니될때가 있더라구요...
아무래두 Excel의 문제점이 아닐지..
전 그냥 프로세스로 제거했는데 깔끔하더군뇨..

Public Sub Process_Kill(ByVal strXL As String)
Dim Xhwnd As Long
Dim HProcess As Long
Dim PID As Long
Dim TID As Long

Xhwnd = FindWindow("XLMAIN", "MicroSoft Excel - " & strXL & ".xls")

TID = GetWindowThreadProcessId(Xhwnd, PID)

HProcess = OpenProcess(SYNCHRONIZE Or PROCESS_TERMINATE, 0&, PID)

Call TerminateProcess(HProcess, 0&)
DoEvents
Call CloseHandle(HProcess)

End Sub






감사합니다.

아주 잘 종료되네요. ㅎㅎ

종료문제로 인하여 편법으로 엑셀개체를 재활용 하다가,

꼭 새개체를 사용해야하는 이유가 생겨서, 이 문제로 반나절을 고민했습니다.

Range 나 Activecell 들을 set 에 정의한 개체로 사용하지 않고 쓰면

종료가 안되네요.

다른분들은 제발 고생 안하셨으면.. ㅜㅜ

덧붙이자면... Selection 도 개체를 앞에 넣어주면 정상적으로 사용 가능하네요 ^^

'워드프로세스 > VBA' 카테고리의 다른 글

VBA 매크로를 Word 추가 기능으로  (0) 2008.12.16
VBA 매크로 코드사인(codesign)  (0) 2008.04.24
모질라/파이어폭스 XPI signing  (0) 2008.03.20

+ Recent posts