Zeichenfläche TCanvas über das Netzwerk versenden



  • So auf Anhieb, kann ich keinen Fehler entdecken.

    In dem Example-Ordner des BCB ist bei den FastNetkomponenten ein Beispiel, in dem ein Bild via Stream versendet wird.

    Du kannst die Bilder natürlich auch zwischenspeichern und sie dann mit Hilfe von TFileStream versenden. Ist aber eigentlich nicht nötig, da es auch direkt geht.

    Kommt denn beim Versenden oder Empfangen eine Fehlermeldung?

    Gruss



  • Die Felhlermeldung kommt erst beim Zeichnen des Sreenshot:
    FEHLER: Leinwand/Bild erlaubt kein zeichnen
    Wenn ich das

    Image1->Canvas->CopyRect(Dest, test, Source);
    

    auskommentiere kommt kein Fehler mehr. Ich vermute, dass ich etwas beim Übertragen des Zeichenfelds nicht beachtet habe, so dass der Client nur Müll empfängt, das er nicht auf ein TImage zeichnen kann.



  • also wenn ich das um die uhrzeit noch richtig verstehe, liegt das Problem daran, dass die Klasse TCanvas mehrere Pointer auf andere Klassen besitzt.
    zB TCanvas::Font ist ja nur ein Zeiger in dem eine Adresse steht, unter welcher eine Instanz der Klasse TFont ist. aber wer sagt dir, dass bei deinem "client-PC" unter der selben adresse auch eine Instanz des TFont ist?

    naja, ich bitte schonmal um berichtung meiner aussage 🙂

    [edit] was mir sowieso gerade auffällt:
    wieviele Bytes werde den übertragen?

    TCanvas* canvas = new TCanvas();
    canvas->Handle = GetWindowDC(GetDesktopWindow());
    ServerSocket1->Socket->Connections[0]->SendBuf((canvas), sizeof(canvas));
    

    canvas ist ein Pointer.

    also gibt sizeof(canvas) sowieso ja immer 4 Bytes

    bei solchen sachen musst du mit pointer aufpassen



  • Hmmm, ohne Quellcode wird das glaube ich nichts auf diesem Wege.

    Speichere die Canvas erst mal zwischen und versende das Bild z.B. als Bitmap.
    Wenn der Client das Bild empfängt und es sich auch manuell öffnen lässt, weisst Du, dass es nicht an der Übertragung liegt.

    Oder Du postet mal ein bisschen mehr Code.

    Habe sowas selber mal gemacht. Vielleicht hilft es Dir ja weiter:

    // Screenshot vom aktuellen Bildschirm schiessen
       HDC dc = GetDC(NULL);
       Graphics::TCanvas *ScreenCanvas = new Graphics::TCanvas();
       ScreenCanvas->Handle = dc;
    
       Image1->AutoSize = true;
       Image1->Center = true;
       Image1->Top   = 0;
       Image1->Left  = 0;
       Image1->Picture->Bitmap->Width  = Settings.DesktopImage.XAufloesung;
       Image1->Picture->Bitmap->Height = Settings.DesktopImage.YAufloesung;
       TRect rectSource = Rect(0,0,Screen->Width, Screen->Height);
       TRect rectDest = Rect(0,0,Settings.DesktopImage.XAufloesung, Settings.DesktopImage.YAufloesung);
       Image1->Picture->Bitmap->Canvas->CopyRect(rectDest, ScreenCanvas, rectSource);
    

    und das ganze dann als JPEG via FastNet versenden:

    TJPEGImage *jp = new TJPEGImage();
             try
               {
                 jp->Assign(Image1->Picture->Bitmap);
                 jp->CompressionQuality = Settings.DesktopImage.Bildqualitaet;
                 jp->PixelFormat = jf24Bit;
                 jp->ProgressiveDisplay = true;
                 jp->ProgressiveEncoding = true;
                 jp->Smoothing = true;
                 jp->Compress();
                 jp->SaveToFile("test.jpg");
               }
             __finally {
               delete jp;
             }
             TFileStream *MyFStream;
             MyFStream = new TFileStream("test.jpg", fmOpenRead);
             try
               {
                 NMStrm1->Host = Parameter;
                 NMStrm1->FromName = "GetDesktopImage:" + IntToStr(Screen->Width);
                 NMStrm1->PostIt(MyFStream);
               }
             catch(...) { }
             MyFStream->Free();
    

    PS: Bei mir gab es damals Probleme, dass die Canvas' verschiedene Grössen hatten



  • ich habe hier noch etwas getestet..
    hier überträgst speicherst du einfach das Array von TColors für jedes Pixel ab und lädst es wieder:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
       Image1->Canvas->Brush->Color = clRed;
       Image1->Canvas->FillRect(Image1->ClientRect);
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
       TCanvas *can = Image1->Canvas;
       TFileStream *Stream = new TFileStream("C:\\temp\\canvas_test.bin",fmCreate);
    
       TRect Rect = can->ClipRect;
    
       for(int x = 0; x < Rect.Width(); x++)
       {
          for(int y = 0; y < Rect.Height(); y++)
          {
             int iSize = sizeof(can->Pixels[x][y]);       //TColor ist soviel ich weiss immer 4 Byte gross
             TColor Color = can->Pixels[x][y];
             Stream->Write((void*)&Color,iSize);
          }
       }
       delete Stream;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
       TCanvas *can = Image2->Canvas;
       TFileStream *Stream = new TFileStream("C:\\temp\\canvas_test.bin",fmOpenRead);
    
       TRect Rect = can->ClipRect;
    
       for(int x = 0; x < Rect.Width(); x++)
       {
          for(int y = 0; y < Rect.Height(); y++)
          {
    
             TColor Color;
             Stream->Read(&Color,4);         //TColor ist soviel ich weiss immer 4 Byte gross
             can->Pixels[x][y] = Color;
          }
       }
       delete Stream;
       Image2->Repaint();
    }
    //---------------------------------------------------------------------------
    

    jedoch ist der Code bestimmt nicht Perfekt und sollte dringenst noch überarbeitet werden. zB könnte man am anfang noch das ClientRect speichern etc..

    evtl dient es etwas als Anregung



  • moin!
    Vielen Dank für eure Hilfestellung.
    Ist es möglich meinen Code um ein paar Zeilen zu erweitern damit man kurzfristig die TCanvas Zeichenfläche zwischenspeichern kann, damit man dann die Zeichenfläche versendet und nicht den Zeiger. Ich hab leider noch zu wenig Ahnung und bekomme einfach mittlerweile keine Idee mehr. Ist bisher alles gescheitert. Die Quellcodes helfen mir dabei leider auch nicht viel weiter, trozdem vielen Dank.



  • Hallo

    Am einfachsten kannst Du die Canvas Speichern indem Du sie in ein Image kopierst und dieses dann in einer Datei speicherst.

    // Code ist nicht getestet!
    Image1->Canvas = YourCanvas;
    Image1->Picture->SaveToFile("C:\\test.bmp");
    

    Diese Datei kannst Du dann mit dem Code, den ich vorher gepostet habe, versenden.

    Gruss



  • Ich habe das Problem das der Server den Screenshot machen soll aber komplett im Hintergrund läuft. Und somit auch kein TImage haben soll wo er es reinspeichert. Er soll es wenn möglich auch nicht auf die Fetsplatte zwischenspeichern. Am liebsten wäre mir das er es in eine Variable zwischenspeichert die Variablle dann an den Client versendet.



  • Hallo

    und zwar eine Variable vom Typ TBitmap.

    bis bald
    akari



  • Ich weiß einfach nicht wie, vieleicht könnte mir einer meinen Code berichtigen. Ich habe bisher einfach noch zu wenig Erfahrung und bin mittlerweile am verzweifeln.

    Server
    ----------------------------------------------------------------------
    TCanvas* canvas = new TCanvas();
    canvas->Handle = GetWindowDC(GetDesktopWindow());
    TBitmap canvas2 = canvas; //???????? wie ?????
    ServerSocket1->Socket->Connections[0]->SendBuf((canvas2), sizeof(canvas2));

    Client
    ----------------------------------------------------------------------
    TCanvas* test = new TCanvas();
    TRect Dest = Image1->ClientRect;
    TBitmap test2 = canvas; //??? wie ????
    TRect Source = Rect(0, 0, 1024, 768);
    Socket->ReceiveBuf(test2,sizeof(test2));
    Image1->Canvas->CopyRect(Dest, test2, Source);



  • Der Code zum senden sollte in etwa so aussehen:

    ClientSocket1->Active = true;
       Graphics::TBitmap *Image1 = new Graphics::TBitmap();
       HDC dc = GetDC(NULL);
       Graphics::TCanvas *ScreenCanvas = new Graphics::TCanvas();
       ScreenCanvas->Handle = dc;
    
       Image1->Width  = Screen->Width;
       Image1->Height = Screen->Height;
       TRect rectSource = Rect(0,0,Screen->Width, Screen->Height);
       TRect rectDest = Rect(0,0,Screen->Width, Screen->Height);
       Image1->Canvas->CopyRect(rectDest, ScreenCanvas, rectSource);
    
       Pic->Picture->Bitmap = Image1;             // nur zum testen
       Pic->Picture->SaveToFile("C:\\test.bmp");  //       - " -
    
         for(int x = 0; x <= ServerSocket1->Socket->ActiveConnections - 1; x++)
           {
             ServerSocket1->Socket->Connections[0]->SendBuf((Image1), sizeof(Image1));
           }
         delete Image1;
         delete ScreenCanvas;
    


  • Jo vielen Dank das Hilft mir schon um einiges weiter. Hab jetzt nur noch eine Frage und das betrifft das Empfangen.

    Client:

    Socket->ReceiveBuf(Image1,sizeof(Image1));
    

    ODER

    TRect Dest = Image1->ClientRect;
    TRect Source = Rect(0, 0, 1024, 768);
    TImage* test = new TImage();              //FEHLER
    Socket->ReceiveBuf(test,sizeof(test));
    Image1->Canvas->CopyRect(Dest, Image1, Source);
    

    so funktzioniert das leider nicht. Das Image1 bleibt leer. Beim zweiten Beispiel bekomme ich eine Fehlermeldung.
    Verzeiht mir meine Unwissenheit, ich schäme mich schon deswegen.



  • LarryXS schrieb:

    Client:

    TImage* test = new TImage();              //FEHLER
    Socket->ReceiveBuf(test,sizeof(test));
    Image1->Canvas->CopyRect(Dest, Image1, Source);
    

    Du musst den Parent angeben, also Deine Form:

    TImage* test = new TImage(Form1);
    

    weiterhin macht

    Socket->ReceiveBuf(test,sizeof(test));
    

    keinen Sinn. Was nützt Dir sizeof test, welches Du eben erst intialisiert hast?
    Wenn schon, dann

    Socket->ReceiveBuf(client, Socket->ReceiveLength());
    

    .

    Um ehrlich zu sein, hänge ich ebenfalls gerade am Empfangen etwas fest. Muss nochmal schauen...



  • Bei sowas kann es nie schaden, sich den FAQ-Eintrag zur Protokollentwicklung anzusehen.



  • Ich habe längere Zeit die FAQ-Beiträge durchstöbert.
    Das mit der Protokollentwicklung hilft mir nicht weiter. Für den Text den ich auch über den Client- Serverprogramm habe ich bereits ein Protokoll geschrieben. Das Protokoll soll natürlich auch Anwendung für den Bilderversand haben. Das Problenm ist nur wie ich das Bild in ein string umwandle um die Protokollinforationen anzuhängen. Das andere Problem ist den string am Clientprogramm wider in eine Bitmap umzuwandeln und ins TImage schreibe zu schreiben.


Anmelden zum Antworten