2013年11月5日火曜日

NOTE: 要約化中にディスク処理が発生しました。

時間が出来たときに、ツールで以下のメッセージを捕捉するように修正する。

  • Processing on disk occurred during summarization. Peak disk usage was approximately nnn Mbytes. Adjusting SUMSIZE may improve performance. 
  • NOTE: 要約化中にディスク処理が発生しました。 ディスクの最大使用はおよそ 2454 M バイトです。 SUMSIZE の調整によって、パフォーマンスを改善できるかもしれません。

Calls a specific routine or module that resides in an external dynamic link library (DLL).

SASからWindowsのAPIを呼び出す方法を整理しています。
その理由を列挙しますと

  • SASのバージョンアップの作業で、Xステートメントが規制されている
  • SASデータセットをCOPYプロシージャで移行するとタイムスタンプが維持できない
  • タイムスタンプが変わると元ファイルが更新されたときに再転送が必要かわからない
  • DOS窓からの実行だと遅い

VBScriptから制御する手もありますが、古いデータセットから順に移行したり、停止の制御を考えるとSAS1本でやるのが良さそうです。

基本はDLLを呼び出すためのI/Fを"The SASCBTBL Attribute Table"で定義して、MODULE関数で呼び出します。この仕組みが繊細で、定義を間違えるとSAS.EXEが落ちます。WinAPIの型を調べて、変数を割り当てるのは面倒な作業です。

2013年11月4日月曜日

SASからCopyFileAを呼び出して、ファイルをコピー

SASからWindowsのAPIをコールするサンプルコードです。バージョンアップによるデータの引越しで、今回はWinAPIを使ったツールを作る予定です。動作確認したのは、Windows7+SAS9.2です。

ネットにはいくつか見本が転がっていますが、PDFで文字が化けていたり、APIがまったく変わってしまったようなものもあります。素直に動くものが少ないので、いくつかの手本を元に作成しました。APIの定義をテキストで別に用意している例が多いのですが、カタログを使った例がまとまりが良いです。

  1. /*  
  2. *++  
  3. * WinAPI(CopyFileA)を呼び出して、ファイルをコピー  
  4. *--  
  5. */  
  6.   
  7. filename winapi catalog 'WORK.Windows.WINAPI.SOURCE';  
  8.   
  9. data _null_;  
  10.  file winapi;  
  11.  input;  
  12.  put _infile_;  
  13.  cards4;  
  14. routine CopyFileA  
  15.  module=KERNEL32  
  16.  minarg=3 maxarg=3 stackpop=called  
  17.  returns=ushort  
  18. ;  
  19. arg 1 input char format=$cstr200.; * FROM ;  
  20. arg 2 input char format=$cstr200.; * TO ;  
  21. arg 3 input num format=pib4. byvalue;  
  22. * 1=Do Not Overwrite Existing ;  
  23. * 0=Overwrite Existing File ;  
  24. ;;;;  
  25. run;  
  26.   
  27. filename winapi;  
  28. filename sascbtbl catalog 'WORK.Windows.WINAPI.SOURCE';  
  29.   
  30. data _null_;  
  31.  rc = modulen('*e', 'CopyFileA', 'c:\temp\foo.txt', 'c:\temp\foo_copy.txt', 0);  
  32.  put _all_;  
  33. run;  
  34.   
  35. filename sascbtbl clear;  

SASからSetFileTimeを呼び出して、ファイルのタイムスタンプを設定

