Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von Floste
Thema: "Echten" Dateinamen über HTTPWebresponse auslesen
Am im Forum: Web-Technologien

Eigendlich ist es doch immer das gleiche:

Die header der response angucken und prüfen ob "Content-Disposition" vorhanden ist.
Wenn vorhanden: Dateinamen aus dem wert des headers auslesen: Z.B. sollte die zweite gruppe des matches von folgendem regex den dateinamen enthalten (ignorecase setzen): "(^|;) *filename *=([^;]+)"
Wenn so kein dateiname gefunden wurde, dann ist der dateiname der teil von HttpWebResponse.ResponseUri.AbsolutePath, der nach dem letzten schrägstrich kommt.

Thema: Tool, um Exe im Ram auszuführen => Löschen und Überschreiben währenddessen möglich
Am im Forum: Projekte

Bei mir geht es auch mit den dlls.
Allerdings führe ich den shadowrunner auch nicht im anwendungsverzeichnis aus, daran ligt es wahrscheinlich. Ich gucks mir später genauer an.

Thema: Internetkommunikation zu einer anderen Anwendung: Geht das ohne Webserver?
Am im Forum: Netzwerktechnologien

Klare Antwort: Jain

Man kann direkte Verbindungen über das Internet herstellen, wenn auf beiden Computern gleichzeitig ein geeignetes Programm läuft und
- entweder einer der Computer keine Firewall hat und nicht hinter einem Nat-Router ist (esseiedenn es wurden entsprechende Regeln definiert, dass es dennoch geht) und der andere die Addresse kennt (also quasi ein Server)
- oder beide Computer gleichzeitig von beiden Enden aus auf vorher festgelegten Ports eine Verbindung aufbauen (erfordert einen Vermittler).

Du musst also in jedem Falle einen Weg haben, wie der eine Computer die Addresse des anderen bekommt.
Wenn kein Router/Firewall im Weg ist:
Das einfachste aber für den Benutzer unfreundlichste ist es, den Benutzer die IPs herausfinden zu lassen. Ansonsten kann man soetwas wie dyndns einsetzen. Alternativ kann man irgendwo auf einem Webspace eine Datei oder ein Script platzieren.

Thema: Silverlight Dienste nur für mein Projekt erlauben
Am im Forum: Netzwerktechnologien

Erschweren ist möglich, aber ganz verhindern kann man es nicht.

Thema: Javascript Analyzer Tool
Am im Forum: Web-Technologien

Ich bin zwar kein profi in sowas, aber so würde ich an die sache rangehen:
1.) Seite mittels browser komplett auf der platte speichern
3.) http://jsbeautifier.org/
2 ) Texteditor mit syntax-highlighting nehmen und variablennamen erraten+ersetzen
4.) Firebug und bei bedarf evl noch Wireshark
5.) Geduld

Du könnetst auch einen blick auf http://jsunpack.jeek.org/ riskieren. ansonsten: Google

Thema: Tool, um Exe im Ram auszuführen => Löschen und Überschreiben währenddessen möglich
Am im Forum: Projekte

Hab das für n kleines Projekt mit etlichen Dateien, die ins Ausgabeverzeichnis kopiert werden gemacht. So nach dem Motto: Mal eben schnell Erstellen drücken (um die Dateien neu zu kopieren), aktualisieren und gucken, wies aussieht, wenn der Knopf unten rechts 5 Pixel schmaler ist. Problem is blolß, dass das normalerweise fehlschlägt, solange das Programm läuft.

Zitat
man muss also das Programmm schon vorher anders starten, falls mans evtl. überschreiben mag?
Ja, es wird das Ladeprogramm gelockt, anstatt des eigendlichen Programms.

Thema: Tool, um Exe im Ram auszuführen => Löschen und Überschreiben währenddessen möglich
Am im Forum: Projekte

Wer kennt das nicht: Man will mal schnell ne Exe überschreiben oder löschen, ohne gleich das dazugehörige Programm zu beenden.
Desahlb habe ich mal ein kleines Programm zusammengehackt, welches eine Exe samt Dlls mittels Assembly.Load(byte[]) lädt und dann ausführt. Der erste Parameter ist die zu ladende Exe-datei, danach folgen die zu übergebenden Parameter.
MIT-Lizenz

Mit WPF-Anwendungen gibts anscheinend Probleme beim Laden von Resourcen.

Vielleicht kanns ja trotzdem jemand gebrauchen^^ Wer das Konsolenfenster loswerden will, der kann einfach in den Projekteigenschaften auf Windows-Anwendung umstellen.

Thema: Humor: Java Programmierer ... und andere lustige Sachen
Am im Forum: Smalltalk

[EDIT] Jetzt auch auf youtube YouTube: Wissenschaftsjournalistischer Unsinn über Wasser
Grade auf 3sat in nano gesehen:

Thema: Prozess stirbt ohne Fehlermeldung o.ä.
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Mein bauchgefühl sagt mir , dass es probleme mit unsafe/unmanaged code und falschen speicherzufriffen sein könnten.

Thema: Schnelles kopieren eines Byte[]
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Wenn die Daten an einem stück liegen ist das sicherleich die beste möglichkeit, aber wenn (wovon ich ausgegangen bin) die daten in mehreren häppchen fragmentiert sind, dann wird das sehr schnell sehr unhandlich.

Thema: Schnelles kopieren eines Byte[]
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

    [Jede Menge Sicherheitschecks...]
    if (count > 0) {
        // Call our faster version of memmove, not the CRT one.
        m_memmove(dst->GetDataPtr() + dstOffset, src->GetDataPtr() + srcOffset, count);
    }

[Deutlich mehr Abfragen und Sicherheitschecks]
if (r == AssignWillWork)
        {
            src = (BYTE*)gc.pSrc->GetDataPtr();
            dst = (BYTE*)gc.pDst->GetDataPtr();
            size = gc.pSrc->GetMethodTable()->GetComponentSize();
            g_IBCLogger.LogMethodTableAccess(gc.pSrc->GetMethodTable());
            m_memmove(dst + ((m_iDstIndex - destLB) * size), src + ((m_iSrcIndex - srcLB) * size), m_iLength * size);
            if (gc.pDst->GetMethodTable()->ContainsPointers())
            {
                GCHeap::GetGCHeap()->SetCardsAfterBulkCopy( (Object**) (dst + (m_iDstIndex * size)), m_iLength * size);
            }        
        }

Beide methoden benutzen "Reflection" (wenn auch in der nativen=schnelleren Variante), um zu prüfen, ob die Operation gültig ist.

Ich denke RtlMoveMemory könnte schneller sein, weil die ganzen Abfragen zur Laufzeit wegfallen.

Ansonsten gibts an platformunabängigen sachen noch cpblk, welches aber äußerst umständlich zu benutzen ist, oder man kann was eigenes mit unsafe schreiben, was dann aber tendenziell eher langsamer ist.

Fazit: Buffer.BlockCopy ist bequem und relativ schnell, ansonsten probier mal RtlMoveMemory.

Dennoch interressiert es mich, ob es nicht einen besseren weg gäbe dein Ziel zu erreichen, als massenweise Bytearrays umzokopieren. Dazu musst du aber mal den Anwendungsfall nennen!

Thema: GlSl Shader für statische/dynamische Lichtquellen
Am im Forum: Grafik und Sound

Zitat
eine routine für jedes erdenkliche Licht
Nein, eine für jeden lichttyp (ambient, punktlicht, paralleles licht und spotlight sind üblich)
Ambient kann man getrost seperat betrachten, ist einfach nur ein farbwert, der dem shader übergeben und überall hinzugefügt wird.
Paralleles licht wird meißt nur von der Sonne ausgestrahlt. Wenn dies der fall ist, dann kann man es getrennt betrachten.
Bleiben noch die sonst verstreuten lichter:
Definier mal ein struct, das alle eigenschaften einer dynamischen lichtquelle (also punktlicht und spotlight) enthält, inclusive dem typ. Eine instanz pro lichtquelle wird zur laufzeit vom hauptprogramm erzeugt und dem shader übergeben.

Wenn man realistische schatten will, muss man shadowmapping einsetzen.
Zitat
Wassereffekte
Dafür rendert man die szene mit manipulierter viewmatrix (so dass die spiegelung passt) zuerst auf eine textur und die textur übergibt man dem sahder, der das wasser rendert.

Thema: 8-Bit Sound mit Console.Beep
Am im Forum: Grafik und Sound

Hmm, nette idee.
Zur deiner konkreten umsetzung: Irgendwie habe ich das gefühl, dass du vorher mit c/c++ gearbeitet hast^^
Schau dir mal folgendes an:
BinaryWriter, Encoding.ASCII

Außerdem kann man von der klasse Stream erben, anstatt einen memorystream zu nehmen.

Thema: 8-Bit Sound mit Console.Beep
Am im Forum: Grafik und Sound

Ich hab einfach mal den code leicht modifiziert, den ich hier schon gepostet hatte im programmier-spiel. Man kann die in dem blog angegebenen formeln 1:1 übernehmen.

    class Program
    {
        const int samplerate = 8000;//8 khz

        private static byte Function(int t)
        {
            return (byte)(t * (((t >> 12) | (t >> 8)) & (63 & (t >> 4))));
        }

        static void Main(string[] args)
        {
            int t=0;

            //Setup OpenAl:
            IntPtr device = ALC.OpenDevice(null);
            IntPtr ctx = ALC.CreateContext(device, null);
            ALC.MakeContextCurrent(ctx);

            uint source = AL.GenSource();
            byte[] samples = new byte[1024];
            uint[] buffers = AL.GenBuffers(10);
            int iCurrentBuffer = 0;
            //Status of sine wave:
            double rotangle = 0, rotangle2 = 0;
            while (true)
            {
                //Get information:
                int numQueued = AL.GetSourcei(source, AlSourceInts.Buffers_Queued);
                int numProcessed = AL.GetSourcei(source, AlSourceInts.Buffers_Processed);
                int numRemaining = numQueued - numProcessed;
                //Take the processed buffers from the queue:
                if (numProcessed > 0) AL.SourceUnqueueBuffers(source, numProcessed);

                if (numRemaining ≥ buffers.Length)//Nothing to do?
                {
                    System.Threading.Thread.Sleep(5);//Then wait a bit
                    continue;//and try again
                }
                //Calculate the new samples:
                for (int i = 0; i < samples.Length; i++)
                {
                    samples[i] = Function(t++);
                }
                //queue the samples
                AL.BufferData(buffers[iCurrentBuffer], AlAudioFormat.Mono8Bit, samples, sizeof(byte) * samples.Length, samplerate);
                AL.SourceQueueBuffer(source, buffers[iCurrentBuffer]);
                if (++iCurrentBuffer ≥ buffers.Length) iCurrentBuffer = 0;//increment iBuffer
                //play if the source is not already playing
                if ((AlSourceState)AL.GetSourcei(source, AlSourceInts.Source_State) != AlSourceState.Playing)
                    AL.SourcePlay(source);
            }
        }
    }

Die bindings für openal (welches übrigends installiert sein muss) hatte ich hier gepostet.

Thema: GlSl Shader für statische/dynamische Lichtquellen
Am im Forum: Grafik und Sound

Ich habe zwar nur wenig erfahrung mit glsl, aber ich antworte einfach trotzdem mal:
Ersteinmal möchte ich euch zum shaderentwickeln in glsl das kostenlose programm rendermonkey von ati ans herz legen. Von nvidea gibts noch fxcomposer, welcher zwar besser zu bedienen ist, aber eher auf directx/hlsl ausgelegt ist.
Dann schreibt am besten ersteinmal nen shader, der für genau eine lichtquelle funktioniert.

Für den einstieg kann man sich im internet belesen: Tutorial glsl2: Per-Pixel-Lighting
(Auf der DGL Wiki -Tutorials gibts noch weitere tutorials)

Wenn es für eine lichtquelle klappt, kann man etweder aus den uniform-variablen statische arrays machen oder die parameter als textur übergeben. Letzteres ist zwar ein bisschen flexibler, aber auch ziemlicher frickelkram.

Beim rendern immer die lichtquellen nach bedeutung für das aktuelle objekt sortieren und dem shader nur die wichtigste(n) übergeben.

Thema: VS Express: Debugger zeigt auftretende/unbehandele Exception nicht an
Am im Forum: Entwicklungs- und Laufzeitumgebung (Infrastruktur)

Ich bekomm da folgendes:

Fehler
Das Objekt des Typs "System.Object" kann nicht in Typ "System.IConvertible" umgewandelt werden.

Probier mal folgendes:


            try
            {
                Convert.ToInt32(new object());
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.ToString(),ex.GetType().Name);
            }

Thema: fremde (nicht-standard-)textbox auslesen
Am im Forum: Rund um die Programmierung

Es wäre vielleicht hilfreich, wenn du uns den namen des chatclients, sowie das verwendete chatprotokoll (irc,icq,skype,etc) und dein eigendliches vorhaben (einzelne nachrichten lesen oder ein kompletter bot oder was? ) nennen würdest. Für viele protokolle gibt es bibliotheken (z.B. für irc) und für viele clients gibt es addins (z.B. skype ermöglicht addins)

Thema: Fremdes Fenster verstecken
Am im Forum: GUI: Windows-Forms

Siehe ShowWindow Function:

Zitat
SW_HIDE
0
Hides the window and activates another window.

Thema: Kommunikation per JSON Strings: Attempted to read or write protected memory
Am im Forum: Rund um die Programmierung

Ich habe eine idee, wie dein fehler kommt.

1.) Deine bibliothek muss eine free-funktion zur verfügung stellen!

extern "C" __declspec(dllexport) char*  __stdcall StringReturnAPI01()
{
....
}

extern "C" __declspec(dllexport) void __stdcall FreeString(char* string)
{
   //todo
}
2.) char* in c++ ist immer Ansi und CharSet.Auto wird standardmäßig unicode. Unicodestrings werden mit 2 nullbytes beendet. Ansi-strings haben aber nur 1 nullbyte. Deshalb wird über das Ende hinaus gelesen und du kiregst ne Access Violation.
3.) Wenn du kein memoryleak haben willst musst du das mashalling eh selber machen.


        [DllImport(@"Pfad zu C++ dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr StringReturnAPI01();


        [DllImport(@"Pfad zu C++ dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void FreeString(IntPtr ptr);


IntPtr lpstr=StringReturnAPI01();
string result=Marshal.PtrToStringAnsi(lpstr);
FreeString( lpstr);

Thema: Kommunikation per JSON Strings: Attempted to read or write protected memory
Am im Forum: Rund um die Programmierung

Zitat

 [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string StringReturnAPI01();
Nur mal so aus neugierde: Wie sorgst du dafür, dass der speicher für den LPStr wieder freigegeben wird? Wie allokierst du ihn?

Benutzt du json ausschließlich für interop? Ist eher ungewöhnlich aber ok.

Thema: Fehler in GetHashCode im Framework?
Am im Forum: Rund um die Programmierung

Ich kann deine erkenntnisse nur bestätigen. Allerdings weist m$ dieser sorte von bugs gerne ein won'tfix zu, da es sonst zu kompatibilitätsproblemen kommt. Workaround ist halt, dass man überall GetHashCode so implementiert, wie mans grade braucht.

Thema: Suchmaschine liefert für Passwort-Hash den Klartext
Am im Forum: Smalltalk

Es gibt auch extra hash-suchmaschinen (z.B. Md5 Suchmaschine, LM / NTLM Decrypter tool)
Ntlm-passwörter (für die windows-anmeldung) kann man übrigends unmöglich sicher bekommen ganz egal wie lang man sie macht. Das passwort wird erst in teile von 7 zeichen länge zerstückelt und dann jeder teil einzeln gehasht. (siehe ophcrack, cain&abel, etc ...)

@ mod: Lass doch mal meine links in ruh...

Thema: GolbalKeyboardHook: Delegat wird vom GC abgeräumt
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat
Zudem stellt ein GCHandle auch sicher, dass der GC das Objekt nicht umkopiert sprich der Pointer ungültig wird.
Das problem stellt sich garnicht erst, da code (marshalling-stub) und daten (mit dem pointer auf den code) in seperaten heaps liegen. Der stub bleibt also so oder so wo er is...

Thema: GolbalKeyboardHook: Delegat wird vom GC abgeräumt
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

GC.KeepAlive kann man getrost übersetzen als: Mach absolut garnichts, aber tu dabei so, alsob du den parameter verwenden würdest => Keinerlei effekt hier

Was ich meine ist:

hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
wird implizit umgewandelt in

hhook = SetWindowsHookEx(WH_KEYBOARD_LL, new globalKeyboardHook.keyboardHookProc(this.hookProc), hInstance, 0);
Man beachte das new.
Du musst nichts weiter tun, als den delegaten zu speichern und das genau so lange, bis du den hook wieder mit UnhookWindowsHookEx entfernst:

this.gepeicherterDeleagte=new globalKeyboardHook.keyboardHookProc(this.hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, this.gepeicherterDeleagte, hInstance, 0);
Es ist zwar eigendlich unnötig, aber wenn du lustig bist kannst nach der zeile mit UnhookWindowsHookEx noch GC.KeepAlive(this.gepeicherterDeleagte); einfügen.

Thema: GolbalKeyboardHook: Delegat wird vom GC abgeräumt
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Ja dein problem ist diese zeile:

hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);

Genauer gesagt wird für den parameter hookProc implizit ein delegate erstellt und dieser wird dann vom gc eingesackt...
Erstell den delegaten explizit und speicher ihn in nem member.

Thema: Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
Am im Forum: Smalltalk

Wirklich glücklich bin ich nicht:
Mein PC mag es irgendwie nicht, dass du die bufferlängen auswürfelst und spielt deshalb keinen ton ab.
Deine Schleifenlogik ist auch recht ulkig:

Wenn mehr als 0 buffer verarbeitet wurden, dann:
1. Nimm 0 (=keine) buffer aus der queue
2. Befülle genau einen buffer mit daten (ganz egal wieviele buffer gespielt wurden)
3. Packe den nachfolger dieses buffers in die warteschlange

Außerdem lastest du einen kern zu 100% aus.

Ich poste hier einfach mal meine lösung und wer will kann eine neue aufgabe stellen.

using System;
using OpenAL;

namespace ALSynthesizer
{
    class Program
    {
        const int samplerate = 44100;
        const double frequency = 1000;
        const double frequency2 = 0.5;

        static void Main(string[] args)
        {
            //Setup OpenAl:
            IntPtr device = ALC.OpenDevice(null);
            IntPtr ctx = ALC.CreateContext(device, null);
            ALC.MakeContextCurrent(ctx);

            uint source = AL.GenSource();
            short[] samples = new short[1024];
            uint[] buffers = AL.GenBuffers(10);
            int iCurrentBuffer = 0;
            //Status of sine wave:
            double rotangle = 0, rotangle2 = 0;
            while (true)
            {
                //Get information:
                int numQueued = AL.GetSourcei(source, AlSourceInts.Buffers_Queued);
                int numProcessed = AL.GetSourcei(source, AlSourceInts.Buffers_Processed);
                int numRemaining = numQueued - numProcessed;
                //Take the processed buffers from the queue:
                if (numProcessed > 0) AL.SourceUnqueueBuffers(source, numProcessed);

                if (numRemaining ≥ buffers.Length)//Nothing to do?
                {
                    System.Threading.Thread.Sleep(5);//Then wait a bit
                    continue;//and try again
                }
                //Calculate the new samples:
                for (int i = 0; i < samples.Length; i++)
                {
                    rotangle += frequency * ((Math.PI * 2) / samplerate);
                    if (rotangle > Math.PI * 2) rotangle -= Math.PI * 2;

                    rotangle2 += frequency2 * ((Math.PI * 2) / samplerate);
                    if (rotangle2 > Math.PI * 2) rotangle2 -= Math.PI * 2;
                    samples[i] = (short)(Math.Sin(rotangle) * Math.Sin(rotangle2) * short.MaxValue);
                }
                //Queue the samples
                AL.BufferData(buffers[iCurrentBuffer], AlAudioFormat.Mono16Bit, samples, sizeof(short) * samples.Length, samplerate);
                AL.SourceQueueBuffer(source, buffers[iCurrentBuffer]);
                if (++iCurrentBuffer ≥ buffers.Length) iCurrentBuffer = 0;//Increment iBuffer
                //Play if the source is not already playing
                if ((AlSourceState)AL.GetSourcei(source, AlSourceInts.Source_State) != AlSourceState.Playing)
                    AL.SourcePlay(source);
            }
        }
    }
}

Thema: Boxing/Unboxing: Es wird in Büchern immer erwähnt, aber welchen Vorteil bringt es mit sich?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Man braucht boxing für virtuelle methoden.
Z.B. folgendes:


string.Format("{0} hat den TypeCode {1}", 1, 1.GetTypeCode());

Quizfrage: Wieviele boxings sind hier versteckt?

Abgesehen von obigem einsatz hat boxing noch ein paar besonderheiten, z.B. folgendes (siehe using Statement (C# Reference)):


Dictionary<string,string> dict=...
using(var enumerator=dict.GetEnumerator())
{
   ...
}
Außerdem sind geboxte objekte nicht zwangsläufig immutable und man kann sogar mit ihnen arbeiten:


interface Interface
{
	string Foo{get;set;}
}
	
struct Struct:Interface
{
	public string Foo{get;set;}
}

Interface box=new Struct{Foo="wert1"};
Interface var1=box;
Struct var2=(Struct)box;
box.Foo="wert2";
Console.WriteLine(string.Format("{0} {1}",var1.Foo,var2.Foo));

Thema: Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
Am im Forum: Smalltalk

Ich weiß ned so recht: Die lösung spielt zwar einen ton mit 125 hz** ab, aber man hört ein regelmäßiges ploppen, da das abspielen erst kurz nach dem ende wieder angestoßen wird.

Der trick es unterbrechungsfrei hinzubekommen liegt eigendlich darin, mehr als einen buffer zu verwenden und diese nacheinander einzureihen, sodass SourcePlay eigendlich nur einmal aufgerufen werden muss! Mit on the fly meinte ich, dass man die buffer neu befüllt, bevor man sie wieder verwendet. Wenn man das nicht macht, hat man ein problem wenn man zwei oder mehr frequenzen gleichzeitig spielen will.


AL.GetSourcei(source, AlSourceInts.Buffers_Queued);
AL.GetSourcei(source, AlSourceInts.Buffers_Processed);
Buffers_Processed gibt an, wieviele buffer vollständig abgespielt wurden und entfernt werden können. Buffers_Queued gibt an, wieviele buffer insgesamt in der schlange sind.

**125hz und nicht 1000 hz, weil der letzte parameter von BufferData die sampelrate ist, aber daran solls ned scheitern^^

Thema: Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
Am im Forum: Smalltalk

Zitat
string.Reverse
String hat garkeine Reverse-methode^^
Du meinst wohl eher System.Linq.Enumerable.Reverse

Naja Ich stell mal eine neue aufgabe:

Erstelle einen "synthesizer" mit OpenAL

Konkret: Ein ton soll on the fly generiert und mittels OpenAL ausgegeben werden.
Das kann z.B. eine sinus- oder sägezahnwelle sein, eine schwebung oder sogar eine melodie wäre auch nett, bei ner einfachen sinusschwingunggilt die aufgabe aber auch schon als gelöst.
Um auf OpenAL zuzugreifen habe ich unten die nötigen dllimports angefügt, aber wer unbedingt will kann auch OpenTK verwenden.
Ich hatte an eine konsolenanwendung gedacht, die einfach solange spielt, bis man das fenster schließt, aber die oberfläche ist jedem selbst überlassen!

Noch ein paar regeln:
-Es ist nicht erlaubt, eine sounddatei auf der platte zu erstellen. Der ton muss die ganze zeit on the fly generiert werden.
-Die tonhöhe muss durch konstanten im quelltext oder sonstige parameter/eingaben frei wählbar sein.

Aufwand: Je nachdem, wie man sich anstellt, sollte es zwischen 55 und 110 zeilen liegen. (Meine lösung hat 55 zeilen)

Ich hoffe, dass ihr es nicht bei einem sinuston belasst, sondern auch ein bisschen damit rumspielt und verschiedene klänge und effekte ausprobiert, denn das war die eigendliche grundidee!

Wer sich nicht so auskennt: Hier sind die benötigten apis:
alcOpenDevice, alcCreateContext, alcMakeContextCurrent, alGenSources, alGenBuffers, alGetSourcei, alSourceUnqueueBuffers, alBufferData, alSourceQueueBuffers, alSourcePlay (lieber zu oft als zu selten), Thread.Sleep

Thema: Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
Am im Forum: Smalltalk

Erster versuch:


return string.Concat(p.Reverse().Select((c)=>(c%3==0?(char)(c-1)+"":"")+c).ToArray());
Zweiter versuch:

return (i+1<p.Length?encode(p,i+1):"")+(p[i]%3==0?(char)(p[i]-1)+"":"")+p[i];