이것은 http://blog.naver.com/mojirhi/150002732458 모지리님의 블로그를 발췌한 내용입니다.
너무 잘 만드신 것 같습니다. 부럽습니다.


소스: http://blog.naver.com/mojirhi/150002732458 (모지리님)

argument는 2가지나 3가지를 받도록 했습니다.
//my_excel.of_excel(DW or DS,ib_detailonly,ib_saveobjectinfo)

1.
첫번째는 Excel로 전환할 개체인데 Powerobject로 변경하여
         Datawiindowsk datastore 아무거나 입력받을 수 있도록 하였습니다.
두번째는 ib_detailonly로 grid와 closstab에서만 효고가 있으며 true일때는
         detail만 grid 형태로 출력하며 숫자형 문자열을 정확히 문자열로 읽어줍니다.
세번째는 ib_saveobjectinfo로 생략가능하며 일반적으로는 사용하지 않습니다.
         개발자를 위해서  dw나 ds의 object들의 각종 속성을 excel로 출력해줍니다.

 

2.ib_detailonly가 false이면 saveasascii를 사용합니다.
  ib_detailonly가 True이면 Processing에 따라서
  '0'(=form/tabular/group/n-up)과 '2'(=label)은 무조건 SaveAsAscii를 사용하고
  '1'(=grid)과 '4'(=closstab)은 of_savedetailonly방식을 사용합니다.
  '5'(composite)는 SaveAsAscii방식이 안되므로 모둔 report를 Grid형태로 변경하여 하나의 Excel파일에 Report별로 Sheet를
     구분하여 출력합니다.

3. 입력object를 powerobject로 변경하여 관련함수를 공통함수로 재정의하여 사용합니다.
   of_describe(),of_modify(),of_getitemstring(),of_getitemnumber(),of_getchild()등입니다.

4.QueryTable관련 부분은 잘 확인해보시기 바라며 column정보는 of_analyze에서 설정합니다.

 


/* 사용방법 */

my_n_excel my_excel
my_excel = create my_n_excel
my_excel.of_excel(dw_2,false)


/* 엑셀 Userobject*/
/* 아래소스 import 하면 됨*/


$PBExportHeader$my_n_excel.sru
$PBExportComments$Excel 저장 서비스
forward
global type my_n_excel from nonvisualobject
end type
end forward

global type my_n_excel from nonvisualobject
end type
global my_n_excel my_n_excel

type variables
protected:
datastore       ids_excel           // display값을 임시 저장하기 위한 것.
datastore       ids_object          // dwo 정보를 정리하기 위한 것
unsignedinteger ii_excelcol = 40    // excel의 열 제한 (255이하)
unsignedinteger ii_excelrow = 65535 // excel의 행 제한 (65535이하)
unsignedinteger ii_titlerow = 1     // header부위의 행 (확장예비용)
unsignedinteger ii_celllen  = 255   // excel의 cell값 길이 Max제한
boolean  ib_detailonly =True        // False는 saveasascii를 사용
                                    // Grid, Crosstab 에서만 효과가 있음.
boolean  ib_saveobjectinfo = False  // object 정보를 저장할 지
boolean  ib_composite = False       // Composite인지
            
//*************************************************************
powerobject     ipo_source          // Excel로 전환할 개체
object          ipo_sourcetype      // Datawindow!, Datastore!, Datawindowchild! ,,,
string          is_processing       // ipo_source의 processing
long            il_rowcount         // 전환할 row 수
datawindow      idw_source          // DataWindow용
datastore       ids_source          // DataStore용
datawindowchild idc_source          // DataWindowChild용
datawindowchild idc_report          // Composite의 Report용         
string          is_detail[]         // detail밴드의 column, compute 개체이름
string          is_objtype[]        // is_detail의 objtype
string          is_label[]          // is_detail의 Label
string          is_coltype[]        // is_detail의 coltype
long            il_objx[]           // is_detail의 x좌표

end variables

forward prototypes
public function string of_describe (string as_exp)
public function string of_modify (string as_exp)
public function integer of_string2array (string as_source, string as_separator, ref string as_result[])
public function string of_replaceall (string as_source, string as_before, string as_after)
public function string of_replaceall (string as_source, string as_before, string as_after, boolean ab_matchcase)
public function integer of_analyze ()
public function integer of_saveasascii ()
public function string of_getitemstring (long al_row, string as_colname)
public function string of_getitemnumber (long al_row, string as_colname)
public function integer of_saveinformation ()
public function integer of_savecomposite ()
public function integer of_savedetailonly ()
public function string of_datacopy ()
public function integer of_getchild (string as_report, ref datawindowchild adc_source)
public function long of_writetext (string as_string, string as_filepath)
public function integer of_createdwo (ref datastore ads_source, integer ai_col, boolean ab_header)
public function integer of_excel (powerobject apo_source, boolean ab_detailonly)
public function integer of_excel (powerobject apo_source, boolean ab_detailonly, boolean ab_saveobjectinfo)
end prototypes

public function string of_describe (string as_exp);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_describe
//
// DESCRIPTION : idw_source, ids_source, idc_surce, idc_report에 대하여
//               Describe함수를 공용으로 사용할 수 있도록 함.
//
// RETURN  : String
//               성공 - as_exp의 결과
//               실패 - '!'
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// as_exp                describe()에 전달할 내용 
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

if ib_composite then
 return idc_report.describe(as_exp)
else
 Choose Case ipo_sourcetype
  Case DataWindow!
   Return idw_source.describe(as_exp)
  Case DataStore!
   Return ids_source.describe(as_exp)
  Case DataWindowChild!
   Return idc_source.describe(as_exp)
  Case Else
   Return "!"
 End Choose
end if

end function

public function string of_modify (string as_exp);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_modify
//
// DESCRIPTION : idw_source, ids_source, idc_surce, idc_report에 대하여
//               Modify함수를 공용으로 사용할 수 있도록 함.
//
// RETURN  : String
//               성공 - as_exp의 결과
//               실패 - '!'
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// as_exp                Modify()에 전달할 내용 
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

if ib_composite then
 return idc_report.modify(as_exp)
else
 Choose Case ipo_sourcetype
  Case DataWindow!
   Return idw_source.modify(as_exp)
  Case DataStore!
   Return ids_source.modify(as_exp)
  Case DataWindowChild!
   Return idc_source.modify(as_exp)
  Case Else
   Return "!"
 End Choose 
end if

end function

public function integer of_string2array (string as_source, string as_separator, ref string as_result[]);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_string2array
//
// DESCRIPTION : 주어진 문자열을 특정 구분자(예, 탭)로 구분하여
//               배열에 저장하는 함수
//
// RETURN  : Integer
//
// ARGUMENTS         DESCRIPTION
// ------------------------------------------------------------------------
// as_source       string soruce_string
// as_separator    string separate_charactor
// as_result[]     string array result_saved
//
///////////////////////////////////////////////////////////////////////////////
//                 mojirhi @ gmail . com
///////////////////////////////////////////////////////////////////////////////

string ret[]
integer li_pos, i, li_nextpos, li_len

// Argument 확인
IF len(as_source) = 0 or isnull(as_source) then return -1
IF len(as_separator) = 0 or isnull(as_source) then as_separator = '~t'
IF isnull(as_result) then return -1

as_result = ret //결과배열 초기화
li_len = len(as_separator) - 1

//li_pos = 1
do while(true)
 i++
 li_pos = li_nextpos + 1
 li_nextpos = pos(as_source, as_separator, li_pos)
 
 if li_nextpos > 0 then
  if i = 1 then //맨 처음이면
   as_result[i] = mid(as_source, li_pos, li_nextpos - li_pos)
  else //중간부분
   as_result[i] = mid(as_source, li_pos + li_len, li_nextpos - li_pos - li_len)
  end if
 else //맨 끝이면
  as_result[i] = mid(as_source, li_pos + li_len, len(as_source))
  exit
 end if
loop

return 1

end function

public function string of_replaceall (string as_source, string as_before, string as_after);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_replaceall
//
// DESCRIPTION : 지정된 문자열을 source에서 모두 찾아 바꾸기 함.
//               (ib_matchcase를 항상 False로 함.)
//
// RETURN  : String
//               바꾸기 된 문자열
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// as_source            전환할 대상 문자열 (~에서)
// as_before            전환전 문자열(~를)
// as_after             전환후 문자열(~으로)
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

return of_replaceall(as_source, as_before, as_after, False)
end function

public function string of_replaceall (string as_source, string as_before, string as_after, boolean ab_matchcase);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_replaceall
//
// DESCRIPTION : 지정된 문자열을 source에서 모두 찾아 바꾸기 함.
//
// RETURN  : String
//               바꾸기 된 문자열
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// as_source            전환할 대상 문자열 (~에서)
// as_before            전환전 문자열(~를)
// as_after             전환후 문자열(~으로)
// ab_matchcase         대/소문자 구별(True이면 구별함)
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

long ll_pos

if isnull(ab_matchcase) then ab_matchcase = False

if ab_matchcase then
 ll_pos = posW(as_source, as_before)
 do while ll_pos > 0
  //as_Source = Replace(as_Source, ll_Start, ll_OldLen, as_New)
  as_source = replaceW(as_source, ll_pos, len(as_before), as_after)
  ll_pos = posW(as_source, as_before)
 loop
else
 ll_pos = posW(upper(as_source), upper(as_before))
 do while ll_pos > 0
  as_source = replaceW(as_source, ll_pos, len(as_before), as_after)
  ll_pos = posW(upper(as_source), upper(as_before))
 loop 
end if

return as_source
end function

public function integer of_analyze ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_analyze
//
// DESCRIPTION : idw_source, ids_source, idc_source, idc_report에 대하여
//               DWObject들의 정보를 수집한다.
//               결과는 is_detail[],  is_label[], is_objtype[],
//               is_coltype[], il_objx[] 에 남긴다.
//
// RETURN  : Integer
//               1 = 성공, -1 = 실패
//
// ARGUMENTS         DESCRIPTION
// ------------------------------------------------------------------------
//   None
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

string ls_objects, ls_object[], ls_labelx[], ls_null[]
string ls_objname, ls_band, ls_type, ls_pos, ls_label, ls_coltype
long   i, ll_pos, ll_row, ll_rowcount, ll_null[]

// 결과배열을 초기화 한다.
is_detail = ls_null[];    is_objtype = ls_null
is_label  = ls_null[];    is_coltype = ls_null
il_objx   = ll_null[]

//-----------------------------------------------------------------------------
// 데이터윈도의 모든 오브젝트를 읽어와서 배열에 담는다.
//-----------------------------------------------------------------------------
ls_objects = of_describe('Datawindow.objects' )
of_string2array(ls_objects, '~t', ls_object)

//-----------------------------------------------------------------------------
// datastore를 이용하여 모든 control의 기본정보를 가져오고
// column의 배열순서를 정하고 Header Text를 가져오도록 한다.
//-----------------------------------------------------------------------------
ids_object.reset()

//개발용 출력을 위해서 Label을 설정함.
ids_object.modify('C1_t.text = "Object_Name"')
ids_object.modify('C2_t.text = "V"')
ids_object.modify('C3_t.text = "Band"')
ids_object.modify('C4_t.text = "Obj_Type"')
ids_object.modify('C5_t.text = "Label"')
ids_object.modify('C6_t.text = "Col_Type"')
ids_object.modify('C7_t.text = "Text"')
ids_object.modify('C8_t.text = "X"')
ids_object.modify('C9_t.text = "Width"')
ids_object.modify('C10_t.text = "X2"')
ids_object.modify('C11_t.text = "Y"')
ids_object.modify('C12_t.text = "Height"')
ids_object.modify('C13_t.text = "Y2"')
ids_object.modify('C14_t.text = "Border"')
ids_object.modify('C15_t.text = "Font.Weight"')
ids_object.modify('C16_t.text = "Color"')
ids_object.modify('C17_t.text = "Bg_Color"')

