Source file: /~heha/hs/Funkuhr.zip/src/Wutils.c

/********************************
 * Projekt: Funkuhr DCF77	*
 * Utility-Sammlung		*
 ********************************/

#include "Funkuhr.h"

/* aus WUTILS.PAS entstanden */

TCHAR MBoxTitle[64];

void _fastcall InitStruct(LPVOID p, UINT len) {
 __stosd(p,0,len>>2);
 *(UINT*)p=len;
}

int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
 TCHAR buf1[256],buf2[1024];
 LoadString(ghInstance,id,buf1,elemof(buf1));
 wvnsprintf(buf2,elemof(buf2),buf1,arglist);
 return MessageBox(Wnd,buf2,MBoxTitle,style);
}

int MBox(HWND Wnd, UINT id, UINT style,...) {
 va_list va;
 va_start(va,style);
 return vMBox(Wnd,id,style,va);
}

// n-ten nullterminierten String liefern
// Liefert <start> für n=0
PTSTR GetStr(PTSTR start, int n) {
 if (n) do start+=lstrlen(start)+1; while(--n);
 return start;
}

#ifndef UNICODE
PWSTR GetStrW(PWSTR start, int n) {
 if (n) do start+=lstrlenW(start)+1; while(--n);
 return start;
}
#endif

#ifdef WIN95
int _cdecl wnsprintf(PTSTR s, int slen, PCTSTR t,...) {
 return wvsprintf(s,t,(va_list)(&t+1));
}
#endif


// Formatierten Fenster-Text setzen
BOOL wndSetText(HWND w, PCTSTR t, ...) {
 TCHAR s[128];
 va_list va;
 va_start(va,t);
 wvnsprintf(s,elemof(s),t,va);
 return SetWindowText(w,s);
}

// Entfernt einzelne & aus Menü- oder Static-String, liefert Ergebnis-Stringlänge
// Entfernt auch alles nach "\t"
int StripAmpersand(PTSTR s) {
 PTSTR d=s, a=s;
 TCHAR c=0;
 do{
  if (c=='&') {
   *d++=c=*s++;	// kopieren ohne zu gucken (bei && nämlich)
  }else{
   c=*s++;	// erst gucken, dann kopieren
   if (c=='\t') c=0;
   if (c!='&') *d++=c;
  }
 }while(c);	// Aufhören, wenn die terminierende Null kopiert wurde
 return (int)(d-1-a);
}

// Entnimmt Hotkey aus Menü- oder Static-String; Großbuchstabe
TCHAR GetHotkey(PCTSTR s) {
 TCHAR c;
 for(;c=*s++;) if (c=='&') {
  c=*s++;
  if (c!='&') return (TCHAR)CharUpper((PTSTR)(TBYTE)c);	// sonst beim Folgezeichen fortsetzen
 }
 return c;	// Null
}

// Findet eindeutigen Hotkey für den gegebenen String in <buf>
// aus den vorhandenen Menüpunkten von <hSubMenu>
// und verdoppelt alle '&' in <buf> zu '&&'
// Liefert Ergebnis-Stringlänge; 0 bei Fehler
int GenerateUniqueHotkey(HMENU hSubMenu, PTSTR buf, int len) {
 TCHAR hot[32];	// sollte reichen
 TCHAR *s,c;
// 1. Menüstrings nach deren Horkeys durchsuchen und einsammeln
 int i,j,k=GetMenuItemCount(hSubMenu);
 for (i=j=0; i<k && j<elemof(hot)-1; i++) {
  TCHAR b2[64];
  if (GetMenuString(hSubMenu,i,b2,elemof(b2),MF_BYPOSITION)
  && (hot[j]=GetHotkey(b2))) j++;
 }
 hot[j]=0;
// 2. Geeigneten freien Hotkey suchen (c=Hotkey, j = Index)
 if (s=StrDup(buf)) {
  for (j=0;c=s[j];j++) {
   c=(TCHAR)CharUpper((PTSTR)(TBYTE)c);
   if (IsCharAlphaNumeric(c) && !StrChr(hot,c)) break;
  }
// 3. Hotkey markieren und vorhandene '&' doppeln
  for (i=k=0;c=s[i];i++) {
   if ((i==j || c=='&') && len>2) *buf++='&', len--, k++;
   if (len>1) *buf++=c, len--, k++;
  }
  *buf=0;
  LocalFree(s);
// Resultierende Länge liefern
  return k;
 }
 return 0;	// Fehler, <buf> unverändert
}

