環境: Delphi2007, Windows XP Professional
DLLの引数にVCLを渡す方法を教えてください。
プログラムはListViewの内容をExcelへ出力するプログラムです。
DLLは今回初めて使用します。
色んなサイトを参考になんとかやってみましたが
最後の最後で分からなくなってしまいました。
問題無く動作していると思ったら
確認メッセージダイアログを閉じた後に
アドレスエラーが出ます。
実装方法が間違っていますでしょうか?
以下がソースです。
宜しくお願い致します。
//DLL側プロジェクトソース
library Project1;
uses
SysUtils,
Classes,
xlscmd in 'sor\xlscmd.pas';
{$R *.res}
exports ExcelProc;
begin
end.
//DLL側メインユニット
unit xlscmd;
interface
uses
SysUtils, Variants, Classes, Dialogs, ComCtrls, ComObj, Clipbrd, ActiveX;
//Excelへ出力する
procedure ExcelProc(ListView: TListView);
implementation
const
//VBA Excel定数
//位置
xlLeft = -4131; //左寄せ
xlCenter = -4108; //中央寄せ
xlRight = -4152; //右寄せ
xlBottom = -4107; //下寄せ
xlNone = -4142;
xlAutomatic = -4105;
//罫線線種
xlContinuous = 1;
//罫線幅
xlHairline = 1;
xlThin = 2;
xlThick = 4;
//罫線位置
xlDiagonalDown = 5;
xlDiagonalUp = 6;
xlEdgeLeft = 7;
xlEdgeRight = 10;
xlEdgeTop = 8;
xlEdgeBottom = 9;
xlInsideVertical = 11;
xlInsideHorizontal = 12;
//用紙向き
xlPortrait = 1; //縦向き
xlLandscape = 2; //横向き
//頁方向
xlDownThenOver = 1; //下→上
xlOverThenDown = 2; //上→下
//ListViewのデータをクリップボードへコピー
function CopyToClipBoard(ListView: TListView): string;
var
i, k: Integer;
S: string;
begin
S := '';
//タイトル
for i:=0 to ListView.Columns.Count-1 do
S := S + ListView.Column[i].Caption + #9;
Result := S + #13#10;
//データ
for i:=0 to ListView.Items.Count-1 do begin
S := Trim(ListView.Items[i].Caption) + #9;
for k:=0 to ListView.Columns.Count-2 do
S := S + Trim(ListView.Items[i].SubItems[k]) + #9;
Result := Result + S + #13#10;
end;
end;
//Excelへ出力する
procedure ExcelProc(ListView: TListView);
var
V: OleVariant;
//数字を英字に変換
function NumToAlph(N: Integer): string;
begin
Result := '';
if (N > 26) then Result := Chr(Ord('A') + ((N-27) div 26));
Result := Result + Chr(Ord('A') + ((N-1) mod 26));
end;
//枠線を引く
procedure DrawRectangle(R: string);
begin
V.ActiveSheet.Range[R].Borders[xlDiagonalDown].LineStyle := xlNone;
V.ActiveSheet.Range[R].Borders[xlDiagonalUp].LineStyle := xlNone;
V.ActiveSheet.Range[R].Borders[xlEdgeLeft].LineStyle := xlContinuous;
V.ActiveSheet.Range[R].Borders[xlEdgeLeft].Weight := xlThin;
V.ActiveSheet.Range[R].Borders[xlEdgeLeft].ColorIndex := xlAutomatic;
V.ActiveSheet.Range[R].Borders[xlEdgeTop].LineStyle := xlContinuous;
V.ActiveSheet.Range[R].Borders[xlEdgeTop].Weight := xlThin;
V.ActiveSheet.Range[R].Borders[xlEdgeTop].ColorIndex := xlAutomatic;
V.ActiveSheet.Range[R].Borders[xlEdgeBottom].LineStyle := xlContinuous;
V.ActiveSheet.Range[R].Borders[xlEdgeBottom].Weight := xlThin;
V.ActiveSheet.Range[R].Borders[xlEdgeBottom].ColorIndex := xlAutomatic;
V.ActiveSheet.Range[R].Borders[xlEdgeRight].LineStyle := xlContinuous;
V.ActiveSheet.Range[R].Borders[xlEdgeRight].Weight := xlThin;
V.ActiveSheet.Range[R].Borders[xlEdgeRight].ColorIndex := xlAutomatic;
end;
var
Sheet: Variant;
S, KeepClipBrd: string;
i, RecCnt, ColCnt: Integer;
begin
RecCnt := ListView.Items.Count;
ColCnt := ListView.Columns.Count;
try
V := CreateOleObject('Excel.Application');
except
MessageDlg('Excel起動に失敗しました。', mtError, [mbOK], 0);
// VarClear(V);
{V変数の破棄は VarClear or Unassignedのどっちを使うのが正解?}
V := Unassigned;
Exit;
end;
//クリップボードの内容を退避
KeepClipBrd := ClipBoard.AsText;
try
V.Visible := True;
//画面更新の抑制
V.Application.ScreenUpdating := False;
V.WorkBooks.Add;
V.WorkBooks[1].Activate;
V.WorkSheets[1].Activate;
//先頭以外のシートを消す
V.DisplayAlerts := False;
for i:=V.WorkBooks[1].Sheets.Count downto 2 do V.Sheets[i].Delete;
V.DisplayAlerts := True;
V.ActiveSheet.Name := 'データ一覧'; //表題
V.ActiveWindow.Zoom := 75; //ズーム 75%
V.ActiveWindow.SplitColumn := 0; //行ウインドウ枠設定
V.ActiveWindow.SplitRow := 3; //列ウインドウ枠設定
V.ActiveWindow.FreezePanes := True; //ウインドウ枠固定
Sheet := V.Sheets[1];
Sheet.Activate;
Sheet.PageSetUp.Orientation := xlLandscape; //用紙方向(横)
Sheet.PageSetUp.PrintTitleRows := '$1:$3'; //行タイトルの設定
Sheet.PageSetUp.RightHeader := '&11 ' + FormatDateTime('yyyy/mm/dd hh:nn:ss', Now) + ' 発行'; //日付を設定する
Sheet.PageSetUp.CenterFooter := '&9&P/&N ページ'; //頁数/総頁数の設定
Sheet.PageSetUp.Order := xlOverThenDown; //頁方向(上から下)
//余白の設定
Sheet.PageSetUp.LeftMargin := V.InchesToPoints(05/25.4);//左余白
Sheet.PageSetUp.TopMargin := V.InchesToPoints(10/25.4);//上余白
Sheet.PageSetUp.RightMargin := V.InchesToPoints(05/25.4);//右余白
Sheet.PageSetUp.BottomMargin := V.InchesToPoints(10/25.4);//下余白
Sheet.PageSetUp.HeaderMargin := V.InchesToPoints(10/25.4);//ヘッダー幅
Sheet.PageSetUp.FooterMargin := V.InchesToPoints(05/25.4);//フッター幅
//フォント設定
Sheet.Cells.Font.Size := 9;
//タイトル設定
Sheet.Cells[1,1].Font.Size := 20;
Sheet.Cells[1,1].Font.Name := 'MS Pゴシック';
Sheet.Cells[1,1].Font.Bold := True;
Sheet.Rows[3].Font.Size := 10;
Sheet.Rows[3].HorizontalAlignment := xlCenter;
Sheet.Rows[3].Font.Name := 'MS Pゴシック';
Sheet.Rows[3].Font.Bold := True;
//書式設定
Sheet.Columns[1].NumberFormatLocal := '@';
Sheet.Columns[2].NumberFormatLocal := '@';
Sheet.Columns[3].NumberFormatLocal := '@';
//罫線を引く
S := 'A3:'+ NumToAlph(ColCnt) + IntToStr(RecCnt+3);
DrawRectangle(S);
Sheet.Range[S].Borders[xlInsideVertical].LineStyle := xlContinuous;
Sheet.Range[S].Borders[xlInsideVertical].Weight := xlHairline;
Sheet.Range[S].Borders[xlInsideVertical].ColorIndex := xlAutomatic;
Sheet.Range[S].Borders[xlInsideHorizontal].LineStyle := xlContinuous;
Sheet.Range[S].Borders[xlInsideHorizontal].Weight := xlHairline;
Sheet.Range[S].Borders[xlInsideHorizontal].ColorIndex := xlAutomatic;
S := 'A3:'+ NumToAlph(ColCnt) + '3';
DrawRectangle(S);
//クリップボードからデータの貼り付け
V.Application.StatusBar := 'データ作成中...';
ClipBoard.AsText := CopyToClipBoard(ListView);
V.Application.StatusBar := 'データ出力中...';
Sheet.Cells[3,1].PasteSpecial; //貼り付け
Sheet.Cells[3,1].Select;
//カラム幅の設定
Sheet.Columns['A:' + NumToAlph(ColCnt)].EntireColumn.AutoFit;
//オートフィルタの設定
Sheet.Range[S].Select;
V.Selection.AutoFilter;
Sheet.Cells[1,1] := 'データ一覧';
Sheet.Cells[4,1].Select;
//画面更新の抑制解除
V.Application.ScreenUpdating := True;
V.Application.StatusBar := False;
// Sheet := Unassigned; {Sheet変数も破棄する?}
// VarClear(V);
{V変数の破棄は VarClear or Unassignedのどっちを使うのが正解?}
V := Unassigned;
finally
//退避したクリップボードの内容を戻す
ClipBoard.AsText := KeepClipBrd;
end;
end;
//「CoInitializeが呼び出されていません」のエラー回避策
initialization
CoInitialize(nil); //COM初期化
finalization
CoUnInitialize; //COM解放
end.
//呼び出しEXE
unit main;
interface
uses
SysUtils, Classes, Controls, Forms, ComCtrls, StdCtrls, Dialogs;
type
TfrmMain = class(TForm)
Button1: TButton;
ListView1: TListView;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
procedure SetData;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure ExcelProc(ListView: TListView); stdcall; external 'project1.dll';
procedure TfrmMain.SetData;
var
Item: TListItem;
i: Integer;
begin
for i:=0 to 9 do begin
Item := ListView1.Items.Add;
Item.Caption := Format('A%d', [i+1]);
Item.SubItems.Add(Format('B%d', [i+1]));
Item.SubItems.Add(Format('C%d', [i+1]));
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
SetData;
end;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
ExcelProc(ListView1);
ShowMessage('呼び出し完了');
//ダイアログボタンの[OK]ボタンの後にアドレスエラーが出ます
end;
自己解決しました。
DLL側メインユニットのExcelProc関数に呼出規約のstdcallの記述が漏れていました。
記述したつもりだったのですが、
どうやら呼出側の静的インポートの宣言部に記述したものと混同していたようです(^^;;
//DLL側メインユニット
unit xlscmd;
interface
uses
SysUtils, Variants, Classes, Dialogs, ComCtrls, ComObj, Clipbrd, ActiveX;
//Excelへ出力する
//procedure ExcelProc(ListView: TListView)
procedure ExcelProc(ListView: TListView); stdcall; ←呼出規約を付加
implementation
すみません。
解決チェックを入れるのを忘れていました(^^;
ツイート | ![]() |