Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /home/www/novacisko.cz/subdomains/bredy/init.php on line 11

Warning: mysql_connect(): Headers and client library minor version mismatch. Headers:50562 Library:100020 in /home/www/novacisko.cz/subdomains/bredy/init.php on line 11

Warning: Cannot modify header information - headers already sent by (output started at /home/www/novacisko.cz/subdomains/bredy/init.php:11) in /home/www/novacisko.cz/subdomains/bredy/index.php on line 38
Základy komunikace mezi procesy (ve Windows) - Bredyho blog - Ondřej Novák
Bredyho blog - Ondřej Novák

Základy komunikace mezi procesy (ve Windows)

Od počátku Windows95 jsou procesy v paměti oddělené. Každý proces je vlastně svým světem sám pro sebe. Jak tedy mohou komunikovat procesy mezi sebou? Podrobný výčet možností...


Zprávy

Typická windows aplikace se vyznačují svým windows-specifickým způsobem běhu. Každá Windows aplikace totiž pracuje tak, že z nějaké systémové fronty vyzvedává zprávy, které posléze zpracovává. Zprávy vznikají nejčastěji činností uživatele, ale i systému, periferíí atd. Dalo by se říct, že zprávy jsou nejrozšířenějším prostředkem jak sdělit aplikaci nějaké důležité informace. Každá UI aplikace totiž na ně dřív nebo později zareaguje.

Zpráva ve windows struktura o několika málo bajtů.

  1. Cíl zprávy (ve Windows je vždy cílem zprávy nějaké okno - nemusí být viditelné na obrazovce)
  2. ID zprávy - domluvené číslo zprávy určující "jaká činnost se bude dít"
  3. wParam - zprávou definovaný parametr 32bit
  4. lParam - zprávou definovaný parametr 32bit.

Mezi další údaje patří ještě systémový čas a poloha myšího kurzoru v okamžiku odeslání zprávy, tyto údaje však nejsou pro nás moc užitečné.

Poslání zprávy
HWND hWndTarget=<příjemce>;
UINT iMsg=<číslo zprávy>;
WPARAM wParam=<data1>;
LPARAM lParam=<data2>;
PostMessage(hWndTarget,iMsg,wParam,lParam);

Zpráva dorazí do WinProc příslušného okna, zaslané parametry jsou ihned k dispozici
Sami vidíte, že prostoru není mnoho. Na krátkou informaci to stačí, poslat ale něco delší je prakticky nemožné. Máme k dispozici 8 bajtů, trikem pomoci využití čísla zprávy lze vyšetřit ještě jeden bajt navíc. Pro přenos dat je tedy nutné použít jiný prostředek. Jak se ale dále dozvíme, zpráva často stačí pro přenos zástupců (handlů) těchto prostředků mezi procesy.

Určitou výjimku nacházíme ve zprávě WM_COPYDATA. Posláním zprávy do aplikace můžeme přenést námi zvolený blok dat. Data se fyzicky zkopírují do paměti cílového prostoru. Zpráva má ale několik nevýhod, jednou z nich je napříkad existence odesílatele (okna). Což se může ukázat jako problém, pokud náš process žádná okna nemá (a nechce je vytvářet).

Příklad na WM_COPYDATA najdeme například stránkách CodeProject

PostMessage nebo SendMessage

PostMessage zasílá zprávu a nečeká na její zpracování. Pokud nás nezajímá výsledek operace (nebo se výsledek dozvíme jinak), je tato zpráva dostačující.

SendMessage zasílá zprávu a počká na její zpracování. Zpráva je poslána přímo oknu, takže neprochází přes smyčku zpráv. Zpráva přitom může být zpracována kdykoliv když volaná aplikace volá jádro (ale většinou se to týká jen příkazu pracující se zprávami, např. GetMessage, PeekMessage, atd). Volaná aplikace může na zprávu odpovědět, odpovědí je přímo návratová hodnota z WndProc. Aplikace ale může odpovědet i předčasne pomocí ReplyMessage.

Z hlediska stability je PostMessage lepší. Používání SendMessage totiž může způsobit uváznutí, zejména v případě, kdy oba procesy mezi sebou komunikují pomocí SendMessage. Může se totiž stát, že oba procesy volají SendMessage a začnou čekat. Přitom ani jeden proces nemůže zprávu toho druhého vyzvednou a zpracovat.

Roury

