unit mySaveFileDialog;
interface
uses
  Windows;
function gfnbSaveFileDialog(
var sFile: 
WideString; sDir: 
WideString; hHandle: HWND; iOpt: Integer): Boolean; 
overload;
function gfnbSaveFileDialog(
var sFile: 
WideString; sDir: 
WideString): Boolean; 
overload;
function gfnbSaveFileDialog(
var sFile: 
WideString): Boolean; 
overload;
implementation
uses
  Classes,
  CommDlg,
  Forms,
  Messages,
  MultiMon,
  SysUtils;
// File ------------------------------------------------------------------------
function gfnsFileNameGet(sFile: 
WideString): 
WideString;
{
パスを除いたファイル名を返す
拡張子はつく
\ はつかない
}
var
  i, li_Len, li_Pos: Integer;
begin
  Result := '';
  
if (sFile <> '') 
then begin
    li_Len := Length(sFile);
    li_Pos := li_Len + 1;  
//sFileの最後が'\'であった場合への対策
    for i := li_Len 
downto 1 
do begin
      if (sFile[i] = '\') 
or (sFile[i] = '/') 
or(sFile[i] = ':') 
then begin
        Break;
      
end;
      li_Pos := i;
    
end;
    Result := Copy(sFile, li_Pos, MaxInt);
  
end;
end;
function gfnbFolderExists(sFolder: 
WideString): Boolean;
//DirectoryExitsのWideString対応版
var
  lh_Handle: THandle;
  lr_Info:   TWin32FindDataW;
  li_Len:    Integer;
begin
  Result  := False;
  
if (sFolder <> '') 
then begin
    li_Len := Length(sFolder);
    
if (sFolder[li_Len] = '\') 
then begin
      SetLength(sFolder, li_Len -1);
    
end;
  
end;
  FillChar(lr_Info, SizeOf(TWin32FindDataW), 0);
  lh_Handle:= FindFirstFileW(PWideChar(sFolder), lr_Info);
  
try
    if (lh_Handle<> INVALID_HANDLE_VALUE) 
then begin
      repeat
        if  (
WideString(lr_Info.cFileName) <> '.')
        
and (
WideString(lr_Info.cFileName) <> '..')
        
and ((lr_Info.dwFileAttributes 
and FILE_ATTRIBUTE_DIRECTORY) <> 0)
        
then begin
          Result := True;
          Break;
        
end;
      
until not(FindNextFileW(lh_Handle, lr_Info));
    
end;
  
finally
    Windows.FindClose(lh_Handle);
  
end;
end;
// Rect ------------------------------------------------------------------------
function gfniRectWidth (rtRect: TRect): Integer;
//rtRectの幅を返す
begin
  with rtRect 
do begin
    Result := Right - Left;
  
end;
end;
function gfniRectHeight(rtRect: TRect): Integer;
//rtRectの高さを返す
begin
  with rtRect 
do begin
    Result := Bottom - Top;
  
end;
end;
function gfnrcRectCenter(rcParent, rcChild: TRect): TRect;
//rcChildがrcParentの真ん中にくるようなRectを返す
var
  li_Left, li_Top, li_Width, li_Height: Integer;
begin
  li_Width  := gfniRectWidth (rcChild);
  li_Height := gfniRectHeight(rcChild);
  li_Left   := rcParent.Left + (gfniRectWidth (rcParent) - li_Width)  
div 2;
  li_Top    := rcParent.Top  + (gfniRectHeight(rcParent) - li_Height) 
div 2;
  Result := Rect(li_Left, li_Top, li_Left + li_Width, li_Top + li_Height);
end;
function gfnrcMonitorWorkAreaRectGet(hHandle: HWND): TRect;
//ウィンドウのあるモニターのワークエリアを返す。
var
  lr_Info: TMonitorInfo;
begin
  lr_Info.cbSize := SizeOf(lr_Info);
  GetMonitorInfo(MultiMon.MonitorFromWindow(hHandle, MONITOR_DEFAULTTONEAREST), @lr_Info);
  Result := lr_Info.rcWork;
end;
//コールバック関数
function l_fniCallbackFileDialog(hHandle: HWND; iMsg: UINT; wParam: WPARAM; lParam: LPARAM): UINT 
stdcall;
var
  lh_Handle: HWND;
  lrc_Rect:  TRect;
begin
  if (iMsg = WM_INITDIALOG) 
then begin
    lh_Handle := GetParent(hHandle);  
//ダイアログボックスのウィンドウハンドルを取得
    GetWindowRect(lh_Handle, lrc_Rect);
    
//表示位置をモニターの真ん中に
    lrc_Rect := 
gfnrcRectCenter(
gfnrcMonitorWorkAreaRectGet(lh_Handle), lrc_Rect);
    SetWindowPos(lh_Handle, HWND_TOP, lrc_Rect.Left, lrc_Rect.Top, 0, 0, SWP_NOSIZE 
or SWP_NOZORDER);
  
end;
  Result := 0;
end;
//SaveDialog
function gfnbSaveFileDialog(
var sFile: 
WideString; sDir: 
WideString; hHandle: HWND; iOpt: Integer): Boolean;
//SaveFileDialogだけども実際はファイル名取得のためだけにも使える。
var
  lr_Info: TOpenFilenameW;
  ls_Str: 
WideString;
begin
  FillChar(lr_Info, SizeOf(lr_Info), 0);  
//0で初期化
  with lr_Info 
do begin
    lStructSize := SizeOf(lr_Info);
    hWndOwner   := hHandle;
    hInstance   := 0;
    lpstrTitle  := '';
    lpstrFilter := '';
    lpstrDefExt := '';
    
if (sDir <> '') 
and (
gfnbFolderExists(sDir)) 
then begin
      lpstrInitialDir := PWideChar(sDir);
    
end else begin
      lpstrInitialDir := 
nil;
    
end;
    FlagsEx := 0;
    Flags   := iOpt 
or OFN_EXPLORER;
    
if ((Flags 
and OFN_ALLOWMULTISELECT) <> 0) 
then begin
      Flags := Flags - OFN_ALLOWMULTISELECT;
    
end;
    
if (hHandle = Application.Handle) 
then begin
      //Application.Handleだとダイアログが右下に表示されるのでフックを行って真ん中にする
      Flags    := Flags 
or OFN_ENABLEHOOK;
      lpfnHook := 
l_fniCallbackFileDialog;
    
end else begin
      if ((Flags 
and OFN_ENABLEHOOK) <> 0) 
then begin
        Flags := Flags - OFN_ENABLEHOOK;
      
end;
      lpfnHook := 
nil;
    
end;
    nMaxFile  := 1024;  
//ファイルは一つだけなのでとりあえずこれくらいで充分かなと。
    lpstrFile := AllocMem((nMaxFile + 2) * 2);
    lstrcpyW(lpstrFile,  PWideChar(
gfnsFileNameGet(sFile)));
    
try
      Result := GetSaveFileNameW(lr_Info);
      
if (Result) 
then begin
        sFile := 
WideString(lr_Info.lpstrFile);
      
end else begin
        sFile := '';
      
end;
    
finally
      FreeMem(lpstrFile);
    
end;
  
end;
end;
function gfnbSaveFileDialog(
var sFile: 
WideString; sDir: 
WideString): Boolean;
begin
  Result := gfnbSaveFileDialog(sFile, sDir, Application.Handle, 0);
end;
function gfnbSaveFileDialog(
var sFile: 
WideString): Boolean;
begin
  Result := gfnbSaveFileDialog(sFile, '', Application.Handle, 0);
end;
end.