For i = 1 to upperbound(ls_object)
 ls_objname = ls_object[i]
 ids_object.InsertRow(0)
 ids_object.SetItem(i , 'C1' , ls_objname) // object name
 ids_object.SetItem(i , 'C2' , upper(of_describe( ls_objname + '.visible' ))) // True = '1'
 ls_band = upper(of_describe( ls_objname + '.band' ))
  ids_object.SetItem(i , 'C3' , ls_band) // 'DETAIL', 'HEADER' ,,,
 ls_type = upper(of_describe( ls_objname + ".type" ))
  ids_object.SetItem(i , 'C4' , ls_type) // 'COLUMN', 'TEXT' ,,,
 if ls_type = 'COLUMN' or ls_type = 'COMPUTE' then
  ls_label = of_describe(ls_objname + '_t.text')
   ids_object.SetItem(i , 'C5' , ls_label) // Label 없으면 '!'
  ls_coltype = of_describe(ls_objname + '.coltype')
   ids_object.SetItem(i , 'C6' , ls_coltype) // coltype
 end if
 if ls_type = 'TEXT' then
  ids_object.SetItem(i , 'C7' , of_describe( ls_objname + '.text' )) // Label의 내용
 end if
 ll_pos = integer(of_describe( ls_objname + ".x" ))
  //column의 x값이 32766이상은 음수로 나올때의 에러 정정
  IF ll_pos < 0 then ll_pos += 65536
  ls_pos = right('000000' + string(ll_pos),6)
  ids_object.SetItem(i , 'C8' , ls_pos) // X position
 ids_object.SetItem(i , 'C9' , of_describe( ls_objname + '.width' )) // 너비
 ids_object.SetItem(i , 'C10' , string(ll_pos + long(of_describe( ls_objname + '.width' )))) // X2
 ids_object.SetItem(i , 'C11' , right('000000' + (of_describe( ls_objname + ".y" )),6)) // Y Pisition
 ids_object.SetItem(i , 'C12' , of_describe( ls_objname + '.height' )) // 높이
 ids_object.SetItem(i , 'C13' , string(long(of_describe( ls_objname + ".y" )) + long(of_describe( ls_objname + '.height' )))) // Y2
 ids_object.SetItem(i , 'C14' , of_describe( ls_objname + '.border' )) // 테두리
 ids_object.SetItem(i , 'C15' , of_describe( ls_objname + '.font.weight' )) // 굵게 보통-400, 굵게 700
 ids_object.SetItem(i , 'C16' , of_describe( ls_objname + '.color' ))   // 글자색
 ids_object.SetItem(i , 'C17' , of_describe( ls_objname + '.background.color' ))   // 배경색
Next

//-----------------------------------------------------------------------------
// Header Text가 '_t'로 되어 있지 않은 것을 x좌표로 재확인
// Visible='1', label ='' 것을 모은다.
//-----------------------------------------------------------------------------
ls_object = ls_null //배열 초기화
ids_object.setfilter('C2="1" and C5="!"')
ids_object.filter()
if ids_object.rowcount() > 0 then
 for i = 1 to ids_object.rowcount()
  ls_object[i] = ids_object.getitemstring(i, 'C1') // object.name
  ls_labelx[i] = ids_object.getitemstring(i, 'C8') // object.x
 next
end if

ids_object.setfilter('')   // filter제거
ids_object.filter()
ids_object.setsort('C9 D') // header중에서 아래쪽에 있는 것을 우선 찾기 위해.
ids_object.sort()

ll_rowcount = ids_object.rowcount()
if upperbound(ls_object) > 0 then
 for i = 1 to upperbound(ls_object)
  ls_label = ''
  ll_row = ids_object.find('C3 > "HEAD" and C4="TEXT" and C8="' + ls_labelx[i] + '"', 1, ll_rowcount)
  
  if ll_row > 0 then
   ls_label = ids_object.getitemstring(ll_row, 'C7')
   ll_row = ids_object.find('C1="' + ls_object[i] + '"', 1, ll_rowcount)
   ids_object.setitem(ll_row, 'C5', ls_label)
  end if
 next
end if

//-----------------------------------------------------------------------------
// detail밴드에 있는 것을 출력순서로 sort한다.
//-----------------------------------------------------------------------------
ids_object.setfilter('C2="1" and C3="DETAIL" and (C4="COLUMN" or C4="COMPUTE") ')
ids_object.filter()
if is_processing = '2' or is_processing = '4' then
 // 출력순서를 'x'좌표를 기준으로 순서대로 출력(왼쪽 순서대로)
 ids_object.SetSort('C8 A')
else
 // 출력순서를 'y'좌표우선 'x'좌표차선으로 출력(위쪽 왼쪽 순서대로)
 ids_object.setSort('C11 A, C8 A')
end if
ids_object.Sort()

for i = 1 to ids_object.rowcount()
 is_detail[i]  = ids_object.getitemstring(i, 'C1')
 is_objtype[i] = ids_object.getitemstring(i, 'C4')
 is_label[i]   = ids_object.getitemstring(i, 'C5')
 is_coltype[i] = ids_object.getitemstring(i, 'C6') 
 il_objx[i]    = long(ids_object.getitemstring(i, 'C8'))
next

return 1
end function

public function integer of_saveasascii ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_saveasascii
//
// DESCRIPTION : idw_source, ids_source에 대하여
//               SaveAsAscii함수를 사용하여 저장화되 Grid와 Crosstab은
//               Excel에서 Column Type에 따라 문자열을 지정해 줌.
//               (숫자형 문자열의 Excel전환시 숫자화 되는 것 방지)
//
// RETURN  : Integer
//               성공 : 1
//               실패 : -1
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
//   None
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

integer i, li_rc, li_colDatatype[]
string ls_process, ls_mode, ls_coltype[]
string ls_filepath = 'C:\Excel.txt', ls_filename = 'excel'

OleObject MyExcel, MyQuery
/*
li_rc = GetFileSaveName ( "Select File", ls_filepath, ls_filename, "TXT", &
                          "Text Files (*.txt),*.txt") //, "C:\", 32770)
If li_rc <> 1 then Return li_rc //0 파일 저장 취소, -1 Error

IF FileExists(ls_filepath) Then
 li_rc = Messagebox ("저장확인", ls_filepath + "'~r~n 위 파일이 이미 있습니다.~r~n" + &
          "기존 파일을 바꾸시겠습니까?", Question!, YesNo!, 2)
 IF li_rc = 2 Then Return 0 // 파일 저장 취소
End If
*/

if ib_detailonly then //'1','4'
 li_rc = ids_excel.saveasascii(ls_filepath)
 if li_rc <> 1 then &
 MessageBox('1,4', 'SaveAsAscii로 변환중 오류가 발생했습니다.', stopsign!)

else // False or '0', '2'
 choose case ipo_sourcetype
  case datawindow!
   li_rc = idw_source.saveasascii(ls_filepath)
  case datastore!
   li_rc = ids_source.saveasascii(ls_filepath)
  case datawindowchild!
   messagebox('잠깐!', '이 객체는 SaveAsAscii를 지원하지 않습니다.~r~n' &
                     + 'grid형태로 출력을 변경합니다.', exclamation!)
   ib_detailonly = true
   return of_excel(idc_source, ib_detailonly)
  case else
   messagebox('Error', 'of_saveasascii() ~r~n 알수없는 오류가 발생했습니다.', stopsign!)
   return -1
 end choose
end if

if li_rc <> 1 then
 MessageBox('오류!', 'SaveAsAscii로 변환중 오류가 발생했습니다.', stopsign!)
 return -1
end if

MyExcel = Create OleObject
li_rc = MyExcel.ConnectToNewObject("excel.application")
IF li_rc <> 0 THEN
 MessageBox("Information!", "File Conversion Failed!")
 MessageBox('오류!', 'Excel을 구동하면서 오류가 발생했습니다.', stopsign!)
 Destroy MyExcel
 RETURN -1
end if

MyExcel.WorkBooks.Add()
MyExcel.ActiveWorkbook.Sheets.Add.Name = ls_filename
MyQuery = MyExcel.ActiveWorkbook.Sheets(1).QueryTables.Add('TEXT;' + ls_filepath , MyExcel.ActiveWorkbook.Sheets(1).Cells(1,1) )

if ib_detailonly then
 // Column Type을 확인한다.
 for i = 1 to upperbound(is_coltype)
  IF left(upper(is_coltype[i]), 4) = 'CHAR' then
   li_coldatatype[i] = 2
  else
   li_coldatatype[i] = 1
  end if
 next
 MyQuery.TextFileColumnDataTypes = li_coldatatype
end if

MyQuery.refresh()
MyExcel.ActiveWorkbook.sheets(1).Columns.AutoFit
MyExcel.ActiveWorkbook.sheets(1).Cells(1,1).select
MyExcel.Application.Visible = True
MyExcel.DisconnectObject()
if ib_saveobjectinfo then of_saveinformation()

Return 1

end function

public function string of_getitemstring (long al_row, string as_colname);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_getitemstring
//
// DESCRIPTION : idw_source, ids_source, idc_surce, idc_report에 대하여
//               getitemstring함수를 공용으로 사용할 수 있도록 함.
//
// RETURN  : String
//               성공 - 결과문자열
//               실패 - '!'
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// al_row                row 번호
// as_colname            column 이름
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

Choose Case ipo_sourcetype
 Case DataWindow!
  Return idw_source.getitemstring(al_row, as_colname)
 Case DataStore!
  Return ids_source.getitemstring(al_row, as_colname)
 Case DataWindowChild!
  Return idc_source.describe( 'Evaluate(as_colname), al_row)' )
 Case Else
  Return "!"
End Choose  

end function

public function string of_getitemnumber (long al_row, string as_colname);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_getitemnumber
//
// DESCRIPTION : idw_source, ids_source, idc_surce, idc_report에 대하여
//               getitemnumber함수를 공용으로 사용할 수 있도록 함.
//
// RETURN  : String (number가 아님에 주의할 것)
//               성공 - 결과문자열
//               실패 - '!'
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// al_row                row 번호
// as_colname            column 이름
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

Choose Case ipo_sourcetype
 Case DataWindow!
  Return string(idw_source.getitemnumber(al_row, as_colname))
 Case DataStore!
  Return string(ids_source.getitemnumber(al_row, as_colname))
 Case DataWindowChild!
  Return idc_source.describe( 'Evaluate(as_colname), al_row)' )
 Case Else
  Return "!"
End Choose  

end function

public function integer of_saveinformation ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_saveinformation
//
// DESCRIPTION : Excel로 전환한 개체의 DWObject들의 정보를 출력함.
//               (개발시의 column자료를 확인하기 위함)
//
// RETURN  : Integer
//               성공 : 1
//               실패 : -1
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
//   None
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

integer i, li_rc, li_colDatatype[]
string ls_process, ls_mode, ls_coltype[]
string ls_filepath, ls_filename
OleObject MyExcel
OleObject MyQuery

li_rc = GetFileSaveName ( "오브젝트 정보 저장", ls_filepath, ls_filename, "TXT", &
                          "Text Files (*.txt),*.txt") //, "C:\", 32770)
If li_rc <> 1 then Return li_rc //0 파일 저장 취소, -1 Error

IF FileExists(ls_filepath) Then
 li_rc = Messagebox ("저장확인", ls_filepath + "'~r~n 위 파일이 이미 있습니다.~r~n" + &
          "기존 파일을 바꾸시겠습니까?", Question!, YesNo!, 2)
 IF li_rc = 2 Then Return 0 // 파일 저장 취소
End If

ids_object.setfilter('')
ids_object.filter()
li_rc = ids_object.saveasascii(ls_filepath)

if li_rc <> 1 then
 MessageBox('오류!', 'SaveAsAscii로 변환중 오류가 발생했습니다.', stopsign!)
 return -1
end if

MyExcel = Create OleObject
li_rc = MyExcel.ConnectToNewObject("excel.application")
IF li_rc <> 0 THEN
 MessageBox('오류!', 'Excel을 구동하면서 오류가 발생했습니다.', stopsign!)
 Destroy MyExcel
 RETURN -1
end if

MyExcel.WorkBooks.Open(ls_filepath)
MyExcel.ActiveWorkbook.sheets(1).Cells(1,1).select
MyExcel.Application.Visible = True
MyExcel.DisconnectObject()

Return 1

end function

public function integer of_savecomposite ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_savecomposite
//
// DESCRIPTION : idw_source, ids_source에 대하여 composite형태를
//               Report 단위로 처리하여 Excel의 sheet로 변환해 줌.
//               DataWindowChild는 saveasascii방식이 적용안된므로
//               Grid형태로 변형하여 출력함.
//
// RETURN  : Integer
//               성공 : 1
//               실패 : -1
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
//   None
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

Integer   i, j, li_rpt, li_rc, li_dwc, li_cnt, li_coldatatype[]
String  ls_copydata, ls_objects, ls_object[], ls_type
string    ls_rc, ls_report[], ls_process, ls_filepath='C:\excel.txt'
OleObject MyExcel, MyQuery

// datawindow의 모든 dwobject를 읽어와 ls_object[]에 저장
ls_objects = of_describe('Datawindow.objects')
of_string2array(ls_objects, '~t', ls_object[])

// report만 ls_report[]에 저장
for i = 1 to upperbound(ls_object[])
 ls_type = of_describe( ls_object[i] + ".type" )
 choose case ls_type
  case 'report'
   li_rpt++
   ls_report[li_rpt] = ls_object[i]
  case else
   // report가 아닌것은 제외
 end choose
next

//-----------------------------------------------------------------------------
// Report개체는 시트를 구분하여 처리한다.
//-----------------------------------------------------------------------------
TRY
 myExcel = CREATE oleobject

 li_rc = myExcel.ConnectToNewObject ( "Excel.Application" )
 IF li_rc <> 0 THEN
  MessageBox ( "Error", String ( li_rc ) )
  Return -1
 END IF
 myExcel.Application.Visible = False
 myExcel.Workbooks.Add()
 
 ib_composite = true // 여기서부터 공용함수를 idc_report로 바꾼다.
 
 if upperbound(ls_report) > 0 then
  messagebox('참고', '선택하신 개체는 Excel로 전환시 ~r~n' &
                   + '부득이하게 형태가 변경됩니다.', Information!)
        
  for i = 1 to upperbound(ls_report)
   li_rc = of_getchild(ls_report[i], idc_report)
    if li_rc <> 1 then
     messagebox('잠깐!', 'getchild 실패', Stopsign!)
     return -1
    end if
   il_rowcount = idc_report.rowcount()
   ls_process = of_describe('Datawindow.Processing')
   
   choose case ls_process
    case '0', '1', '2', '4'
     // 무조건 Grid형태로 진행함.
    case else
     continue
   end choose
   li_rc = of_analyze()
   if li_rc <> 1 then
    messagebox('잠깐!', 'report분석 실패', Stopsign!)
    return -1
   end if

   li_rc = of_createdwo(ids_excel, upperbound(is_detail), False)
   if li_rc <> 1 then
    messagebox('잠깐!', 'report용 datastore작성 실패', Stopsign!)
    return -1
   end if
     
   ls_copydata = of_datacopy()
   if ls_copydata = '!' then
    messagebox('잠깐!', 'report용 datacopy실패:'+ls_copydata, Stopsign!)
    continue
   end if
   for j = 1 to upperbound(is_coltype)
    IF left(upper(is_coltype[j]), 4) = 'CHAR' then
     li_coldatatype[j] = 2
    else
    li_coldatatype[j] = 1
    end if
   next
 
   li_cnt++
   of_writetext(ls_copydata, ls_filepath)
   
   ClipBoard ( ls_copydata ) //복사
   myExcel.ActiveWorkbook.Sheets.add.name = ls_report[i]
   MyQuery = MyExcel.ActiveWorkbook.Sheets(ls_report[i]).QueryTables.Add &
             ('TEXT;' + ls_filepath , MyExcel.ActiveWorkbook.Sheets(ls_report[i]).Cells(1,1) )   
   MyQuery.TextFileColumnDataTypes = li_coldatatype
   MyQuery.refresh
   myExcel.ActiveWorkbook.sheets(ls_report[i]).Cells(1,1).select
   myExcel.ActiveWorkbook.sheets(ls_report[i]).Columns.AutoFit
   if ib_saveobjectinfo then of_saveinformation()
    
  next
  //한개도 성공하지 못한 경우에는 에러를 반환함.
  if li_cnt < 1 then return -1
 else
  messagebox('잠깐!', '작업할 Report가 하나도 없습니다.', Stopsign!)
  return -1
 end if