Roury znají lépe spíš linuxáři. Ale i ve Windows máme spoustu nástrojů jako s rourami pracovat. Na roury existuje celé API. My si v tomto článku ukážeme nejjednodušší způsob práce s rourami.

Roura je z pohledu obou aplikací prezentována jako soubor. Z jedné strany je to soubor do kterého lze zapisovat, z druhé strany je to soubor, který lze číst. Není tedy zde žádné adresování, množství přenesených dat rourou není omezen. Data zapisovaná na jedné straně roury vypadávají na druhé straně.

Každá roura také obsahuje malou vyrovnávací paměť. Data zapsaná do roury tak mohou být chvíly uchovány v paměti "nikoho" (nepatří žádnému procesu) dokud si je druhá strana nepřečte. Paměť je ale limitovaná a pokud dojde, musí proces, který do roury zapisuje počkat až si druhá strana data vyzvedne. Tohle často bývá největší zdroj uváznutí. Opět je třeba dát pozor, aby příjemce nečekal na nějakou akci odesílatele a ten naopak čekal na to až si příjemce vyzvedne data z roury. Naštěstí lze velikost vyrovnávací paměti nastavit.

Roura po vzoru linuxu

V linuxu je běžné tvořit roury. Jedna aplikace spustí jinou aplikace a spojí se s ní pomocí roury. Roura je přitom napojená na standardní vstup a výstup aplikace. Spouštící aplikace (master) pak pomocí příkazů zasílaných do roury řídí činnost spouštěné aplikace (slave). Výsledky čte master z druhé roury, která je spojena z výstupem slave.

Ve Windows lze toho docílit velice jednoduše.

 char *cmdline="c:\cesta\Aplikace.exe"; //jméno spouštěné aplikace
//deklarujeme příslušné handly
HANDLE pipeInLeft=NULL, pipeInRight=NULL,pipeOutLeft=NULL,pipeOutRight=NULL;

SECURITY_ATTRIBUTES sa;

//příprava struktury umožňující sdílení handlů mezi procesy
memset(&sa,0,sizeof(sa));
sa.bInheritHandle=TRUE;

//vytvoř vstupní rouru
CreatePipe(&pipeInLeft,&pipeInRight,&sa,0);
//vytvoř výstupní rouru
CreatePipe(&pipeOutRight,&pipeOutLeft,&sa,0);

//nyní spustíme process

STARTUPINFO startupinfo;
PROCESS_INFORMATION processinfo;
memset(&startupinfo,0,sizeof(startupinfo));
memset(&processinfo,0,sizeof(processinfo));

//následující operace nám zajistí, že handly pro druhou stranu se budou sdílet s dalšími aplikacemi.
DuplicateHandle(GetCurrentProcess(), pipeInLeft, GetCurrentProcess(),
&pipeInLeft, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);
DuplicateHandle(GetCurrentProcess(), pipeOutLeft, GetCurrentProcess(),
&pipeOutLeft, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);

//naplníme startupinfo
startupinfo.cb=sizeof(startupinfo);
startupinfo.dwFlags=STARTF_USESTDHANDLES;
startupinfo.hStdInput=pipeOutRight;
startupinfo.hStdOutput=pipeInRight;
//stderr přesměrujeme na naši konzoli, abysme případne viděli chyby
startupinfo.hStdError=GetStdHandle(STD_ERROR_HANDLE);

//spuštění procesu
bool result=CreateProcess(NULL,cmdline,NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,&startupinfo,&processinfo)!=FALSE;

Pokud spuštění procesu proběhlo bez chyby, máme vytvořeno spojení s právě spuštěnou aplikací. Data zapsaná do pipeInLeft budou putovat na standardní vstup aplikace, data čtená z popeOutLeft budou obsahovat standardní výstup z aplikace.

Pozor na malý zádrhel, roury zůstávají aktivní i po ukončení slave. Nelze se tedy spolehnout na to, že se v rouře objeví EOF při ukončení aplikace. Lze to případně obejít pomocí malého kontrolního vlákna, které se spustí po spuštění aplikace a jehož úkolem je uzavřit pravou stranu roury když zjistí, že aplikace skončila. Lze k tomu využít WaitForSingleObject spolu s handle procesu. Po zavření pravých stran rour způsobí, že příkazy ReadFile a WriteFile skončí s chybou Broken pipe, což je přesně to co potřebujeme.

Roura do běžící aplikace

