Diskuze: Printscreen v konzolové aplikaci
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 18 zpráv z 18.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Pro příště: musíš taky napsat, jestli ti to píše chybu nebo jestli se to spustí, ale nic to nedělá. Pravděpodobně sis nepřidal referenci na System.Drawing.dll.
Také je třeba přidat referenci pro System.Windows.Forms, protože používáš třídu Screen. Také není úplně dobré dávat metodám nebo proměnným stejný název jako je třída, které už používáš (Mám na mysli metodu Screen a třídu Screen)...
Tvůj kód pak funguje bez problémů...
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ScreenTest
{
class Program
{
static void Main(string[] args)
{
GetScreen("test.jpg");
}
static void GetScreen(string cesta)
{
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(cesta, ImageFormat.Jpeg);
}
}
}
Obrázky asi vypoví více než tisíc slov. V příloze je máš. Ale ve zkratce prostě klikneš pravým na Reference v Solution exploreru a vybereš vlevo Assemblies a tam najdeš seznam s výše zmíněnými namespacy.
Kromě "using namespace", které ti pouze zjednodušší psaní názvů tříd (např. namísto System.Drawing.Bitmap můžeš psát jenom Bitmap) musíš přidat ještě referenci na soubor .dll, kde je toto namespace a jeho třídy definované. Namespace System.Drawing se nachází v System.Drawing.dll, System.Windows.Forms v System.Windows.Forms.dll (ne vždy to takhle pěkně koresponduje). Ve Visual studiu klikneš pravým na projekt v solution exploreru, zvolíš Add -> Reference. V seznamu pak najdeš všechny dll, co potřebuješ a dáš OK. V projektu Windows Forms se tyto reference přidají automaticky, v Console application ne (protože se nepředpokládá, že by to někdo chtěl použít).
Díky, to už jsem si dohledal a pochopil, díky moc oběma
Měl bycj ještě prosbu hoši. Učím se trošku pokus omyl, tak vám předem děkuji za vaši trpělivost. Když to zacyklím se čítením vstupu a napřiklad při vstupu čísla 9 mi to udělá screen. S každým screenem mi program vezme 9MB z paměti, nevíte, jak tuto pamět znovu uvolnit?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ScreenTest
{
class Program
{
static void Main(string[] args)
{
string znak = "";
while(1==1)
{
znak = Console.ReadLine();
if (znak == "9")
{
GetScreen(@"C:\Users\okay\AppData\Roaming\KlavModul\screen");
}
}
}
static void GetScreen(string cesta)
{
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(cesta + DateTime.Now.ToString("MMddyyHmmss") + ".jpeg", ImageFormat.Jpeg);
}
}
}
Paměť za tebe uvolní (v tomto případě) GarbageCollector. Ten Bitmap
bitmap je lokální proměnná funkce, takže po jejím skončení na ten objekt
nevede žádná reference, tudíž je označen jako připravený k odstranění.
Jakmile by tvojí aplikaci docházela paměť, tak se GC zapne a uvolní
všechny takto označené objekty. Jestli to chceš dělat explicitně, tak
použij bitmap.Dispose().
Dále: while(true) je hezčí než while(1==1)
Alternativně můžeš použít direktivu "using":
using(Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
{
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(cesta + DateTime.Now.ToString("MMddyyHmmss") + ".jpeg", ImageFormat.Jpeg);
}
Jediné, co "using" způsobí je, že po skončení kódu ve složených závorkách se automaticky zavolá metoda Dispose() na objektu, který jsi deklaroval v "using" závorkách.
Paměť za tebe uvolní (v tomto případě) GarbageCollector
Všechno co implementuje IDisposable bys měl mít v using bloku (pokud to používáš v jednom scopu), jinak si koleduješ o memory leaky. Jestliže to máš uvnitř nějaké třídy jako propertu/field, tak bys měl pak implementovat taky IDisposable a prochainovat ty věci a uvolnit je (zavolat na nich Dispose()).
Btw, Dispose neznamená smazání objektu, ale uvolnění unmanaged věcí, které nejsou v řežii GC. To jen tak na okraj... Otázka na zamyšlení, to bys potom při každém " ... new " měl uzavírat do usingu? Pogoogli si rozdíl mezi managed a unmanaged resourcy
Rozdíl znám z WinAPI programování, jen jsem asi blbě popsal to Dispose Že mám všechno, co implementuje IDisposable dávat do using nebo na tom volat Dispose(), jsem už slyšel mockrát, ale opravdu je to třeba tady nutné? Reference bitmap se po skončení metody dostane mimo scope => je ready na destrukci. Pokud náhodou dojde paměť, GC se spustí a zavolá na těchto objektech destruktor. A destruktor ve třídě Image volá Dispose() => zdroje se uvolní. To samé s Graphics.
Nejde jenom u ovolňování zdrojů. Pokud máš třída destrujtor, tak je to naprosto mimo rámec GC. Pro prvním průchodu se přesune do Finalizovane fronty a tam straší jeste pěkně dlouho, než se destruktor zavola. Pomocí using se tomu vyhneš, destruktor obejdeš a vrátíš správu zpět GC.
Je s podivem, kolik programátorů netuší, jak funguje GC, Disposable i finalizer. Chápu, že každý nemusí vědět o generacích a rozdílech například u velkých objektů. Ale alespoň by už mohli vědět, kdy GC co může uvolnit a jak to udělat dříve.
GC a destruktory si moc nerozumí.
Protože GC běží na vlastním vlákně, nedá se nikdy říct, kdy a co
provede.
Pokud třída pracuje s unmanaged resources nebo potřebuje při své destrukci
vyvolat explicitní akci, nelze se na destruktor spoléhat, to mají společné
všechny platformy s GC.
Další platformy, které se tomu snaží vyhnout (.NET tam nepatří), a které kombinují reference counting s GC mají zase problém s garancí zavolání destruktoru.
Při vyšším tlaku na heap nebo zaplnění paměti se GC nezavolá, to je
daleko komplikovanější.
Spuštění GC v takové situaci znamená zamrznutí programu, což bylo dlouhá
léta chování, kterým trpěla Java.
Odtud také pochází původní varování, že Java nesměla být nasazována v
real-time systémech jako letadla nebo elektrárny.
Dříve by program spadl na out of memory a GC by proti tomu nic neudělal.
Dnes zafunguje virtualizace paměti na úrovni OS a začnou se dít nehezké
věci, často horší, než ukončení programu.
A na závěr perlička.
Protože je GC chování nedeterministické a záleží na plánování zdrojů
OS, bude se program chovat jinak ve vývojovém režimu a jinak v produkčním,
jinak na i5 a jinak na i7 nebo i7 se zakázanou virtulizací.
Objekt, který ve vývojovém režimu spadne do vyšší generace, může v
produkčním režimu zůstat v nulté generaci a získat prioritu pro uvolnění
před jiným objektem, pořadí destrukce objektů je tedy také
nepredikovatelné.
Jinak řečeno, na různých počítačích se začnou projevovat různé chyby,
které nepůjdou verifikovat.
Takže ano, pokud si nejsi naprosto jistý, že objekt implementuje Dispose jen proto, že musí, měl bys vždy používat using.
Reference counting nemá problém se zavoláním destruktoru, problém je samotné počítání referencí, které o hodně zpomaluje program. Koneckonců, je to jedna z věcí, která je doporučena pro C++. Tam si ale dokážeme spoustu paměti hlídat sami, takže se reference nepočítá u naproto každého objektu a nároky nejsou tak vysoké.
Jak jsem psal, kombinace refcountingu s GC má velký problém s voláním
destruktorů.
Počínaje určením vlákna, na kterém destruktor poběží a konče
synchronizací GC/RC, když dochází k uvolnění.
Narozdíl od GC je RC velice rychlé (teď už nemluvím o jejich kombinaci), protože odpadá synchronizace vláken a neběží tam explicitní vlákno pro GC. RC se dá implementovat velice efektivně, implementace retain/release cyklu může být levná i při synchronizaci.
Zobrazeno 18 zpráv z 18.