CATCH ( exception e )
 MessageBox ( "Error", e.GetMessage() )
 
FINALLY
 if isvalid(myExcel) then
  myExcel.Application.Visible = True
  myExcel.DisconnectObject()
  DESTROY myExcel
 end if

END TRY

RETURN 1
return 1
end function

public function integer of_savedetailonly ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_savedetailonly
//
// DESCRIPTION : idw_source, ids_source에 대하여
//               SaveAsAscii를 적용하지않고 Detail만 Grid형태로 출력함.
//               문자열에 대햐여 숫자로 전환된지 않도록 방지하고,
//               SaveAsAscii에서 잘못되는 group소계 부분을 삭제함.
//               (Excel에서 부분합 같은 기능으로 간단히 할 수 있으며
//                Excel로 전환후 Data의 재사용성을 제고함.)
//
// RETURN  : Integer
//               성공 : 1
//               실패 : -1
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
//   None
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

integer li_rc
string  ls_rc
if is_processing = '4' then
 // Crosstab은 StaticMode를 설정함.
 ls_rc = of_modify('DataWindow.crosstab.StaticMode = "yes"')
 if ls_rc = '!' or ls_rc = '?' then
  messagebox ('오류', 'of_modify() ~r~n Crosstab의 StaticMode를 설정하지 못했습니다.', Stopsign!)
  return -1
 end if
end if
   
li_rc = of_analyze() // objects를 분석.
if li_rc <> 1 then
 messagebox ('오류', 'of_analyze() ~r~n Datawindow를 분석하는 중에 오류가 발생했습니다.', Stopsign!)
 return -1
end if

li_rc = of_createdwo(ids_excel, upperbound(is_detail), False) // 복사용 DataStore 준비.
if li_rc <> 1 then
 messagebox ('오류', 'of_createdwo ~r~n 복사용 DataStore를 만들지 못했습니다.', Stopsign!)
 return -1
end if

ls_rc = of_datacopy() // 자료를 ids_excel로 복사.
if ls_rc = '!' then
 messagebox ('오류', 'of_displaycopy() ~r~n Data를 복사하지 못했습니다.', Stopsign!)
 return -1
end if

return of_saveasascii() // Excel로 읽기
end function

public function string of_datacopy ();///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_datacopy
//
// DESCRIPTION : Datawindow를 Display된 내용으로 임시 DataStore로 Copy한다.
//               Detail Band와 그 Title만 전환함. 서식적용없는 내용Copy임.
//
// RETURN  : String
//               성공 - Tab으로 분리된 문자열
//               실피 - '!'
//
// ARGUMENTS         DESCRIPTION
// ------------------------------------------------------------------------
//   None
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////
string         ls_exp, ls_data, ls_editstyle, ls_copydata
Long    i, j, ll_rowcount
Integer        li_colcount

//-----------------------------------------------------------------------------
// row , column의 개수를 가져온다.
// row의 갯수는 Excel의 한계인 65536에서 Title열을 뺀 65535로 제한한다.
//-----------------------------------------------------------------------------
li_colcount = upperbound(is_detail)
ll_rowcount = il_rowcount
IF ll_rowcount < 1 then return '!' //처리할 행이 없음.
IF li_colcount < 1 then return '!' //처리할 열이 없음.
IF li_colcount > ii_excelcol then li_colcount = ii_excelcol
IF ll_rowcount > ii_excelrow then ll_rowcount = ii_excelrow

//-----------------------------------------------------------------------------
// Data복사용 DataStore를 설정한다.
//-----------------------------------------------------------------------------
ids_excel.reset()

FOR i = 1 TO ll_rowcount    // row의 수만큼 DataStore에 추가한다.
   ids_excel.InsertRow(0)
NEXT

//-----------------------------------------------------------------------------
// 자료를 copy한다.
// data에 탭, 개행문자 등이 있을 경우에는 copy & paste에 문제가 되므로 공백대체.
// 계산필드에 혹시 따옴표가 있을 경우에는 Evaluate의 따옴표 종류를 다르게 할 것.
//-----------------------------------------------------------------------------

FOR j = 1 to li_colcount

 ls_data = ''
 ls_editstyle = of_describe(is_detail[j] + ".edit.style")

 // 원Data의 coltype을 보고 char(##), number에 따라 처리
 choose case ls_editstyle
  case 'checkbox'
   if left(is_coltype[j],4) = 'CHAR' then  //문자열인 경우
    for i = 1 to ll_rowcount
     ls_data = of_getitemstring(i, is_detail[j])
     // 0으로 시작하는 숫자형 문자열의 변형 방지 예) 0001 -> 1 대안 '0001
     if asc(ls_data) = 48 then ls_data = "'" + ls_data
     ids_excel.SetItem(i , j , ls_data)
    next
   else //숫자형인 경우
    for i = 1 to ll_rowcount
     ls_data = of_getitemnumber(I,is_detail[j])
     ids_excel.SetItem(i , j , ls_data)
    next
   end if
  case else //checkbox 이외의 edit style
   IF is_objtype[j] = 'COLUMN' then
    FOR i = 1 TO ll_rowcount
     ls_data = of_Describe("Evaluate('LookupDisplay(" + &
                   is_detail[j] + ")' , " + String(i) + ")")
     if isnull(ls_data) then ls_data = ''
     // 탭과 개행문자를 공백으로 대치
     of_replaceall(ls_data, char(9) , ' ') //Tab
     of_replaceall(ls_data, char(10), ' ') //LF
     of_replaceall(ls_data, char(13), ' ') //CR
     of_replaceall(ls_data, char(34), char(39)) //쌍따옴표
     // 0으로 시작하는 숫자형 문자열의 변형 방지 예) 0001 -> 1 대안 '0001
     //if upper(adw_source.describe( ls_detail[j] + ".coltype" )) = 'CHAR' then
     //  if asc(ls_data) = 48 then ls_data = "'" + ls_data
     //end if
     
     ids_excel.SetItem(i , j , ls_data)
    NEXT
      
   //계산필드 내용 확인
   elseif is_objtype[j] = 'COMPUTE' then
    ls_exp = of_describe(is_detail[j] + ".expression")
    FOR i = 1 TO ll_rowcount
     ls_data = of_Describe("Evaluate('" + ls_exp + "'," + string(i) + ")")
     if isnull(ls_data) then ls_data = ''
     // 탭과 개행문자를 공백으로 대치
     of_replaceall(ls_data, char(9) , ' ') //Tab
     of_replaceall(ls_data, char(10), ' ') //LF
     of_replaceall(ls_data, char(13), ' ') //CR
     of_replaceall(ls_data, char(34), char(39)) //쌍따옴표
     // 0으로 시작하는 숫자형 문자열의 변형 방지 예) 0001 -> 1 대안 '0001
     //if upper(adw_source.describe( ls_detail[j] + ".coltype" )) = 'CHAR' then
     //  if asc(ls_data) = 48 then ls_data = "'" + ls_data
     //end if
     ids_excel.SetItem(i , j , ls_data)
    NEXT
   end if
   
 end choose //edit style에 따른 처리 종료