Rouru lze vytvořit i do již běžícího procesu. To už ale vyžaduje spolupráci obou procesů. Iniciační process (iniciátor) si nejprve připraví rouru

 CreatePipe(&tvoje,&moje,0,0); //já budu zapisovat, on bude číst

Nyní je nutné nějak získat ID procesu druhé strany. Možností je několik. Buď to ID už víme, nebo známe aspoň okno patřící druhé straně. Pak nám stačí zavolat GetWindowProcessThreadId. Pokud neznáme ani okno, můžeme využít například registrované zprávy. Tuto zprávu si zaregistrují obě aplikace. Iniciátor pak rozešle tuto zprávu všem aplikacím a příjemce na zprávu odpoví a předá svoje ID. Další možností je pomocí CreateToolHelp32Snapshot prohledat systém a nalézt cílovou aplikace například podle diskové cesty a jména .exe souboru. A neopomeňme i možnost výběru cílové aplikace uživatelem.

Jakmile máme ID druhé strany stačí zavolat

 HANDLE othersize=OpenProcess(PROCESS_DUP_HANDLE,FALSE,ID); //vytvoř handle k procesu

Nyní musíme přenest handle druhé strany roury do kontextu druhé strany. Na to slouží funkce DuplicateHandle

 
DuplicateHandle(GetCurrentProcess(), tvoje, otherside,
&tvoje, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);

A roura je hotova. Jenže má to háček. Existuje sice roura, ale příjemce se o ní nedozví. Musíme tedy příjemci předat informaci o tom, že jiná aplikace si přeje s příjemcem komunikovat pomocí roury. Nejčastěji se používá předem domluvená zpráva, která se zašle příjemci a jako parametr se předá hodnota tvoje.

Aplikace umožňující napojení roury mají často definovaný nějaký protokol, jak navázat spojení. Jedním z řešení je vytvořením speciálního okna (s unikátním jménem), jehož jediným účelem je příjem výše uvedené zprávy. Iniciátor tedy ještě musí provést vyhledání tohoto okna. K tomu můžeme využít EnumWindows

struct FindTargetWindow
{
DWORD processID; //ID procesu prijemce
const char *uName; //unikatni jmeno
HWND hWndFound; //nalezené okno
};

BOOL CALLBACK FindTargetWindowProc(HWND hwnd, LPARAM lParam)
{
FindTargetWindow *s=(FindTargetWindow *)lParam;
DWORD wID;
char buff[256];
GetWindowThreadProcessId(hwnd,&wID);
if (wID==s->processID)
{
GetWindowText(hwnd,buff,256);
if (strcmp(buff,s->uName)==0)
{
s->hWndFound=hwnd; //nalezeno
return FALSE;
}
}
return TRUE; //hledej dal
}

Nalezení okna:

FindTargetWindow fnd;
fnd.processID=ID;
fnd.uName="unikatni jmeno okno";
fnd.hWndFound=0;
EnumWindows(FindTargetWindowProc,(LPARAM)&fnd);
if (fnd.hWndFound) //úspech...
{
PostMessage(fnd.hWndFound,<domluvené číslo zprávy>,0,(LPARAM)tvoje);
}
else
chyba();

Příjemce vyzvedne zprávu, vyzvedne handle tvoje a komunikace může začít.

Výše uvedený příklad předpokládal jednosměrnou rouru. Pro oboustranou komunkaci postupujeme stejne, akorát vytváříme dve roury. Oba handly pak můžeme poslat v jedné zprávě.

Sdílená pamět

Sdílená paměť jako taková vymizela s Windows 95 díky izolaci procesů. V dobách Win3.11 totiž bylo možné získat ukazatel na paměť patřící jinému procesu. Bylo možné alokovat paměť, kam směly přistoupit různé procesy. Ochrana přitom byla zajištěna pouze na úrovni handlů, tedy například nebylo možné získat ukazatel na paměť, která byla alokována privátně - ale pokud někdo ten ukazatel získal jinak, paměť přístupná byla.

Win32 procesy jsou izolované, takže není možné jakýmkoliv přímým způsobem vstupovat do paměti cizího procesu (přímým myšleno pomocí ukazatele).

K vytváření paměti sdílené mezi procesy, tj. tak aby oba procesy měli část své paměti přístupnou sobě navzájem se používá prostředek, který je v základě (alespoň podle volených názvů funkcí a jeho ovládání) určen k něčemu úplně jinému. Používá se mapování souboru do paměti

