모니터링 자동화 - Excel Write

2023. 12. 21. 21:56개발/C#

2023.12.18 - [개발 기록/C#] - 모니터링 자동화 - Teamviewer 상태 관리

 

모니터링 자동화 - Teamviewer 상태 관리

2023.12.17 - [개발 기록/C#] - 모니터링 자동화 - 웹 크롤링 모니터링 자동화 - 웹 크롤링 C# Selenium 라이브러리를 활용해 모니터링 페이지의 데이터를 크롤링 테스트한다. 크롤링 함수 public bool GetMonit

iruk.tistory.com

 

웹 크롤링 / 팀뷰어 API 호출로 받아온 데이터를

엑셀 보고서 파일에 자동 Write하는 로직을 추가한다.

 

라이브러리는 MS Excel 라이브러리 사용


WriteExcel() bool형 함수

        public bool WriteExcel(string filePath)
        {
            int retryCount = 0;
            bool success = false;
            bool flag = false;

            Excel.Application excelApp = null;
            Excel.Workbook workbook = null;
            Excel.Worksheet worksheet = null;

            endRow = companyListMgr.companyNameList.Count + startRow - 1;

            try
            {
                if (ExcelClosed())
                {
                    #region 엑셀파일 open 및 예외처리

                    while (retryCount < MaxRetries && !success)
                    {
                        try
                        {
                            excelApp = new Excel.Application();
                            excelApp.Visible = false;
                            //excelApp.WindowState = Excel.XlWindowState.xlMaximized;
                            workbook = excelApp.Workbooks.Open(filePath);
                            worksheet = (Excel.Worksheet)workbook.Sheets[1];

                            success = true;
                        }
                        catch (COMException ex)
                        {
                            if (ex.ErrorCode == -2147417846 || ex.ErrorCode == -2147418111)  // 엑셀파일 이미 사용중 에러코드
                            {
                                Thread.Sleep(RetryInterval);
                                retryCount++;
                            }
                            else
                            {
                                return flag;
                            }
                        }
                    }

                    if (!success) // 엑셀파일 오픈 retry 10회 실패 시
                    {
                        return flag;
                    }

                    #endregion

                    WriteCompanyName(worksheet);
                    Thread.Sleep(300);

                    WriteTeamviewer(worksheet);
                    Thread.Sleep(300);

                    WriteReception(worksheet);
                    Thread.Sleep(300);

                    WriteDeviceTotal(worksheet);
                    Thread.Sleep(300);

                    WriteDeviceConnected(worksheet);
                    Thread.Sleep(300);

                    WriteDate(worksheet);
                    Thread.Sleep(300);

                    #region 엑셀파일 저장 및 메모리해제

                    workbook.Save();
                    workbook.Close();
                    excelApp.Quit();

                    ReleaseObject(worksheet);
                    ReleaseObject(workbook);
                    ReleaseObject(excelApp);

                    #endregion

                    flag = true;
                    return flag;
                }
                return flag;
            }
            catch
            {
                return flag;
            }
        }

 

일단 윈폼에서, 버튼 클릭으로 파일 찾기를 실행하고

선택된 파일의 경로를 저장해서

엑셀 Write 시 경로를 호출해 파일을 연다.

private bool ExcelClosed()
{
    bool flag = false;

    Process[] processes = Process.GetProcessesByName("EXCEL");
    Process targetProcess = null;

    foreach (Process process in processes) // 연동보고서 EXCEL 파일이 실행중인지 확인
    {
        if (process.MainWindowTitle.Contains("연동보고서"))
        {
            targetProcess = process;
            break;
        }
    }

    if(targetProcess == null)
    {
        flag = true;
        return flag;
    }
    else // 실행중인 "연동보고서 .... " EXCEL 이 있으면
    {
        try
        {
            targetProcess.Kill();

            if (targetProcess.HasExited)
            {
                flag = true;
            }

            return flag;
        }
        catch (Exception)
        {
            return flag;
        }
    }
}

 

이미 보고서 파일이 실행중인데,

또 보고서 파일을 열고 작업 시

충돌 가능성이 있어서

 

실행중인 보고서 파일은 종료하는 예외처리

 

일단 단순하게 엑셀 파일 명에 "연동보고서"를 체크하는데

더 세밀하게 관리하려면

내부 시트값을 읽거나..

뭐 여러가지 고민해봐야 될듯

 

현재는 사용자의 파일 인지상태를 가정하고 구상했다.

#region 엑셀파일 open 및 예외처리

while (retryCount < MaxRetries && !success)
{
    try
    {
        excelApp = new Excel.Application();
        excelApp.Visible = false;
        //excelApp.WindowState = Excel.XlWindowState.xlMaximized;
        workbook = excelApp.Workbooks.Open(filePath);
        worksheet = (Excel.Worksheet)workbook.Sheets[1];

        success = true;
    }
    catch (COMException ex)
    {
        if (ex.ErrorCode == -2147417846 || ex.ErrorCode == -2147418111)  // 엑셀파일 이미 사용중 에러코드
        {
            Thread.Sleep(RetryInterval);
            retryCount++;
        }
        else
        {
            return flag;
        }
    }
}

if (!success) // 엑셀파일 오픈 retry 10회 실패 시
{
    return flag;
}

#endregion

region 으로 따로 구분한 부분은

단순 "엑셀 파일 실행" 및 "예외처리" 인데

 

함수 자체에 try catch 문이 있어

엑셀 실행 시 에러코드는 예외처리 제외할까 생각중이다.

 

catch 에서 전체 Exception 을 받아오면 되니까

굳이 이중으로 catch를 쓸 필요가 없는듯

 

아무튼 엑셀 파일이 정상적으로 열릴때까지 시도

WriteCompanyName(worksheet);
Thread.Sleep(300);

WriteTeamviewer(worksheet);
Thread.Sleep(300);

WriteReception(worksheet);
Thread.Sleep(300);

WriteDeviceTotal(worksheet);
Thread.Sleep(300);

WriteDeviceConnected(worksheet);
Thread.Sleep(300);

WriteDate(worksheet);
Thread.Sleep(300);

 

그 후, Write 할 데이터별로 구분한 함수들을 전부 실행한다.

 

수신율 / 장비 수/ 팀뷰어 등의 정보를 각각 개별 함수에서 Write

Thread.Sleep(300) 은, 바로 실행 시 누락되는 경우가 있어

최소한의 딜레이 시간을 잡았다.

private void WriteCompanyName(Excel.Worksheet worksheet)
{
    for (int j = 0; j < companyListMgr.companyNameList.Count; j++)
    {
        worksheet.Cells[j+7, 3].Value = companyListMgr.companyNameList[j];
    }
}

Write는 로직은 되게 간단하다.

보고서 양식을 이해한 상태에서,

작성할 양식의 행/열에 맞게 Value 를 입력한다.

ReleaseObject(worksheet);
ReleaseObject(workbook);
ReleaseObject(excelApp);

flag = true;
return flag;

 

모든 데이터 Write 가 완료되면

엑셀 종료 및 메모리 해제

private void ReleaseObject(object obj)
{
    try
    {
        Marshal.ReleaseComObject(obj);
        obj = null;
    }
    catch (Exception)
    {
        obj = null;
    }
    finally
    {
        GC.Collect();
    }
}

 

이건 메모리 해제 및 가비지 수집 함수


문제점

지금까지 웹 크롤링 / 팀뷰어 API / 엑셀 Write 등의

기능 구현은 됐는데

 

한 가지 문제점이

 

프로그램 화면에서

버튼을 누르고

위 3가지 과정이 실행되면

 

1. 버튼 클릭

2. 사용자가 끝날때까지 대기

 

해야 한다.

 

애초에 처음부터 비동기로 설계를 했어야 했는데

웹 크롤링 / API / 엑셀 Write 기능 여부만 확인한다는게

로직을 다 구현해버려서

 

아무튼 비동기로 수정해야겠다.

프로세스 별 비동기로 처리하고

화면상에 로딩바? 현재상태? 를 나타내줄 예정