NEXT

//-----------------------------------------------------------------------------
// Header title 삽입
//-----------------------------------------------------------------------------
ids_excel.InsertRow(1)
for i = 1 to upperbound(is_label)
 ids_excel.SetItem(1 , i , is_label[i])
next

ls_copydata = ids_excel.object.datawindow.data

RETURN ls_copydata

end function

public function integer of_getchild (string as_report, ref datawindowchild adc_source);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_getchild
//
// DESCRIPTION : idw_source, ids_source에 대하여
//               getchild함수를 공용으로 사용할 수 있도록 함.
//
// RETURN  : String
//               성공 :  1
//               실패 : -1
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// as_report             getchild에 사용할 report명
// adc_source            getchild로 할당될 DataWindowChild 참조개체
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

Choose Case ipo_sourcetype
 Case DataWindow!
  Return idw_source.getchild(as_report, adc_source)
 Case DataStore!
  Return ids_source.getchild(as_report, adc_source)
 Case DataWindowChild!
  Return -1 // idc_source.getchild(as_report, adc_source)
 Case Else
  Return -1
End Choose  

end function

public function long of_writetext (string as_string, string as_filepath);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_writetext
//
// DESCRIPTION : 주어진 문자열을 Text파일로 출력함.
//
// RETURN  : Long (출력된 문자열 길이)
//
// ARGUMENTS         DESCRIPTION
// ------------------------------------------------------------------------
// as_string       파일로 쓸 문자열
// as_filepath     저장할 파일명을 포함한 Path
//
///////////////////////////////////////////////////////////////////////////////
//                 mojirhi @ gmail . com
///////////////////////////////////////////////////////////////////////////////

long i, ll_len, ll_loop, ll_FileNo, ll_pos
string ls_temp

ll_len  = len(as_string)
ll_loop = integer(ll_len/32765) + 1

ll_FileNo = FileOpen(as_filepath, StreamMode!, Write!, LockWrite!, Replace!)

ll_pos = 1
For i = 1 To ll_loop
   ls_temp = Mid(as_string, ll_pos, 32765)
   ll_pos += 32765
   If FileWrite(ll_FileNo, ls_temp) = -1 Then
      Return -1
   End if
Next
FileClose(ll_FileNo)

return ll_len
end function

public function integer of_createdwo (ref datastore ads_source, integer ai_col, boolean ab_header);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_createdwo
//
// DESCRIPTION : 임시로 사용할 datastore의 dwobject를 만든다.
//
// RETURN  : Integer
//
// ARGUMENTS         DESCRIPTION
// ------------------------------------------------------------------------
// ads_source       source datastore
// ai_col                        dwobject의 열수를 지정.
// ab_header                     Header름 만드는지의 옵션
//                               True 만듦 - DwObject용
//                               False 만들지 않음 - DataCopy용
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

string ls_syntax
integer  i

if not isvalid(ads_source) or isnull(ads_source) then return -1

if ai_col > 255 then ai_col = 255

ls_syntax = ls_syntax + 'release 9;'
ls_syntax = ls_syntax +  '~r~n' + 'datawindow(units=0 timer_interval=0 color=16777215 processing=1 HTMLDW=no print.printername="" print.documentname="" print.orientation = 0 print.margin.left = 110 print.margin.right = 110 print.margin.top = 96 print.margin.bottom = 96 print.paper.source = 0 print.paper.size = 0 print.canusedefaultprinter=yes print.prompt=no print.buttons=no print.preview.buttons=no print.cliptext=no print.overrideprintjob=no print.collate=yes hidegrayline=no grid.lines=0 )'
ls_syntax = ls_syntax +  '~r~n' + 'header(height=80 color="536870912" )'
ls_syntax = ls_syntax +  '~r~n' + 'summary(height=0 color="536870912" )'
ls_syntax = ls_syntax +  '~r~n' + 'footer(height=0 color="536870912" )'
ls_syntax = ls_syntax +  '~r~n' + 'detail(height=92 color="536870912" )'
ls_syntax = ls_syntax +  '~r~n' + 'table('
 for i = 1 to ai_col
  ls_syntax = ls_syntax +  '~r~n' + 'column=(type=char('+ string(ii_celllen) + ') updatewhereclause=yes name=C' + string(i) + ' dbname="C' + string(i) +'" )'
 next
ls_syntax = ls_syntax +  '~r~n' + ' )'
if ab_header then
 for i = 1 to ai_col
  ls_syntax = ls_syntax +  '~r~n' + 'text(band=header alignment="2" text="C' + string(i) + '" border="0" color="0" x="' + string(i*100 + 9) + '" y="8" height="64" width="91" html.valueishtml="0"  name=C' + string(i) + '_t visible="1"  font.face="Arial" font.height="-10" font.weight="400"  font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="536870912" )'
 next