// ClientToScreen() mit short-Koordinaten
DWORD ClientToScreenS(HWND w, DWORD pos) {
 POINT pt={GET_X_LPARAM(pos),GET_Y_LPARAM(pos)};
 ClientToScreen(w,&pt);
 return MAKELONG(pt.x,pt.y);
}

// Gruppe von Checkboxen zur Bitmaske abfragen
UINT GetCheckboxGroup(HWND Wnd, UINT u, UINT o) {
 UINT v,m;
 for (v=0,m=1; u<=o; u++,m+=m) if (IsDlgButtonChecked(Wnd,u)==1) v|=m;
 return v;
}

// Gruppe von Checkboxen anhand Bitmaske setzen
void SetCheckboxGroup(HWND Wnd, UINT u, UINT o, UINT v) {
 for (; u<=o; u++,v>>=1) CheckDlgButton(Wnd,u,v&1);
}

void EnableDlgItem(HWND Wnd, UINT id, BOOL state) {
 Wnd=GetDlgItem(Wnd,id);
 if (Wnd) EnableWindow(Wnd,state);
}

void ShowDlgItem(HWND Wnd, UINT id, int state) {
 Wnd=GetDlgItem(Wnd,id);
 if (Wnd) ShowWindow(Wnd,state);
}

BOOL CheckMenuRadio(HMENU m, UINT id) {
 return CheckMenuRadioItem(m,id,id,id,0);
}

// wie Get/SetCheckboxGroup, nur für Menüpunkte, via MF_BYCOMMAND
void SetCheckMenuGroup(HMENU m, UINT u, UINT o, UINT v) {
 for (; u<=o; u++,v>>=1) CheckMenuItem(m,u,v&1?MF_CHECKED:MF_UNCHECKED);
}

UINT GetCheckMenuGroup(HMENU me, UINT u, UINT o) {
 UINT v,m;
 for (v=0,m=1; u<=o; u++,m+=m) if (GetMenuState(me,u,0)&MF_CHECKED) v|=m;
 return v;
}

void EnableMenuGroup(HMENU m, UINT u, UINT o, UINT state) {
 for (; u<=o; u++) EnableMenuItem(m,u,state);
}

// Sovielwie SetDlgItemText(), jedoch Markierung beibehalten
// id kann -1 sein, dann Wirkung wie SetWindowText()
void SetEditText(HWND Wnd, UINT id, LPTSTR s) {
 DWORD SelStart,SelEnd;
 if (id!=(UINT)-1) Wnd=GetDlgItem(Wnd,id);
 SendMessage(Wnd,EM_GETSEL,(WPARAM)&SelStart,(LPARAM)&SelEnd);
 SetWindowText(Wnd,s);
 Edit_SetSel(Wnd,SelStart,SelEnd);
}

