Strona 1 z 1

Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 11:03
przez Cyfrowy Baron
Mam obiekt typu IStream i chciałbym przepisać jego zawartość do obiektu typu TMemoryStream lub do typu TStream.
Pobranie danych do obiektu typu IStream nie nastręcza mi problemów, ale nie mogę w żaden sposób przepisać jego zawartości do innego typu:

Kod: Zaznacz cały
     AnsiString Url ="http://cyfbar.republika.pl/images/img1.jpg";

     IStream* pStream;

     URLOpenBlockingStream(0, Url.c_str(), &pStream, 0, 0);

    TStream *stream;
    // jak przepisać pStream do stream

   TMemoryStream *mStream = new TMemoryStream;
   // jak przepiać pStream do mStream

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 17:27
przez Witold

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 20:00
przez Cyfrowy Baron
W przytoczonym przez ciebie linku nie ma nic o TMemoryStream ani TStream, a nie mogę użyć przedstawionej tam funkcji LoadFromIStream gdyż TMemoryStream i TStrem nie mają takich funkcji.

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 21:14
przez polymorphism
A co za problem napisać wrapper na IStream?

Coś w tym stylu:
Kod: Zaznacz cały
class CIStreamWrapper:public TStream
{
private:
   IStream*   m_stream;
public:
   virtual int __fastcall Read(void *Buffer, int Count) { ... }
   virtual int __fastcall Write(const void *Buffer, int Count) { ... }
   virtual int __fastcall Seek(int Offset, Word Origin) { ... }
   virtual __int64 __fastcall Seek(const __int64 Offset, TSeekOrigin Origin) { ... }
   //...itd

   CIStreamWrapper(IStream &ps) : m_stream(&ps)
   {
      m_stream->AddRef();
   }
   
   ~CIStreamWrapper()
   {
      m_stream->Release();
   }
};

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 23:49
przez kinio
Witam!
Cyfrowy Baron napisał(a):Mam obiekt typu IStream i chciałbym przepisać jego zawartość do obiektu typu TMemoryStream lub do typu TStream.
Pobranie danych do obiektu typu IStream nie nastręcza mi problemów, ale nie mogę w żaden sposób przepisać jego zawartości do innego typu:

Najłatwiej to zrobić sobie jakiś buforek pośredni. Mi udało się coś takiego napisać i nawet ściąga ten obrazek - pewnie o to chodziło :)
Kod: Zaznacz cały
AnsiString Url ="http://cyfbar.republika.pl/images/img1.jpg";

     IStream* pStream;

     URLOpenBlockingStream(0, Url.c_str(), &pStream, 0, 0);

     LARGE_INTEGER liZero = { 0 };      // Aby odnosic sie wzgledem poczatku strumienia
     ULARGE_INTEGER CurPos;             // Do zapisania pozycji wskaznika

     // Zapisujemy bierzaca pozycje wskaznika
     pStream->Seek(liZero, STREAM_SEEK_CUR, &CurPos);

     // Sprawdzamy wielkosc stumienia
     ULARGE_INTEGER SizeOfFile;
     pStream->Seek(liZero, STREAM_SEEK_END, &SizeOfFile);

     // Przywracamy wskaznik na poprzednia pozycje
     LARGE_INTEGER Pos;
     Pos.QuadPart = CurPos.QuadPart;
     pStream->Seek(Pos, STREAM_SEEK_SET, NULL);

     // Tworzymy bufor przejsciowy dla strumienia
     void* data = new char[SizeOfFile.QuadPart];
     pStream->Read(data, SizeOfFile.QuadPart, NULL);

     // Przepisujemy dane do TMemoryStream
     TMemoryStream *mStream = new TMemoryStream;
     mStream->Write(data, SizeOfFile.QuadPart);

     // Zapis do pliku
     mStream->SaveToFile("a.jpg");

     delete [] data;

Pozdrawiam!

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: czwartek, 15 stycznia 2009, 23:52
przez Witold
Cyfrowy Baron napisał(a):W przytoczonym przez ciebie linku nie ma nic o TMemoryStream ani TStream

jest klasa pochodna: Type TInterfaceStream = Class ( TMemoryStream )

zresztą chodziło mi o sam przykład wykorzystania TOLEStream'a (znalazłem tylko w Delphi, pisałem na podstawie tego i śmigało.)

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: sobota, 17 stycznia 2009, 13:33
przez Cyfrowy Baron
Pomysł kinio w zupełności mi odpowiada. Nie miałem czasu się tym zając, więc takie gotowe rozwiązanie jest jak znalazł. Próbowałem czegoś podobnego ze zmienną char jako buforem, ale ograniczała mnie jej pojemność. Trochę liczyłem, że o czymś nie wiem i istnieje jakaś prosta funkcja, ale tak jest dobrze.
Przedstawiony kod chciałem wykorzystać, nie do zapisywania plików pobranych z sieci d pliku na dysku, lecz do wczytywania bezpośrednio z sieci do obiektu Image, dlatego nieco zmodyfikowałem kod kinio:

Kod: Zaznacz cały
// dołączamy do projektu bibliotekę urlmon.lib
//---------------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
AnsiString Url ="http://cyfbar.republika.pl/images/img1.jpg";

     IStream* pStream;

     URLOpenBlockingStream(0, Url.c_str(), &pStream, 0, 0);

    LARGE_INTEGER liZero = { 0 };     
    ULARGE_INTEGER CurPos;

     pStream->Seek(liZero, STREAM_SEEK_CUR, &CurPos);

     ULARGE_INTEGER SizeOfFile;
     pStream->Seek(liZero, STREAM_SEEK_END, &SizeOfFile);

     LARGE_INTEGER Pos;
     Pos.QuadPart = CurPos.QuadPart;
     pStream->Seek(Pos, STREAM_SEEK_SET, NULL);

    void* data = new char[SizeOfFile.QuadPart];
    pStream->Read(data, SizeOfFile.QuadPart, NULL);


    std::auto_ptr<TMemoryStream>mStream(new TMemoryStream);
    mStream->Write(data, SizeOfFile.QuadPart);
    mStream->Position = 0;

     String ext = ExtractFileExt(Url);

    if(ext.LowerCase() == ".jpg")
    {
     std::auto_ptr<TJPEGImage> JImage(new TJPEGImage());
     JImage->LoadFromStream(mStream.get());
     Image1->Picture->Assign(JImage.get());
    }
    if(ext.LowerCase() == ".bmp")
    {
          std::auto_ptr<Graphics::TBitmap> Bmp(new Graphics::TBitmap());
     Bmp->LoadFromStream(mStream.get());
     Image1->Picture->Assign(Bmp.get());
       }

    delete [] data;
}
//---------------------------------------------------------------------------


umknęła mi przedstawiona możliwość: void* data = new char i próbowałem podobnie ala zamiast typu void używałem bezpośrednio char o zdefiniowanej wielkości maksymalna dopuszczalna 999999.



Jeżeli chodzi o ściąganie plików na dysk to istnieje prostszy sposób:

Kod: Zaznacz cały
//---------------------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{
  AnsiString Url ="http://cyfbar.republika.pl/images/img3.jpg";
URLDownloadToFile(0, Url.c_str(), "c:\\img3.jpg", 0, 0);
}
//---------------------------------------------------------------------------

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: niedziela, 18 stycznia 2009, 19:30
przez Witold
Coś podobnego z TOleStream:
Kod: Zaznacz cały
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    AnsiString Url ="http://cyfbar.republika.pl/images/img1.jpg";
    IStream* pStream = 0;

    if (S_OK == URLOpenBlockingStream(0, Url.c_str(), &pStream, 0, 0))
    {
        std::auto_ptr<TOleStream> Adapt(new TOleStream(pStream));
        String ext = ExtractFileExt(Url);

        if (ext.LowerCase() == ".jpg")
        {
            std::auto_ptr<TJPEGImage> JImage(new TJPEGImage());
            JImage->LoadFromStream(Adapt.get());
            Image1->Picture->Assign(JImage.get());
        }
        if  (pStream) pStream->Release();
    }
}


1. Wydaje mi się że pStream->Release() jest potrzebne, może ktoś potwierdzić?
2. To jest legalne w C++ (new char[], delete [] void* ) ?
Kod: Zaznacz cały
  void* data = new char[SizeOfFile.QuadPart];
   delete [] data;

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: niedziela, 18 stycznia 2009, 19:53
przez polymorphism
1. Wydaje mi się że pStream->Release() jest potrzebne, może ktoś potwierdzić?

Potwierdzam.

2. To jest legalne w C++ (new char[], delete [] void* ) ?

Dla purystów językowych nie jest. Akurat w tym konkretnym przypadku nie ma wielkiego problemu, wszak są to typy proste, które nie wymagają specyficznej dekonstrukcji. Ale jeśli mielibyśmy do czynienia z obiektami jakiejś klasy, byłby to poważny błąd. Należy unikać takich praktyk, coby nie nabrać złych nawyków.

PS. W C++ do takich zastosowań, czyli tymczasowych buforów, jest klasa vector.

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: niedziela, 18 stycznia 2009, 23:00
przez banita
zauwaz ze tutaj ten przejsciowy bufor jest modyfikowany w stylu API C, do funkcji podaje sie wskaznik na pierwszy element.
w przypadku vectora i interfejsu API C mozemy zastosowac jedynie funkcje do odczytu ktore nie zmodyfikuja vectora(inaczej vector sie pogubi bo zmiany nastapia bez wykozystania jego interfejsu), przekazujac im adres pierwszego elementu vectora.
zakladajac ze vector jest zaimplementowany w taki sposob ze pracuje na ciaglym obszarze pamieci to przed przekazaniem go do interfejsu API C musimy zarazerwowac odpowiednia ilosc pamieci dla niego (juz nie mamy uroku samo powieksznia sie), dodatkowo musimy funkcja resize zwiekszyc vector o te wpisane elementy aby moc je odczytac.

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: niedziela, 18 stycznia 2009, 23:44
przez polymorphism
zakladajac ze vector jest zaimplementowany w taki sposob ze pracuje na ciaglym obszarze pamieci

Nie zakładamy, to jest w specyfikacji vectora. Zakładać możesz w stringu, bo tu nie ma wymogu ciągłości pamięci.

API C musimy zarazerwowac odpowiednia ilosc pamieci dla niego (juz nie mamy uroku samo powieksznia sie), dodatkowo musimy funkcja resize zwiekszyc vector o te wpisane elementy aby moc je odczytac.

No ale w czym jest problem? Co się stanie jeśli to:
Kod: Zaznacz cały
void* data = new char[SizeOfFile.QuadPart];
pStream->Read(data, SizeOfFile.QuadPart, NULL);

zamienię na:
Kod: Zaznacz cały
vector<char> data(SizeOfFile.QuadPart);
pStream->Read(&data.front(), data.size(), NULL);

:?:



PS. Interface'y COM to API typowe raczej dla C++.

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: niedziela, 18 stycznia 2009, 23:49
przez banita
nic sie nie stanie tylko ze wtedy vector staje sie zwykla ograniczona tablica plus dodatkowa tu niepotzrebna nadbudowa.
do funkcji o strukturze API C powinno sie przekazywac tablice chyba ze akurat mamy kontener STL to wtedy nalezy kontener -> vector -> API C badz bezposrednio vector -> API C.

po co twozyc duzy obiekt (jakim jest vector w porownaniu z tablicami) i swiadomie obcinac jego mozliwosci do zwyklej tablicy o rozmiarze ktory ustala sie raz(tu nie ma balansu rozmiaru).

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: poniedziałek, 19 stycznia 2009, 00:33
przez polymorphism
tylko ze wtedy vector staje sie zwykla ograniczona tablica plus dodatkowa tu niepotzrebna nadbudowa.

No nie taka zwykła i ograniczona - funkcjonalność pozostaje ta sama. No i sama się usunie. Same korzyści :P

po co twozyc duzy obiekt (jakim jest vector w porownaniu z tablicami)

Jak duży? 10, 20 bajtów? Co jak co, ale tym bym się nie przejmował, zważywszy że mowa o VCL'u napisanym w Delphi i stylu programowania w VCL jaki często widuję.

Re: Jak przepisać zawartość obiektu typu IStrem do TMemoryStream

Nowy postNapisane: poniedziałek, 19 stycznia 2009, 00:38
przez Witold
banita napisał(a):po co twozyc duzy obiekt (jakim jest vector w porownaniu z tablicami) i swiadomie obcinac jego mozliwosci do zwyklej tablicy o rozmiarze ktory ustala sie raz(tu nie ma balansu rozmiaru).

całe ogromniaste 24 bajty (bcb6) lub 32 bajty (bcb2009). Co to jest balast rozmiaru ? Poza tym w std c++ nie ma auto_ptr dla tablic, gdy potrzeba bezpiecznego "auto-zwalniania" pozostaje vector (można też skorzystać z bogactwa boost'a).

Polymorphism, dzięki za potwierdzenie w sprawie pStream->Release().