end if
for i = 1 to ai_col
 ls_syntax = ls_syntax +  '~r~n' + 'column(band=detail id='+ string(i) +' alignment="0" tabsequence=' + string(i*10) + ' border="0" color="0" x="'+ string(i*100 + 9) +' " y="8" height="76" width="91" format="[general]" html.valueishtml="0"  name=C' +string(i) + ' visible="1" edit.limit=0 edit.case=any edit.focusrectangle=no edit.autoselect=yes edit.autohscroll=yes edit.imemode=0  font.face="Arial" font.height="-10" font.weight="400"  font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="536870912" )'
next
ls_syntax = ls_syntax +  '~r~n' + 'htmltable(border="1" )'
ls_syntax = ls_syntax +  '~r~n' + 'htmlgen(clientevents="1" clientvalidation="1" clientcomputedfields="1" clientformatting="0" clientscriptable="0" generatejavascript="1" encodeselflinkargs="1" netscapelayers="0" )'
ls_syntax = ls_syntax +  '~r~n' + 'export.xml(headgroups="1" includewhitespace="0" metadatatype=0 savemetadata=0 )'
ls_syntax = ls_syntax +  '~r~n' + 'import.xml()'
ls_syntax = ls_syntax +  '~r~n' + 'export.pdf(method=0 distill.custompostscript="0" xslfop.print="0" )'

return ads_source.create(ls_syntax)

end function

public function integer of_excel (powerobject apo_source, boolean ab_detailonly);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_excel
//
// DESCRIPTION : Datawindow와 DataStrore를 Excel로 전환함.
//
// RETURN  : Integer
//               1 = 성공, -1 = 실패
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// apo_source           Excel로 전환할 Object
// ab_detailonly        Detail만 grid로 전환할 옵션
//                      Grid 및 Crosstab에서만 유효하며
//                      Composite는 무조건 True임
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

integer li_rc
string ls_rc, ls_mode
powerobject lpo_source

if isnull(ib_detailonly) then return -1
ib_detailonly = ab_detailonly

lpo_source = apo_source
if not(isnull(lpo_source)) and isvalid(lpo_source) then
 ipo_sourcetype = lpo_source.typeof()
 choose case ipo_sourcetype
  case datawindow!
   idw_source = lpo_source
   il_rowcount = idw_source.rowcount()
  case datastore!
   ids_source = lpo_source
   il_rowcount = ids_source.rowcount()
  case datawindowchild!
   idc_source = lpo_source
   il_rowcount = idc_source.rowcount()
  case else
   return -1
 end choose

 is_processing = of_describe('Datawindow.Processing')
 ls_rc = of_describe('Datawindow.data')
 if trim(ls_rc) = '' and is_processing <> '5' then
  messagebox('잠깐!', 'Data가 없습니다.', stopsign!)
  return -1
 end if
else
 messagebox('잠깐!', '작업할 수 없는 상태입니다.', stopsign!)
 return -1
end if

choose case is_processing
 case '0', '2' // Form/Tabular/N-Up, Label은 무조건 saveasascii를 적용함.
  ib_detailonly = False
  li_rc = of_saveasascii()
 case '1', '4' // Grid, Crosstab은 ib_detailonly에 따름.
  if ib_detailonly then
   li_rc = of_savedetailonly()
  else
   li_rc = of_saveasascii()
  end if
 case '5'  // Composite는 SaveAsAscii가 안됨
  ib_detailonly = True
  li_rc = of_savecomposite()
 case else
  messagebox('잠깐!', '지원하지 않은 형식의 Data입니다.', Stopsign!)
end choose
if li_rc <> 1 then
 messagebox('잠깐!', 'Excel로 전환되지 못했습니다.', Stopsign!)
end if

return 1
end function

public function integer of_excel (powerobject apo_source, boolean ab_detailonly, boolean ab_saveobjectinfo);///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION  : of_excel(개발용)
//
// DESCRIPTION : Datawindow와 DataStrore를 Excel로 전환함.
//               추가로 전환된 후에 DWObject정보를 Excel로 출력함.
//               (Composite는 맨 마지막 Report만 나옴.)
//
// RETURN  : Integer
//               1 = 성공, -1 = 실패
//
// ARGUMENTS             DESCRIPTION
// ------------------------------------------------------------------------
// apo_source           Excel로 전환할 Object
// ab_detailonly        Detail만 grid로 전환할 옵션
//                      Grid 및 Crosstab에서만 유효하며
//                      Composite는 무조건 True임
// ab_saveobjectinfo    개발용으로 DWObject를 출력할 것인지 옵션
//
///////////////////////////////////////////////////////////////////////////////
//                      mojirhi (mojirhi @ gmail . com)
///////////////////////////////////////////////////////////////////////////////

integer li_rc
if isnull( ab_saveobjectinfo ) then return -1

li_rc = messagebox('DWObject정보저장', &
                  'DWObject정보를 저장하시겠습니까?', &
       Question!, YesNo!, 1)
if li_rc = 1 then
 ib_saveobjectinfo = ab_saveobjectinfo
end if

return of_excel(apo_source, ab_detailonly)

end function

event constructor;ids_object = create datastore
ids_excel  = create datastore
of_createdwo(ids_object, 30, True)

end event

on my_n_excel.create
call super::create
TriggerEvent( this, "constructor" )
end on

on my_n_excel.destroy
TriggerEvent( this, "destructor" )
call super::destroy
end on

event destructor;if isvalid(ids_excel)  then destroy ids_excel
if isvalid(ids_object) then destroy ids_object


end event

Posted by 민서정
l