// Die Mindesthöhe des Editelements scheint 14 Dialogeinheiten zu sein!
// Die Zahl <cur> erscheint automatisch im Editfenster.
HWND AttachUpDown(HWND Wnd, UINT id, UINT udid, int min, int max, int cur) {
 HWND w=GetDlgItem(Wnd,id);
#if 1
 HWND r=CreateWindowEx(0,UPDOWN_CLASS,NULL,	// hier: für 32-Bit-Zahlen
   WS_CHILD|WS_VISIBLE|WS_BORDER|UDS_ALIGNRIGHT|UDS_NOTHOUSANDS|UDS_HOTTRACK|UDS_SETBUDDYINT|UDS_ARROWKEYS,
   0,0,0,0,Wnd,(HMENU)udid,ghThisInst,NULL);
 SetWindowPos(r,w,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);	// Reihenfolge sinnvoll setzen
 SendMessage(r,UDM_SETBUDDY,(WPARAM)w,0);
 SendMessage(r,UDM_SETRANGE32,min,max);
 SendMessage(r,UDM_SETPOS32,0,cur);
 return r;
#else
 return CreateUpDownControl(	// wirklich nur für 16 Bit! (Reicht nicht für Filterfrequenz)
   WS_CHILD|WS_VISIBLE|WS_BORDER|UDS_ALIGNRIGHT|UDS_NOTHOUSANDS|UDS_HOTTRACK|UDS_SETBUDDYINT|UDS_ARROWKEYS,
   0,0,0,0,Wnd,udid,ghThisInst,w,max,min,cur);
#endif
}

bool GetUpDownInt(HWND Wnd, UINT id, int *val) {
 BOOL err;
 int i=(int)SendDlgItemMessage(Wnd,id,UDM_GETPOS32,0,(LPARAM)&err);
 if (err) return false;
 if (val) *val=i;
 return true;
}

bool GetUpDownByte(HWND Wnd, UINT id, BYTE *val) {
 int i;
 if (!GetUpDownInt(Wnd,id,&i)) return false;
 if (val) *val=(BYTE)i;
 return true;
}

#ifdef _M_IX86
// mangels Laufzeitbibliothek "longlong"-Routinen nachreichen,
// diese erwartet in EDX:EAX das "longlong" und in CL die Schiebeweite
// "_cdecl" notwendig, schaltet Namensgarnierung zurück auf '_'-Präfix
// Int64ShrlMod32 usw. sind weniger effektiv als diese Routinen!
ULONGLONG _declspec(naked) _cdecl _allshl(ULONGLONG ll, BYTE shift) {
 _asm{
	test	cl,32
	jnz	l1
	shld	edx,eax,cl
	shl	eax,cl
	ret
l1:	mov	edx,eax
	xor	eax,eax
	shl	edx,cl
	ret
 }
}
ULONGLONG _declspec(naked) _cdecl _aullshr(ULONGLONG ll, BYTE shift) {
 _asm{
	test	cl,32
	jnz	l1
	shrd	eax,edx,cl
	shr	edx,cl
	ret
l1:	mov	eax,edx
	xor	edx,edx
	shr	eax,cl
	ret
 }
}
LONGLONG _declspec(naked) _cdecl _allshr(LONGLONG ll, BYTE shift) {
 _asm{
	test	cl,32
	jnz	l1
	shrd	eax,edx,cl
	sar	edx,cl
	ret
l1:	mov	eax,edx
	xor	edx,edx
	sar	eax,cl
	ret
 }
}

// hier: Einschränkung, nur für DWORD-Divisor
ULONGLONG _declspec(naked) _cdecl _aulldiv(ULONGLONG z, ULONGLONG n) {
 _asm{
	mov	eax,[esp+8]	;High-Teil Zähler
	mov	ecx,[esp+12]	;Nenner
	xor	edx,edx
	div	ecx		;EAX=Ergebnis, EDX=Rest
	push	eax
	 mov	eax,[esp+8]	;Low-Teil Zähler
	 div	ecx		;EAX=Ergebnis, EDX=Rest
	pop	edx		;Rest durch High-Teil ersetzen
	ret	16
 }
}

// hier: Einschränkung, nur für DWORD-Divisor (und damit Rest)
ULONGLONG _declspec(naked) _cdecl _aullrem(ULONGLONG z, ULONGLONG n) {
 _asm{
	mov	eax,[esp+8]	;High-Teil Zähler
	mov	ecx,[esp+12]	;Nenner
	xor	edx,edx
	div	ecx		;EAX=Ergebnis, EDX=Rest
	mov	eax,[esp+4]	;Low-Teil Zähler
	div	ecx		;EAX=Ergebnis, EDX=Rest
	xor	eax,eax		;Ergebnis löschen
	xchg	edx,eax		;Rest in Low-Teil
	ret	16
 }
}

// MSVC macht aus dieser (Nicht-Windows-)Funktion ein _aullmul: unnötig!
ULONGLONG _declspec(naked) _fastcall UInt32x32To64(DWORD x,DWORD y) {
 _asm{	xchg	ecx,eax
	mul	edx
	ret
 }
}

div_t _declspec(naked) _fastcall mydiv(int n, __int64 z) {_asm{
	mov	eax,[esp+4]
	mov	edx,[esp+8]
	idiv	ecx
	or	edx,edx
	jns	exi
	dec	eax
	add	edx,ecx
exi:	ret	8
}}

#pragma intrinsic(sin,cos,tan,log,atan,atan2)

static const short v180=1800;
_declspec(naked) POINT _fastcall sincos(int radius, int deg) {_asm{
	push	ecx			// Radius
	push	edx			// Winkel in Zehntelgrad
	fild	dword ptr[esp+4]	// Radius laden
	fild	dword ptr[esp]		// Winkel laden
	fldpi				// PI laden
	fmul
	fidiv	[v180]			// jetzt st(0) = Winkel im Bogenmaß, st(1) = Radius
	fsincos				// st(0) = Kosinus, st(1) = Sinus, st(2) = Radius
	fmul	st,st(2)
	fxch	st(1)			// st(0) = Sinus, st(1) = Kosinus, st(2) = Radius
	fmulp	st(2),st		// st(0) = Kosinus, st(1) = Sinus
	fistp	dword ptr[esp]		// Kosinuswert
	fistp	dword ptr[esp+4]	// Sinuswert
	pop	eax
	pop	edx
	ret
}}
// tatsächlich so kompliziert!
double _cdecl _CIexp() {
 _asm{	fldl2e
	fmulp	st(1),st	// st = f * log2 e
	fld	st		// duplizieren
	frndint			// st = ganzzahliger Teil (Rundung mathematisch)
	fsub	st(1),st	// st(1) = gebrochener Teil (± 0,5)
	fxch	st(1)
	f2xm1			// st = 2^gebrochener_Teil-1 (f2xm1 kann nur im Intervall ±1 arbeiten!)
	fld1
	faddp	st(1),st	// 1 addieren: st = 2^gebrochener_Teil
	fscale			// st = 2^gebrochener_Teil * 2^ganzzahliger_Teil
	fxch	st(1)
	fstp	st		// ganzzahligen Teil entfernen
 }
}

double _cdecl _CIfmod() {
 _asm{	fxch	st(1)
 	fprem
	fxch	st(1)
	fstp	st(0)		// verwerfen
}}

float hypotf(float x, float y) {
 _asm{	fld	x
	fmul	st(0),st
	fld	y
	fmul	st(0),st
	faddp	st(1),st
	fsqrt
 }
}

#endif
char _fltused;

int sinus(int radius, int sec) {
#if 0
// Sinustabelle für ganze Sekunden (6°),
// erster Quadrant, 256=1.0
 static const BYTE Sintab[]={
   0,27,53,79,104,128,150,171,190,207,222,234,243,250,255};
 int quadrant=sec/15;
 sec%=15;
 if (quadrant&1) sec=15-sec;
 if (sec!=15) radius=MulDiv(radius,Sintab[sec],256);
 if (quadrant&2) radius=-radius;
 return radius;
#else
 return sincos(radius,sec*60).y;
#endif
}

BOOL _stdcall Line(HDC dc, int x1, int y1, int x2, int y2) {
#ifdef _M_IX86
 return Polyline(dc,(const POINT*)&x1,2);	// Parameter liegen auf dem Stack bereits richtig
#else
 POINT pt[2]={{x1,y1},{x2,y2}};	// umstapeln
 return Polyline(dc,pt,2);
#endif
}

static struct{
// CRITICAL_SECTION CritSec;	// BUG: ob CritSec oder hMutex, es hängt mit eingeschaltetem Beep
 HANDLE hMutex;			// beim Senden von PAUSE an den Dienst ... unerklärlich
 HWAVEOUT hWave;		// und die Lautstärkeeinstellung funktioniert auch nicht so wie's soll
 MMTIME start;			// (= geht nicht aus beim Schieben)
 WAVEHDR wh[3];		// 3 Indizes für anschwellend, konstant (mit WH_BEGINLOOP und WH_ENDLOOP), abfallend
 short buffer[3][60];	// Jeder Puffer benötigt 1,36 ms
}pw;	// Piep-Wave

void InitCritSec(void) {
// InitializeCriticalSection(&pw.CritSec);
 pw.hMutex=CreateMutex(NULL,FALSE,NULL);
}

static void CalcBuffers(BYTE negvol) {
 int i;
 for (i=0; i<60; i++) pw.buffer[0][i]=sinus((i<<9)>>negvol,i);
 for (i=0; i<60; i++) pw.buffer[1][i]=sinus((120<<8)>>negvol,i);
 for (i=0; i<60; i++) pw.buffer[2][i]=sinus(((120<<8)-(i<<9))>>negvol,i);
}

// Sinus (367,5 Hz) auf Soundkarte ausgeben
// mit ansteigender Lautstärke am Anfang und abfallender am Ende
void InitBeep(BYTE negvol) {	// negvol=0 (volle Lautstärke) bis 6 (leises Rechteck)
 static const WAVEFORMATEX wf={
  WAVE_FORMAT_PCM,
  1,		// Kanäle
  22050,	// Samples pro Sekunde
  44100,	// Bytes pro Sekunde
  2,		// Ausrichtung: WORD
  16};		// Bits pro Sample
 int i;
 if (pw.hWave) {CalcBuffers(negvol); return;}	// nur Lautstärke ändern
// EnterCriticalSection(&pw.CritSec);
 if (WaitForSingleObject(pw.hMutex,100)) return;
 if (!waveOutOpen(&pw.hWave,WAVE_MAPPER,&wf,0,0,0)) {
  waveOutPause(pw.hWave);
  CalcBuffers(negvol);
  for (i=0; i<3; i++) {
   WAVEHDR*wh=pw.wh+i;
   wh->lpData=(PBYTE)pw.buffer[i];
   wh->dwBufferLength=60*2;
   wh->dwFlags=i==1?WHDR_BEGINLOOP|WHDR_ENDLOOP:0;
   wh->dwLoops=(DWORD)-1;
   waveOutPrepareHeader(pw.hWave,wh,sizeof(WAVEHDR));
   waveOutWrite(pw.hWave,wh,sizeof(WAVEHDR));
  }
  pw.start.wType=TIME_SAMPLES;
 }
// LeaveCriticalSection(&pw.CritSec);
 ReleaseMutex(pw.hMutex);
}

void StartBeep(void) {
 int i;
// EnterCriticalSection(&pw.CritSec);
 if (WaitForSingleObject(pw.hMutex,100)) return;
 if (pw.hWave) {
  waveOutPause(pw.hWave);
  for (i=0; i<3; i++) {
   waveOutWrite(pw.hWave,pw.wh+i,sizeof(WAVEHDR));
  }
  waveOutGetPosition(pw.hWave,&pw.start,sizeof(pw.start));	// Zeitpunkt vermerken
  waveOutRestart(pw.hWave);
 }
// LeaveCriticalSection(&pw.CritSec);
 ReleaseMutex(pw.hMutex);
}

void StopBeep(void) {
// EnterCriticalSection(&pw.CritSec);
 if (WaitForSingleObject(pw.hMutex,100)) return;
 if (pw.hWave) {
  MMTIME t;
  int i=10;	// Zähler zur Notbremse (W2k in Nieschütz)
  t.wType=TIME_SAMPLES;
  do{		// Schneller-Rechner-Problem: Abwarten, bis der mittlere Datenblock an der Reihe ist
   waveOutGetPosition(pw.hWave,&t,sizeof(t));
   t.u.sample-=pw.start.u.sample;
//   if (!t.u.sample) break;	// nicht gestartet (waveOutBreakLoop ohne Konsequenz)
   if (t.u.sample>60) break;	// Mittlerer Block ist erreicht
   Sleep(0);			// Etwas warten bis zum mittleren Block (Spinlock)
   DbgPrintf(("Sleep(0)%d,%d\n",i,t.u.sample));
  }while (t.u.sample && --i);
  waveOutBreakLoop(pw.hWave);
 }
// LeaveCriticalSection(&pw.CritSec);
 ReleaseMutex(pw.hMutex);
}

void DoneBeep(void) {
 int i;
// EnterCriticalSection(&pw.CritSec);
 if (WaitForSingleObject(pw.hMutex,100)) return;
 if (pw.hWave) {
  waveOutBreakLoop(pw.hWave);	// waveOutReset() knackt unangenehm
  for (i=0; i<3; i++) {
   while (waveOutUnprepareHeader(pw.hWave,pw.wh+i,sizeof(WAVEHDR))) Sleep(0);
  }
  waveOutClose(pw.hWave);
  pw.hWave=0;
 }
// LeaveCriticalSection(&pw.CritSec);
 ReleaseMutex(pw.hMutex);
}

#ifdef _DEBUG
#if 0//def _M_IX86
// Speicher auf Stack (niemals NULL), len sollte durch 4 teilbar sein!
// Aufrufer muss Standard-Stapelrahmen (mit EBP) haben!
// Diese einfache Routine funktioniert nur mit Alloc-Größen < 4 KByte!
// Ansonsten alloca() aus msvcrt.dll benutzen.
LPVOID _declspec(naked) _fastcall StackAlloc(DWORD len) {
 _asm{
	pop	edx		// Rückkehradresse -> EDX
	sub	esp,ecx		// Platz auf Stack
	mov	eax,esp		// Zeiger auf Anfang -> EAX
	jmp	edx		// Rücksprung
 }
}
#endif

void _cdecl dprintf(const char *s,...) {
 va_list va;
 char buf[256];
 va_start(va,s);
 if (IS_INTRESOURCE(s)) {
  char t[256];
  LoadStringA(ghInstance,(UINT)s,t,elemof(t));
  s=t;
 }
 wvnsprintfA(buf,elemof(buf),s,va);
 OutputDebugStringA(buf);
}
#endif

bool _fastcall dynaload(dynaprocs*dp,const char*e) {
 int i;
 if (!(dp->hLib=LoadLibraryA(e))) return false;
 for (i=0;;i++) {
  e+=lstrlenA(e)+1;
  if (!*e) return true;	// Ende erreicht
  if (!(dp->proc[i]=GetProcAddress(dp->hLib,e))) return false;
 }
}

// BUG/FEATURE: So wie's ist liefert's FALSE bei abgeschaltetem UAC
bool IsUserAdmin() {
 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
 PSID AdministratorsGroup; 
 BOOL b;
#if defined(_M_IX86) && !defined(UNICODE)	// dynamische Einsprungpunkte (advapi32.lib) wegen Windows 9x
 struct{
  HINSTANCE hLib;
  BOOL (WINAPI*AllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,
    DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);	// Komisch: Windows98 hat diesen Einsprunkpunkt!
  BOOL (WINAPI*CheckTokenMembership)(HANDLE,PSID,PBOOL);	// Diesen nicht
  BOOL (WINAPI*FreeSid)(PSID);					// Diesen hat Win98 wieder
 }dp;
 static const char names[]="advapi32.dll\0" "AllocateAndInitializeSid\0" "CheckTokenMembership\0" "FreeSid\0";
 b=dynaload((dynaprocs*)&dp,names);
 FreeLibrary(dp.hLib);
 if (!b) return true;
#define DL dp.
#else
#define DL
#endif
 b = DL AllocateAndInitializeSid(&NtAuthority,	// liefert so unter Win98 FALSE
   2,
   SECURITY_BUILTIN_DOMAIN_RID,
   DOMAIN_ALIAS_RID_ADMINS,
   0, 0, 0, 0, 0, 0,
   &AdministratorsGroup); 
 if (b) {
  if (!DL CheckTokenMembership(NULL, AdministratorsGroup, &b)) b = false;
  DL FreeSid(AdministratorsGroup); 
 }
 return b;
#undef DL
}