Mapování souboru do paměti je služba která využívá systém swapování paměti na disk. Umožňuje připojit již k exiujícímu swapu další soubor a určit, na kterých adresách lineárního adresového prostoru bude tento soubor mapován jako swapfile. Po této operaci pracuje proces stejně jako by paměť alokoval a do paměti natáhl kopii souboru. Výhodou je, že data do paměti putují až v okamžiku, kdy je proces potřebuje. Načítání probíhá po stránkách, do paměti se tedy přesouvají pouze ty stránky, které proces potřebuje. Stejným způsobem funguje zápis, proces normálně zapíše do paměti, a do souboru se obsah přepíše až když je potřeba stránku odložit.

Mapované soubory defacto nezabírají žádnou paměť (z hlediska ukazatel "alokovaná paměť" ve správci úloh). V okamžiku nedostatku paměti jsou totiž stránky souborů odkládány na disk a zpět načítány až když je potřeba (stejně jako u swapfile).

Mapované soubory mají jednu docela užitečnou výhodu. Pokud si totiž dva procesy do své paměti namapují stejný soubor (stejnou jeho část), je obsah souboru mezi procesy synchronizován. Tedy, pokud jeden proces zapíše do nějkteré stránky mapované paměti, změna se ihned projeví v druhém procesu. Na úrovni systému je toto implementované skutečně tak, že oba procesy "vidí" stejnou část paměti. A této výhody se při vytváření sdílené paměti využívá.

Aby nebylo nutné pro každou sdílenou paměť vytvářet soubor na disku, Windows umožňují vytvořit mapovaný soubor uvntř swapfile. Soubor je tedy dočasný, a jeho podkladem je kus souboru swapfile (virtuálně - fyzicky to ani být nemusí, pokud je paměti dostatek a neswapuje se). V tomto případě se využívá pouze výhoda možností sdílet paměť s jiným procesem, což je práve to co potřebujeme.

Sdílenou paměť vytváříme pomocí funkce CreateFileMapping

HANDLE sharedMem=CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, size, shareName);

size představuje požadovanou velikost a shareName představuje unikátní jméno pro sdílenou paměť a může být NULL (bez jména). Abysme získali ukazatel, musíme sdílenou paměť namapovat do našeho lineárního prostoru. O to se stará funkce MapViewOfFile

void *sharePtr=MapViewOfFile(sharedMem, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0)

Tento zápis mapuje celou paměť.

Pro pořádek si uveďme ještě ukončení práce se sdílenou pamětí

UnmapViewOfFile(sharePtr);
CloseHandle(sharedMem);

Sdílení pomocí jména

Jak sdílet sdílenou paměť mezi procesy? Ten nejjednodušší způsob tkví v použití unikátního jména sdílené paměti. Toto jméno zadáváme u funkce CreateFileMapping.

Pokud totiž uvedeme jméno již existující sdílené paměti, nedojde k vytvoření nové, ale získáme handle k té existující. Postup je tedy naprosto stejný.

Tento způsob je s výhodou používaný u DLL knihoven. Pokud DLL knihovna potřebuje mít paměť společnou pro všechny své instance, pak výše uvedený postup se provádí v DLL_PROCESS_ATTACH. Objekt sdílené paměti zůstává tak dlouho, dokud existuje jediná instance DLL, který jej referencuje.

Jiný způsob, kdy existuje něco jako master (který vytváří sdílenou paměť) a slave který ji jen používá se trochu liší. V prvním případě je nutné aby master si zajistil, že jeho jméno je dostatečně unikátní. Může po zavolání funkce CreateFileMapping zkontrolovat stav GetLastError. Najdeme zde chybový kód, pokud sdílená paměť s uvedeným jménem existuje. Pak musí master handle zavřít a použít jiné jméno. Na straně slave se použije funkce OpenFileMapping která je jednodušší. Pomocí níž ale nelze založit novou sdílenou paměť, pouze otevřít již existující.

Sdílení pomocí handle

Často můžeme chtít najít řešení nezávislé na unikátnosti nějakého jména. A hledat opravdu unikátního jména může být zbytečná komplikace. Je tedy mnohem lepší použít handle.

Postup je v tomto případě naprosto totožný. Jedinou změnou je, že místo jména uvedeme NULL

HANDLE sharedMem=CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, size, 0);
void *sharePtr=MapViewOfFile(sharedMem, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);

