// ======================================================
// Приложение DOS2WIN
//
// Запуск приложений Windows из
// коммандной строки MS-DOS
//
// Используется совместно с DLL-библиотекой
// d2w.dll и VxD-драйвером VXDSRV.386 версии 1.1  
// ------------------------------------------------------
// Copyright (C) 1995 Alexandr Frolov
// ======================================================
#define STRICT
#include <windows.h>
#include <mem.h>
#include <string.h>
#include <dir.h>

#include "dos2win.hpp"
#include "vxdcall.hpp"

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export
WndProc(HWND, UINT, WPARAM, LPARAM);

extern "C" void FAR PASCAL _export
WinAppStart(HWND hwnd, LPSTR szPath);

extern "C" void FAR PASCAL _export
RegisterWnd(HWND hwnd);

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

char const szClassName[]   = "DOS2WINClass";
char const szWindowTitle[] = "dos2win";
HINSTANCE  hInst;
HMENU hmenuSystemMenu;

struct LOADPARMS
{
  WORD   segEnv;      // среда
  LPSTR  lpszCmdLine; // коммандная строка
  LPWORD lpwShow;     // режим отображения
  LPWORD lpwReserved; // зарезервировано
};
struct LOADPARMS parms;

// Точка входа API VxD-драйвера
VXDAPI vxd;

WORD  awShow[2] = { 2, SW_SHOW };
LPSTR lpszCmd, lpszParm;
char  szBuf[256];
char  szCurPath[128];

DLGPROC lpfnDlgProc;

// ======================================================
// Функция WinMain
// ======================================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpszCmdLine, int nCmdShow)
{
  MSG  msg;
  HWND hwnd;

  if(hPrevInstance) // только одна копия приложения
    return FALSE;

  if(!InitApp(hInstance))
    return FALSE;

  hInst = hInstance;

  hwnd = CreateWindow(
    szClassName, szWindowTitle,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT,  CW_USEDEFAULT,
    0, 0, hInstance, NULL);

  if(!hwnd)
    return FALSE;

  // Получаем точку входа API VxD-драйвера
  vxd = vxdGetDeviceAPI(VXD_ID);

  // Если VxD-драйвер не загружен, выводим
  // сообщение об ошибке
  if(vxd == NULL)
  {
    MessageBox(hwnd, "Error Loading DOS2WIN\n"
      "VxD Driver VXDSRV.386 not found",
      "DOS2WIN", MB_OK | MB_ICONHAND);
    return FALSE;
  }

  // Отображаем окно в виде пиктограммы
  ShowWindow(hwnd, SW_SHOWMINIMIZED);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// ======================================================
// Функция InitApp
// Выполняет регистрацию класса окна
// ======================================================
BOOL InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass;
  WNDCLASS wc;

  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = "APP_MENU";
  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC) WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon(hInstance, "AppIcon");
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}

