unit MainUnit; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.WebBrowser, Winapi.WebView2, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo, FMX.Edit, FMX.StdActns, // FMX.Edit für TEdit, FMX.StdActns oft für Standardaktionen // Korrekte und vollständige Units System.IOUtils, System.Threading, System.Diagnostics, Winapi.Windows, LLVM.Runner; const // Pfad zur HTML-Datei (BITTE HIER ANPASSEN!) PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; // <--- PRÜFEN UND ANPASSEN! // Pfade zu den LLVM-Werkzeugen (BITTE HIER ANPASSEN!) CLANG_PATH = 'clang.exe'; // <--- PRÜFEN UND ANPASSEN! LLD_PATH = 'lld-link.exe'; // <--- PRÜFEN UND ANPASSEN! type TMainForm = class( TForm ) WebBrowser1: TWebBrowser; SaveWorkspaceButton: TSpeedButton; GenerateCodeButton: TSpeedButton; Memo: TMemo; InputEdit: TEdit; // <--- Hinzugefügt (Optional für Beschriftung des InputEdit) procedure FormCreate( Sender: TObject ); procedure FormDestroy( Sender: TObject ); procedure RefreshButtonClick( Sender: TObject ); procedure SaveWorkspaceButtonClick( Sender: TObject ); procedure GenerateCodeButtonClick( Sender: TObject ); procedure WebBrowser1DidFinishLoad( ASender: TObject ); private { Private declarations } FWebView: ICoreWebView2; FWebMessageReceiver: ICoreWebView2WebMessageReceivedEventHandler; FWebMessageReceivedToken: EventRegistrationToken; procedure HandleWebMessage( const AMessage: string ); function ExecuteProcess( const ACommand: string; const AParameters: string ): string; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.fmx} type TWebMessageReceiver = class( TInterfacedObject, ICoreWebView2WebMessageReceivedEventHandler ) private FOwner: TMainForm; public constructor Create( AOwner: TMainForm ); function Invoke( const Sender: ICoreWebView2; const args: ICoreWebView2WebMessageReceivedEventArgs ): HResult; stdcall; end; constructor TWebMessageReceiver.Create( AOwner: TMainForm ); begin inherited Create; FOwner := AOwner; end; function TWebMessageReceiver.Invoke( const Sender: ICoreWebView2; const args: ICoreWebView2WebMessageReceivedEventArgs ): HResult; var message: PWideChar; begin Result := args.TryGetWebMessageAsString( message ); if Succeeded( Result ) then begin TThread.Queue( nil, procedure begin FOwner.HandleWebMessage( string( message ) ); end ); end; Result := S_OK; end; procedure TMainForm.FormCreate( Sender: TObject ); begin TLLVMRunner.Log := Memo.Lines; // Optional: Standardwert für InputEdit InputEdit.Text := '123'; end; procedure TMainForm.FormDestroy( Sender: TObject ); begin TLLVMRunner.Log := nil; if ( FWebView <> nil ) and ( FWebMessageReceivedToken.value <> 0 ) then begin FWebView.remove_WebMessageReceived( FWebMessageReceivedToken ); end; end; function TMainForm.ExecuteProcess( const ACommand: string; const AParameters: string ): string; var sa: TSecurityAttributes; si: TStartupInfo; pi: TProcessInformation; stdOutRead, stdOutWrite: THandle; success: Boolean; cmdLine: string; buffer: TBytes; bytesRead: Cardinal; output, errors: string; begin // Pipe für StdOut und StdErr erstellen sa.nLength := SizeOf( TSecurityAttributes ); sa.lpSecurityDescriptor := nil; sa.bInheritHandle := True; // Für stdout und stderr wird dieselbe Pipe verwendet, da clang Fehler auf stdout ausgibt if not CreatePipe( stdOutRead, stdOutWrite, @sa, 0 ) then Exit( 'Error creating pipe.' ); try // StartupInfo vorbereiten FillChar( si, SizeOf( TStartupInfo ), 0 ); si.cb := SizeOf( TStartupInfo ); si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW; si.wShowWindow := SW_HIDE; // Fenster explizit verstecken si.hStdInput := GetStdHandle( STD_INPUT_HANDLE ); si.hStdOutput := stdOutWrite; si.hStdError := stdOutWrite; // Leite stderr auf dieselbe Pipe wie stdout cmdLine := Format( '"%s" %s', [ACommand, AParameters] ); success := CreateProcess( nil, PChar( cmdLine ), nil, nil, True, CREATE_NO_WINDOW, nil, nil, si, pi ); // Schreib-Handle der Pipe sofort schließen CloseHandle( stdOutWrite ); if not success then Exit( Format( 'CreateProcess failed. Code: %d', [GetLastError] ) ); try output := ''; // Warten, bis der Prozess fertig ist und alle Daten in die Pipe geschrieben hat if WaitForSingleObject( pi.hProcess, 5000 ) = WAIT_TIMEOUT then // 5s Timeout begin TerminateProcess( pi.hProcess, 1 ); Exit( 'Process timed out.' ); end; // Bytes aus der Pipe lesen repeat SetLength( buffer, 1024 ); bytesRead := 0; if ReadFile( stdOutRead, buffer[0], Length( buffer ), bytesRead, nil ) and ( bytesRead > 0 ) then begin SetLength( buffer, bytesRead ); // Gelesene Bytes mit dem Default-System-Encoding in einen String umwandeln output := output + TEncoding.Default.GetString( buffer ); end else begin break; // Pipe ist leer oder Fehler end; until False; finally CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); end; Result := output; finally CloseHandle( stdOutRead ); end; end; procedure TMainForm.RefreshButtonClick( Sender: TObject ); begin WebBrowser1.Navigate( PATH_BLOCKLY ); end; procedure TMainForm.SaveWorkspaceButtonClick( Sender: TObject ); begin WebBrowser1.EvaluateJavaScript( 'saveWorkspace()' ); end; procedure TMainForm.GenerateCodeButtonClick( Sender: TObject ); begin WebBrowser1.EvaluateJavaScript( 'generateAndPostCode()' ); end; procedure TMainForm.HandleWebMessage( const AMessage: string ); var llFile, objFile, dllFile: string; compilerOutput: string; success: Boolean; InputInteger: Integer; // <--- Hinzugefügt begin Memo.Lines.Clear; Memo.Lines.Add( 'LLVM-Code empfangen. Starte Kompilierung...' ); Memo.Lines.Add( AMessage ); Memo.Lines.Add( '--------------------' ); // Wert aus dem InputEdit-Feld holen try InputInteger := StrToIntDef( InputEdit.Text, 0 ); // Standardwert 0, falls ungültig except InputInteger := 0; Memo.Lines.Add( 'WARNUNG: Ungültige Eingabe im InputEdit-Feld. Verwende 0.' ); end; TTask.Run( procedure var llFileLocal, objFileLocal, dllFileLocal: string; // Lokale Variablen für den Task compilerOutputLocal: string; successLocal: Boolean; begin llFileLocal := TPath.Combine( TPath.GetTempPath, 'poc.ll' ); objFileLocal := TPath.ChangeExtension( llFileLocal, '.obj' ); dllFileLocal := TPath.ChangeExtension( llFileLocal, '.dll' ); successLocal := False; try TFile.WriteAllText( llFileLocal, AMessage ); TThread.Queue( nil, procedure begin Memo.Lines.Add( 'Kompiliere zu Objektdatei...' ); end ); compilerOutputLocal := ExecuteProcess( CLANG_PATH, Format( '-c "%s" -o "%s"', [llFileLocal, objFileLocal] ) ); TThread.Queue( nil, procedure begin Memo.Lines.Add( compilerOutputLocal ); end ); if not TFile.Exists( objFileLocal ) then begin TThread.Queue( nil, procedure begin Memo.Lines.Add( 'FEHLER: Kompilierung fehlgeschlagen. .obj-Datei nicht erstellt.' ); end ); Exit; end; TThread.Queue( nil, procedure begin Memo.Lines.Add( 'Linke zu DLL...' ); end ); compilerOutputLocal := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFileLocal, dllFileLocal] ) ); TThread.Queue( nil, procedure begin Memo.Lines.Add( compilerOutputLocal ); end ); if TFile.Exists( dllFileLocal ) then begin successLocal := True; TThread.Queue( nil, procedure begin Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFileLocal] ) ); end ); end else begin TThread.Queue( nil, procedure begin Memo.Lines.Add( 'FEHLER: Linken fehlgeschlagen. .dll-Datei nicht erstellt.' ); end ); end; finally if TFile.Exists( llFileLocal ) then TFile.Delete( llFileLocal ); if TFile.Exists( objFileLocal ) then TFile.Delete( objFileLocal ); if not successLocal and TFile.Exists( dllFileLocal ) then TFile.Delete( dllFileLocal ); end; TThread.Queue( nil, procedure begin Memo.Lines.Add( '---------------------------------------------------------' ); Memo.Lines.Add( 'DLL wird aufgerufen:' ); if TFile.Exists( dllFileLocal ) then begin var Runner := TLLVMRunner.Create( dllFileLocal ); try Runner.Execute( InputInteger ); // <--- HIER DEN INPUT-PARAMETER ÜBERGEBEN finally Runner.Free; end; end; Memo.Lines.Add( 'DLL beendet.' ); Memo.CaretPosition := TCaretPosition.Create( Memo.Lines.Count-1, 1 ); end ); end ); end; procedure TMainForm.WebBrowser1DidFinishLoad( ASender: TObject ); begin if Assigned( FWebMessageReceiver ) then Exit; if Supports( WebBrowser1, ICoreWebView2, FWebView ) then begin FWebMessageReceiver := TWebMessageReceiver.Create( Self ); FWebView.add_WebMessageReceived( FWebMessageReceiver, FWebMessageReceivedToken ); end; end; end.