Zeichen und Tastennachrichten darstellen



  • hallo.

    ich beschäftige mich grat mit der WINAPI.
    dazu hab ich mir auch das Buch "Windows programmierung" von C. Petzold geholt.

    nun bin ich beim Kapitel über Tastennachrichten angelangt.

    da kommt folgender Quelltext vor:

    #include <windows.h>
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
    	static TCHAR szAppName []= TEXT("Keyview1");
    	HWND hwnd;
    	MSG msg;
    	WNDCLASS wndclass;
    
    	wndclass.style=CS_HREDRAW|CS_VREDRAW;
    	wndclass.lpfnWndProc=WndProc;
    	wndclass.cbClsExtra=0;
    	wndclass.cbWndExtra=0;
    	wndclass.hInstance=hInstance;
    	wndclass.hIcon=LoadIcon (NULL,IDI_APPLICATION);
    	wndclass.hCursor=LoadCursor (NULL, IDC_ARROW);
    	wndclass.hbrBackground=(HBRUSH) GetStockObject (WHITE_BRUSH);
    	wndclass.lpszMenuName=NULL;
    	wndclass.lpszClassName= szAppName;
    
    	RegisterClass(&wndclass);
    
    	hwnd=CreateWindow(szAppName, TEXT("Keyview die Erste"), WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
    
    	ShowWindow(hwnd,iCmdShow);
    	UpdateWindow(hwnd);
    
    	while (GetMessage (&msg,NULL,0,0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static int cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar;
    	static int cLinesMax, cLines;
    	static PMSG pmsg;
    	static RECT rectScroll;
    
    	static TCHAR szTop[] = TEXT("Nachricht     Taste   Zeichen     Wdh.  OEM-Code Erw. ALT   vorher  jetzt");
    	static TCHAR szUnd[] = TEXT("_________     _____   _______     ____  ________ ____ ___   ______  _____");
    
    	[b]static TCHAR *szFormat[2]= { TEXT ("%-13s %3d %-13s%c%6u  %4d      %4s %4s %8s %8s"),
    		                         TEXT ("%-13s          0x%04X%1s%c %6u %4d       %4s %4s %8s %8s") };[/b]	static TCHAR *szYes = TEXT("Ja");
    	static TCHAR *szNo =  TEXT("Nein");
    	static TCHAR *szDown= TEXT("Gedrückt");
    	static TCHAR *szUp=   TEXT("Gelöst");
    
    	static TCHAR *szMessage[]= {
    		                        TEXT ("WM_KEYDOWN"),    TEXT("WM_KEYUP"),
    		                        TEXT ("WM_CHAR")   ,    TEXT("WM_DEADCHAR"),
    								TEXT ("WM_SYSKEYDOWN"),  TEXT("WM_SYSKEYUP"),
    							    TEXT ("WM_SYSCHAR"),    TEXT("WM_SYSDEADCHAR") };
    
    	HDC hdc;
    	int i, iType;
    	PAINTSTRUCT ps;
    	TCHAR szBuffer[128], szKeyName [32];
    	TEXTMETRIC tm;
    
    	switch(message)
    	{
    	case WM_CREATE:
    	case WM_DISPLAYCHANGE:
    
    		//Maximalgröße des Anwendungdbereichs
    		cxClientMax = GetSystemMetrics(SM_CXMAXIMIZED);
    		cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED);
    
    		//nicht proportionale Schrift setzen
    		hdc=GetDC(hwnd);
    		SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
    		GetTextMetrics(hdc,&tm);
    		cxChar=tm.tmAveCharWidth;
    		cyClient= tm.tmHeight;
    
    		ReleaseDC(hwnd,hdc);
    
    		//Speicherplatz freigeben(für die Ausgabezeilen)
    		if (pmsg)
    			free(pmsg);
    
    		//Speicherplatz für die maximale Anzahl von Zeilen im Andwendungsbereich bereitstellen
    		cLinesMax = cyClientMax/cyChar;
    		pmsg=malloc(cLinesMax * sizeof(MSG));
    		cLines=0;
    
    		//weiter mit WM_SIZE
    
    	case WM_SIZE:
    
    		if(message==WM_SIZE)
    		{
    			cxClient = LOWORD (lParam);
    			cyClient = HIWORD (lParam);
    		}
    
    		//Rechteck für ScrollWindow
    		rectScroll.left  = 0;
    		rectScroll.right = cxClient;
    		rectScroll.top   = cyChar; //es wird 1 Zeile am oberen Rand freigelassen
    		rectScroll.bottom= cyChar * (cyClient/cyChar);
    
    		//InvalidateRect(hwnd, Rect (falls NULL -> ganzer Anwendungsbreich wird aktualisiert), soll Hintergrund bei WM_PAINT beibehalten werden? -> bei TRUE wird er gelöscht
    		InvalidateRect(hwnd, NULL, TRUE);
    		return 0;
    
    	case WM_KEYDOWN:
    	case WM_KEYUP:
    	case WM_CHAR:
    	case WM_DEADCHAR:
    	case WM_SYSKEYDOWN:
    	case WM_SYSKEYUP:
    	case WM_SYSCHAR:
    	case WM_SYSDEADCHAR:
    
    		//bereits gespeicherte Nachrichten nach hinten stapeln
    		for (i=cLinesMax-1; i>0; i--)
    		{
    			pmsg[i] = pmsg[i-1];
    		}
    
    		//neues ereignis ins element 0
    		pmsg[0].hwnd = hwnd;
    		pmsg[0].message = message;
    		pmsg[0].wParam= wParam;
    		pmsg[0].lParam= lParam;
    
    		[b]cLines = min(cLines+1, cLinesMax);[/b]
    
    		//Anzeige um eine Zeile nach oben, es darf nicht das ganze gescrollt werden--> obere Zeile mussen bleiben
    		ScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);
    
    		break;  //->DefWindowProc -  sonst würden Systemtasten nicht mehr funktionieren
    
    	case WM_PAINT:
    		hdc=BeginPaint(hwnd, &ps);
    
    		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
    		SetBkMode(hdc, TRANSPARENT);
    		TextOut(hdc,0,0,szTop, lstrlen(szTop));
    		TextOut(hdc,0,0,szUnd, lstrlen(szUnd));
    
    		for(i=0; i<min(cLines, cyClient/cyChar - 1) ; i++)
    		{
    			[b]iType=pmsg[i].message == WM_CHAR ||
    				  pmsg[i].message == WM_DEADCHAR ||
    				  pmsg[i].message == WM_SYSCHAR ||
    				  pmsg[i].message == WM_SYSDEADCHAR;[/b]
    
    			[b]GetKeyNameText(pmsg[i].lParam, szKeyName, sizeof(szKeyName)/ sizeof (TCHAR));[/b]
    
    			TextOut(hdc, 0, (cyClient /cyChar - 1- i) * cyChar, szBuffer,
    				    wsprintf(szBuffer, [b]szFormat [iType],
    					         szMessage[pmsg[i].message - WM_KEYFIRST],
    							 pmsg[i].wParam,
    							 (PTSTR) (iType ? TEXT(" ") : szKeyName),
    							 (TCHAR) (iType ? pmsg[i].wParam : ' '),
    							 LOWORD (pmsg[i].lParam),
    							 HIWORD (pmsg[i].lParam) & 0xFF,
    							 0x01000000 & pmsg[i].lParam ? szYes  : szNo,
    							 0x20000000 & pmsg[i].lParam ? szYes  : szNo,
    							 0x40000000 & pmsg[i].lParam ? szDown : szUp,
    							 0x80000000 & pmsg[i].lParam ? szUp   : szDown));[/b]		}
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage (0);
    		return 0;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    

    nun habe ich da einige Probleme den zu verstehen.
    ich hoffe ihr könnt es mir erklären.

    -> die textstellen die ich net versteh hab ich fett gemacht und nochmal rauskopiert...

    1)static TCHAR *szFormat[2]= { TEXT ("%-13s %3d %-13s%c%6u %4d %4s %4s %8s %8s"),
    TEXT ("%-13s 0x%04X%1s%c %6u %4d %4s %4s %8s %8s") };

    hier das erste Porblem: was soll das? 😕 z.b das 0x04X.
    auch den Rest versteh ich hier net ganz

    1. cLines = min(cLines+1, cLinesMax);

    was sagt cLines aus?
    wieviel Zeilen im Moment dargestellt werden?

    3)iType=pmsg[i].message == WM_CHAR ||
    pmsg[i].message == WM_DEADCHAR ||
    pmsg[i].message == WM_SYSCHAR ||
    pmsg[i].message == WM_SYSDEADCHAR;

    was wird hier getan?
    wozu braucht es hier das == und das ||?

    4)GetKeyNameText(pmsg[i].lParam, szKeyName, sizeof(szKeyName)/ sizeof (TCHAR));

    was wird hier getan?
    sry MSDN versteh ich net wirklich...
    wird da lParam in die einzelnen Teile (OEM-Code, Context-Code ect.) zerlegt 😕

    szFormat [iType],
    szMessage[pmsg[i].message - WM_KEYFIRST],
    pmsg[i].wParam,
    (PTSTR) (iType ? TEXT(" ") : szKeyName),
    (TCHAR) (iType ? pmsg[i].wParam : ' '),
    LOWORD (pmsg[i].lParam),
    HIWORD (pmsg[i].lParam) & 0xFF,
    0x01000000 & pmsg[i].lParam ? szYes : szNo,
    0x20000000 & pmsg[i].lParam ? szYes : szNo,
    0x40000000 & pmsg[i].lParam ? szDown : szUp,
    0x80000000 & pmsg[i].lParam ? szUp : szDown));

    und hier versteh ich NUR Bahnhof.
    was solln die ganzn ":" und "&" und das 0xFF 0x01000000 und so weiter da???
    wär super wenn ihr mir das Zeile für Zeile erklärt...

    1. was ist PMSG?
      ein Zeiger auf MSG?

    2. wieso muss man break aufrufen, damit die Systemtasten wieder funktionieren?

    3. wenn ich die exe starten will kommt immer ein Fehler, dass das Prog. einen Fehler gefunden hat und beendet werden muss.
      woran liegt das?

    sry dass mein post so lange ist, hab aber eben ne menge frage.
    wär sehr nett wenn ihr mir weiterhelfen könntet.

    ntürlich bin ich auch froh wenn ihr mir nur einen Teil oder so beantwortet.
    alles in einmal wär doch ein bissele viel arbeit

    also vielen dank schon im voraus
    roozy



  • 1.) Schau dir mal die Beschreibung von wsprintf an...
    z.B. 0x%04X - das 0x ist einfach eine Zeichenkette die ausgegeben werden soll, und mit % wird dann eingeleitet, dass hier die entsprechende Variable aus dem wsprintf Ausruf dann eingesetzt werden soll (X sagt, dass der Wert als Hex-Code ausgegeben werden soll, die 4 steht für die Länge und die 0 gibt an, womit die Zahl ggf. aufgefüllt werden soll, falls sie z.B. nur 3 Ziffern lang ist

    2.) Wenn ich das richtig sehe, dann gibt cLines an, bis zu welcher Zeile der Bufer mit Nachrichten gefüllt ist.

    3.) hier wird in iType gespeichert, ob pmsg[i].message einer der Werte WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, oder WM_SYSDEADCHAR ist. == führt dazu die jeweiligen Vergleiche aus, die dann true oder false zurückliefern und mit || werden diese "Teil-Ergebnisse" dann oder verknüpft.

    4.) Hier wird zu pmsg[i].lParam die passende Bezeichnung der Tast geholt und in szKeyName gespeichert. Im 3. Parameter soll die Zeichenanzahl angegeben werden - dazu wird mit sizeof(szKeyName) die Anzahl Bytes ermittelt szKeyName belegt und durch sizeof (TCHAR) geteilt, weil ein Zeichen bei Unicode 2 Bytes lang ist und damit sizeof(szKeyName) alleine nicht das richtige Ergebis liefert (Anz. Bytes != Anz. Zeichen)

    5.) Mit & wird eine bitweise Und-Verknüpfung durchgeführt, das heißt es werden im Ergebniss all diejenigen Bits gesetzt, die sowohl im Parameter vor, als auch hinter dem Operator (&) gesetzt sind. 0x01000000 stellt hierbei die Bitmaske dar, die auf eine Variable angewandt wird. Nur wenn in dieser das in der Bitmaske angegebene Bit auch gesetzt ist, so wird das Bit im Ergebnis gesetzt.
    Man kann damit entweder bestimmte Bits aus einer Variable herausfiltern (und die nicht interessierenden auf 0 setzen), oder eben schauen ob bestimmte Bits gesetzt, d.h. 1 sind.

    0x40000000 & pmsg[i].lParam ? szDown : szUp
    Die ist eine Kurzschreibweise. Hierbei wird ein (boolscher) Ausdruck ausgewertet (der vor dem Fragezeichen) und in abhängigkeit davon der Code vor (im Falle dass der Ausdruck wahr ist) oder der hinter dem Doppelpunkt (falls unwahr) verwendet / ausgewertet.

    6.) Habe den Code mal ausgeführt und wenn du das ganze mit dem Debugger machst, bekommst du gesagt, dass der Fehler bei cLinesMax = cyClientMax/cyChar; erzeugt wird und zwar dadurch, dass cyChar 0 ist und deswegen durch 0 dividiert wird.



  • zu 6.) Habe gerade nochmal in meinem Petzold nachgeschaut und du scheinst falsch angetippt zu haben! Bei WM_DISPLAYCHANGE heißt es oberhalb von ReleaseDC nicht cyClient = tm.tmHeight; sondern cyChar = tm.tmHeight; 😉



  • hallo flenders.
    zu erst einmal danke für deine hilfe 👍

    zu 1) werd ich mir mal genauer anschauen, vill. muss ich dazu später noch ne frage stellen.

    ad 2)ok. hab ich verstanden

    ad 3)ist das dann so eine Art if, d.h wenn pmsg[i].message WM_CHAR ist , dann bekommt iType WM_CHAR zugeordnet???

    ad 4)verstanden

    ad 5)wie das mit den Bits funktioniert muss ich mir mal anschauen. davon hab ich keinen Tau. weißt du zufällig ein gutes Tut?
    wahrscheinlich stell ich dazu aber noch ne Frage, das scheint mir ist der komplizierteste Teil des Quelltextes

    ad 6)(jetzt 8, hab editiert) ok vielen Dank. diesen kleinen dummen Fehler 😉

    und noch mal ein rießiges Danke für deine Mühe.



  • roozy schrieb:

    ad 3)ist das dann so eine Art if, d.h wenn pmsg[i].message WM_CHAR ist , dann bekommt iType WM_CHAR zugeordnet???

    Nein, iType wird entweder 0 oder 1. Der Code besteht aus vier Vergleichen, die dann jeweils true oder false zurückliefern. Und diese Ergebnisse werden dann oder-verknüpft. iType ist also 1, wenn pmsg[i].message einer der Werte WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, oder WM_SYSDEADCHAR ist und sonst 0.

    roozy schrieb:

    ad 5)wie das mit den Bits funktioniert muss ich mir mal anschauen. davon hab ich keinen Tau. weißt du zufällig ein gutes Tut?

    Tutorial kenne ich leider keines, aber so kompliziert ist das eigentlich auch gar nicht. Eine Wert (z.B. ein char) besteht ja aus mehreren Bits (in diesem Falle 8). Die Bit-Operatoren(&, | und ~) sind eigentlich fast identisch mit den logischen Operatoren (&&, || und !). Der Unterschied ist eben nur, dass bei den logischen Operatoren immer nur zwei boolsche Werte miteinander verknüpft werden, während bei den Bit-Operatoren immer alle Bits einzeln miteinander verknüpft werden.

    1010 & 0110 => 0010 (nur die Bits gesetzt, die in beiden Werten gesetzt waren)
    1010 | 0110 => 1110 (alle Bits gesetzt, die in einem der beiden Eingangs-Werte gesetzt waren)
    ~1010 => 0101 (alle Bits die vorher auf 1 waren, sind nun 0 und umgekehrt)



  • also das mit dem iType hab ich jetzt auch verstanden.

    und das mit den Bits auch.
    hab auch ein wenig gegoogelt und dabei einige infos zu bytes und bits gefunden.

    jetzt probier ich herum, was in szBuffer alles gespeichert wird.
    hab auch schon viel rausgefunden und verstanden.

    nur folgende Zeile bereitet mir noch Probleme:

    szMessage[pmsg[i].message - WM_KEYFIRST]

    außerdem leuchtet mir nicht ganz ein wie man auf die zahlen kommt:
    HIWORD (pmsg[i].lParam) & 0xFF,
    0x01000000 & pmsg[i].lParam ? szYes : szNo,
    0x20000000 & pmsg[i].lParam ? szYes : szNo,
    0x40000000 & pmsg[i].lParam ? szDown : szUp,
    0x80000000 & pmsg[i].lParam ? szUp : szDown));



  • Hier mal ein Auszug aus der WinUser.h - vielleicht hilft er dir ein wenig für das Verständnis:

    #define WM_KEYFIRST                     0x0100
    #define WM_KEYDOWN                      0x0100
    #define WM_KEYUP                        0x0101
    #define WM_CHAR                         0x0102
    #define WM_DEADCHAR                     0x0103
    #define WM_SYSKEYDOWN                   0x0104
    #define WM_SYSKEYUP                     0x0105
    #define WM_SYSCHAR                      0x0106
    #define WM_SYSDEADCHAR                  0x0107
    

    Wie du sehen kannst sind die Konstanten fortlaufend durchnummeriert. Wenn man nun von pmsg[i].message WM_KEYFIRST abzieht erhält man also für diese Messages einen Wert zwischen 0 (WM_KEYDOWN) und 7 (WM_SYSDEADCHAR), welcher dann als Index für das String-Array szMessage verwendet wird, in dem zu genau diese Konstanten jeweils ihre Textform als String hinterlegt ist.

    Zu den Zahlen: z.B. hier kannst du sehen, in welchen Bits welche Information abgelegt ist. Diese Zahlen sind nur die entsprechende Hexadezimal-Darstellungen davon...
    Eine Ziffer fasst dabei immer 4 Bits zusammen:

    0000 => 0
    0001 => 1
    0010 => 2
    0100 => 4
    1000 => 8

    0xFF => 1111 1111 (filtert also die unteren 8 Bits heraus)



  • aha.
    interessant.

    vielend danke flenders, jetzt hab ich alles gut verstanden 👍 👍

    mfg
    roozy



  • da ich nicht ein neues thema aufmachen wollte frag ich jetzt hier.

    also:

    static PMSG pmsg;
    	static TCHAR *szText="Tastendruck darstellen";
    	static TCHAR szBuffer[128];
    	static TCHAR szKeyName[32];
    
    	switch(message)
    	{
    	case WM_CREATE:
    		return 0;
    	case WM_KEYDOWN:
    		hdc=GetDC(hwnd);
                      pmsg.lParam=lParam;
    		GetKeyNameText(pmsg->lParam, szKeyName, sizeof(szKeyName) /sizeof (TCHAR));
    		TextOut(hdc, 20,20, szBuffer,wsprintf(szBuffer, "%s", szKeyName));
    
    		ReleaseDC(hwnd,hdc);
    		return 0;
    	case WM_PAINT:
    		hdc=BeginPaint(hwnd, &ps);
    		TextOut (hdc, 0, 0, szText, lstrlen(szText));
    
    		EndPaint(hwnd, &ps);
    		return 0;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    
    }
    

    wenn ich das Programm laufen lass wird es immer mit einem Fehler unterbrochen, sobald ich eine Taste drücke...
    wieso, und was muss ich anders machen?
    (ich möchte die eingegebene Taste dastellen...

    1. wieso muss ich hier über "->" zugreifen
      im obigen quelltext muss ich das doch auch net?


  • Das liegt daran, dass deine Zeigervariable pmsg
    Siehe hier:

    // Definition:
    static PMSG pmsg;
    // Verwendung in deinem WM_KEYDOWN-Handler:
    pmsg.lParam=lParam;
    

    vorher nicht mit einem gültigen Speicherbereich initialisiert wurde.
    Du kannst entweder mit new/malloc Speicher allokieren, oder du verzichtest
    hier vollständig auf den Zeiger (meines Erachtens die bessere Wahl 😉 ).



  • aha.

    und wie weiß ich ihr zuerst einen gültigen Speicherbereich zu?



  • pmsg = new MSG;
    

    oder eben gar keinen Pointer sondern so:

    MSG msg;
    

    Wenn du einen Pointer hast greifst du mit -> auf die Member zu, wenn du direkt eine Instanz / Struktur / etc. hast, dann nimmst du dazu .

    Wobei ich bei deinem Beispielcode nicht so ganz verstehe, wozu du das mit der MSG-Struktur überhaupt machst und nicht gleich so:

    GetKeyNameText(lParam, szKeyName, sizeof(szKeyName) /sizeof (TCHAR));
    

    Zeichnen sollte man außerdem normalerweise nur in WM_PAINT...



  • ich machs mit dem pointer, damit ich das ein bisschen üben kann,
    wie du siehst bin ich da noch ziemlich unsicher...

    wenn ich das mit new mach kommen folgende 4 Fehler/Warnungen:

    C:\Programme\Microsoft Visual Studio\MyProjects\WinApi\Keylogger_v1\Keylogger_v1.c(56) : error C2065: 'new' : nichtdeklarierter Bezeichner
    C:\Programme\Microsoft Visual Studio\MyProjects\WinApi\Keylogger_v1\Keylogger_v1.c(56) : warning C4047: '=' : Anzahl der Dereferenzierungen bei 'struct tagMSG *' und 'int ' unterschiedlich
    C:\Programme\Microsoft Visual Studio\MyProjects\WinApi\Keylogger_v1\Keylogger_v1.c(56) : error C2146: Syntaxfehler : Fehlendes ';' vor Bezeichner 'MSG'
    C:\Programme\Microsoft Visual Studio\MyProjects\WinApi\Keylogger_v1\Keylogger_v1.c(56) : error C2275: "MSG" : Ungültige Verwendung dieses Typs als  Ausdruck
            c:\programme\microsoft visual studio\vc98\include\winuser.h(1236) : Siehe Deklaration von 'MSG'
    

    ich machs so:

    static PMSG pmsg;
    //anderer Quelltext
    pmsg= new MSG;
    pmsg->lParam=lParam;
    //ausgabe:
    GetKeyNameText(pmsg->lParam, szKeyName, sizeof(szKeyName) /sizeof (TCHAR));
    TextOut(hdc, 20,20, szBuffer,wsprintf(szBuffer, "%s", szKeyName));
    


  • flenders schrieb:

    Wenn du einen Pointer hast greifst du mit -> auf die Member zu, wenn du direkt eine Instanz / Struktur / etc. hast, dann nimmst du dazu .

    Ergänzend: Das wär auch noch möglich:

    PMSG pMsg = new MSG;
    (*pMsg).lParam = ...;
    // oder wie flenders schon gesagt hat, mit dem Pfeiloperator:
    pMsg->lParam = ...;
    

    Aber der Pfeiloperator bietet sich natürlich mehr als an :p .

    PS: Was hast du für einen Compiler ?



  • ich hab Microsoft Visual C++

    wieso?



  • roozy schrieb:

    ich hab Microsoft Visual C++

    Hm weil ich mich Frage, warum er 'new' nicht kennt...
    Welche Version ?



  • 6.0



  • Oh, jo ist klar....Fällt mir jetzt erst auf:
    Du musst, wenn du C++ verwendest, als Dateiendung '*.cpp' angeben. Oder du benutzt malloc ( ➡ memory allocation). Das wäre C, ist aber natürlich 'etwas veraltet' 😉 .



  • ich tippe, du hast eine Datei .c und nicht .cpp und da new eben C++ ist musst du dem Compiler auch sagen, dass du das als C++ kompilieren willst (oder du verwendest eben malloc) 🙄

    edit: zu langsmam...



  • 4 Sekunden 😃 ...


Anmelden zum Antworten