Vraťme se nyní ke kapitole o rourách, jakým způsobem jsme předávali handle roury do jiné aplikace.

HANDLE othersize=OpenProcess(PROCESS_DUP_HANDLE,FALSE,ID); //vytvoř handle k procesu
HANDLE tvojeSharedMem;
DuplicateHandle(GetCurrentProcess(), sharedMem, otherside, &tvojeSharedMem, 0, FALSE, DUPLICATE_SAME_ACCESS);

odeslání handle tvojeSharedMem je naprosto stejn8 jako u rour. Na druhé straně provede process následující

HANDLE sharedMem=0;
void *sharePtr=0
void OnIncomeSharedMemRequest(WPARAM wParam, LPARAM lParam); //handle je v lParam
{
sharedMem=(HANDLE)lParam;
sharePtr=MapViewOfFile(sharedMem, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);

//...
}

Pochopitelně nezapomenout paměť odmapovat a handle zavřít v okamžiku, kdy není potřeba.

Problémy se sdílení

Pokud se někde něco sdílí, pak je nutná vhodná synchronizace. Nezapomeňme, že mezi procesy se nesmí používat kritická sekce. Je třeba použít Mutex nebo Semafor, popřípadě Event. I s těmito objekty pozor. K objektům se přistupuje pomocí handlů, a ty jsou hodnotově unikátní pouze v rámci procesu. Stejný objekt má tedy v jiném procesu jiný handle. Pokud chceme objekty sdílet mezi procesy, máme opět dvě možnosti.

použít jméno objektu
Není pak problém příslušný synchronizační objekt otevřít jménem.
přenést handle objektu do druhého procesu pomocí DuplicateHandle
Postup je totožný jako při přenosu roury. U sdílené paměťi můžeme převedený handle uložit do sdílené paměti, nebo také použít zprávu. Zálež na nás.

Implementace velké zprávy

Existuje jedno řešení jak se vyhnout nutnosti synchronizace, potřebujeme-li sdílenou paměť pro přenos nějakých dat, které není možné přenést zprávou. Kdyby to nebylo mezi procesy ale třeba mezi thready, použijeme přímo ukazatel, jeho hodnotu uložíme do lParam a v jiném threadu si jej vyzvedneme. Mezi procesy však toto dělat nemůžen, ukazatel se v kontextu jiného procesu stává neplatným.

Leckomu už svítá... Přesně takto můžeme použít handle sdílené paměti.

Alokace sdílené paměti:

HANDLE sharedMem=CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, size, 0);
sharePtr=MapViewOfFile(sharedMem, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);

tady naplníme data

UnmapViewOfFile(sharePtr);

nyní získáme handle cílového procesu

HANDLE othersize=OpenProcess(PROCESS_DUP_HANDLE,FALSE,ID); //vytvoř handle k procesu

převedeme náš handle na jeho (a zavřeme ten náš)

DuplicateHandle(GetCurrentProcess(),  sharedMem, otherside, &sharedMem, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);

odešleme

FindTargetWindow fnd;
fnd.processID=ID;
fnd.uName="unikatni jmeno okna v rámci cílového procesu";
fnd.hWndFound=0;
EnumWindows(FindTargetWindowProc,(LPARAM)&fnd);
if (fnd.hWndFound) //úspech...
{
PostMessage(fnd.hWndFound,<domluvené číslo zprávy>,0,(LPARAM)tvoje);
}

Zavřeme handle procesu

CloseHandle(othersize);

To je vše na naší straně. Teď se podíváme na opačnou stranu. Tam předpokládejme, že přes WinProc jsme vyzvedli zprávu mající domluvené číslo a zavolali funkci OnIncomeSharedMemRequest

void OnIncomeSharedMemRequest(WPARAM wParam, LPARAM lParam); //handle je v lParam
{
sharedMem=(HANDLE)lParam;
sharePtr=MapViewOfFile(sharedMem, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);
//...vyzvedni data
UnmapViewOfFile(sharePtr);
CloseHandle(sharedMem); //v tuto chvíly paměť přestala existovat.
}

Atomy

Atomy jsou starší záležitost, ale fungují celkem dobře. Jsou výhodné pro předávání řetězcových hodnot, které se do zprávy nevejdou. Místo tedy abysme předávali řetězec, alokujeme atom a předáváme číslo atomu. Druhá strana si atom vyzvedne. Je dobré, když se obě strany domluví, kdo atom nakonec smaže. Při použití SendMessage je ideální, když atom smaže odesílatel.

