Subversion Repositories aysalia

Rev

Rev 28 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. program AyDos;
  2.  
  3. // Aysalia DOS Launcher
  4. // Revision 2018-12-07
  5. // (C) 2018 Daniel Marschall, ViaThinkSoft
  6.  
  7. // This launcher does launch DOSBox with the correct *.conf file,
  8. // centers the window and changes the window title and icon at runtime.
  9.  
  10. uses
  11.   SysUtils,
  12.   ShellAPI,
  13.   Windows,
  14.   Messages;
  15.  
  16. {$R *.RES}
  17.  
  18. const
  19.   DOSBOX_EXE = 'DOSBox.exe';
  20.   AYDOS_MNU = 'AyDos.mnu';
  21.   AYDOS_COM = 'AyDos.com';
  22.  
  23. var
  24.   hPsApiDll: Cardinal = 0;
  25.   hIcon: THandle = 0;
  26.   bCeneredOnce: boolean = false;
  27.  
  28. (*
  29. function GetModuleFileNameEx(inProcess: THandle; inModule: THandle;
  30.       Filename: PChar; size: DWord): DWord; stdcall;
  31.       external 'psapi.dll' name 'GetModuleFileNameExA';
  32. *)
  33. {$IFDEF UNICODE}
  34. function GetModuleFileNameEx(inProcess: THandle; inModule: THandle; Filename: PWideChar; size: DWord): Integer;
  35. type
  36.   TGetModuleFileNameExFunc = function (inProcess: THandle; inModule: THandle; Filename: PWideChar; size: DWord): DWord; stdcall;
  37. var
  38.   funcGetModuleFileNameEx: TGetModuleFileNameExFunc;
  39. begin
  40.   if hPsApiDll <> 0 then
  41.   begin
  42.     @funcGetModuleFileNameEx := GetProcAddress(hPsApiDll, 'GetModuleFileNameExW');
  43.     if Assigned(funcGetModuleFileNameEx) then
  44.       result := funcGetModuleFileNameEx(inProcess, inModule, Filename, size)
  45.     else
  46.       result := -1;
  47.   end
  48.   else result := -2;
  49. end;
  50. {$ELSE}
  51. function GetModuleFileNameEx(inProcess: THandle; inModule: THandle; Filename: PAnsiChar; size: DWord): Integer;
  52. type
  53.   TGetModuleFileNameExFunc = function (inProcess: THandle; inModule: THandle; Filename: PAnsiChar; size: DWord): DWord; stdcall;
  54. var
  55.   funcGetModuleFileNameEx: TGetModuleFileNameExFunc;
  56. begin
  57.   if hPsApiDll <> 0 then
  58.   begin
  59.     @funcGetModuleFileNameEx := GetProcAddress(hPsApiDll, 'GetModuleFileNameExA');
  60.     if Assigned(funcGetModuleFileNameEx) then
  61.       result := funcGetModuleFileNameEx(inProcess, inModule, Filename, size)
  62.     else
  63.       result := -1;
  64.   end
  65.   else result := -2;
  66. end;
  67. {$ENDIF}
  68.  
  69. procedure ChangeTitleAndIcon(hWnd: Thandle);
  70. var
  71.   Title: array[0..255] of Char;
  72. const
  73.   TargetWinWidth = 640;
  74.   TargetWinHeight = 480;
  75. resourcestring
  76.   AyDosTitle = 'Aysalia DOS';
  77.   AyDos1Title = 'Aysalia DOS 1';
  78.   AyDos2Title = 'Aysalia DOS 2';
  79. begin
  80.   ZeroMemory(@Title, sizeof(Title));
  81.   GetWindowText(hWnd, @Title, sizeof(Title)-1);
  82.  
  83.   // Center window (once)
  84.   if (Title = 'DOSBox') and not bCeneredOnce then
  85.   begin
  86.     MoveWindow(hWnd, GetSystemMetrics(SM_CXSCREEN) div 2 - TargetWinWidth div 2,
  87.                      GetSystemMetrics(SM_CYSCREEN) div 2 - TargetWinHeight div 2,
  88.                      TargetWinWidth,
  89.                      TargetWinHeight,
  90.                      true);
  91.     bCeneredOnce := true;
  92.   end;
  93.  
  94.   // Change window title
  95.   if Pos('AYDOS1', Title) > 0 then
  96.     SetWindowText(hWnd, PChar(AyDos1Title))
  97.   else if Pos('AYDOS2', Title) > 0 then
  98.     SetWindowText(hWnd, PChar(AyDos2Title))
  99.   else if Pos('AYDOS', Title) > 0 then
  100.     SetWindowText(hWnd, PChar(AyDosTitle));
  101.  
  102.   // Change window and taskbar icon
  103.   if hIcon > 0 then
  104.   begin
  105.     // Change both icons to the same icon handle.
  106.     SendMessage(hWnd, WM_SETICON, ICON_SMALL, hIcon);
  107.     SendMessage(hWnd, WM_SETICON, ICON_BIG, hIcon);
  108.  
  109.     // This will ensure that the application icon gets changed too.
  110.     SendMessage(GetWindow(hWnd, GW_OWNER), WM_SETICON, ICON_SMALL, hIcon);
  111.     SendMessage(GetWindow(hWnd, GW_OWNER), WM_SETICON, ICON_BIG, hIcon);
  112.   end;
  113. end;
  114.  
  115. function EnumWindowsProc(Handle: hWnd; dummy: DWORD): BOOL; stdcall;
  116. var
  117.   Title: array[0..255] of Char;
  118.   WinFileName: array[0..MAX_PATH] of Char;
  119. var
  120.   PID: DWORD;
  121.   hProcess: THandle;
  122.   Len: Integer;
  123. begin
  124.   Result := True;
  125.   ZeroMemory(@WinFileName, sizeof(WinFileName));
  126.   GetWindowThreadProcessId(Handle, @PID);
  127.   hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
  128.   Len := GetModuleFileNameEx(hProcess, 0, WinFileName, sizeof(WinFileName)-1);
  129.   if Len > 0 then
  130.   begin
  131.     // GetModuleFileNameEx is available on newer operating systems;
  132.     // it ensures that we find the correct window by checking its EXE filename.
  133.     if SameText(WinFileName, ExtractFilePath(ParamStr(0)) + DOSBOX_EXE) then
  134.     begin
  135.       Result := False; // stop enumeration
  136.       ChangeTitleAndIcon(Handle);
  137.     end;
  138.   end
  139.   else if Len < 0 then
  140.   begin
  141.     // At Win9x, there is no psapi.dll, so we try it the old fashioned way,
  142.     // finding the window by parts of its title
  143.     ZeroMemory(@Title, sizeof(Title));
  144.     GetWindowText(Handle, Title, sizeof(Title)-1);
  145.     if IsWindowVisible(Handle) then
  146.     begin
  147.       if (title = 'DOSBox') or ((Pos('DOSBox ',   title) > 0) and
  148.                                 (Pos('Cpu speed', title) > 0)) then
  149.       begin
  150.         Result := False; // stop enumeration
  151.         ChangeTitleAndIcon(Handle);
  152.       end;
  153.     end;
  154.   end;
  155. end;
  156.  
  157. function ShellExecuteWait(hWnd: HWND; Operation, FileName, Parameters,
  158.   Directory: PChar; ShowCmd: Integer): DWord;
  159. var
  160.   Info: TShellExecuteInfo;
  161.   pInfo: PShellExecuteInfo;
  162. begin
  163.   pInfo := @Info;
  164.   with Info do
  165.   begin
  166.     cbSize       := SizeOf(Info);
  167.     fMask        := SEE_MASK_NOCLOSEPROCESS;
  168.     wnd          := hWnd;
  169.     lpVerb       := Operation;
  170.     lpFile       := FileName;
  171.     lpParameters := PChar(Parameters + #0);
  172.     lpDirectory  := PChar(Directory);
  173.     nShow        := ShowCmd;
  174.     hInstApp     := 0;
  175.   end;
  176.   ShellExecuteEx(pInfo);
  177.  
  178.   repeat
  179.     result := WaitForSingleObject(Info.hProcess, 10);
  180.     EnumWindows(@EnumWindowsProc, 0);
  181.   until (result <> WAIT_TIMEOUT);
  182. end;
  183.  
  184. function CanRunDosBox: boolean;
  185. var
  186.   windir: array[0..MAX_PATH] of char;
  187.   osVerInfo: TOSVersionInfo;
  188. begin
  189.   osVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  190.   if GetVersionEx(osVerInfo) then
  191.   begin
  192.     // DOSBox does not work with Windows 95
  193.     // It works on Windows 98 (but the VC++ Runtime must be installed)
  194.     if osVerInfo.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then
  195.     begin
  196.       result := (osVerInfo.dwMajorVersion > 4) or
  197.                ((osVerInfo.dwMajorVersion = 4) and (osVerInfo.dwMinorVersion >= 10{Win98}));
  198.     end
  199.     else if osVerInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then
  200.     begin
  201.       result := true;
  202.     end
  203.     else
  204.     begin
  205.       // This should not happen
  206.       result := false;
  207.     end;
  208.   end
  209.   else
  210.   begin
  211.     if GetWindowsDirectory(windir, sizeof(windir)) > 0 then
  212.     begin
  213.       // In case GetVersionEx fails, we are trying to see if command.com exists
  214.       result := FileExists(windir + '\command.com');
  215.     end
  216.     else
  217.     begin
  218.       // This should never happen
  219.       result := false;
  220.     end;
  221.   end;
  222. end;
  223.  
  224. function Main: Integer;
  225. var
  226.   sFile: string;
  227. begin
  228.   if CanRunDosBox then
  229.   begin
  230.     ShellExecuteWait(0, 'open', DOSBOX_EXE, '-noconsole -conf DOSBox.conf',
  231.       PChar(ExtractFilePath(ParamStr(0))), SW_NORMAL);
  232.  
  233.     sFile := IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))) + 'stdout.txt';
  234.     if FileExists(sFile) then DeleteFile(PChar(sFile));
  235.  
  236.     sFile := IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))) + 'stderr.txt';
  237.     if FileExists(sFile) then DeleteFile(PChar(sFile));
  238.   end
  239.   else
  240.   begin
  241.     // SEE_MASK_CLASSNAME cannot be used with pure MZ files (it does only work for NE/PE files!)
  242.     // So we need to do the dirty rename-hack...
  243.     RenameFile(AYDOS_MNU, AYDOS_COM);
  244.     try
  245.       ShellExecuteWait(0, 'open', PChar(AYDOS_COM), '', PChar(ExtractFilePath(ParamStr(0))), SW_NORMAL);
  246.     finally
  247.       RenameFile(AYDOS_COM, AYDOS_MNU);
  248.     end;
  249.   end;
  250.  
  251.   result := 0;
  252. end;
  253.  
  254. begin
  255.   hPsApiDll := LoadLibrary('psapi.dll');
  256.   try
  257.     hIcon := LoadIcon(hInstance, 'MainIcon');
  258.     ExitCode := Main;
  259.   finally
  260.     FreeLibrary(hPsApiDll);
  261.     hPsApiDll := 0;
  262.   end;
  263. end.
  264.