Source file: /~heha/hs/wmfview.zip/src/WmfView.cpp

#define WIN32_LEAN_AND_MEAN
#include "wmfview.h"

HINSTANCE hInst;	// da kommen die Ressourcen her (notfalls externe DLL!)
HMENU hMenu;	// Menü des Hauptfensters
HWND hMainWnd;	// Gesamt-Containerfenster
HWND hContent;	// Baumdiagramm (erst mal nur 2 Ebenen) für Metadatei-Inhalt
HWND hObjects;	// Baumdiagramm für Metadatei-Objekte
HWND hShow;	// Skalierbare Metadatei-Anzeige
HWND hStatus;	// Statuszeile
HWND hToolbar;	// Werkzeugleiste
cRECT DiviV;	// Vertikaler Teilungsstrich (keine Fenster??)
cRECT DiviH;	// Horizontaler Teilungsstrich
POINT OldPos;
UINT DragWhat;
TCHAR MBoxTitle[64];
LPBYTE pLoadedMeta;	// Zeiger geladene Metadaten
DWORD lLoadedMeta;	// Länge geladene Metadaten
CONFIG Config;	// In Registry speicherbare Einstellungen
bool EmfMode;
bool ClipboardMode;

#define WM_CONTINUEINIT	(WM_USER+0)
#define WM_OPENFILE	(WM_USER+1)	// lParam=Dateiname (wParam=Mehrfach-Flag)
#define WM_LOADED	(WM_USER+2)

#if !defined(_M_AXP64) && !defined(_M_IA64)
UINT _declspec(naked) _fastcall InitStr(UINT len/*ecx*/, LPVOID buf/*edx*/) {
 _asm{	mov	eax,ecx
 	xchg	edx,edi
	stosd
	shr	ecx,2
	xor	eax,eax
	dec	ecx
	rep stosd
	xchg	edi,edx
	ret
 }
}
#endif

#ifdef _DEBUG
void _cdecl DebugOut(const char*t, ...) {
 char s[256];
 lstrcpyA(s+_vsnprintf(s,sizeof(s)-3,t,(va_list)(&t+1)),"\r\n");
 OutputDebugStringA(s);
}
#endif

int vMBox(HWND Wnd, LPCTSTR id, UINT style, va_list arglist) {
 TCHAR buf1[256],buf2[1024];
 if (IS_INTRESOURCE(id)) {
  LoadString(hInst,(UINT)(ULONG_PTR)id,buf1,elemof(buf1));
  id=buf1;
 }
 _sntprintf(buf2,elemof(buf2),buf1,arglist);
 return MessageBox(Wnd,buf2,MBoxTitle,style);
}

int _cdecl MBox(HWND Wnd, LPCTSTR id, UINT style,...) {
 return vMBox(Wnd,id,style,(va_list)(&style+1));
}

static void SetOpt(BYTE NewOpt,BYTE Change=0) {
 Change|=Config.fopt^NewOpt;		// Sich ändernde Flags merken
 Config.fopt=NewOpt;			// alle Flags setzen
 if (Change&optRecordHead) {
  CheckMenuItem(hMenu,0x121,NewOpt&optRecordHead?MF_CHECKED:MF_UNCHECKED);
  InvalidateRect(hContent,NULL,TRUE);	// Baum neu zeichnen
 }
 if (Change&optRaw) {
  CheckMenuItem(hMenu,0x122,NewOpt&optRaw?MF_CHECKED:MF_UNCHECKED);
  PostMessage(hMainWnd,WM_LOADED,0,0);	// Alles neu zeichnen
 }
}

static void GetReducedClientRect(cRECT*r) {
 int yStatus, yToolbar;
 GetWindowRect(hStatus,r);
 yStatus=r->Height();
 GetWindowRect(hToolbar,r);
 yToolbar=r->Height();
 GetClientRect(hMainWnd,r);
 r->top+=yToolbar;
 r->bottom-=yStatus;
}

static BYTE ShiftRect(int*r, const int*r2) {
// Hilfsfunktion, schiebt horizontal bzw. vertikal
 int ax,dx;             // jaja, Assembler
 ax=r2[0]-r[0];         // Differenz links bzw. oben
 if (!ax) return 0;
 if (ax<0) {
  dx=r2[2]-r[2];        // Differenz rechts bzw. unten
  if (dx>=0) return 0;
  if (ax<dx) ax=dx;     // die kleinere Zahl
 }
 r[0]+=ax;
 r[2]+=ax;
 return ax<0?(BYTE)4:(BYTE)1;	// rechts=4, links=1
}

BYTE MoveRectIntoRect(RECT*R, const RECT*R2) {
// Falls R in R2 teilweise oder vollständig außerhalb liegt,
// wird R verschoben und TRUE zurückgeliefert, um maximale Sichtbarkeit
// zu realisieren.
// Dient zum Hineinholen von außerhalb liegenden Fenstern in den Desktop.
 return (BYTE)(ShiftRect((PINT)&R->left,(PINT)&R2->left)
       +(ShiftRect((PINT)&R->top, (PINT)&R2->top)<<1));
}

static void ReposWindows(bool OnWmSize=false) {
 cRECT r;
 GetReducedClientRect(&r);
 if (OnWmSize) {
  static POINT OldSize;
  POINT NewSize={r.Width(),r.Height()};
// Teilungsstriche werden auf 1/3 und 1/2 festgelegt, ODER
// Teilungsstriche behalten ihr relatives Maß
  if ((unsigned)OldSize.x<10) DiviV.left=NewSize.x/3;
  else DiviV.left=r.left+MulDiv(DiviV.left-r.left,NewSize.x,OldSize.x);
  if ((unsigned)OldSize.y<10) DiviH.top=NewSize.y/2;
  else DiviH.top=r.top+MulDiv(DiviH.top-r.top,NewSize.y,OldSize.y);
  DiviV.right=DiviV.left+3;
  DiviV.top=r.top;
  DiviV.bottom=r.bottom;
  DiviH.left=r.left;
  DiviH.right=DiviV.left;
  DiviH.bottom=DiviH.top+3;
  OldSize=NewSize;
 }
 MoveRectIntoRect(&DiviV,&r);
 DiviH.right=DiviV.left;
 MoveRectIntoRect(&DiviH,&r);
 HDWP hdwp=BeginDeferWindowPos(3);
 hdwp=DeferWindowPos(hdwp,hContent,0,r.left,r.top,DiviV.left-r.left,DiviH.top-r.top,SWP_NOZORDER);
 hdwp=DeferWindowPos(hdwp,hObjects,0,r.left,DiviH.bottom,DiviV.left,r.bottom-DiviH.bottom,SWP_NOZORDER);
 hdwp=DeferWindowPos(hdwp,hShow,0,DiviV.right,r.top,r.right-DiviV.left,r.Height(),SWP_NOZORDER);
 EndDeferWindowPos(hdwp);
 InvalidateRect(hMainWnd,&DiviV,FALSE);
 InvalidateRect(hMainWnd,&DiviH,FALSE);
}
/*
DECODEINFO*GetSelected(void) {
 TVITEM item;
 item.mask=TVIF_PARAM;
 item.hItem=TreeView_GetSelection(hContent);
 if (!item.hItem) return NULL;
 TreeView_GetItem(hContent,&item);
 return (DECODEINFO*)item.lParam;
}
*/

// EMF in Speicher laden
bool LoadEmf(HENHMETAFILE hEMF) {
 bool ret=false;
 UINT NeedBytes=GetEnhMetaFileBits(hEMF,0,NULL);
 if (NeedBytes) {
  if (pLoadedMeta) GlobalFree(pLoadedMeta);
  pLoadedMeta=(BYTE*)GlobalAlloc(GMEM_FIXED,NeedBytes);
  if (pLoadedMeta) {
   if (GetEnhMetaFileBits(hEMF,NeedBytes,pLoadedMeta)==NeedBytes) ret=true;;
   PostMessage(hMainWnd,WM_LOADED,0,0);
  }else MBox(hMainWnd,MAKEINTRESOURCE(6),MB_OK,NeedBytes);
 }else MBox(hMainWnd,MAKEINTRESOURCE(5),MB_OK);
 return ret;
}

// Datei-Öffnen-Dialog und WM_OPENFILE versenden
void OpenFileDlg() {
 OPENFILENAME ofn;
 TCHAR FileName[MAX_PATH];
 TCHAR Filter[256];
 Filter[LoadString(hInst,2,Filter,elemof(Filter)-1)+1]=0;
 InitStruct(&ofn,sizeof(ofn));
 ofn.hwndOwner=hMainWnd;
 ofn.lpstrFilter=Filter;
 ofn.lpstrFile=FileName;
 ofn.nMaxFile=elemof(FileName);
 FileName[0]=0;
 ofn.Flags|=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST;
 if (GetOpenFileName(&ofn)) {
  HCURSOR hPrevCursor=SetCursor(LoadCursor(0,IDC_WAIT));
  SendMessage(hMainWnd,WM_OPENFILE,0,(LPARAM)(LPCTSTR)FileName);
  SetCursor(hPrevCursor);
 }
}

// Toolbar buttons
static const TBBUTTON buttons[]={
 {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
 {STD_FILEOPEN, 0x111, TBSTATE_ENABLED, TBSTYLE_BUTTON,{0,0},0,201},
 {STD_PASTE, 0x112, TBSTATE_ENABLED, TBSTYLE_BUTTON},
 {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP},
 {STD_HELP, 299, TBSTATE_ENABLED, TBSTYLE_BUTTON}
};

static void InitZoomMenu() {
 MENUITEMINFO mii;
 mii.cbSize=sizeof(mii);
 mii.fMask=MIIM_FTYPE|MIIM_STATE;
 mii.fType=MFT_RADIOCHECK;
 switch (CurZoom) {
  case ZOOM_BBOX:
  case ZOOM_FRAME: mii.fState=MFS_CHECKED; break;
  case ZOOM_BBOX_A:
  case ZOOM_BBOX_W:
  case ZOOM_FRAME_A:
  case ZOOM_FRAME_W: mii.fState=0; break;
  case ZOOM_TEXT: mii.fState=MFS_DISABLED; break;
  default: mii.fState=MFS_CHECKED|MFS_DISABLED;
 }
 SetMenuItemInfo(hMenu,0x141,FALSE,&mii);
 switch (CurZoom) {
  case ZOOM_BBOX_A:
  case ZOOM_FRAME_A: mii.fState=MFS_CHECKED; break;
  case ZOOM_BBOX:
  case ZOOM_BBOX_W:
  case ZOOM_FRAME:
  case ZOOM_FRAME_W: mii.fState=0; break;
  case ZOOM_TEXT: mii.fState=MFS_CHECKED|MFS_DISABLED; break;
  default: mii.fState=MFS_DISABLED;
 }
 SetMenuItemInfo(hMenu,0x142,FALSE,&mii);
 switch (CurZoom) {
  case ZOOM_BBOX_W:
  case ZOOM_FRAME_W: mii.fState=MFS_CHECKED; break;
  case ZOOM_BBOX_A:
  case ZOOM_BBOX:
  case ZOOM_FRAME_A:
  case ZOOM_FRAME: mii.fState=0; break;
  default: mii.fState=MFS_DISABLED;
 }
 SetMenuItemInfo(hMenu,0x143,FALSE,&mii);
 switch (CurZoom) {
  case ZOOM_BBOX:
  case ZOOM_BBOX_A:
  case ZOOM_BBOX_W: mii.fState=MFS_CHECKED; break;
  default: mii.fState=0;
 }
 SetMenuItemInfo(hMenu,0x144,FALSE,&mii);
 switch (CurZoom) {
  case ZOOM_FRAME:
  case ZOOM_FRAME_A:
  case ZOOM_FRAME_W: mii.fState=MFS_CHECKED; break;
  default: mii.fState=0;
 }
 SetMenuItemInfo(hMenu,0x145,FALSE,&mii);
 mii.fState=CurZoom==ZOOM_TEXT?MFS_CHECKED:0;
 SetMenuItemInfo(hMenu,0x146,FALSE,&mii);
 mii.fState=CurZoom==1000?MFS_CHECKED:0;
 SetMenuItemInfo(hMenu,0x147,FALSE,&mii);
 mii.fState=CanZoom(hShow,ZOOM_UD+1)?0:MFS_DISABLED;
 SetMenuItemInfo(hMenu,0x148,FALSE,&mii);
 mii.fState=CanZoom(hShow,ZOOM_UD-1)?0:MFS_DISABLED;
 SetMenuItemInfo(hMenu,0x149,FALSE,&mii);
 mii.fState=CanZoom(hShow,ZOOM_PREV)?0:MFS_DISABLED;
 SetMenuItemInfo(hMenu,0x14A,FALSE,&mii);
 mii.fState=CanZoom(hShow,ZOOM_NEXT)?0:MFS_DISABLED;
 SetMenuItemInfo(hMenu,0x14B,FALSE,&mii);
}

static void HandleZoomMenu(UINT id) {
 int zoom=ZOOM_UD;	// Kode für: Zoom nicht setzen (nichts tun)
 switch (id) {
  case 0x141: switch (CurZoom) {
   case ZOOM_BBOX_A:
   case ZOOM_BBOX_W: zoom=ZOOM_BBOX; break;
   case ZOOM_FRAME_A:
   case ZOOM_FRAME_W: zoom=ZOOM_FRAME; break;
  }break;
  case 0x142: switch (CurZoom) {
   case ZOOM_BBOX:
   case ZOOM_BBOX_W: zoom=ZOOM_BBOX_A; break;
   case ZOOM_FRAME:
   case ZOOM_FRAME_W: zoom=ZOOM_FRAME_A; break;
  }break;
  case 0x143: switch (CurZoom) {
   case ZOOM_BBOX:
   case ZOOM_BBOX_A: zoom=ZOOM_BBOX_W; break;
   case ZOOM_FRAME:
   case ZOOM_FRAME_A: zoom=ZOOM_FRAME_W; break;
  }break;
  case 0x144: switch (CurZoom) {
   case ZOOM_BBOX_A: break;
   case ZOOM_FRAME_A:
   case ZOOM_TEXT: zoom=ZOOM_BBOX_A; break;
   case ZOOM_BBOX_W: break;
   case ZOOM_FRAME_W: zoom=ZOOM_BBOX_W; break;
   default: zoom=ZOOM_BBOX;
  }break;
  case 0x145: switch (CurZoom) {
   case ZOOM_FRAME_A: break;
   case ZOOM_BBOX_A:
   case ZOOM_TEXT: zoom=ZOOM_FRAME_A; break;
   case ZOOM_FRAME_W: break;
   case ZOOM_BBOX_W: zoom=ZOOM_FRAME_W; break;
   default: zoom=ZOOM_FRAME;
  }break;
  case 0x146: zoom=ZOOM_TEXT; break;
  case 0x147: zoom=1000; break;
  case 0x148: zoom=ZOOM_UD+1; break;
  case 0x149: zoom=ZOOM_UD-1; break;
  case 0x14A: zoom=ZOOM_PREV; break;
  case 0x14B: zoom=ZOOM_NEXT; break;
 }
 SetZoom(hShow,zoom);
}

LRESULT CALLBACK MainWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_CREATE: {
   LPCREATESTRUCT cs=(LPCREATESTRUCT)lParam;
   hMenu=cs->hMenu;
   hMainWnd=Wnd;
   hContent=CreateWindowEx(WS_EX_CLIENTEDGE,WC_TREEVIEW,NULL,
     WS_CHILD|WS_BORDER|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT,
     0,0,0,0,Wnd,(HMENU)1,hInst,NULL);
//   TreeView_SetUnicodeFormat(hContent,FALSE);
   hObjects=CreateWindowEx(WS_EX_CLIENTEDGE,WC_TREEVIEW,NULL,
     WS_CHILD|WS_BORDER|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT,
     0,0,0,0,Wnd,(HMENU)2,hInst,NULL);
   hShow=CreateWindowEx(WS_EX_CLIENTEDGE,T("show"),NULL,
     WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|WS_HSCROLL|WS_VSCROLL|WS_CLIPCHILDREN,
     0,0,0,0,Wnd,(HMENU)3,hInst,NULL);
   hStatus=CreateStatusWindow(WS_CHILD|WS_VISIBLE,NULL,Wnd,4);
   static const int widths[]={100,200,350,500,-1};
   SendMessage(hStatus,SB_SETPARTS,elemof(widths),(LPARAM)widths);
   hToolbar=CreateToolbarEx(Wnd,WS_CHILD|WS_VISIBLE|TBSTYLE_TOOLTIPS,5,
     11,HINST_COMMCTRL,IDB_STD_SMALL_COLOR,buttons,elemof(buttons),0,0,0,0,sizeof(TBBUTTON));
   SetOpt(3,0xFF);
   if (cs->lpCreateParams && *(LPTSTR)cs->lpCreateParams)
     SendMessage(Wnd,WM_OPENFILE,0,(LPARAM)cs->lpCreateParams);
  }break;

  case WM_SIZE:
  SendMessage(hStatus,Msg,wParam,lParam);
  SendMessage(hToolbar,Msg,wParam,lParam);
  if (wParam!=SIZE_MINIMIZED) {
   ReposWindows(true);
  }break;

  case WM_SETCURSOR: if (LOWORD(lParam)==HTCLIENT) {
   DWORD p=GetMessagePos();
   POINT pt={GET_X_LPARAM(p),GET_Y_LPARAM(p)};
   ScreenToClient(Wnd,&pt);
   if (PtInRect(&DiviV,pt)) {
    SetCursor(LoadCursor(0,IDC_SIZEWE));
    return TRUE;
   }
   if (PtInRect(&DiviH,pt)) {
    SetCursor(LoadCursor(0,IDC_SIZENS));
    return TRUE;
   }
  }break;

  case WM_LBUTTONDOWN: {
   MessageBeep(-1);
   POINT NewPos={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
   if (PtInRect(&DiviV,NewPos)) {
    SetCapture(Wnd);
    OldPos=NewPos;
    DragWhat=1;
   }
   if (PtInRect(&DiviH,NewPos)) {
    SetCapture(Wnd);
    OldPos=NewPos;
    DragWhat=2;
   }
  }break;

  case WM_MOUSEMOVE: if (wParam&MK_LBUTTON && GetCapture()==Wnd) {
   POINT NewPos={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
   switch (DragWhat) {
    case 1: {
     OffsetRect(&DiviV,NewPos.x-OldPos.x,0);
     DiviH.right=DiviV.left;
    }break;
    case 2: {
     OffsetRect(&DiviH,0,NewPos.y-OldPos.y);
    }
   }
   ReposWindows();
   OldPos=NewPos;
  }break;

  case WM_LBUTTONUP: {
   ReleaseCapture();
   DragWhat=0;
  }break;

  case WM_PAINT: {
   PAINTSTRUCT ps;
   BeginPaint(Wnd,&ps);
   FillRect(ps.hdc,&DiviV,GetSysColorBrush(COLOR_3DFACE));
   FillRect(ps.hdc,&DiviH,GetSysColorBrush(COLOR_3DFACE));
//   r.left+=DiviV.right;
//   FillRect(ps.hdc,&r,GetStockBrush(WHITE_BRUSH));
   EndPaint(Wnd,&ps);
  }return FALSE;

  case WM_OPENFILE: {
   if (Config.fopt&optRaw) {
    HANDLE f=CreateFile((LPCTSTR)lParam,
      GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0);
    if (f==INVALID_HANDLE_VALUE) break;
    lLoadedMeta=GetFileSize(f,NULL);
    if (pLoadedMeta) GlobalFree(pLoadedMeta);
    ClipboardMode=false;
    pLoadedMeta=/*new BYTE[lLoadedMeta]; */(BYTE*)GlobalAlloc(GPTR,lLoadedMeta);
    if (!pLoadedMeta) {
      MBox(Wnd,MAKEINTRESOURCE(6),MB_OK,lLoadedMeta);
     break;
    }
    ReadFile(f,pLoadedMeta,lLoadedMeta,&lLoadedMeta,NULL);
    CloseHandle(f);
   }else{
    HENHMETAFILE hMeta;
    hMeta=GetEnhMetaFile((LPCTSTR)lParam);
    if (hMeta) {
     LoadEmf(hMeta);
     DeleteEnhMetaFile(hMeta);	// schließt die Datei
    }else{
     HMETAFILE hMeta;
     hMeta=GetMetaFile((LPCTSTR)lParam);
// geht im allgemeinen schief, weil's praktisch nur Placeable Metafiles gibt
     if (!hMeta) {
// probieren, ab Dateioffset 22 die Metadatei einzulesen
      HANDLE f=CreateFile((LPCTSTR)lParam,
        GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0);
      if (f!=INVALID_HANDLE_VALUE) {
       DWORD fsize=GetFileSize(f,NULL);
       if (fsize>30) {
        HANDLE fm=CreateFileMapping(f,NULL,PAGE_READONLY,0,0,NULL);
        if (fm) {
         const BYTE *p=(const BYTE*)MapViewOfFile(fm,FILE_MAP_READ,0,0,0);
	 if (p && *(DWORD*)p==0x9AC6CDD7) {
	  hMeta=SetMetaFileBitsEx(fsize-22,p+22);
	 }
	 CloseHandle(fm);
	}
       }
       CloseHandle(f);
      }
     }
     if (hMeta) {
      lLoadedMeta=GetMetaFileBitsEx(hMeta,0,NULL);
      if (pLoadedMeta) GlobalFree(pLoadedMeta);
      ClipboardMode=false;
      pLoadedMeta=(BYTE*)GlobalAlloc(GPTR,lLoadedMeta);
      if (!pLoadedMeta) {
       MBox(Wnd,MAKEINTRESOURCE(6),MB_OK,lLoadedMeta);
       break;
      }
      GetMetaFileBitsEx(hMeta,lLoadedMeta,pLoadedMeta);
      DeleteMetaFile(hMeta);	// schließt die Datei?
     }else MBox(Wnd,MAKEINTRESOURCE(7),MB_OK,lParam);
    }
   }
   PostMessage(Wnd,WM_LOADED,0,0);
  }break;


  case WM_LOADED: {
//   RECT r;
   union{
    const BYTE *b;
    const WORD *w;
    const METARECORD *wmr;
    const METAHEADER *wmh;
    const PLACEABLE_HEADER *pmh;
    const METAFILEPICT *mfp;
    const ENHMETARECORD *emr;
    const ENHMETAHEADER *emh;
   }pMetaStart;
   const BYTE *pMetaEnd;

   SetWindowRedraw(hContent,FALSE);
   SetWindowRedraw(hObjects,FALSE);
   TreeView_DeleteAllItems(hContent);
   TreeView_DeleteAllItems(hObjects);

   pMetaStart.b=pLoadedMeta;
   pMetaEnd=pLoadedMeta+lLoadedMeta;

   if (!pMetaStart.b) break;
   EmfMode = pMetaStart.emh->dSignature==ENHMETA_SIGNATURE;
// Typ (WMF, Platzierbares WMF, EMF, EMFplus, SPL, CLP) herausfinden
   if (EmfMode) {
    rcFrame=pMetaStart.emh->rclBounds;
#if 1
    dpi.x=MulDiv(pMetaStart.emh->szlDevice.cx,254,pMetaStart.emh->szlMillimeters.cx*10);
    dpi.y=MulDiv(pMetaStart.emh->szlDevice.cy,254,pMetaStart.emh->szlMillimeters.cy*10);
#else
    dpi.x=MulDiv(pMetaStart.emh->rclBounds.right-pMetaStart.emh->rclBounds.left,2540,
		 pMetaStart.emh->rclFrame .right-pMetaStart.emh->rclFrame .left);
    dpi.y=MulDiv(pMetaStart.emh->rclBounds.bottom-pMetaStart.emh->rclBounds.top,2540,
		 pMetaStart.emh->rclFrame .bottom-pMetaStart.emh->rclFrame .top);
#endif
    if (Config.fopt&optRaw) { // Hand-Enumeration
     for (; pMetaStart.b<pMetaEnd; pMetaStart.b+=pMetaStart.emr->nSize) {
      DWORD nSize=pMetaStart.emr->nSize;
      if (nSize<8) nSize=8;		// trotzdem listen!
      MfEnumInternal(EMF_RECORD,pMetaStart.b,nSize);
     }
    }else{	// Windows enumeriert (reicht _nicht_ die Zeiger durch!)
     HENHMETAFILE hMeta;
     hMeta=SetEnhMetaFileBits(lLoadedMeta,pLoadedMeta);
     EnumEnhMetaFile(0,hMeta,EmfEnumProc,NULL,NULL);
     DeleteEnhMetaFile(hMeta);
    }
   }else{	// WMF - mit diversen Headern
    if (ClipboardMode) {	// nur für WMF, nicht für EMF
     rcFrame.left=0;
     rcFrame.top=0;
     rcFrame.right=pMetaStart.mfp->xExt;
     rcFrame.bottom=pMetaStart.mfp->yExt;
     switch (pMetaStart.mfp->mm) {
      case MM_HIENGLISH: dpi.y=dpi.x=1000; break;
      case MM_HIMETRIC:  dpi.y=dpi.x=2540; break;
      case MM_LOENGLISH: dpi.y=dpi.x=100; break;
      case MM_LOMETRIC:  dpi.y=dpi.x=254; break;
      case MM_TWIPS:	 dpi.y=dpi.x=1440; break;
     }
     MfEnumInternal(WMF_METAFILEPICT,pMetaStart.mfp++,sizeof(METAFILEPICT));
    }else if (pMetaStart.pmh->id==PLACEABLE_HEADER_ID) {
     rcFrame=pMetaStart.pmh->rcArea;
     dpi.y=dpi.x=pMetaStart.pmh->inch;
     MfEnumInternal(WMF_PLACEABLE_HEADER,pMetaStart.pmh,sizeof(PLACEABLE_HEADER));
     pMetaStart.pmh++;
    }
    if (Config.fopt&optRaw) { // Hand-Enumeration
     MfEnumInternal(WMF_METAHEADER,pMetaStart.wmh,sizeof(METAHEADER));
     pMetaStart.wmh++;
     for (; pMetaStart.b<pMetaEnd; pMetaStart.w+=pMetaStart.wmr->rdSize) {
      DWORD nSize=pMetaStart.wmr->rdSize<<1;
      if (nSize<6 || nSize>(size_t)(pMetaEnd-pMetaStart.b)) { // Murks, Rest listen!
       MfEnumInternal(WMF_GARBAGE,pMetaStart.b,pMetaEnd-pMetaStart.b);
       break;
      }
      MfEnumInternal(WMF_RECORD,pMetaStart.wmr,nSize);
     }
    }else{	// Windows enumeriert (als WMF, keine Konvertierung zu EMF)
     HMETAFILE hMeta;
     hMeta=SetMetaFileBitsEx((UINT)(pMetaEnd-pMetaStart.b),pMetaStart.b);
     EnumMetaFile(0,hMeta,WmfEnumProc,0);
     DeleteMetaFile(hMeta);
    }
   }
   TCHAR s[32];
   s[_sntprintf(s,elemof(s)-1,dpi.x!=dpi.y?T("%d/%d dpi"):T("%d dpi"),dpi.x,dpi.y)]=0;
   SendMessage(hStatus,SB_SETTEXT,2,(LPARAM)s);
   SetWindowRedraw(hContent,TRUE);
   SetWindowRedraw(hObjects,TRUE);
   CopyRect(&rcBBox,&rcFrame);
   SetZoom(hShow,ZOOM_FRAME);		// neu zeichnen lassen
  }break;

  case WM_DROPFILES: {
   TCHAR FileName[MAX_PATH];
   HDROP hDrop=(HDROP)wParam;	// der Compiler optimiert's schon!
   UINT nFiles=DragQueryFile(hDrop,(UINT)-1,NULL,0);
   DragQueryFile(hDrop,0,FileName,elemof(FileName));
   if (nFiles!=1) MBox(Wnd,MAKEINTRESOURCE(1),MB_OK,FileName);
   SendMessage(Wnd,WM_OPENFILE,0,(LPARAM)(LPCTSTR)FileName);
   DragFinish(hDrop);
  }break;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 0x111: OpenFileDlg(); break;

   case 0x112: {	// Clipboard WMF
    if (OpenClipboard(Wnd)) {
     HGLOBAL hMFP;
     hMFP=GetClipboardData(CF_METAFILEPICT);
     if (hMFP) {
      METAFILEPICT *pMFP;
      UINT NeedBytes;
      pMFP=(METAFILEPICT*)GlobalLock(hMFP);
      NeedBytes=GetMetaFileBitsEx(pMFP->hMF,0,NULL);
      if (NeedBytes) {
       if (pLoadedMeta) GlobalFree(pLoadedMeta);
       ClipboardMode=true;	// am Anfang ein METAFILEPICT-Header
       pLoadedMeta=(BYTE*)GlobalAlloc(GMEM_FIXED,sizeof(METAFILEPICT)+NeedBytes);
       if (pLoadedMeta) {
        METAFILEPICT *p2=(METAFILEPICT*)pLoadedMeta;
        *p2++=*pMFP;		// Ganzen Header kopieren
	GetMetaFileBitsEx(pMFP->hMF,NeedBytes,p2);
	PostMessage(Wnd,WM_LOADED,0,0);
       }else MBox(Wnd,MAKEINTRESOURCE(6),MB_OK,sizeof(METAFILEPICT)+NeedBytes);
      }else MBox(Wnd,MAKEINTRESOURCE(5),MB_OK);
      GlobalUnlock(hMFP);
     }else MBox(Wnd,MAKEINTRESOURCE(4),MB_OK);
     CloseClipboard();
    }else MBox(Wnd,MAKEINTRESOURCE(3),MB_OK);
   }break;

   case 0x113: {	// Clipboard EMF
    if (OpenClipboard(Wnd)) {
     HENHMETAFILE hEMF;
     hEMF=(HENHMETAFILE)GetClipboardData(CF_ENHMETAFILE);
     if (hEMF && LoadEmf(hEMF)) ClipboardMode=true;
     else MBox(Wnd,MAKEINTRESOURCE(4),MB_OK);
     CloseClipboard();
    }else MBox(Wnd,MAKEINTRESOURCE(3),MB_OK);
   }break;

   case 0x11F: SendMessage(Wnd,WM_CLOSE,0,0); break;

   case 0x121: SetOpt(Config.fopt^optRecordHead); break;
   case 0x122: SetOpt(Config.fopt^optRaw); break;

   case 0x123: {	// Konvertieren WMF<->EMF mittels SetWinMetafileBits
    HENHMETAFILE hMeta;
    UINT BytesNeeded;
    union{
     const BYTE *b;
     const METARECORD *wmr;
     const METAHEADER *wmh;
     const PLACEABLE_HEADER *pmh;
     const METAFILEPICT *mfp;
     const ENHMETAHEADER *emh;
    }pMetaStart;
    const BYTE *pMetaEnd=pLoadedMeta+lLoadedMeta;
    pMetaStart.b=pLoadedMeta;
    if (!pMetaStart.b) break;
    if (pMetaStart.emh->dSignature==ENHMETA_SIGNATURE) {
// EMF->WMF:
     HDC dc=GetDC(Wnd);
     hMeta=SetEnhMetaFileBits(lLoadedMeta,pLoadedMeta);
     BytesNeeded=GetWinMetaFileBits(hMeta,0,NULL,MM_ANISOTROPIC,dc);
     if (BytesNeeded) {
      GlobalFree(pLoadedMeta);
      pLoadedMeta=(BYTE*)GlobalAlloc(GMEM_FIXED,BytesNeeded);
      GetWinMetaFileBits(hMeta,BytesNeeded,pLoadedMeta,MM_ANISOTROPIC,dc);
      lLoadedMeta=BytesNeeded;
     }
     DeleteEnhMetaFile(hMeta);
     ReleaseDC(Wnd,dc);
    }else{
// WMF->EMF
     METAFILEPICT mfp={MM_ANISOTROPIC,1000,1000};
     if (ClipboardMode) {
      mfp=*pMetaStart.mfp++;
     }else if (pMetaStart.pmh->id==PLACEABLE_HEADER_ID) {
      mfp.xExt=pMetaStart.pmh->rcArea.right-pMetaStart.pmh->rcArea.left;
      mfp.yExt=pMetaStart.pmh->rcArea.bottom-pMetaStart.pmh->rcArea.top;
      pMetaStart.pmh++;
     }
     hMeta=SetWinMetaFileBits((UINT)(pMetaEnd-pMetaStart.b),pMetaStart.b,0,&mfp);
     if (hMeta) {
      LoadEmf(hMeta);	// macht PostMessage
      DeleteEnhMetaFile(hMeta);
     }else MBox(Wnd,MAKEINTRESOURCE(8),MB_OK);
    }
   }break;

   case 0x141:
   case 0x142:
   case 0x143:
   case 0x144:
   case 0x145:
   case 0x146:
   case 0x147:
   case 0x148:
   case 0x149:
   case 0x14A:
   case 0x14B: HandleZoomMenu(LOWORD(wParam)); break;

  }break;

  case WM_INITMENU: {
   EnableMenuItem(hMenu,0x112,IsClipboardFormatAvailable(CF_METAFILEPICT)?MF_ENABLED:MF_GRAYED);
   EnableMenuItem(hMenu,0x113,IsClipboardFormatAvailable(CF_ENHMETAFILE)?MF_ENABLED:MF_GRAYED);
   InitZoomMenu();
  }break;

  case WM_MENUSELECT: {
   debug(("MenuSelect: %d",LOWORD(wParam)));
   UINT ids[2]={0,LOWORD(wParam)};
   if (ids[1]<0x10) ids[1]=(ids[1]<<4)+0x100;
   MenuHelp(Msg,wParam,lParam,hMenu,hInst,hStatus,ids);
  }break;

#if 1
  case WM_NOTIFY: {
#define hdr ((LPNMHDR)lParam)
   switch (hdr->idFrom) {
    case 1: switch (hdr->code) {
     case TVN_GETDISPINFO: {
      LPNMTVDISPINFO tvdi=(LPNMTVDISPINFO)lParam;
      DECODEINFO*di=(DECODEINFO*)tvdi->item.lParam;
      if (EmfMode) {
       if (tvdi->item.mask&TVIF_TEXT) {
#ifdef UNICODE
        TCHAR buffer[1600];
#else
        TCHAR buffer[2048];
#endif
        EmfDecodeFunc(di,NULL,buffer,buffer+tvdi->item.cchTextMax);
        lstrcpyn(tvdi->item.pszText,buffer,tvdi->item.cchTextMax);
       }
       if (tvdi->item.mask&TVIF_CHILDREN)
         tvdi->item.cChildren=Config.fopt&optRecordHead||di->emr.nSize>8 ? 1 : 0;
      }else{
       if (tvdi->item.mask&TVIF_TEXT) WmfDecodeFunc(di,NULL,tvdi->item.pszText,tvdi->item.pszText+tvdi->item.cchTextMax);
       if (tvdi->item.mask&TVIF_CHILDREN)
         tvdi->item.cChildren=Config.fopt&optRecordHead||di->wmr.rdSize>3 ? 1 : 0;
      }
     }break;
    
     case TVN_ITEMEXPANDING: {
      LPNMTREEVIEW tv=(LPNMTREEVIEW)lParam;
      if (tv->action==TVE_EXPAND) {
       DECODEINFO*di=(DECODEINFO*)tv->itemNew.lParam;
       TVINSERTSTRUCT tvis;
       TCHAR buf[64];
       if (!di) break;
       tvis.hParent=tv->itemNew.hItem;
       tvis.hInsertAfter=TVI_LAST;
       tvis.item.mask=TVIF_TEXT;
       tvis.item.pszText=buf;
       if (EmfMode) EmfDecodeFunc(di,&tvis,buf,buf+elemof(buf));	// trägt alle Sub-Zeilen ein
       else WmfDecodeFunc(di,&tvis,buf,buf+elemof(buf));
      }
     }break;

     case TVN_ITEMEXPANDED: {
      LPNMTREEVIEW tv=(LPNMTREEVIEW)lParam;
      if (tv->action==TVE_COLLAPSE) {
       DECODEINFO*di=(DECODEINFO*)tv->itemNew.lParam;
       HTREEITEM Child=TreeView_GetChild(hContent,tv->itemNew.hItem);
       HTREEITEM Sibling;
       if (!di) break;
       for(;Sibling=TreeView_GetNextSibling(hContent,Child),Child;Child=Sibling)
         TreeView_DeleteItem(hContent,Child);
      }
     }break;
    
     case TVN_SELCHANGED: {
      LPNMTREEVIEW tv=(LPNMTREEVIEW)lParam;
      RECT r;
      DECODEINFO *di;
      di=(DECODEINFO*)tv->itemOld.lParam;
      if (di) di->Flags&=~DI_SELECTED;
      di=(DECODEINFO*)tv->itemNew.lParam;
      if (di) di->Flags|=DI_SELECTED;
      GetClientRect(Wnd,&r);
      r.left=DiviV.right;
      InvalidateRect(Wnd,&r,TRUE);
     }break;

     case NM_CUSTOMDRAW: {
      NMTVCUSTOMDRAW*cd=(NMTVCUSTOMDRAW*)lParam;
      switch (cd->nmcd.dwDrawStage) {
       case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW;
       case CDDS_ITEMPREPAINT: return CDRF_NOTIFYPOSTPAINT;
       case CDDS_ITEMPOSTPAINT: {
        TCHAR s[256],*p;
        TVITEMEX tv;
	tv.mask=TVIF_TEXT;
	tv.hItem=(HTREEITEM)cd->nmcd.dwItemSpec;
	tv.pszText=s;
	tv.cchTextMax=elemof(s);
	if (SendMessage(hdr->hwndFrom,TVM_GETITEM,0,(LPARAM)&tv)
	 && (p=StrChr(s,'#'))) {
	 int i;
	 COLORREF cr;
	 HBRUSH br,obr;
	 *--p='0';
	 p[1]='x';
	 p[8]=0;
	 StrToIntEx(p,STIF_SUPPORT_HEX ,&i);
	 cr=RGB(i>>16,i>>8,i);
	 br=CreateSolidBrush(cr);
	 obr=SelectBrush(cd->nmcd.hdc,br);
	 i=cd->nmcd.rc.bottom-cd->nmcd.rc.top;	// Höhe
	// Kreis mit Farbfüllung am rechten Rand (besser hinter dem Text, noch besser vor der Farbangabe)
	 Ellipse(cd->nmcd.hdc,cd->nmcd.rc.right-i+1,cd->nmcd.rc.top+1,cd->nmcd.rc.right-1,cd->nmcd.rc.bottom-1);
	 SelectBrush(cd->nmcd.hdc,obr);
	 DeleteBrush(br);
	}
       }break;
      }
     }break;
    }break;

    case 5:	// Toolbar
    debug(("code=%d",hdr->code));
    switch (hdr->code) {
     case TBN_GETINFOTIP: {
      LPNMTBDISPINFO di=(LPNMTBDISPINFO)lParam;
      LoadString(hInst,di->idCommand,di->pszText,di->cchText);
     }break;
    }break;
   }
  }break;
#endif

  case WM_DESTROY: {
   PostQuitMessage(0);
  }break;

 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

void WINAPI WinMainCRTStartup() {
 MSG Msg;
 WNDCLASSEX wc;

 InitStruct(&wc,sizeof(wc));
 wc.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
 wc.lpfnWndProc=ShowProc;
 wc.hInstance=hInst=GetModuleHandle(NULL);
 wc.hCursor=LoadCursor(0,IDC_CROSS);
 wc.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE+1);
 wc.lpszClassName=T("show");
 RegisterClassEx(&wc);

 wc.style=CS_DBLCLKS;
 wc.lpfnWndProc=MainWndProc;
 wc.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(100));
 wc.hCursor=LoadCursor(0,IDC_ARROW);
 wc.hbrBackground=0;
 wc.lpszMenuName=MAKEINTRESOURCE(100);
 wc.lpszClassName=T("WmfView");
 RegisterClassEx(&wc);

 InitCommonControls();
 LoadString(hInst,100,MBoxTitle,elemof(MBoxTitle));

 hMainWnd=CreateWindowEx(
   WS_EX_ACCEPTFILES,
   T("WmfView"),MBoxTitle,WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,0,CW_USEDEFAULT,0,0,0,hInst,
   PathGetArgs(GetCommandLine()));

 ShowWindow(hMainWnd,SW_SHOWDEFAULT);

 while (GetMessage(&Msg,0,0,0)) {
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }

 ExitProcess((UINT)Msg.wParam);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded