8

私は、単一の .XLS ファイルのいくつかの異なるスプレッドシートからデータを抽出し、後で処理するためにテキスト ファイルに統合する Delphi プログラムを作成しました。Delphi 7コンソールプログラムです。

コードの最も関連性の高い部分を抜粋すると、明らかに、私のプログラムはかなり適切に動作しているか、少なくとも必要なだけ動作していることがわかります。

uses ...  ActiveX, ComObj ... ;

procedure Fatal(s:string);
  ...
  Halt(1);

var ExcelApp:Variant; (* global var *)

begin (* main program block *)
  coInitialize(nil);
  ExcelApp:=CreateOleObject('Excel.Application');
  try
    ExcelApp.Visible:=False;
    ExcelApp.WorkBooks.Open(ExcelFileName);
  ...
    XLSSheet := ExcelApp.Worksheets[ExcelSheetName];
  ...
    try
      XLSRange := XLSSheet.Range[ExcelRangeName];
    except
      Fatal('Range "'+ExcelRangeName+'" not found');
    end;
    if VarIsNull(XLSRange) then Fatal('Range '+ExcelRangeName+' not found');
    for row:=XLSRange.Row to XLSRange.Rows[XLSRange.Rows.Count].Row do
      for col:=XLSRange.Column to XLSRange.Columns[XLSRange.Columns.Count].Column do
        CellValue:=XLSSheet.Cells[Row,Col].Value;
        ...
        if CellValue<>'' then ...
        ...
    ExcelApp.Workbooks.Close;
    ...
  finally
    ExcelApp.Quit;
    coUninitialize;
  end;   
end.

プログラムが終了しても、XLS がロックされたままになることがあります。タスク マネージャーを見ると、クライアント プログラムの実行時に開始された Excel.exe プロセスが、クライアント プログラムが終了して正常にアンロードされたにもかかわらず、まだ実行されていることがわかります。

この行動の通常の容疑者は誰か知っていますか? クライアントの実行時に常にExcelをアンロードする場所を探す場所はありますか?

4

3 に答える 3

6

ExcelAppバリアントをリリースする必要があります。まだ参照カウントが 1 であるため、Excel は完全には閉じられていません。

これをコードに追加します (マークされた行):

finally
  ExcelApp.Quit;
  ExcelApp := Unassigned;        // Add this line
  coUninitialize;
end;  

問題を再現し、解決策をテストするための簡単なコードを次に示します。

// Add two buttons to a form, and declare a private form field. 
// Add OnClick handlers to the two buttons, and use the code provided. 
// Run the app, and click Button1. Wait until Excel is shown, and then click
// Button2 to close it. See the comments in the Button2Click event handler.
type
  TForm1=class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    ExcelApp: Variant;
  end;

implementation

uses
  ComObj;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExcelApp := CreateOleObject('Excel.Application');
  ExcelApp.Visible := True;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ExcelApp.Visible := False;
  ExcelApp.Quit;

  // Leave the next line commented, run the app, and click the button.
  // After exiting your app NORMALLY, check Task Manager processes, and you'll
  // see an instance of Excel.exe still running, even though it's not
  // in the Applications tab. 
  //
  // Do an "end process" in Task Manager to remove the orphaned instance 
  // of Excel.exe left from the above. Uncomment the next line of code
  // and repeat the process, again closing your app normally after clicking
  // Button2. You'll note that Excel.exe is no longer in
  // Task Manager Processes after closing your app.

  // ExcelApp := Unassigned;
end;

end.
于 2012-09-06T16:42:44.720 に答える
4

私は XE2 でほぼ同じ問題に遭遇しました。私の解決策は、そのようなコード サンプルを置き換えることでした。

fExcel.ActiveWorkBook.ActiveSheet.Range[
    fExcel.ActiveWorkBook.ActiveSheet.Cells[3, 2],
    fExcel.ActiveWorkBook.ActiveSheet.Cells[3+i,1+XL_PT_Tip_FieldCount]
].Formula := VarArr;

と:

cl := fExcel.ActiveWorkBook.ActiveSheet.Cells[3, 2];
ch := fExcel.ActiveWorkBook.ActiveSheet.Cells[3+i,1+XL_PT_Tip_FieldCount];
fExcel.ActiveWorkBook.ActiveSheet.Range[cl, ch].Formula := VarArr;

シート変数が使用されているこの場合も同じことが起こります。

sheetDynamicHb := fExcel.ActiveWorkBook.Sheets['Dynamics Hb'];
cl := sheetDynamicHb.Cells[52, 2];
ch := sheetDynamicHb.Cells[52+i, 2+3];
sheetDynamicHb.Range[cl, ch].Formula := VarArr;

何らかの方法で一時変数 ( cl,ch: Variant) を導入するとうまくいきます。ネストされた Excel 変数へのアクセスが奇妙なことをしているようです。なぜこれがそのように機能するのか説明できませんが、機能します..

于 2015-01-19T13:25:01.137 に答える
3

「ゾンビ」Excelプロセス(アプリから起動してアプリを強制終了しても実行されたままになるプロセス)を閉じようとすると、同じ問題に直面しました。提案されたすべてのアクションを試してみましたが、うまくいきませんでした。最後に、通常の COM メソッドが役に立たない場合に、WinApi を使用してトリックを確実に実行する複合キラー プロシージャを作成しました。

procedure KillExcel(var App: Variant);
var
  ProcID: DWORD;
  hProc: THandle;
  hW: HWND;
begin
  hW := App.Application.Hwnd;
  // close with usual methods
  App.DisplayAlerts := False;
  App.Workbooks.Close;
  App.Quit;
  App := Unassigned;
  // close with WinApi
  if not IsWindow(hW) then Exit; // already closed?
  GetWindowThreadProcessId(hW, ProcID);
  hProc := OpenProcess(PROCESS_TERMINATE, False, ProcID);
  TerminateProcess(hProc, 0);
end;
于 2015-10-23T13:25:05.450 に答える