Subversion Repositories aysalia

Rev

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