<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ZetBlog&#187; прикладное</title>
	<atom:link href="http://zetblog.ru/category/programming/application/feed/" rel="self" type="application/rss+xml" />
	<link>http://zetblog.ru</link>
	<description>Зеты говорят. Блог о программировании, администрировании и безопасности.</description>
	<lastBuildDate>Sat, 29 Oct 2011 18:59:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Delphi: Пишем шаблон клиент-серверного приложения.</title>
		<link>http://zetblog.ru/programming/201004/delphi-%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%ba%d0%bb%d0%b8%d0%b5%d0%bd%d1%82-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd/</link>
		<comments>http://zetblog.ru/programming/201004/delphi-%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%ba%d0%bb%d0%b8%d0%b5%d0%bd%d1%82-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 07:47:37 +0000</pubDate>
		<dc:creator>C0ffe1n</dc:creator>
				<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[сетевое]]></category>
		<category><![CDATA[ClientSocket]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[ServerSocket]]></category>
		<category><![CDATA[сеть]]></category>
		<category><![CDATA[файлы]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=913</guid>
		<description><![CDATA[Продолжая тему разработки собственной утилиты администрирования, в данной статье я рассмотрю типовой шаблон программы клиент-сервер, на базе которой можно разрабатывать собственные клиент-серверные приложения. Данный типовой шаблон для простоты понимания и удобства применения будет рассмотрен на примере компонентов ClientSocket и ServerSocket. Примечание Для тех, кто не в курсе, что значит клиент-серверное приложение, поясню: это комплекс программ [...]]]></description>
			<content:encoded><![CDATA[<p>Продолжая тему разработки собственной утилиты администрирования, в данной статье я рассмотрю типовой шаблон программы клиент-сервер, на базе которой можно разрабатывать собственные клиент-серверные приложения. Данный типовой шаблон для простоты понимания и удобства применения будет рассмотрен на примере компонентов <strong>ClientSocket</strong> и <strong>ServerSocket</strong>.<br />
<span id="more-913"></span><strong></strong></p>
<p><strong>Примечание</strong></p>
<div class="codesnip-container" >Для тех, кто не в курсе, что значит клиент-серверное приложение, поясню: это комплекс программ (модулей) состоящий из двух частей &#8212; клиентской и серверной. Серверная часть является &#171;главной&#187;- так называемый командный пункт, на который возложена задача поддержания связи со всеми клиентами и раздача команд управления, соответствующих их функционалу (управление клиентами). Клиентская же часть является второстепенной, но не менее важной, так как является важным и связующим элементом, позволяющим выполнять удаленные команды (функции) на компьютере, на котором установлен.</div>
<div class="codesnip-container" >Компоненты <strong>ClientSocket</strong> и <strong>ServerSocket</strong>, которые мы будем использовать, находятся на вкладке <strong>Internet</strong>. Если на этой вкладке у Вас нет этих компонентов (а по умолчанию при установке <strong>Delphi7</strong> они не ставятся), необходимо установить их самостоятельно (пакет <strong>dclsocketsXX.bpl</strong>). Данный пакет можно найти на установочном диске<strong> Delphi7</strong> (или в папке<span style="text-decoration: underline;"> C:\Temp</span>, куда распаковывается дистрибутив перед установкой &#8212; <span style="text-decoration: underline;">C:\Temp\delphi7\install\program files\borland\delphi7\bin\</span>) или можете его скачать <a href="/forum/downloadf/components/dclsockets70.rar">отсюда</a>. Чтобы установить данный пакет, запустите <strong>Delphi7</strong>. Зайдите в меню <span style="text-decoration: underline;">Component-&gt;Install Packages</span>. В появившемся окне нажмите кнопку <strong>&#171;Add&#187;</strong> и укажите место, где расположен пакет <strong>dclsocketsXX.bpl</strong>. После этого жмите<strong> ОК</strong>. Компонент на месте. ;)</div>
<p>Надеюсь все объяснил доступно =). А теперь приступим к делу и начнем мы с серверной части. Для этого создадим проект и скинем на форму компонент <strong>ServerSocket</strong>. В настройках компонента <strong>ServerSocket</strong> укажем следующее:</p>
<div class="codesnip-container" >Active = false<br />
Name = ss<br />
Port = 4321<br />
ServerType = stNonBlocking</div>
<p>А также для удобства скинем компонент <strong>Memo</strong>, в который будем выводить всякую инфу. Дадим имя компоненту <strong>log</strong>.</p>
<p>Далее, для компонента <strong>ServerSocket</strong> определяем метод <strong>onClientConnect</strong>, чтобы определять момент подключения клиентов. Вставим следующий код:</p>
<pre class=".brush: pascal">procedure TForm1.ssClientConnect(Sender: TObject;
  Socket: TCustomWinSocket);
var
  myDate : TDateTime;
  formattedDateTime : string;
begin
  mydate:=Now;
  DateTimeToString(formattedDateTime, 'c', myDate);
  log.Lines.Add(formattedDateTime+': Есть коннект c '+Socket.RemoteAddress);
  { далее вы можете вставить свой код }
end;</pre>
<p>А теперь определим метод <strong>onClientRead</strong>, в котором будем обрабатывать получаемые сообщения от клиентов:</p>
<pre class=".brush: pascal">procedure TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  s:string;
begin
  s:=Socket.ReceiveText;
  log.Lines.Add(s);
end;</pre>
<p>Определим метод<strong> onClientDisconnect</strong>, чтобы фиксировать факт отключения клиента:</p>
<pre class=".brush: pascal">procedure TForm1.ssClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
var
  myDate : TDateTime;
  formattedDateTime : string;
begin
  mydate:=Now;
  DateTimeToString(formattedDateTime, 'c', myDate);
  log.Lines.Add(formattedDateTime+': Клиент '+socket.RemoteAddress+' отключился.');
end;</pre>
<p>На этом с серверной частью покончено =).</p>
<p>Приступим к клиентской части. Для этого создадим новый проект, скинем на форму компонент <strong>ClientSocket</strong> и таймер. В настройках компонента укажем следующее:</p>
<div class="codesnip-container" >Active = false<br />
Name = сs<br />
Port = 4321<br />
ClientType = ctNonBlocking<br />
Address = 127.0.0.1</div>
<p>Для простоты пояснения я в <strong>Address</strong> указал <strong>&#171;петлю&#187;</strong>. Но для гибкости приложения рекомендую это поле обрабатывать программно, во время <strong>CreateForm</strong> при обработке конфигурационного файла, в котором можно указывать нужный <strong>IP-адрес сервера</strong>.</p>
<p>Здесь также для удобства скинем на форму компонент <strong>Memo</strong>, в который будем выводить техническую инфу. Имя дадим соответствующее<strong> log</strong>.</p>
<p>Для компонента <strong>ClientSocket</strong> определяем метод <strong>onError</strong> для того, чтобы обрабатывать исключения, возникшие в момент подключения клиента к серверу. Вставим следующий код:</p>
<pre class=".brush: pascal">procedure TForm1.csError(Sender: TObject; Socket: TCustomWinSocket;
  ErrorEvent: TErrorEvent; var ErrorCode: Integer);
var
  myDate : TDateTime;
  formattedDateTime : string;
begin
 if ErrorEvent= eeConnect  then
  begin
    cs.Active:=false;
      {добавить запись в журнал};
    mydate:=Now;
    DateTimeToString(formattedDateTime, 'c', myDate);
    log.lines.Add(formattedDateTime+': Невозможно установить соединение с сервером - '+cs.Address);
  end;
  ErrorCode:=0;
  { далее вы можете вставить свой код }
end;</pre>
<p>Теперь определим метод <strong>onConnect</strong>. Вставим следующий код:</p>
<pre class=".brush: pascal">procedure TForm1.csConnect(Sender: TObject; Socket: TCustomWinSocket);
var
  myDate : TDateTime;
  formattedDateTime : string;
begin
  mydate:=Now;
  DateTimeToString(formattedDateTime, 'c', myDate);
  log.lines.Add(formattedDateTime+': Соединение с сервером - '+socket.RemoteAddress+' установлено.');
end;</pre>
<p>И чтобы фиксировать момент потери связи с сервером, определим метод <strong>onDisconnect</strong>:</p>
<pre class=".brush: pascal">procedure TForm1.csDisconnect(Sender: TObject; Socket: TCustomWinSocket);
var
  myDate : TDateTime;
  formattedDateTime : string;
begin
  mydate:=Now; //получим текущее время
  DateTimeToString(formattedDateTime, 'c', myDate); //преобразуем в строку
  log.Lines.Add(formattedDateTime+': Соединение с сервером '+socket.RemoteAddress+' потеряно.');
end;</pre>
<p>Таймер настроим так:</p>
<div class="codesnip-container" >Enabled = true<br />
Name = te<br />
Interval = 5000</div>
<p>И определяем метод <strong>onTimer</strong>. Вставим следующий код:</p>
<pre class=".brush: pascal">procedure TForm1.Timer1Timer(Sender: TObject);
begin
if not cs.Active then
  begin
    cs.Active:=true;
  end;
  cs.Socket.SendText('ping client');
end;</pre>
<p>Данный код проверяет наличие соединения с сервером и в случае его отсутствия пытается его установить.<br />
И каждые 5 секунд отправляет серверу сообщение <strong>&#171;ping client&#187;</strong>.</p>
<p>Вот примерно так выглядит типовой шаблон приложения <strong>&#171;клиент-сервер&#187;</strong>.</p>
<p><strong>Примечание</strong></p>
<div class="codesnip-container" >Хотел бы обратить внимание на обработчик <strong>onError</strong> тех, кто ранее не знал как избавиться от злосчастного сообщения &#8212; <strong>&#171;Asynchronous socket error 10061&#8243;</strong>, возникающее при попытке осуществления подключения к серверу, который недоступен.</div>
<p>Рабочий пример можно скачать <a title="Рабочий пример" href="/forum/downloadf/simples/client-server.rar">отсюда</a>.</p>
<p>Если есть кому что добавить или задать вопрос по теме &#8212; прошу отписываться в комментах ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/201004/delphi-%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%ba%d0%bb%d0%b8%d0%b5%d0%bd%d1%82-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Python: Проблема с подвисанием GUI на TkInter.</title>
		<link>http://zetblog.ru/programming/200910/python-tkinter-antifreeze/</link>
		<comments>http://zetblog.ru/programming/200910/python-tkinter-antifreeze/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 18:57:03 +0000</pubDate>
		<dc:creator>lizz</dc:creator>
				<category><![CDATA[общие вопросы]]></category>
		<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[TkInter]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=731</guid>
		<description><![CDATA[Встала задача написания небольшой графической оболочки для программы с минимальным интерфейсом (одна кнопка и одно поле вывода). Много нам не надо, поэтому решил обойтись идущей вместе с python&#8217;ом в поставке библиотекой TkInter. И тут столкнулся с небольшой проблемой &#8212; при нажатии кнопки запускаются довольно долгие и объёмные вычисления, из-за которых виснет весь интерфейс программы. Решений [...]]]></description>
			<content:encoded><![CDATA[<p>Встала задача написания небольшой графической оболочки для программы с минимальным интерфейсом (одна кнопка и одно поле вывода). Много нам не надо, поэтому решил обойтись идущей вместе с python&#8217;ом в поставке библиотекой TkInter. И тут столкнулся с небольшой проблемой &#8212; при нажатии кнопки запускаются довольно долгие и объёмные вычисления, из-за которых виснет весь интерфейс программы.<br />
<span id="more-731"></span></p>
<p>Решений у этого вопроса несколько.<br />
1 &#8212; Запускать эти вычисления в отдельном потоке, что меня вполне устроило:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="co1"># -*- coding: utf-8 -*-</span>
<span class="kw1">from</span> <span class="kw3">Tkinter</span> <span class="kw1">import</span> <span class="sy0">*</span>
<span class="kw1">import</span> <span class="kw3">threading</span>, <span class="kw3">time</span>

<span class="kw1">class</span> AppGUI:
&nbsp; &nbsp; <span class="kw1">def</span> <span class="kw4">__init__</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="me1">tk</span> = Tk<span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; frame = Frame<span class="br0">&#40;</span><span class="kw2">self</span>.<span class="me1">tk</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; frame.<span class="me1">pack</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; Button<span class="br0">&#40;</span>frame, text=<span class="st0">&quot;Compute&quot;</span>, command=<span class="kw2">self</span>.<span class="me1">computeWithGUIAntifreeze</span><span class="br0">&#41;</span>.<span class="me1">pack</span><span class="br0">&#40;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> run<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="me1">tk</span>.<span class="me1">mainloop</span><span class="br0">&#40;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; <span class="kw1">def</span> computeWithGUIAntifreeze<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot; Метод-прокладка для запуска в новом потоке вычислений &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">threading</span>.<span class="me1">Thread</span><span class="br0">&#40;</span>target=<span class="kw2">self</span>.<span class="me1">compute</span><span class="br0">&#41;</span>.<span class="me1">start</span><span class="br0">&#40;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; <span class="kw1">def</span> compute<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;&quot; Тут как раз и происходят длительные вычисления &quot;&quot;&quot;</span><span class="st0">&quot;
&nbsp; &nbsp; &nbsp; &nbsp; time.sleep(15)

gui = AppGUI()
gui.run()</span></div>
</div>
</pre>
<p>2 &#8212; Использовать время от времени методы:</p>
<ul>
<li><strong>Tk.update()</strong>;</li>
<li><strong>Tk.update_idletasks()</strong> &#8212; только обновляет окна, но не вызывает обработку событий, сгенерированных пользователем, в отличие от Tk.update();</li>
</ul>
<p>Использовать эти методы стоит с осторожностью, дабы не возникло неприятных ситуаций (подробнее в документации). Таково предостережение официальной документации.</p>
<p>Ссылки к посту:<br />
<a href="http://www.tcl.tk/man/tcl8.5/TclCmd/update.htm">http://www.tcl.tk/man/tcl8.5/TclCmd/update.htm</a> &#8212; официальная документация по Tcl/Tk.</p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/200910/python-tkinter-antifreeze/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python: генератор списка паролей для перебора (brute-force).</title>
		<link>http://zetblog.ru/programming/200910/python-passwords-list-for-bruteforce/</link>
		<comments>http://zetblog.ru/programming/200910/python-passwords-list-for-bruteforce/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 11:56:19 +0000</pubDate>
		<dc:creator>lizz</dc:creator>
				<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Brute Force]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=716</guid>
		<description><![CDATA[При написании в очередной раз программы для подбора паролей к чему-либо (исключительно в образовательных целях и для учёбы ;-) ) опять возникла необходимость в итераторе по заданному алфавиту. Т.е. необходимо написать класс, который мог бы генерировать пароли для последующей передачи их в программу, которая &#171;примеряла&#187; их к чему-надо. К чему &#8212; дело третье и сейчас [...]]]></description>
			<content:encoded><![CDATA[<p>При написании в очередной раз программы для подбора паролей к чему-либо (исключительно в образовательных целях и для учёбы ;-) ) опять возникла необходимость в итераторе по заданному алфавиту. Т.е. необходимо написать класс, который мог бы генерировать пароли для последующей передачи их в программу, которая &#171;примеряла&#187; их к чему-надо. К чему &#8212; дело третье и сейчас нас (вернее Вас) не волнует. В результате получился скромный класс, который приведён ниже со столь же скромными моими комментариями к нему :-).<br />
<span id="more-716"></span></p>
<p>Сам класс для перебора паролей ко всему и вся таков:</p>
<pre class="brush: python"># -*- coding: utf-8 -*-

EN = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
en = "abcdefghijklmnopqrstuvwxyz"
RU = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"
ru = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
digits = "1234567890"
space = " "
p = ",.-!?;:'\"/()"
op = "+-*/:^()&lt;&gt;="
all_spec = "`~!@#$%^&amp;*-_=+\\|/?.&gt;,&lt; '\";:[]{}"

class ABCIterator:
    firstuse_flag = True
    stop = None

    def __init__(self, start="", stop=None, start_len=None, stop_len=None, abc=en+EN+digits+all_spec):
        assert len(abc) &gt; 0
        if start_len is not None:
            assert start_len &gt; 0
            #assert start == ""
            self.current_str = list(abc[0]*start_len)
        else:
            self.current_str = list(filter(lambda x: x in abc, start))
        if stop_len is not None:
            assert (start_len is None) or (start_len &lt;= stop_len)
            assert stop_len &gt; 0
            self.stop = list(abc[0]*(stop_len+1)) # т.к. итератор работает с полуотрезками
        else:
            if stop is not None: self.stop = list(filter(lambda x: x in abc, stop))
        self.abc = list(abc)

    def __iter__(self):
        return self

    def next(self):
        if (self.stop is not None) and (self.stop == self.current_str):
            raise StopIteration
        if self.current_str == []:
            self.current_str.append(self.abc[0])
            self.firstuse_flag = False
            return self.abc[0]
        elif self.firstuse_flag:
            self.firstuse_flag = False
            return "".join(self.current_str)
        offset = 0
        while offset &lt; len(self.current_str):
            if self.current_str[offset] != self.abc[-1]:
                self.current_str[offset] = self.abc[self.abc.index(self.current_str[offset])+1]
                # выпендрёшь для полуотрезка
                if (self.stop is not None) and (self.current_str == self.stop):
                    raise StopIteration
                return "".join(self.current_str)
            self.current_str[offset] = self.abc[0]
            offset += 1
        self.current_str = [self.abc[0]] + self.current_str
        # опять же оно же
        if (self.stop is not None) and (self.current_str == self.stop):
            raise StopIteration
        return "".join(self.current_str)
</pre>
<p>А таковы мои пояснения к нему:</p>
<ul>
<li><strong>firstuse_flag</strong> &#8212; свойство, которое указывает на то, что итератор используется впервые и не стоит производить итерацию, надо лишь вернуть текущее значени строки. Это нужно для того, чтобы в цикле не &#171;съедалось&#187; первое значение (напр., инициализировали итератор с начальной строкой &#171;aaa&#187;, а при 1й же итерации он вернул бы, без этого чудесного свойства, &#171;baa&#187;)</li>
<li><strong>stop</strong> &#8212; указывает на значение, на котором пора бы уже остановиться и вызвать исключение StopIteration. Если не задано (имеет значение None), то не пора, итератор будет бесконечным</li>
<li><strong>__init__(start=&#187;, stop=None, start_len=None, stop_len=None, abc=en+EN+digits+all_spec, **kwargs)</strong> &#8212; в общем то сам конструктор. <strong>start</strong> &#8212; начальное значение, <strong>stop</strong> &#8212; конечное, если не задано или идёт до начального, то итерации происходят бесконечно. Можно так же задать начальную и конечную длину, место конкретных значений с помощью <strong>start_len</strong> и <strong>stop_len</strong>. Если задана длина и начальные значения, то приоритет будет иметь длина, что можно легко исправить, если для кого-то это критично. <strong>abc</strong> &#8212; алфавит, над которым будут производиться итерации.</li>
<li><strong>__iter__()</strong> &#8212; необходимо для того чтобы класс был полноценным итератором.</li>
<li><strong>next()</strong> &#8212; возвращает следующую строку, в общем-то этот метод так же необходим для того чтобы класс был итератором.</li>
</ul>
<p>Пример использования:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="sy0">&gt;&gt;&gt;</span> <span class="kw1">for</span> i <span class="kw1">in</span> ABCIterator<span class="br0">&#40;</span>start_len=<span class="nu0">1</span>, stop_len=<span class="nu0">3</span>, abc=<span class="st0">&quot;abc&quot;</span><span class="br0">&#41;</span>:
... &nbsp; &nbsp; <span class="me1">i</span>
...
<span class="st0">'a'</span>
<span class="st0">'b'</span>
<span class="br0">&#91;</span>...<span class="br0">&#93;</span>
<span class="st0">'bcc'</span>
<span class="st0">'ccc'</span>
<span class="sy0">&gt;&gt;&gt;</span></div>
</div>
</pre>
<p>Ну, как-то так оно и работает. Если кому-то что-то непонятно, то могу объяснить подробнее. В общем как обычно, всё в комменты ;-). Неплохо было бы ещё реализовать преобразование чисел в строки и наоборот, но это позже&#8230; может быть.</p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/200910/python-passwords-list-for-bruteforce/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>WinAPI: Смена серийного номера тома.</title>
		<link>http://zetblog.ru/programming/200902/winapi-%d1%81%d0%bc%d0%b5%d0%bd%d0%b0-%d1%81%d0%b5%d1%80%d0%b8%d0%b9%d0%bd%d0%be%d0%b3%d0%be-%d0%bd%d0%be%d0%bc%d0%b5%d1%80%d0%b0-%d1%82%d0%be%d0%bc%d0%b0/</link>
		<comments>http://zetblog.ru/programming/200902/winapi-%d1%81%d0%bc%d0%b5%d0%bd%d0%b0-%d1%81%d0%b5%d1%80%d0%b8%d0%b9%d0%bd%d0%be%d0%b3%d0%be-%d0%bd%d0%be%d0%bc%d0%b5%d1%80%d0%b0-%d1%82%d0%be%d0%bc%d0%b0/#comments</comments>
		<pubDate>Fri, 20 Feb 2009 23:32:29 +0000</pubDate>
		<dc:creator>C0ffe1n</dc:creator>
				<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[системное]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[WinAPI]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=534</guid>
		<description><![CDATA[В статье &#171;WinAPI: Определение типа носителя и его серийного номера&#187; я показывал простой пример как реализовать защиту программ путем привязки к железу. Но все бы хорошо, да только бывают моменты, когда просто необходимо &#171;отвязаться&#187; от железки. С такой проблемой столкнулся sansan. Собственно, благодаря ему и была написана эта статья. Ну а теперь от слов к [...]]]></description>
			<content:encoded><![CDATA[<p>В статье &#171;<a href="/?p=347">WinAPI: Определение типа носителя и его серийного номера</a>&#187; я показывал простой пример как реализовать защиту программ путем привязки к железу. Но все бы хорошо, да только бывают моменты, когда просто необходимо &#171;отвязаться&#187; от железки. С такой проблемой столкнулся <a href="/forum/memberlist.php?mode=viewprofile&amp;u=68"><strong>sansan</strong></a>. Собственно, благодаря ему и была написана эта статья.<br />
<span id="more-534"></span></p>
<p>Ну а теперь от слов к делу. Вся информация для корректной работы накопителя хранится в его загрузочной области. Нас будет интересовать используемая файловая система, от которой зависит расположения нужных байт относительно начала загрузочной области. Поэтому основная наша задача &#8212; это определить тип файловой системы, и сместившись на нужный адрес, переписать значение серийного номера. Всё просто ;). Изучим таблицу.<br />
<strong>Таблица:</strong></p>
<table border="1" width="420">
<tbody>
<tr>
<td width="109"><strong>Поле/ФС</strong></td>
<td width="61"><strong>FAT</strong></td>
<td width="40"><strong>FAT32</strong></td>
<td width="109"><strong>NTFS</strong></td>
</tr>
<tr>
<td width="109">Метка тома</td>
<td width="61">0x2B</td>
<td width="40">0&#215;47</td>
<td width="109">?</td>
</tr>
<tr>
<td width="109">Файловая система</td>
<td width="61">0&#215;36</td>
<td width="40">0&#215;52</td>
<td width="109">0&#215;3</td>
</tr>
<tr>
<td width="109">Серийный номер</td>
<td width="61">0&#215;27</td>
<td width="40">0&#215;43</td>
<td width="109">0&#215;48</td>
</tr>
</tbody>
</table>
<p>В приведенной таблице показаны основные файловые системы и соответствующие для них смещения. Оперируя этой информацией мы и будем реализовывать нашу программу. Нам необходимо реализовать следующие функции:</p>
<ul>
<li>DiskOpen &#8212; открыть диск</li>
<li> DiskClose &#8212; закрыть диск</li>
<li> ReadSector &#8212; прочитать сектор</li>
<li> WriteSector &#8212; записать сектор</li>
</ul>
<p>Реализовывать нашу программу мы будем С++ под семейство Windows NT. В частности, в среде разработки <strong>С++ Builder 6</strong>. Нам понадобиться глобальная переменная, в которой будем хранить указатель на используемый диск:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;">HANDLE hD<span class="sy4">;</span></div>
</div>
</pre>
<p>Начнем c описания функции <strong>DiskOpen()</strong>:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;"><span class="kw4">bool</span> DiskOpen<span class="br0">&#40;</span><span class="kw4">char</span> <span class="sy2">*</span>vol<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp; <span class="kw4">char</span> szDrive<span class="br0">&#91;</span>10<span class="br0">&#93;</span><span class="sy4">;</span>
&nbsp; <span class="kw3">sprintf</span><span class="br0">&#40;</span>szDrive, <span class="st0">&quot;<span class="es1">\\</span><span class="es1">\\</span>.<span class="es1">\\</span>%c:&quot;</span>, vol<span class="br0">&#91;</span>0<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; hD <span class="sy1">=</span> CreateFile<span class="br0">&#40;</span>
&nbsp; &nbsp; szDrive,
&nbsp; &nbsp; GENERIC_READ <span class="sy3">|</span> GENERIC_WRITE,
&nbsp; &nbsp; FILE_SHARE_READ <span class="sy3">|</span> FILE_SHARE_WRITE,
&nbsp; &nbsp; <span class="kw2">NULL</span>,
&nbsp; &nbsp; OPEN_EXISTING,
&nbsp; &nbsp; 0,
&nbsp; &nbsp; <span class="kw2">NULL</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; <span class="kw1">return</span> hD <span class="sy3">!</span><span class="sy1">=</span> INVALID_HANDLE_VALUE<span class="sy4">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>Данной функции в качестве параметра передаётся буква диска, которому нужно сменить серийный номер. С помощью API-функции <strong>CreateFile()</strong>, мы получаем указатель на диск (подробней о CreateFile можно прочитать в статье &#171;<a href="/?p=227">WinAPI: Работа с файлами (основные функции)</a>&#171;). В случае успеха возвращает &#171;истину&#187;, иначе &#8212; &#171;ложь&#187;.</p>
<p>Далее опишем функцию <strong>DiskClose()</strong>:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;"><span class="kw4">void</span> DiskClose<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>hD <span class="sy3">!</span><span class="sy1">=</span> INVALID_HANDLE_VALUE<span class="br0">&#41;</span>
&nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>hD<span class="br0">&#41;</span><span class="sy4">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>Эта функция достаточно простая, она освобождает используемый указатель на диск.</p>
<p>Следующая функция <strong>ReadSector()</strong>:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;"><span class="kw4">bool</span> ReadSector <span class="br0">&#40;</span>DWORD sector, <span class="kw4">char</span> <span class="sy2">*</span>Buffer, <span class="kw4">int</span> sectorSize<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp; DWORD read <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>

&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>SetFilePointer<span class="br0">&#40;</span>hD, sector, <span class="kw2">NULL</span>, FILE_BEGIN<span class="br0">&#41;</span> <span class="sy1">==</span> INVALID_SET_FILE_POINTER<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span><span class="sy4">;</span>

&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>ReadFile<span class="br0">&#40;</span>hD, Buffer, sectorSize, <span class="sy3">&amp;</span>amp<span class="sy4">;</span>read, <span class="kw2">NULL</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span><span class="sy4">;</span>
&nbsp; <span class="kw1">return</span> <span class="kw2">true</span><span class="sy4">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>Эта функция уже посложней. Исользует 3 параметра:</p>
<ul>
<li> первый &#8212; указатель на диск.</li>
<li> второй &#8212; буфер, куда будут записаны данные, считанные из загрузочной области.</li>
<li> третий &#8212; размер загрузочной области, а следовательно и буфера.</li>
</ul>
<p>Функция переводит указатель смещения в файле на нужную позицию с помощью API-функции <strong>SetFilePointer()</strong>, а потом с помощью <strong>ReadFile()</strong> считывает в буфер данные из загрузочной области (подробней о ReadFile() можно прочитать в статье &#171;<a href="/?p=227">WinAPI: Работа с файлами (основные функции)</a>&#171;). В случае успеха возвращает &#171;истину&#187;, иначе &#8212; &#171;ложь&#187;.</p>
<p>И последняя функция <strong>WriteSector()</strong>:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;"><span class="kw4">bool</span> WriteSector<span class="br0">&#40;</span>DWORD sector, <span class="kw4">char</span> <span class="sy2">*</span>Buffer, <span class="kw4">int</span> sectorSize<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp; DWORD wrote <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>

&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>SetFilePointer<span class="br0">&#40;</span>hD, sector, <span class="kw2">NULL</span>, FILE_BEGIN<span class="br0">&#41;</span> <span class="sy1">==</span> INVALID_SET_FILE_POINTER<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span><span class="sy4">;</span>

&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>WriteFile<span class="br0">&#40;</span>hD, Buffer, sectorSize, <span class="sy3">&amp;</span>amp<span class="sy4">;</span>wrote, <span class="kw2">NULL</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span><span class="sy4">;</span>

&nbsp; <span class="kw1">return</span> <span class="kw2">true</span><span class="sy4">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>Эта функция аналогична предыдущей. Только вместо API-функции <strong>ReadFile()</strong> используется <strong>WriteFile()</strong> (подробней о WriteFile() можно прочитать в статье &#171;<a href="/?p=227">WinAPI: Работа с файлами (основные функции)</a>&#171;). В случае успеха возвращает &#171;истину&#187;, иначе &#8212; &#171;ложь&#187;.</p>
<p>Ну что, базовые функции мы реализовали, пора приступить и к завершающей стадии. Скинем на форму 1 кнопку, 1 поле ввода (сюда будем вводить новый серийный номер), и 1 ComboBox (определим выбор основных букв дисков). И для кнопки опишем следующий код:</p>
<pre>
<div class="codesnip-container" >
<div class="cpp codesnip" style="font-family:monospace;"><span class="kw4">void</span> __fastcall TForm1<span class="sy4">::</span><span class="me2">Button1Click</span><span class="br0">&#40;</span>TObject <span class="sy2">*</span>Sender<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
<span class="co1">//проверяем не пусты ли необходимые поля</span>
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>ComboBox1<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>Text <span class="sy3">!</span><span class="sy1">=</span><span class="st0">&quot;&quot;</span><span class="br0">&#41;</span> <span class="sy3">&amp;</span>amp<span class="sy4">;</span><span class="sy3">&amp;</span>amp<span class="sy4">;</span> <span class="br0">&#40;</span>Edit1<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>Text <span class="sy3">!</span><span class="sy1">=</span><span class="st0">&quot;&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; DWORD newSerial <span class="sy1">=</span> StrToInt<span class="br0">&#40;</span>Edit1<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>Text<span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="kw4">const</span> max_pbsi <span class="sy1">=</span> <span class="nu0">3</span><span class="sy4">;</span>
<span class="co1">//определяем структуру для сигнатуры загрузочной области</span>
&nbsp; &nbsp; <span class="kw4">struct</span> partial_boot_sector_info
&nbsp; &nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; LPSTR Fs<span class="sy4">;</span> <span class="co1">// название файловой системы</span>
&nbsp; &nbsp; &nbsp; DWORD FsOffs<span class="sy4">;</span> <span class="co1">// смещение системного имени в загрузочной области</span>
&nbsp; &nbsp; &nbsp; DWORD SerialOffs<span class="sy4">;</span> <span class="co1">// смещение серийного номера в загрузочной области</span>
&nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy4">;</span>
<span class="co1">//описываем часто используемые сигнатуры</span>
&nbsp; &nbsp; partial_boot_sector_info pbsi<span class="br0">&#91;</span>max_pbsi<span class="br0">&#93;</span> <span class="sy1">=</span>
&nbsp; &nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&quot;FAT32&quot;</span>, <span class="nu12">0x52</span>, <span class="nu12">0x43</span><span class="br0">&#125;</span>,
&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&quot;FAT&quot;</span>, &nbsp; <span class="nu12">0x36</span>, <span class="nu12">0x27</span><span class="br0">&#125;</span>,
&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&quot;NTFS&quot;</span>, &nbsp;0x03, 0x48<span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy4">;</span>
<span class="co1">//инициализируем необходимые переменные</span>
&nbsp; &nbsp; TCHAR Drive<span class="br0">&#91;</span>12<span class="br0">&#93;</span><span class="sy1">=</span><span class="br0">&#123;</span>0<span class="br0">&#125;</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="kw4">char</span> Sector<span class="br0">&#91;</span>512<span class="br0">&#93;</span><span class="sy4">;</span>
&nbsp; &nbsp; DWORD i<span class="sy4">;</span>
&nbsp; &nbsp; <span class="kw3">sprintf</span><span class="br0">&#40;</span>Drive, <span class="st0">&quot;%s:<span class="es1">\\</span>&quot;</span>, ComboBox1<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>Text<span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp;<span class="co1">//открываем диск</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>DiskOpen<span class="br0">&#40;</span>Drive<span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="br0">&#123;</span><span class="co1">//если ошибка - оповещаем пользователя и выходим из процедуры</span>
&nbsp; &nbsp; &nbsp; Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Невозможно открыть диск!&quot;</span>,<span class="st0">&quot;Ошибка&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>

&nbsp; <span class="co1">// читаем загрузочную область</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>ReadSector<span class="br0">&#40;</span>0, Sector,512<span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Невозможно считать данные&quot;</span>,<span class="st0">&quot;Ошибка&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>

&nbsp; <span class="co1">// посик сигнатуры загрузочной области</span>
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>i<span class="sy1">=</span><span class="nu0">0</span><span class="sy4">;</span>i<span class="sy1">=</span> max_pbsi<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Невозможно сменить номер для этой файловой системы!&quot;</span>,<span class="st0">&quot;Ошибка&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>

&nbsp; <span class="co1">// меняем номер</span>
&nbsp; &nbsp; <span class="sy2">*</span><span class="br0">&#40;</span>PDWORD<span class="br0">&#41;</span><span class="br0">&#40;</span>Sector<span class="sy2">+</span>pbsi<span class="br0">&#91;</span>i<span class="br0">&#93;</span>.<span class="me1">SerialOffs</span><span class="br0">&#41;</span> <span class="sy1">=</span> newSerial<span class="sy4">;</span>

&nbsp; <span class="co1">// записываем в загрузочную область</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>WriteSector<span class="br0">&#40;</span>0, Sector,512<span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Невозможно записать на диск!&quot;</span>,<span class="st0">&quot;Ошибка&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span><span class="sy4">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; DiskClose<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; &nbsp; Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Серийный номер успешно изменен! Необходимо перезагрузить компьютер.&quot;</span>,<span class="st0">&quot;Внимание&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
&nbsp; <span class="br0">&#125;</span><span class="kw1">else</span>
&nbsp; &nbsp; &nbsp;Application<span class="sy2">-</span><span class="sy3">&amp;</span>gt<span class="sy4">;</span>MessageBoxA<span class="br0">&#40;</span><span class="st0">&quot;Не указан диск или серийный номер!&quot;</span>,<span class="st0">&quot;Внимание&quot;</span><span class="br0">&#41;</span><span class="sy4">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>Если Вы меняете серийный номер тома флешки, то компьютер перезагружать не обязательно, достаточно отключить и заново подключить флешку. В других случаях результат буде виден только после перезагрузки. На этом всё, замечания и вопросы можете высказать на форуме. Спасибо за внимание! ;)</p>
<p><strong>Рабочий пример</strong> <a href="http://depositfiles.com/files/3vg3psz21">скачать</a>.</p>
<p><strong>Рекомендуемые статьи для прочтения:</strong><br />
<a href="/?p=227">WinAPI: Работа с файлами (основные функции).</a><br />
<a href="/?p=347">WinAPI: Определение типа носителя и его серийного номера.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/200902/winapi-%d1%81%d0%bc%d0%b5%d0%bd%d0%b0-%d1%81%d0%b5%d1%80%d0%b8%d0%b9%d0%bd%d0%be%d0%b3%d0%be-%d0%bd%d0%be%d0%bc%d0%b5%d1%80%d0%b0-%d1%82%d0%be%d0%bc%d0%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WinAPI: Работа с файлами (основные функции).</title>
		<link>http://zetblog.ru/programming/200902/winapi-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d1%84%d0%b0%d0%b9%d0%bb%d0%b0%d0%bc%d0%b8-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8/</link>
		<comments>http://zetblog.ru/programming/200902/winapi-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d1%84%d0%b0%d0%b9%d0%bb%d0%b0%d0%bc%d0%b8-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 23:27:55 +0000</pubDate>
		<dc:creator>C0ffe1n</dc:creator>
				<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[системное]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[WinAPI]]></category>
		<category><![CDATA[файлы]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=227</guid>
		<description><![CDATA[Категорически всех приветствую! На этот раз в наше поле зрения попала группа API-функций для работы с файлами. Ибо, как мне кажется, помимо меня многие программисты сталкиваются с необходимостью их использования в своих программах. Но откровенно скажу в голове все эти функции вместе с их возможными параметрами и не упомнишь, а иметь возможность &#171;вспомнить все&#187; ;) [...]]]></description>
			<content:encoded><![CDATA[<p>Категорически всех приветствую! На этот раз в наше поле зрения попала группа API-функций для работы с файлами. Ибо, как мне кажется, помимо меня многие программисты сталкиваются с необходимостью их использования в своих программах. Но откровенно скажу в голове все эти функции вместе с их возможными параметрами и не упомнишь, а иметь возможность &#171;вспомнить все&#187; ;)  прочитав эту статью &#8212; очень неплохая затея. За сим и приступаю к рассмотрению оного =).<br />
<span id="more-227"></span><br />
В этой статье мы рассмотрим следующие API-функции:</p>
<ol>
<li>CreateFile</li>
<li>OpenFile</li>
<li>ReadFile</li>
<li>WriteFile</li>
<li>CloseFile</li>
<li>DeleteFile</li>
<li>CopyFile</li>
<li>FindFirstFile</li>
<li>FindNextFile</li>
<li>GetFileSize</li>
</ol>
<p><strong>Замечание</strong></p>
<p>Но прежде чем  приступить к делу, замечу для случая, когда действия над файлами происходят удаленно.  За счет реализации в ОС Windows механизма прозрачного доступа к файлам,  данные функции работают и с удаленными файлами, отличием является задание сетевого пути к файлу, а именно для С/С++ необходимо указывать путь в следующем формате:   &#171;\\\\имя_удаленного_компа\\путь_к_ файлу\\ имя_файла&#187;.<br />
<strong>Пример:</strong></p>
<pre class=".brush: cpp">
DeleteFile("\\\\comp1\\user1\\test\\test1.txt");
</pre>
<p>Для Delphi необходимо указывать путь в следующем формате:   &#171;\\имя_удаленного_компа\путь_к_ файлу\имя_файла&#187;.</p>
<p><strong>Пример:</strong></p>
<pre class=".brush: pascal">
DeleteFile('\\comp1\user1\test\test1.txt');
</pre>
<p><strong>Функция CreateFile</strong></p>
<p>Функция создаёт указатель на новое устройство типа:</p>
<ul>
<li>Файл</li>
<li>Канал</li>
<li>mailslot (почтовый канал)</li>
<li>комуникационный ресурс (например COM порт)</li>
<li>дисковые устройства (только для Windows NT)</li>
<li>консоли</li>
<li>директории (открывает их)</li>
</ul>
<p>Все эти функции описаны в одном файле. Для С/С++ в хедере (заголовочный файл) winbase.h, для Delphi в модуле windows.pas.  Чтобы использовать в своей программе эти функции, достаточно подключить их к своему проекту и вперед. Замечу для тех, кто кодит на С/С++ &#8212; кроме winbase.h надо подключить еще windows.h.</p>
<p><strong>Описание</strong>:<br />
<strong>С/С++</strong></p>
<pre class=".brush: cpp">HANDLE CreateFile(
   LPCTSTR lpFileName,        // Указатель на имя файла (устройства)
   DWORD dwDesiredAccess,  //Параметры доступа
   DWORD dwShareMode,        //Разделяемый доступ
   LPSECURITY_ATTRIBUTES lpSecurityAttributes,    //безопасность
   DWORD dwCreationDistribution,// Описание
   DWORD dwFlagsAndAttributes,     // Атрибуты файла
   HANDLE hTemplateFile      // Файл шаблона
);</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function CreateFile(
   lpFileName: PChar;   // Указатель на имя файла (устройства)
   dwDesiredAccess,     //Параметры доступа
   dwShareMode: DWORD;  //Разделяемый доступ
   lpSecurityAttributes: PSecurityAttributes; //безопасность
   dwCreationDisposition,       // Описание
   dwFlagsAndAttributes: DWORD; // Атрибуты файла
   hTemplateFile: THandle       // Файл шаблона
): THandle; stdcall;
</pre>
<p>Теперь к главному &#8212; как создать файл? Делается это так:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
   Handle FileHandle;
   FileHandle=CreateFile("file1.txt",GENERIC_READ |
      GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CREATE_NEW,
      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
   CloseHandle(FileHandle);
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileHandle:THandle;
begin
   FileHandle:=CreateFile(PChar('file1.txt'),GENERIC_READ or
      GENERIC_WRITE, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, CREATE_NEW,
      FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
   CloseHandle(FileHandle);
end;
</pre>
<p>Теперь расмотрим используемые параметры:</p>
<ul>
<li>параметры <strong>GENERIC_READ</strong> и      <strong>GENERIC_WRITE</strong> определяют доступ на чтение и запись. Однако, можно указать что то одно в зависимости от ваших потребностей. Если в качестве этого параметра указать 0, то в этом случае функция отработает успешно, однако никакого доступа к файлу не получит. Этот вариант удобно использовать для теста существования файла.</li>
<li>параметры <strong>FILE_SHARE_WRITE</strong> or <strong>FILE_SHARE_READ</strong> &#8212; общий доступ на чтение и запись к данному файлу. То есть файл будет доступен при одновременном использовании несколькими процессами.</li>
<li>следующий параметр &#8212; атрибут безопасности неопределен. То есть все дескрипторы будут доступны дочерним процессам вашего приложения.</li>
<li>параметр <strong>CREATE_NEW</strong> указывает на создание нового файла. Если файл с заданным именем существует, то будет возвращен код ошибки. Если использовать параметр <strong>CREATE_ALWAYS</strong>, то в этом случае, если файл существует, то он будет перезаписан.</li>
<li>параметр <strong>FILE_ATTRIBUTE_NORMAL</strong> определяет файлу стандартные атрибуты. А если указывать флаг <strong>FILE_FLAG_OVERLAPPED</strong>, то файл будет доступен в асинхронном режиме.</li>
<li>так как мы не используем шаблоны, то в качестве этого параметра указываем ноль.</li>
</ul>
<p><strong>Функция OpenFile</strong></p>
<p>Данная функция, по сути своей, предназначена для открытия файлов или устройств. Функция OpenFile в случае успеха возвращает дескриптор (указатель) открываемого файла. В случае неудачи мы получим в качестве дескриптора значение <strong>INVALID_HANDLE_VALUE</strong>.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
 HFILE WINAPI OpenFile(
  LPCSTR lpFileName,
  LPOFSTRUCT lpReOpenBuff,
  UINT uStyle
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function OpenFile(
   lpFileName: LPCSTR,   //имя файла (устройства)
   lpReOpenBuff: TOFStruct,  //специальная структура открываемого файла
   uStyle: UINT //флаги доступа к файлу
): HFILE; stdcall;
</pre>
<p>Теперь рассмотрим используемые параметры:</p>
<ul>
<li>в качестве первого параметра указывается имя открываемого файла.</li>
<li>вторым параметром является специальная структура открываемого файла, в которую заносится полезная инфа о файле.</li>
<li>3 параметр представляет собой совокупность флагов для работы с файлом.</li>
</ul>
<p>Собственно, как открыть файл:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
   Handle FileHandle;
   FileHandle= OpenFile("Settings.ini", Buf, OF_Create |
                            OF_READWRITE | OF_SHARE_EXCLUSIVE);
   CloseHandle(FileHandle);
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileHandle:THandle;
begin
   FileHandle:=OpenFile(PChar('Settings.ini'), Buf, OF_Create
                     Or OF_READWRITE Or OF_SHARE_EXCLUSIVE);
   CloseHandle(FileHandle);
end;
</pre>
<p>В данном примере стоит уделить внимание флагам. Первые 2 из которых определяют доступ к файлу, а флаг <strong>OF_SHARE_EXCLUSIVE</strong> сообщает системе, что доступ к этому файлу запрещен до тех пор, пока указатель на этот файл не освободится.</p>
<p><strong>Функция ReadFile</strong></p>
<p>Данная функция предназначена для чтения файлов или с устройств ввода/вывода. В случае успешного выполнения операции  чтения, возвращается логическое значение true, иначе false. А функция <strong>GetLastError</strong> вернет <strong>ERROR_HANDLE_EOF</strong>.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
BOOL ReadFile(HANDLE hFile, //собственно указатель на файл
         LPVOID lpBuffer,  // указатель на буфер - куда записываем считанные данные
         DWORD nNumberOfBytesToRead, //объем считываемых данных, не может превышать размер буфера
         LPDWORD lpNumberOfBytesRead, //фактический размер считанных данных
         LPOVERLAPPED lpOverlapped   // флаг режима доступа к файлу: асинхронный(<strong>FILE_FLAG_OVERLAPPED</strong>)
                                    //или синхронный(<strong>NULL</strong>)
);
</pre>
<p><strong>Delphi</strong>
<pre class=".brush: pascal">
function ReadFile(
         hFile: THandle;   //собственно указатель на файл
         var Buffer;       // указатель на буфер - куда записываем считанные данные
         nNumberOfBytesToRead: DWORD;  //объем считываемых данных, не может превышать размер буфера
         var lpNumberOfBytesRead: DWORD; //фактический размер считанных данных
         lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(<strong>FILE_FLAG_OVERLAPPED</strong>)
                                    //или синхронный(<strong>nil</strong>)
): BOOL; stdcall;
</pre>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
Handle FileHandle;
char Buf[1000];
...
     ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileHandle:THandle;
  Buf:array[1..1000] of char;
begin
...
   ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
end;
</pre>
<p><strong>Функция WriteFile</strong></p>
<p>Данная функция предназначена для записи в файл или устройство ввода/вывода. С ней все аналогично что и с функцией ReadFile(). В случае успешного выполнения операции  чтения, возвращается логическое значение true, иначе false. А функция <strong>GetLastError</strong> вернет <strong>ERROR_HANDLE_EOF</strong>.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
BOOL WriteFile(HANDLE hFile, //собственно указатель на файл
         LPVOID lpBuffer,  // указатель на буфер - откуда записываем данные в  файл
         DWORD nNumberOfBytesToWrite, //объем записываемых данных
         LPDWORD lpNumberOfBytesWrite, //фактический размер записанных данных
         LPOVERLAPPED lpOverlapped   // флаг режима доступа к файлу: асинхронный(<strong>FILE_FLAG_OVERLAPPED</strong>)
                                    //или синхронный(<strong>NULL</strong>)
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function WriteFile(
         hFile: THandle;   //собственно указатель на файл
         var Buffer;       // указатель на буфер - откуда записываем данные в  файл
         nNumberOfBytesToWrite: DWORD;  //объем записываемых данных
         var lpNumberOfBytesWrite: DWORD; //фактический размер записанных данных
         lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(<strong>FILE_FLAG_OVERLAPPED</strong>)
                                    //или синхронный(<strong>nil</strong>)
): BOOL; stdcall;
</pre>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
Handle FileHandle;
char Buf[1000];
...
     ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileHandle:THandle;
  Buf:array[1..1000] of char;
begin
...
   ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
end;
</pre>
<p><strong>Функция CloseFile</strong></p>
<p>Эта функция достаточно проста. Выполняет действия по освобождению указателя на файл. Ее необходимость очевидна &#8212; чтобы не засорять память ненужными указателями и позволять работу с файлами другим процессам. В качестве ее единственного параметра передается указатель на ранее открытый файл.</p>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
   Handle FileHandle;
   FileHandle= OpenFile("Settings.ini", Buf, OF_Create |
                            OF_READWRITE | OF_SHARE_EXCLUSIVE);
...
   CloseHandle(FileHandle);
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileHandle:THandle;
begin
   FileHandle:=OpenFile(PChar('Settings.ini'), Buf, OF_Create
                     Or OF_READWRITE Or OF_SHARE_EXCLUSIVE);
...
   CloseHandle(FileHandle);
end;
</pre>
<p><strong>Функция DeleteFile</strong></p>
<p>Эта функция так же не отличается особой сложностью. Данная функция удаляет файл, который указан в параметре.  Функция возвращает значение true если выполняется успешно. Иначе возвращает false, а код ошибки можно получить с помощью <strong>GetLastError</strong>.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
BOOL WINAPI DeleteFile(
      LPCTSTR lpFileName // имя файла, который надо удалить
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function DeleteFile(
      lpFileName: PChar; // имя файла, который надо удалить
 ): BOOL; stdcall;
</pre>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
...
     DeleteFile("C:\Temp\test.txt");// если указать только
// имя файла, то будет осуществлена попытка удалить файл из папки, откуда была запущена программа

...
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
begin
...
     DeleteFile('C:\Temp\test.txt');// если указать только
// имя файла, то будет осуществлена попытка удалить файл из папки, откуда была запущена программа
...
end;
</pre>
<p><strong>Функция CopyFile</strong></p>
<p>Основное назначение этой функции &#8212; копирование файла. В случае успеха возвращает true, иначе &#8212; false. Код ошибки можно получить с помощью <strong>GetLastError</strong>.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
BOOL CopyFile(
     LPCTSTR lpExistingFileName,// Указатель на файл, который надо копировать
     LPCTSTR lpNewFileName, // Указатель на имя файла, куда надо копировать
     BOOL bFailIfExists //если указать true, то в случае существования такого файла,
      // произойдет ошибка, если false - то существующий файл будет перезаписан.
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function CopyFile(
     lpExistingFileName, // Указатель на файл, который надо копировать
     lpNewFileName: PChar; // Указатель на имя файла, куда надо копировать
     bFailIfExists: BOOL //если указать true, то в случае существования такого файла,
      // он будет перезаписан, если false - то произойдет ошибка.
): BOOL; stdcall;
</pre>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
...
     CopyFile("C:\Temp\test1.txt","C:\Temp\test2.txt", true); // если указать только
// имя файла, то все действия будут выполнены в папке из которой запущена прога
...
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
begin
...
     CopyFile('C:\Temp\test1.txt','C:\Temp\test2.txt', false);// если указать только
// имя файла, то все действия будут выполнены в папке из которой запущена прога...
end;
</pre>
<p><strong>Функция FindFirstFile</strong></p>
<p>Данная функция запускает поиск в указанной директории. Функция возвращает указатель на найденный файл, если нет, то возврат будет типа ERROR_NO_MORE_FILES.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
HANDLE FindFirstFile(
    LPCTSTR lpFileName,	// Строка, содержащая путь для поиска файлов.
    LPWIN32_FIND_DATA lpFindFileData // Информация о найденном файле
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function FindFirstFile(
   lpFileName: PChar; // Строка, содержащая путь для поиска файлов.
   var lpFindFileData: TWIN32FindData // Информация о найденном файле
): THandle; stdcall;
</pre>
<p>Теперь рассмотрим параметры подробней:</p>
<ul>
<li> lpFileName &#8212; строка, содержащая путь для поиска файла. Эта строка может указывать наконкретный файл типа &#8216;c:\filename.txt&#8217; или может хранить шаблон &#8216;c:\*.*&#8217;. Если указывать шаблон, то это даёт возможность перечислить все файлы удовлетворяющие шаблону.</li>
</ul>
<ul>
<li> lpFindFileData &#8212; структура WIN32_FIND_DATA, в которую будет записана инфа о найденом файле.</li>
</ul>
<p>Расмотрим структуру WIN32_FIND_DATA немного подробнее:</p>
<pre class=".brush: cpp">
typedef struct _WIN32_FIND_DATA {
          DWORD dwFileAttributes;  // Атрибуты файла
          FILETIME ftCreationTime; // Время создания
          FILETIME ftLastAccessTime;  //Время последнего доступа
          FILETIME ftLastWriteTime;   //Время последней записи в файл
          DWORD    nFileSizeHigh;     //Верхний байт размера файла
          DWORD    nFileSizeLow;      //Нижний байт размера файла
          DWORD    dwReserved0;       //Зарезервировано
          DWORD    dwReserved1;       //Зарезервировано
          TCHAR    cFileName[ MAX_PATH ];    //Имя файла
          TCHAR    cAlternateFileName[ 14 ]; //Имя файла для отображения в DOS (8:3)
} WIN32_FIND_DATA;
</pre>
<p>Атрибутами файла может быть комбинация из флагов:</p>
<ul>
<li>FILE_ATTRIBUTE_ARCHIVE &#8212; архивный</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_COMPRESSED &#8212; сжатый</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_HIDDEN &#8212; скрытый</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_NORMAL &#8212; нормальный</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_OFFLINE &#8212; данные файла недоступны</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_READONLY &#8212; только для чтения</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_SYSTEM &#8212; системный</li>
</ul>
<ul>
<li> FILE_ATTRIBUTE_TEMPORARY &#8212; временный</li>
</ul>
<p>Размер файла разложен на два байта. Чтоб получить полный размер файла нужно выполнить действие (FInfo.nFileSizeHigh * MAXDWORD) + FInfo.nFileSizeLow. Это не самый эффективный (эффективнее сдвигать), но самый понятный способ.</p>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
   WIN32_FIND_DATA sss;
   HANDLE f;
   f = FindFirstFile("C:\*.*", &amp;sss);
...
   FindClose(f);
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  sss: TWIN32FindData;
  f:THandle;
begin
...
  f:=FindFirstFile(PChar('C:\*.*'), sss);
...
  Windows.FindClose(f);
end;
</pre>
<p><strong>Функция FindNextFile</strong></p>
<p>Данная функция продолжает поиск начатый функцией FindFirstFile(). Если очередной файл найден, то функция возвращает значение true, иначе  возврат будет типа ERROR_NO_MORE_FILES.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
BOOL FindNextFile(
    HANDLE hFindFile,	// Указатель на файл из предыдущего поиска
    LPWIN32_FIND_DATA lpFindFileData // Информация об очередном найденном файле
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function FindNextFile(
    hFindFile: THandle; // Указатель на файл из предыдущего поиска
    var lpFindFileData: TWIN32FindData // Информация об очередном найденном файле
): BOOL; stdcall;
</pre>
<p>Со вторым параметром всё ясно, из описания функции FindFirstFile. А вот первый &#8212; это указатель на файл из предыдущего поиска. Он нужен, чтобы функция FindNextFile знала на каком файле был остановлен поиск и какой надо найти следующим.</p>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
   WIN32_FIND_DATA sss;
   HANDLE f;
   f = FindFirstFile("C:\*.*", &amp;sss);
   if(f != INVALID_HANDLE_VALUE)
      do{
         ...
      }while(FindNextFile(f,&amp;sss));
   FindClose(f);
}
</pre>
<p>А теперь пример рекурсивного поиска файлов:<br />
<strong>Delphi</strong></p>
<pre class=".brush: pascal">
function find(put: string):integer;
var
  sss: TWIN32FindData;
  f:THandle;
  result:Boolean;
begin
...
  f:=FindFirstFile(PChar('C:\*.*'), sss);
  if f&lt;&gt;INVALID_HANDLE_VALUE then
  begin
    result:=true;
    while(result)do
    begin
      if not((sss.cFileName = '.')or(sss.cFileName = '..')) then
        if (sss.dwFileAttributes and faDirectory) &gt; 0  then
          find(put+'\'+sss.cFileName)
        else
          writeln(sss.cFileName);
       ...
      result:=FindNextFile(f,sss);
    end;
  end;
  Windows.FindClose(f);
end;
</pre>
<p><strong>Функция GetFileSize</strong></p>
<p>Данная функция определяет размер файла, на который ссылается переданный указатель. Если размер файла меньше 4 Гб, то функция возвращает размер файла. Если размер файла больше 4Гб, то размер файла записан во втором параметре. В случае возникновения исключения будет возвращено значение типа INVALID_FILE_SIZE.<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
DWORD GetFileSize(
   HANDLE hFile,	   // Указатель на файл
   LPDWORD lpFileSizeHigh // старший байт размера файла, в качестве
            // этого парамметра можно указать <strong>NULL</strong>
);
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
function GetFileSize(
    hFile: THandle;         // Указатель на файл
    lpFileSizeHigh: Pointer //старший байт размера файла, в качестве
             // этого парамметра можно указать <strong>nil</strong>
): DWORD; stdcall;
</pre>
<p>Теперь поглядим рабочий пример:<br />
<strong>C/C++</strong></p>
<pre class=".brush: cpp">
{
DWORD FileSize;
Handle f;
...
FileSize=GetFileSize(f,NULL);
...
}
</pre>
<p><strong>Delphi</strong></p>
<pre class=".brush: pascal">
var
  FileSize:DWORD;
  f:THandle;
begin
  ...
  FileSize:=GetFileSize(f,nil);
  ...
end
</pre>
<p>На этом пока все, если вдруг возникли какие нибуть вопросы или замечания, прошу их высказывать на <a href="/forum/">форуме</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/200902/winapi-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d1%84%d0%b0%d0%b9%d0%bb%d0%b0%d0%bc%d0%b8-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Python: Масштабирование и наложение водяных знаков на изображение.</title>
		<link>http://zetblog.ru/programming/200902/python-%d0%bc%d0%b0%d1%81%d1%88%d1%82%d0%b0%d0%b1%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b8-%d0%bd%d0%b0%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2%d0%be%d0%b4%d1%8f%d0%bd%d1%8b/</link>
		<comments>http://zetblog.ru/programming/200902/python-%d0%bc%d0%b0%d1%81%d1%88%d1%82%d0%b0%d0%b1%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b8-%d0%bd%d0%b0%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2%d0%be%d0%b4%d1%8f%d0%bd%d1%8b/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 00:40:57 +0000</pubDate>
		<dc:creator>lizz</dc:creator>
				<category><![CDATA[прикладное]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[PIL]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[графика]]></category>

		<guid isPermaLink="false">http://zetblog.ru/?p=517</guid>
		<description><![CDATA[В данной статье будут рассмотрены некоторые возможности PIL (Python Imaging Library), а именно &#8212; использование библиотеки для создания превьюшек и наложения одного изображения на другое. Всё это делается достаточно тривиально, если знать как, ну а те, кто не знает, могут читать дальше ;-). Начнём с функции масштабирования. import Image def create_thumbnails(img, size): thumbnails = [] [...]]]></description>
			<content:encoded><![CDATA[<p>В данной статье будут рассмотрены некоторые возможности PIL (Python Imaging Library), а именно &#8212; использование библиотеки для создания превьюшек и наложения одного изображения на другое. Всё это делается достаточно тривиально, если знать как, ну а те, кто не знает, могут читать дальше ;-).<br />
<span id="more-517"></span></p>
<p>Начнём с функции масштабирования.</p>
<pre class="brush: python">import Image

def create_thumbnails(img, size):
    thumbnails = []
    for s in size:
        if img.size[0] == img.size[1]:
                thumbnails.append(img.resize((s, s), Image.ANTIALIAS))
        elif img.size[0] > img.size[1]:
            ratio = float(img.size[0]) / s
            thumbnails.append(img.resize((s, int(img.size[1]/ratio)), Image.ANTIALIAS))
        else:
            ratio = float(img.size[1]) / s
            thumbnails.append(img.resize((int(img.size[0]/ratio), s), Image.ANTIALIAS))
    return thumbnails

if __name__ == "__main__":
    img = Image.open("1.jpg")
    thumbs = create_thumbnails(img, (1000, ))
    thumbs.extend(create_thumbnails(img, (500, 250, 100)))

    c = 0
    for t in thumbs:
        t.save("%dt.jpg" % c)
        c += 1
</pre>
<p><strong>UPD</strong>: Как оказалось, код выше &#8212; велосипед :). Всё делается проще. Вместо вычисления соотношений пишем просто img.thumbnail((100, 100), Image.ANTIALIAS), остально за Вас всё сделает питон&#8230; Эхх, никакой свободы мысли ;).</p>
<p>Функция Image.open(), как Вы догадались, открывает изображение для дальнейших манипуляций с ним. Наша функция принимает полученный в результате открытия объект и список размеров (поэтому, если мы передаём один размер, то надо указать в параметрах, например, (100, ), а не просто (100)), по которым потом и будем масштабировать. Масштабирование происходит по большей стороне с сохранением пропорций. Возваращает функция так же список полученных превьюшек.</p>
<p>img.resize() &#8212; вторым параметром указывается метод преобразования, с Image.ANTIALIAS получается наиболее лучший вариант, так же можно указать Image.NEAREST, Image.BILINEAR, Image.BICUBIC вместо ANTIALIAS, первым &#8212; список из ширины и высоты нового изображения.</p>
<p>Теперь перейдём к водяным знакам. Для начала создадим какой-нибудь рисунок или надпись с прозрачным фоном и сохраним в формате png &#8212; это и будет наш водяной знак. Можно накладывать его разным способами, мы реализуем клонирование с заданным интервалом. Итак, вот как это примерно будет выглядеть:</p>
<pre class="brush: python">import Image, ImageEnhance

def add_watermark(image, watermark, opacity=1, wm_interval=0):
    assert opacity >= 0 and opacity <= 1
    if opacity < 1:
        if watermark.mode != 'RGBA':
            self.watermark = watermark.convert('RGBA')
        else:
            watermark = watermark.copy()
        alpha = watermark.split()[3]
        alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
        watermark.putalpha(alpha)

    layer = Image.new('RGBA', image.size, (0,0,0,0))
    for y in range(0, image.size[1], watermark.size[1]+wm_interval):
        for x in range(0, image.size[0], watermark.size[0]+wm_interval):
            layer.paste(watermark, (x, y))
    return Image.composite(layer,  image,  layer)

if __name__ == "__main__":
    img = Image.open("1.jpg")
    wm = Image.open("wm.png")
    add_watermark(img, wm, 0.5, 50).save("wm_1.jpg")
</pre>
<p>В этой функции мы уменьшаем прозрачность, если она задана меньше 1, затем создаём новую картинку layer размером с исходное изображение и добавляем на неё водяные знаки с заданным интервалом, наконец возвращаем изображение, полученное из исходной картинки и нашей новой - layer.</p>
<p>Ссылки к статье:<br />
<a href="http://code.activestate.com/recipes/362879/">http://code.activestate.com/recipes/362879/</a> - Watermark with PIL.<br />
<a href="http://www.daniweb.com/code/snippet396.html"></a> - Resize an image (Python).</p>
]]></content:encoded>
			<wfw:commentRss>http://zetblog.ru/programming/200902/python-%d0%bc%d0%b0%d1%81%d1%88%d1%82%d0%b0%d0%b1%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b8-%d0%bd%d0%b0%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2%d0%be%d0%b4%d1%8f%d0%bd%d1%8b/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