GlobalAddAtom
GlobalGetAtomName
GlobalDeleteAtom

Clipboard

Clipboard je jedno z mála dědictví po Win3.1. Služba se dosud ovládá naprosto stejně jako tehdy. S tím rozdílem, že tehdy se používala sdílená paměť, která skutečně byla sdílená, dnes je Clipboard emulovaný a interně používá některou zde uvedenou techniku.

Clipboard je vhodné opravdu spíš používat pro Copy a Paste a nepokoušet se jej používat o nějaou komplexnější komunikaci mezi processy. Zájemci o programování clipboardu bych odkázal sem:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard.asp

Sockety

Využití socketu bych nechal na jiný článek, zmíním se jen o tom, že samozřejmě je možné navazovat síťové TCP/IP spojení v rámci jednoho počítače a použít protokol TCP pro komunikaci mezi procesy. Vlastní komunikace je podobná jako u rour. Opět je třeba mezi procesy přenášet nějakou informaci, například číslo portu na kterém server poslouchá, nebo volit číslo portu pevně.

Výhodou použití soketů je komunikace mezi procesy běžící na jiných strojích. Tuto možnost rozhodně nemá sdílená paměť a u rour je toto možné jen u nazvaných rour v rámci sítě Microsoft Network (a postup vytváření je jiný)

Mailsloty, DDE, COM, RPC a další

Existují i jiné techniky, které bych řekl, že stojí na okraji našeho tématu. Zájemce odkazuji na oficiální stránky MSDN

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/base/interprocess_communications.asp

vytvořeno: 31.8.2006 09:28:53, změněno: 7.9.2006 10:08:13
Jsou informace v článku pro Vás užitečné?
  • (21)
  • (3)
  • (0)
  • (0)
  • (0)
Nick nebo OpenID
Vzkaz
 
25.7.2016 18:36:27

g8rJFutiW

Pretty section of content. I just stumbled upon your web site and in accession capital to assert that I get actually enjoyed account your blog posts. Any way I’ll be subscribing to your feeds and even I achievement you access coelnstintsy rapidly.
15.4.2016 22:59:42

Gucci Outlet Online

Color: neutral color easily with a suit Brown, black, dark brown, camel, beige and other
1.4.2016 18:22:51

Buy Gucci

When Gucci Bags, remember to take out belongings they will usually have everything, if you buy
7.11.2013 12:07:55

cheap nike jerseys

I feel more grateful for you to give a chance to me to become a member in your forum and share this post with me.You need to get involved in a competition for one of the most useful information sites on the web. I
8.8.2013 06:02:23

mulberry outlet

Thank you for sharing! Very good.I hope to see you share more.
mulberry outlet http://www.supermbsale.com
3.8.2013 10:59:06

Armani Watches UK

Wow! I must say this blog is one of the best sources of information on file formats.
Armani Watches UK http://www.armaniwatchesformenuksale.co.uk
24.7.2013 11:25:09

Replica Watches sale

Your article really helps me a lot. I had searched the Internet and library for information for a long time.
Replica Watches sale http://www.watches3.com
3.1.2013 10:24:51

baidu

I like this web site very much, Its a rattling nice billet to read and find information. "As against having beautiful workshops, studios, etc., one writes best in a cellar on a rainy day." by Van Wyck Brooks.
baidu http://www.baidu.com/

Podobné články

Linux vs Windows: Procesy

V prvním díle seriálu Linux vs Windows se zaměříme na úplně nejzákladnější službu OS. Spouštění a správa procesů.

Jaký je rozdíl mezi Windows Vista a Windows XP

Obrázky, ze kterých si obrázek uděláte sami

Linux vs Windows - slovo úvodem

Začátek nového seriálu o rozdílech mezi Windows a Linux

Distributor - vaše objekty budou o svém okolí vědět vše

Největším problémem většiny UI /ale i ne-IU/ aplikací, které potkávám, je špatná nebo vůbec nevyřešená komunikace mezi důležitými objekty. Přitom stačí málo, obstarat si distributora.

Jak bootovat do Linuxu a Windows

Instalace Linuxu do počítače kde běží Windows je dnes už celkem jednoduchá. Instalační program zařídí vše, včetně bootování do různých systémů. Nic ale není bez problémů
Reklama: