Subversion Repositories aysalia

Rev

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