// Byte-Array zu Integer-Array (typischerweise Punkte)
void _fastcall bloat(int*dst, const char*src, int len) {
 do *dst++=*src++; while(--len);
}

#ifndef UNICODE
// Unicode-String bei ANSI-Version laden
int MyLoadStringW(UINT id, PWSTR buf, int len) {
 HRSRC r=FindResource(ghInstance,MAKEINTRESOURCE((id>>4)+1),RT_STRING);
 PCWSTR p=(PCWSTR)LoadResource(ghInstance,r);
 if (!p) return 0;
 for (id&=15; id; id--) {
  p+=*p+1;
 }
 len--;
 if (len<0) return *p+1;	// Allokationsgröße in Zeichen
 if (len>*p) len=*p;
 p++;
 __movsw(buf,p,len);	// inklusive etwaiger Nullen!
 buf[len]=0;
 return len;
}
#endif

/**********************************************
 * Fläche und Flächenschwerpunkt (Wikipedia):
 * A = 1/2 * Summe(x[i]*y[i+1]-x[i+1]*y[i])
 * schwer.x = 1/6/A * Summe((x[i]+x[i+1])*(x[i]*y[i+1]-x[i+1]*y[i]))
 * schwer.y = 1/6/A * Summe((y[i]+y[i+1])*(x[i]*y[i+1]-x[i+1]*y[i]))
 **********************************************/

// liefert Flächeninhalt (in Quadratpixel) und ggf. Schwerpunkt in <*c>
// Nur für Win9x-gängige Polygone mit 16-Bit-Koordinaten!
// (Schwerpunkt == Centroid)
__int64 CalcArea(const POINT *p, int n, POINT *c) {
 int i;
 int a=0;
 int x=0,y=0;	// Schwerpunkt
 for (i=0;i<n;i++) {
  int j=(i+1)%n;	// Folgeindex
  int t=p[i].x*p[j].y-p[j].x*p[i].y;	// gemeinsamer Term: doppelte Dreiecksfläche
  a+=t;		// Fläche summieren
  if (c) {
   x+=(p[i].x+p[j].x)*t;	// Schwerpunkt jedes Dreiecks mit Flächeninhalt wichten
   y+=(p[i].y+p[j].y)*t;	// und summieren (in etwa)
  }
 }
 a>>=1;				// runden nach –Inf: negativ bleibt negativ
 if (c&&a) {
  c->x=(x/(3*a))>>1;		// gleichfalls nach –Inf runden
  c->y=(y/(3*a))>>1;		// Bei n=0 <*c> nicht setzen
 }
 return a;
}

/*
  case 0x31A/*WM_THEMECHANGED* /: {
   HTHEME th=0;
   struct{
    HINSTANCE hLib;
    HTHEME (WINAPI*GetWindowTheme)(HWND);
   }ux;
   static const char names[]="uxtheme.dll\0" "GetWindowTheme\0"
   if (dynaload((dynaprocs*)&ux,names)) {
    th=ux.GetWindowTheme(GetDlgItem(Wnd,16));
   }
   if (ux.hLib) FreeLibrary(ux.hLib);
   di.BrushIndex=th?COLOR_3DHILIGHT:COLOR_3DFACE;
  }break;
*/
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded