달력

52024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31


* 시나리오
      1. Excel 2007이 설치된 Local PC
      2. response 객체를 통한 Local에서의 Excel File 생성.
      3. Excel 2003이 설치된 서버에서 동작하는 Excel File Read 메서드.
      4. Excel File Read 메서드는 Oledb 를 통해 데이터를 가져 옴.


자자. 시작해볼까아?!

가끔 필요에 의해서 웹 상에 출력된 자료를 엑셀로 저장해야 할 경우가 있다.

그 경우 대부분.. 간편하고, 또 샘플 소스로 쉽게 접근 할 수 있는 게.

public static void ToExcel(string HTML, string fileName, HttpResponse response)
 {
  var excelHTML = HTML;
  
  response.Clear();
  response.AppendHeader("Content-Type", "application/vnd.ms-excel");
  response.AppendHeader("Content-disposition", "attachment; filename=" + fileName);
  response.Write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />");
  response.Write(excelHTML);
  response.Flush();
  response.End();
 }

이런 녀석이다.

response 객체로 걍.. 엑셀..처럼 떨구는..

뭐 엑셀에서 파일 열면 최초에 경고 메시지 하나 덜렁 떨구는거 말고는 실제로 읽는데 아무런 장애도 없다.
(이게 정말 문제인건데.. 이씨 -_-.....)

저 메서드를 통해서 만들어진 엑셀 파일을 Oledb를 통해서 읽는 순간!!!!!!

어떻게 될까???


아아.. 이거 전에 집고 넘어가야 할 문제가 있음.

** Target Excel File 의 생성에 관한 경우 2가지 
  
1. 엑셀 2007이 설치된 PC에서 위 메서드를 통해 엑셀 파일이 생성된 경우. (.XLS)

2. 엑셀 2003이 설치된 PC에서 위 메서드를 통해 엑셀 파일이 생성된 경우. (.XLS)

위 두 가지 경우가 "대체 뭔 차이냐!!" 라고 물으신다면.. "함 해보셈.." 하고 대답해버리겠음 ㅡ0ㅡ;;

** Excel File Reading 에 관한 경우 2가지. 

1). 엑셀 2007이 설치된 PC에서 Oledb를 통해 읽는 경우.

2). 엑셀 2003이 설치된 PC에서 Oledb를 통해 읽는 경우.


자 여기서 잠시 머리 식히기 위한 문제~ 위 2가지 조건이 가질 수 있는 경우의 수는??? 









4개 인거 모르는 분은 없겠죠 슬마... ㅋㅋㅋ.



자 뻘소린 요까지만 하고. 

1) 번의 경우 1., 2. 두 가지 조건에 대해서 아~무~ 문제 없음.
     (착해 착해. 쥐알도 모르는 날 위해서... 알아서 다 해주는거구나!)

뭐.. 내가 개발하는 PC에는 2007이다 -_-ㅋ 그래서 서버에 적용시켜놓고 한동안 몰랐다. 문제가 있는지 없는지.. 쿨럭.

2) 번의 경우 1. 조건에 대해서 예외, 2. 조건에 대해서 문제 없음. 
     


Response 객체를 통해 만들어진 엑셀 파일은 무늬만 .XLS 파일인 것이다..

요즘엔.. 파일도 짝퉁이... 쿨럭 쿨럭. (중국 산인가 =ㅅ=;;;)

더군다나.. 읽히지 않을라면 다 읽히지나 말지. 엑셀 버젼 특성을 타는건 아니라고 봐요 ㅠ_ㅠ

여튼 -_-ㅋ 파일의 헤더 정보를 정상적으로 가져오지 못하므로.. Oledb를 통해서 백날 읽어봐야. 읽히지 않는다..


아는 범위 내에선 Oledb를 통해 읽는 거로 해결할 수 있는 방법은 모르겠고..

해결책으로 하고 있는 건. 

Excle application 을 통해서 파일을 읽어서 데이터를 가져오는 방법이다. 


public DataTable Excel_Order_File_Read_To_App(string filePath)
 {
  string file_name = filePath.Split('?')[1];
  Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
  
  excelApp.Visible = true;
  
  Microsoft.Office.Interop.Excel._Workbook excelWb =
   excelApp.Workbooks.Open(filePath.Split('?')[0], 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);
  Microsoft.Office.Interop.Excel._Worksheet excelSheet
= (Microsoft.Office.Interop.Excel.Worksheet)excelWb.Worksheets[1];

  Range aa = excelSheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);
  int a = aa.Row;
  int b = aa.Column;
  //Microsoft.Office.Interop.Excel.Range ran = GetUsedRange(excelSheet,
  for (int i = 0; i <= excelSheet.Rows.Count; i++)
  {
  }

  //Process Kill
  int hWnd = FindWindow(null, "Microsoft Excel - " + file_name);
  SendMessage(hWnd, Process_Terminate, 0, 1);
  return null;
 }

일단은 이정도..?!

처리해주어야 할 부분은 Range 인데. 

이건 지금 신나게 삽질 중이니 완료 되는대로 추가하겠음~

아우~ 신나~!


By.R.E.Ms



 



Posted by 은하비류연
|

** 이녀석을 사정없이 벗겨본 이유 -_-?
 엑셀 파일을 strProvider = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + FilePath + "; Extended Properties=\"Excel 8.0; HDR=No;\""; 를 통해 읽어들이는대..

Sheet Name를 가져오기 위해서.

DataTable dt = oleDBCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

조런 코드를 작성해놓았다.

그리고 DataTable를 읽어보는데!!!

헉쑤!! DataTable이 null?!!!

이 녀석이 null 을 반환하는 이유를 알고 싶었다 -_-ㅋ

(그리고.. 그냥 까보고 싶었어 ㅠ_ㅠ)

public DataTable GetOleDbSchemaTable(Guid schema, object[] restrictions)
{
    DataTable table;
    IntPtr ptr;
    ExecutePermission.Demand();
    Bid.ScopeEnter(out ptr, "<oledb.OleDbConnection.GetOleDbSchemaTable|API> %d#, schema=%p{GUID}, restrictions\n", this.ObjectID, schema);
    try
    {
        this.CheckStateOpen("GetOleDbSchemaTable");
        OleDbConnectionInternal openConnection = this.GetOpenConnection();
        if (OleDbSchemaGuid.DbInfoLiterals == schema)
        {
            if ((restrictions != null) && (restrictions.Length != 0))
            {
                throw ODB.InvalidRestrictionsDbInfoLiteral("restrictions");
            }
            return openConnection.BuildInfoLiterals();
        }
        if (OleDbSchemaGuid.SchemaGuids == schema)
        {
            if ((restrictions != null) && (restrictions.Length != 0))
            {
                throw ODB.InvalidRestrictionsSchemaGuids("restrictions");
            }
            return openConnection.BuildSchemaGuids();
        }
        if (OleDbSchemaGuid.DbInfoKeywords == schema)
        {
            if ((restrictions != null) && (restrictions.Length != 0))
            {
                throw ODB.InvalidRestrictionsDbInfoKeywords("restrictions");
            }
            return openConnection.BuildInfoKeywords();
        }
        if (openConnection.SupportSchemaRowset(schema))
        {
            return openConnection.GetSchemaRowset(schema, restrictions);
        }
        using (IDBSchemaRowsetWrapper wrapper = openConnection.IDBSchemaRowset())
        {
            if (wrapper.Value == null)
            {
                throw ODB.SchemaRowsetsNotSupported(this.Provider);
            }
        }
        throw ODB.NotSupportedSchemaTable(schema, this);
    }
    finally
    {
        Bid.ScopeLeave(ref ptr);
    }
    return table;
}
 
뭐 보면 알겠지만 -_-ㅋ 이건 뭥미 =ㅅ=!!!!
OleDbSchemaGuid 를 비교하는 것까지는 대충 이해했지만..
그 다음에 나온 녀석들은 -_- 뭐.. 성전 MSDN에도 안나오고..
쳇...
 
뭐 여튼.. 결론만 말해보자면..
 
Provider 에서 선언한 File에 대한 접근이 정상적이지 않을때. 
파일이 없거나 할때. 상큼하게 null 을 뱉어주심. 
 
창피한 이야기지만...
애당초 알고 있었다.
 
단지.. 파일 경로명을 잘못 적어놓고, 실제로 저장된 파일을 열어보면 파일은 아~무 문제도 없고..
그저 파일도 정상인데. 커넥션도 열리는데. 왜 왜!! null인것이냐!! 라고 울부짖은것 뿐..
 
커넥션이야 뭐.. 파일이 있건 없건 잘 열리겠지만....
 
정말 결론은.. 그저.. 뻘짓 + 삽질 + 창피 
 
쳇쳇쳇..
 
Posted by 은하비류연
|


* 시나리오
    * 1. 시간 비교.
    * 2. 조건 만족시 실행.
    * 3. 파일 복사 완료 체크.
    * 4. 파일 복사 완료 시 파일 읽기 및 처방 저장.
    * 5. 처방 전송 완료 시 결과 입력.
    * 6. 결과 입력 완료 시 엑셀 내보내기.
    * 7. 엑셀 내보내기 완료 시 서버로 엑셀 파일 전송.
    * 8. 백그라운드에서 항상 실행.

기초적인 공사는 이미 오래전에 끝내놨지만...

굳이 -_-ㅋProcess를 통해 무언가 작업을 할 수 밖에 없었던건. 

GC에 대한 자신감?! 이 충만하신 .NET 때문에..

Excel의 Process가 쉬이 사망하시지 않는 것에 있다.

애당초 Vb에서는 알아서 -_-ㅋ 죄다 해제해 주지만서도..

C#에서는 직접! 모조리! 해제 해주고 나서 종료해야.. 실제로 죽으신다는 말씀..

System.Runtime.InteropServices.Marshal.ReleaseComObject(oRng);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oSheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);

요딴 식으로.. 선언했던 것들을 일일히.. 반복문 안에서 선언된 객체는 반복문 안에서 해제해야대고..

이 무슨 짜증나는 노가다인지..

그래서 결국 그냥 저 따위꺼!! 모조리 재껴버리고. 강제로 종료시켜버리기로 결정!


** Process 핸들값 알아내기.
[DllImportAttribute("User32.dll")]
 private static extern int FindWindow(String ClassName, String WindowName);

[DllImportAttribute("User32.dll")]
 public static extern int SendMessage(int hwnd, int wMsg, int wParam, int lParam);

뭐 이녀석들에 대한 관련 자료는 직접.. 찾아보리라 믿어버리겠다.

FindWindow 함수를 통해 프로세스의 캡션명, 클래스 명으로 핸들 값을 알아낼수 있고, 

SendMessage 함수를 통해 프로세스의 동작을 제어할 수 있다. 

** SendMessage 의 두번째 인자 값으로 넘어갈 녀석이 처음에 약간 애매했는데, 

   "윈도우 메시지" 라는 키워드로 검색하면 쉽게 찾을 수 있을 것이다. 
   메시지의 값은 미리 정의되어 있는 녀석들이니까 알아서 잘 -_-;; 찾아내시길..

강제종료가 필요했으므로 나는

const int Process_Terminate = 0x0010; 요녀석을 선언해놨다. 


두구 두구 두구 두구 두구 두구 두구 자 이제 대망의...!!

프로세스 핸들값으로 엑셀 죽이기!! (죽어버렸!!! 지상에서 영원으로 보내주마.)

자.. 엄청난 코드가 나간다.. 긴장해라.. 머리가 빠개질 수도 있다.









int hWnd = FindWindow(null, "Microsoft Excel - " + file_Name);

SendMessage(hWnd, Process_Terminate, 0, 1); 


(으흐흐 긴장하셨음?? 토닥 토닥 ㅎ)

저 두 줄의 코드가 전부다.

핸들값을 알아오고, 핸들 값으로 메세지를 날려서 강제 종료. 


** 캡션 명에 대한 처리가 좀 ... 맘에 안들지만.. 뭐.. 일단은 유일하게 파일명을 생성하니까 접근은 잘 된다. 
** 엑셀을 열고 바로 SPY++ 을 통해 해당 프로세스를 찾아보면 캡션 명이 "Microsoft - Book1" 이리 되있다. 
    oWB.SaveAs 메서드를 호출하면 인자로 넘긴 파일 이름으로 캡션 명이 변경된다. 
    나 처럼 -_-ㅋ 워크 북 네임을 바꿔보겠다고 삽질하지 말길 바란다 ㅡ0ㅡ;;;;


!! 문제점. 
 동일한 캡션 명, 동일한 클래스 명 을 지닌 경우에 특정 프로세스에 대한 선택적 접근이 불가능하다 -_-ㅋ
Process의 "고유 키" 인 Process ID를 통해 처리하면 되겠지만. 이에 대한건 다시 한번 삽질 후 올려놓겠다. 


자자 이건 Bouns~ 제한적으로 Process를 죽이는게 아니고 몽창 다 죽이고 싶을때..

// Kill Excel Process
System.Diagnostics.Process[] excelProcess = System.Diagnostics.Process.GetProcessesByName("EXCEL");

if (excelProcess.Length > 0)
{
    for (int e = 0; e < excelProcess.Length; e++)
    {
        excelProcess[e].Kill();
    }
}

이건 뭐... 그냥 모조리 죽여버리니까.. 짜증나!!!


By.R.E.Ms












 


   

Posted by 은하비류연
|