コードに間違いがあったので修正しました。
SASからWindowsのAPIをコールするサンプルコードです。バージョンアップによるデータの引越しで、今回はWinAPIを使ったツールを作る予定です。動作確認したのは、Windows7+SAS9.2です。ネットにはいくつか見本が転がっていますが、PDFでうまく転記できなかったり、APIがまったく変わってしまったようなものもありました。

  1. /*  
  2. *++  
  3. * WinAPI(SetFileTime)を呼び出して、ファイルのタイプスタンプを更新  
  4. *--  
  5. */  
  6. filename winapi catalog 'WORK.Windows.WINAPI.SOURCE';  
  7.   
  8. data _null_;  
  9.   file winapi;  
  10.   input;  
  11.   put _infile_;  
  12.   cards4;  
  13. routine CreateFileA  
  14.    module=Kernel32  
  15.    minarg=7  
  16.    maxarg=7  
  17.    stackpop=called  
  18.    returns=long  
  19. ;  
  20. arg 1 char input format=$cstr260.; *  LPCTSTR lpFileName;  
  21. arg 2 num  input format=pib4. byvalue;  * DWORD dwDesiredAccess;  
  22. arg 3 num  input format=pib4. byvalue;  * DWORD dwShareMode;  
  23. arg 4 num  input format=pib4. byvalue;  * LPSECURITY_ATTRIBUTES lpSecurityAttributes (set to null,pass 0 byvalue);  
  24. arg 5 num  input format=pib4. byvalue;  * DWORD dwCreationDispostion;  
  25. arg 6 num  input format=pib4. byvalue;  * DWORD dwFlagsAndAttributes (set to zero);  
  26. arg 7 num  input format=pib4. byvalue;  * HANDLE hTemplateFile (ignored);  
  27. *-------------------------------------------------------------;  
  28.   
  29.   
  30. routine CloseHandle  
  31.    module=Kernel32  
  32.    minarg=1  
  33.    maxarg=1  
  34.    stackpop=called  
  35.    returns=long  
  36. ;  
  37. arg 1 num  input format=pib4. byvalue;  * HANDLE hObject;  
  38.   
  39. *-------------------------------------------------------------;  
  40.   
  41. routine SystemTimeToFileTime  
  42.  minarg=9   
  43.  maxarg=9   
  44.  stackpop=called   
  45.  module=Kernel32   
  46.  returns=long  
  47. ;   
  48. arg 1 num input  fdstart format=pib2.; * WORD wYear ;   
  49. arg 2 num input          format=pib2.; * WORD wMonth ;   
  50. arg 3 num input          format=pib2.; * WORD wDayOfWeek ;   
  51. arg 4 num input          format=pib2.; * WORD wDay ;   
  52. arg 5 num input          format=pib2.; * WORD wHour ;   
  53. arg 6 num input          format=pib2.; * WORD wMinute ;   
  54. arg 7 num input          format=pib2.; * WORD wSecond ;   
  55. arg 8 num input          format=pib2.; * WORD wMilliseconds ;   
  56. arg 9 num output fdstart format=pib8.;  
  57.   
  58. *-------------------------------------------------------------;  
  59.   
  60. routine LocalFileTimeToFileTime   
  61.  minarg=2   
  62.  maxarg=2   
  63.  stackpop=called   
  64.  module=kernel32   
  65.  returns=long  
  66. ;   
  67. arg 1 input  fdstart num format=pib8.; * lpLocalFileTime // converted file time;   
  68. arg 2 output fdstart num format=pib8.; * lpFileTime, // UTC file time to convert;   
  69.   
  70. *-------------------------------------------------------------;  
  71.   
  72. routine SetFileTime   
  73.   minarg=4   
  74.   maxarg=4   
  75.   stackpop=called   
  76.   module=Kernel32   
  77.   returns=long  
  78. ;   
  79. arg 1 num input byvalue format=pib4.; * HANDLE hFindFile // file search handle ;   
  80. arg 2 num input fdstart format=pib8.; * CONST FILETIME * lpCreationTime, // pointer to creation file time ;   
  81. arg 3 num input fdstart format=pib8.; * CONST FILETIME * lpModifiedTime, // pointer to creation file time ;   
  82. arg 4 num input fdstart format=pib8.; * CONST FILETIME * lpAccessedTime, // pointer to creation file time ;   
  83. ;;;;  
  84. run;  
  85.   
  86. filename winapi;  
  87. filename sascbtbl catalog 'WORK.Windows.WINAPI.SOURCE';  
  88.   
  89. %macro touch(path=, year=);  
  90.  data _null_ ;   
  91.   
  92.   GENERIC_READ = 080000100x; /* GENRIC_READ + FILE_WRITE_ATTRIBUTES(0x0100) */  
  93.   FILE_SHARE_READ = 1;  
  94.   OPEN_EXISTING = 3;  
  95.   
  96.   
  97.   length path $260;   
  98.   path = "&path";  
  99.   HANDLE = modulen('CreateFileA', path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0,0);  
  100.   
  101.   If HANDLE >= 1 Then Do;   
  102.   
  103.    cdt_n = datetime();  
  104.    MINUTE = Minute( cdt_n);   
  105.    HOUR = Hour( cdt_n);   
  106.    DAY = DAY( DatePart( cdt_n) );   
  107.    DAYOFWK = WeekDay( DatePart( cdt_n) );   
  108.    MONTH = MONTH( DatePart( cdt_n) );   
  109.    YEAR = &year;  
  110.    SECONDS = 0;  
  111.    MSECONDS = 0;  
  112.    CREATED = .;  
  113.    CREATED1 = .;  
  114.   
  115.    rc = ModuleN('SystemTimeToFileTime', YEAR, MONTH, DAYOFWK,  DAY, HOUR, MINUTE, SECONDS, MSECONDS, CREATED);   
  116.    rc = ModuleN('LocalFileTimeToFileTime', CREATED, CREATED1);  
  117.    put rc= created= created1=;  
  118.   
  119.    ACCESSE1 = CREATED1;  
  120.    WRITTEN1 = CREATED1;  
  121.   
  122.    rc = ModuleN('SetFileTime', HANDLE, CREATED1, ACCESSE1, WRITTEN1);  
  123.    put rc=;  
  124.    rc = Modulen('CloseHandle', HANDLE);   
  125.    put rc=;  
  126.   end;   
  127.  run;   
  128. %mend;  
  129.   
  130. %touch(path=c:\temp\foo.txt, year=2009);  
  131.   
  132. filename sascbtbl clear;