// ======================================================
// Функция WndProc
// ======================================================
LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
    case WM_CREATE:
    {
      // Регистрируем окно приложения в DLL-библиотеке d2w.dll
      RegisterWnd(hwnd);

      // Изменяем системное меню приложения
      hmenuSystemMenu = GetSystemMenu(hwnd, FALSE);

      AppendMenu(hmenuSystemMenu, MF_SEPARATOR, 0, 0);
      AppendMenu(hmenuSystemMenu, MF_BYCOMMAND | MF_ENABLED,
        CM_SYSABOUT, "&About...");

      return 0;
    }

    // Удаляем лишние строки из системного меню приложения
    case WM_INITMENUPOPUP:
    {
      RemoveMenu(hmenuSystemMenu, SC_RESTORE, MF_BYCOMMAND);
      RemoveMenu(hmenuSystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
      RemoveMenu(hmenuSystemMenu, SC_MINIMIZE, MF_BYCOMMAND);
      RemoveMenu(hmenuSystemMenu, SC_SIZE, MF_BYCOMMAND);
      break;
    }

    case WM_SYSCOMMAND:
    {
      switch(wParam & 0xfff0)
      {
        // Блокируем изменение размеров окна
        case SC_RESTORE: case SC_MAXIMIZE: case SC_SIZE:
          return 0;

        // Выводим диалоговую панель "About"
        case CM_SYSABOUT:
        {
          lpfnDlgProc =
            (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);
          DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);
          FreeProcInstance((FARPROC)lpfnDlgProc);
          return 0;
        }

        default:
          return DefWindowProc(hwnd, msg, wParam, lParam);
      }
    }

    // Это сообщение посылается DLL-библиотекой d2w.dll,
    // когда VxD-драйвер фиксирует запуск программы любой
    // виртуальной машиной MS-DOS.
    // Параметр lParam содержит указатель на строку,
    // имеющую следующий формат:
    //
    //  Смещение Размер Описание
    //  0        1      Номер дискового устройства, которое
    //                  было текущим при запуске программы 
    //  1        64     Каталог, который был текущим
    //                  при запуске программы
    //  65       128    Путь к запускаемой программе MS-DOS
    //                  или к запускаемому приложению Windows
    //  193      128    параметры запускаемой программы
    //                  или приложения
    //

    case WM_STARTWINAPP:
    {
      // Проверка указателя
      if(lParam == NULL) return 0;

      // Путь к запускаемой программе
      lpszCmd  = (LPSTR)lParam + 65;

      // Указатель на строку параметров
      lpszParm  = (LPSTR)lParam + 65 + 128;

      // Формируем буфер параметров для функции LoadModule
      // Первый байт резервируем для размера строки
      lstrcpy(szBuf, (LPCSTR)" ");
      lstrcat(szBuf, (LPCSTR)lpszParm);

      // Записываем размер строки
      *szBuf = (BYTE)lstrlen(lpszParm);

      // Заполняем структуру LOADPARMS
      if(lstrlen(lpszParm) != 0)
        parms.lpszCmdLine = (LPSTR)szBuf;
      else
        parms.lpszCmdLine = (LPSTR)"";

      parms.segEnv = 0;
      parms.lpwShow = (LPWORD) awShow;
      parms.lpwReserved = (LPWORD) NULL;

      // Устанавливаем такой же текущий диск,
      // какой был текущим при запуске программы
      // из виртуальной машины MS-DOS
      setdisk(*(LPSTR)lParam);

      // Устанавливаем такой же текущий каталог,
      // какой был текущим при запуске программы
      // из виртуальной машины MS-DOS
      szCurPath[0] = (char)((*(LPSTR)lParam) + 'A');
      lstrcpyn((LPSTR)szCurPath + 1, (LPSTR)":\\", 3);
      lstrcat((LPSTR)szCurPath, (LPSTR)lParam + 1);
      chdir(szCurPath);

      // Выполняем попытку запуска программы.
      // Если запускается приложение Windows, оно
      // будет запущено. Если же был выполнен запуск
      // программы MS-DOS, функция LoadModule вернет
      // код ошибки, который мы игнорируем
      LoadModule(lpszCmd, &parms);
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
    default:
      break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// ======================================================
// Функция vxdGetDeviceAPI
// Получение адреса точки входа API для
// VxD-драйвера, идентификатор которого
// задан параметром vxd_id
// ======================================================
VXDAPI vxdGetDeviceAPI(unsigned short vxd_id)
{
  unsigned axreg, dxreg;

  asm push  di
  asm push  es
  asm mov   ax, 0x1684
  asm mov   bx, vxd_id
  asm xor   di, di
  asm mov   es, di
  asm int   0x2f
  asm mov   ax, di
  asm mov   dx, es
  asm pop   es
  asm pop   di

  asm mov   axreg, ax
  asm mov   dxreg, dx

  return((VXDAPI)MAKELP(dxreg, axreg));
}

// ======================================================
// Функция DldProc
// Обработка сообщений диалоговой панели "About"
// ======================================================
#pragma argsused

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    case WM_INITDIALOG:
      return TRUE;

    case WM_COMMAND:
    {
      switch(wParam)
      {
        case IDOK: case IDCANCEL:
        {
          EndDialog(hdlg, 0);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}
