Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Delphi - проблемы и решения" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
← Август 2002 → | ||||||
1
|
2
|
3
|
4
|
|||
---|---|---|---|---|---|---|
5
|
7
|
8
|
9
|
10
|
11
|
|
12
|
13
|
14
|
15
|
16
|
17
|
18
|
19
|
21
|
22
|
23
|
24
|
25
|
|
26
|
27
|
28
|
29
|
30
|
31
|
Автор
Статистика
6.267 подписчиков
-22 за неделю
-22 за неделю
NTFS : Управление квотами (часть II)
Информационный Канал Subscribe.Ru |
Выпуск номер : 6 [ 20 августа 2002 года]
Здравствуйте, уважаемые подписчики.
Сначала хочу извиниться за то, что на прошлой неделе рассылка не вышла - у меня получился вынужденный незапланированный отпуск :). И еще хочу заметить, что прошлый выпуск по номеру был 5ым, а не 4ым, как было написано в заголовке :)
Теперь небольшая работа над ошибками - в прошлом номере был опубликован исходник dskquota.pas. В нем было сделано несколько ошибок. За обнаружение первой спасибо Вячеславу Васильеву.
То, что в Pascal - переменная интерфейса, в С - указатель на интерфейс. Поэтому в Enum интерфейсах: PPEnumDiskQuotaUsers = ^PEnumDiskQuotaUsers; PEnumDiskQuotaUsers = ^IEnumDiskQuotaUsers; IEnumDiskQuotaUsers = interface(IUnknown) ['{7988B577-EC89-11cf-9C00-00AA00A14F56}'] function Next( cUsers : DWORD; rgUsers : PPDiskQuotaUser; var pcUsersFetched : DWORD):HRESULT;stdcall; function Skip(cUsers : DWORD):HRESULT;stdcall; function Reset():HRESULT;stdcall; function Clone(ppEnum : PPEnumDiskQuotaUsers):HRESULT;stdcall; end; Должно быть: IEnumDiskQuotaUsers = interface(IUnknown) ['{7988B577-EC89-11cf-9C00-00AA00A14F56}'] function Next( cUsers : DWORD; rgUsers : PDiskQuotaUser; //а не PPDiskQuotaUser, можно даже out rgUsers: IDiskQuotaUser var pcUsersFetched : DWORD):HRESULT;stdcall; function Skip(cUsers : DWORD):HRESULT;stdcall; function Reset():HRESULT;stdcall; function Clone(out ppEnum : IEnumDiskQuotaUsers):HRESULT;stdcall; //не PPEnum... end; и т.д. по всем ссылкам на PPинтерфейс.
Вторая обнаружилась сама собой - у всех функций должна присутствовать директива stdcall (в одной функции она отсутствовала)
исправленный вариант модуля можно найти по адресу http://www.delphi.xonix.ru/download/dskquota.zip (размер 3Кб)
Сегодня в рассылке:
- Вопросы
- NTFS : Управление дисковыми квотами
Вопросы
Может кто-нибудь подскажет решение (или обходные пути ;) такого вопроса (ответы направляйте автору):
Хотелось бы поиметь задержки менее 1мс, например 1мкс. Вроде знаю направление где копать - получать время исполнения процесса... там вроде приращение идет с дельтой 100нс.
а вот в остальном это темный лес ;)
Зачем это необходимо - да в разных ситуациях. В моем случае - для управления микросхемой через LPT - строб на RD - через 1мкС - считываем данные. Если паузы будут идти кратными 1мс - очень медленная работа.
DeMoN Gardemarin@rambler.ru
Статья
NTFS : Управление дисковыми квотами
Для тех, кто не читал прошлый выпуск, коротко расскажу - дисковые квоты позволяют ограничивать место на диске для определенных пользователей. Для понимания, о чем мы говорим, прочитайте предыдущий выпуск. Найти его можно в архиве рассылки - http://subscribe.ru/archive/comp.soft.prog.de lphint/200208/06163552.html
В этом выпуске - о записях квот для определенных пользователей. Получить список пользователей, которым сопоставлены квоты можно с помощью интерфейса IEnumDiskQuotaUsers. Точнее не список пользователей, а интерфейс IDiskQuotaUser, позволяющий получить и изменять информацию о квотах для определенного пользователя. В моем примере информация о квотах пользователей будет выводиться (как и в закладке "Квоты" | "Записи квот...") в ListView (который в Delphi - TListView). Поэтому для простоты использования создадим класс TUserQuota = class(TListItem).
type TUserQuota = class(TListItem) private fUserID : Cardinal; protected function GetQuotaLimit :Int64; function GetThreshold :Int64; function GetQuotaUsed :Int64; procedure SetQuotaLimit(AValue : Int64); procedure SetThreshold (AValue : Int64); public QuotaInterface : IDiskQuotaUser; LogonName : array [0..100] of WideChar; FullName : array [0..100] of WideChar; procedure SetValues; procedure Clicked; property UserID : Cardinal read fUserID; property Limit : Int64 read GetQuotaLimit write SetQuotaLimit; property Threshold : Int64 read GetThreshold write SetThreshold; property QuotaUsed : Int64 read GetQuotaUsed; end;
Как видно из описания, этот класс содержит (указатель на) интерфейс IDiskQuotaUser и несколько свойств и функций, упрощающих работу с этим интерфейсом. К реализации этого класса мы вернемся позже.
Получить интерфейс IEnumDiskQuotaUsers можно функцией CreateEnumUsers интерфейса IDiskQuotaControl. Третьим параметром функции является fNameResolution, определяющий, как будет получаться информация. Определены 2 типа DISKQUOTA_USERNAME_RESOLVE_SYNC и DISKQUOTA_USERNAME_RESOLVE_ASYNC (третий - DISKQUOTA_USERNAME_RESOLVE_NONE нас не интересует). Разница между этими типами проявляется при получении следующих "записей" функцией IEnumDiskQuotaUsers.Next(). Рассмотрим, что происходит поподробнее.
Вполне логично, что в записях квот используются только SID, а имя пользователя и домена уже определяется по нему. Выполняется функция Next IEnumDiskQuotaUsers. Получается SID пользователя следующей записи и по нему определяется имя и домен пользователя (а процедура эта не самая быстрая). Вот тут и проявляется разница - в случае DISKQUOTA_USERNAME_RESOLVE_SYNC функция Next ждет пока будет обнаружено имя пользователя (а имя пользователя ищется и в соседних доменах и эта процедура может занимать несколько секунд), а при DISKQUOTA_USERNAME_RESOLVE_ASYNC функция возвращает результат немедленно, даже если функция не может сразу найти имя пользователя. При этом, если в полученном интерфейсе IDiskQuotaUser выполнить GetAccountStatus(Status), то Status <> DISKQUOTA_USER_ACCOUNT_RESOLVED.
Логично было бы предположить, что если запрос на получение имени пользователя был "отправлен", то через некоторое время можно будет получить имя этого пользователя. Для получения этого события используется интерфейс IDiskQuotaEvents, который должна реализовывать ваша программа, в случае асинхронного (DISKQUOTA_USERNAME_RESOLVE_ASYNC) получения информации (а дальше мы будем рассматривать только этот вариант как более сложный).
Примечание. Если имя пользователя уже ранее получалось и находится в кэше, то оно берется именно из него и разницы в скорости между двумя вариантами нет. Также нет большой разницы в случае, если все пользователи локальные. Но это частные случаи.
Вернемся к реализации интерфейса IDiskQuotaEvents. Реализовать его можно так:
type TForm1 = class(TForm,IDiskQuotaEvents) //. пропущено ... public { Public declarations } CPointContainer : IConnectionPointContainer; CPoint : IConnectionPoint; QEvents : IDiskQuotaEvents; dwCookie : Integer; // Единственная функция интерфейса IDiskQuotaEvents function OnUserNameChanged(pUser : IDiskQuotaUser):HRESULT;stdcall; end;
Функцию OnUserNameChanged система будет вызывать, когда сможет (или не сможет из-за ошибки) получить имя пользователя. Естественно у этой функции должна быть реализация, но мы оставим ее пока пустой
function TForm1.OnUserNameChanged(pUser : IDiskQuotaUser):HRESULT; begin // крайне не рекомендуется делать это сообщение модальным // (т.е. вместо 0 использовать Form1.Handle) MessageBox(0,'OnUserNameChanged','System',MB_OK); end;
Теперь надо сообщить системе, что у нас есть нужный интерфейс. Для этого IDiskQuotaControl наследует интерфейс IConnectionPointContainer.
Это надо писать где-нибудь после Quota.Initialize (т.е. после инициализации)
// Получаем интерфейс IConnectionPointContainer Res:=Quota.QueryInterface(IConnectionPointContainer,CPointContainer); if FAILED(Res) then ShowMessage('IConnectionPointContainer Error'); // Получаем интерфейс IConnectionPoint соответствующий IDiskQuotaEvents Res:=CPointContainer.FindConnectionPoint(IDiskQuotaEvents,CPoint); if FAILED(Res) then ShowMessage('FindConnectionPoint Error'); // Получаем интерфейс IDiskQuotaEvents (который мы реализовали) Res:=Self.QueryInterface(IDiskQuotaEvents,QEvents); if FAILED(Res) then ShowMessage('Self.QueryInterface Error'); // Подписываемся на события Res:=CPoint.Advise(QEvents,dwCookie); if FAILED(Res) then ShowMessage('CPoint.Advise Error');
Ну а теперь перейдем к основной части - получению списка записей (читать интерфейсов IDiskQuotaUser) квот.
procedure TForm1.GetQuotaEntriesList; var Res : Cardinal; Enum : IEnumDiskQuotaUsers; ui : TUserQuota; begin // Создаем объект, содержащий IEnumDiskQuotaUsers Res:=Quota.CreateEnumUsers(nil,0,DISKQUOTA_USERNAME_RESOLVE_ASYNC,Enum); if FAILED(Res) then ShowMessage('CreateEnumUsers Error'); repeat // Создаем TUserQuota (он же потомок TListItem) ui := TUserQuota.Create(LV.Items); // LV как можно догадаться TListView // Перечисляем записи по одной // Признак ошибки - результат не равен нулю. if Enum.Next(1,UI.QuotaInterface,nil)<>0 then Break; // Добавляем элемент списка в ListView // и устанавливаем значения функцией SetValue (реализация ниже) (LV.Items.AddItem(UI) as TUserQuota).SetValues(); until false; // Ну здесь можно было бы сделать ui.Free; // т.к. мы создаем его до вызова Next (и получения ею результата) end;
Вот и все перечисление :) В результате мы получим несколько пустых строк в ListView и возможно несколько сообщений из OnUserNameChanged. Пустые строки получатся из-за того, что мы еще не реализовали наш самодельный TUserQuota (вообще-то не реализовав этот класс с кодом выше мы бы получили ошибку при компиляции :))
/ Устанавливаем значения procedure TUserQuota.SetValues(); var i : Integer; begin // Получаем ID пользователя QuotaInterface.GetID(fUserID); // получаем логин и полное имя пользователя QuotaInterface.GetName(nil,0,@LogonName[0],100,@FullName[0],100); // устанавливаем заголовок элемента равным логину Caption:=LogonName; // Добавляем текст в первый столбец ListView // (установите ViewStyle в vsReport) SubItems.Add(FullName); // Создаем еще 3 столбца // они будут заполнены функциями ниже for i:=1 to 3 do SubItems.Add(''); GetQuotaLimit; GetThreshold; GetQuotaUsed; end; // устанавливает значение квоты пользователя procedure TUserQuota.SetQuotaLimit(AValue : Int64); begin QuotaInterface.SetQuotaLimit(AValue,TRUE); end; // получает численное значение квоты // и выводит ее текстовое значение во 2 столбец function TUserQuota.GetQuotaLimit:Int64; var // 150 символов более чем достаточно Text : array [0..150] of WideChar; begin // Числовое значение QuotaInterface.GetQuotaLimit(Result); // текстовое QuotaInterface.GetQuotaLimitText(Text,150); SubItems.Strings[1]:=Text; end; // устанавливает значение порога предупреждения procedure TUserQuota.SetThreshold(AValue : Int64); begin QuotaInterface.SetQuotaThreshold(AValue,TRUE); end; // получает значение порога предупреждения function TUserQuota.GetThreshold():Int64; var Text : array [0..150] of WideChar; begin QuotaInterface.GetQuotaThreshold(Result); QuotaInterface.GetQuotaThresholdText(Text,150); SubItems.Strings[2]:=Text; end; // получает размер файлов пользователя на диске function TUserQuota.GetQuotaUsed:Int64; var Text : array [0..150] of WideChar; begin QuotaInterface.GetQuotaUsed(Result); QuotaInterface.GetQuotaUsedText(Text,150); SubItems.Strings[3]:=Text; end; // Это просто "заглушка" функции, которая будет вызываться // при даблклике на элементе procedure TUserQuota.Clicked(); begin // в оригинале здесь диалоговое окно для изменения параметров MessageBox(0,FullName,LogonName,MB_OK or MB_ICONINFORMATION); end;
Теперь состояние квоты пользователя можно менять например так
UserQuota.Limit:=100000; //в байтах
Ну и последнее - реализация OnUserNameChanged.
function TForm1.OnUserNameChanged(pUser : IDiskQuotaUser):HRESULT; var cbSid : Cardinal; Sid : PSID; StrSid : PWideChar; ID : Cardinal; i : Integer; Status : Cardinal; begin // Возвращаемое значение игнорируется. Result:=0; if not Assigned(pUser) then begin ShowMessage('Error'); exit;end; // получаем ID пользователя pUser.GetID(ID); // А вот для этого собственно и был использован класс TUserQuota // вместо обычного TListItem // Перебираем все элементы TListView for i:=0 to LV.Items.Count-1 do begin // ищем такой-же ID, как и у этого интерфейса try with (LV.Items.Item[i] as TUserQuota) do begin if UserID = ID then begin // Проверяем статус pUser.GetAccountStatus(Status); // Если имя пользователя мы не получили, // то выводим его SID if Status<>DISKQUOTA_USER_ACCOUNT_RESOLVED then begin // получаем размер SID pUser.GetSidLength(cbSid); // Выделяем память и получаем SID GetMem(Sid,cbSid); pUser.GetSid(Sid,cbSid); // Получаем и выводим текстовый SID ConvertSidToStringSidW(Sid,StrSid); Caption:=StrSid; // очищаем память LocalFree(Cardinal(StrSid)); FreeMem(Sid); end; end; end; except // мало ли чего может быть в TListView вместо TUserQuota // например, тот же TListItem ;) и в результате Exception on EInvalidCast do begin end; end; end; end;
И еще - как вы могли заметить, в списке записей квот находятся не все пользователи, а только те, чьи файлы есть на интересующем нас диске. Запись создается автоматически при первой записи пользователем информации на диск. При этом ему назначаются размеры квот по умолчанию.
Если вам необходимо вручную добавить пользователя, то воспользуйтесь функциями AddUserName(добавление по имени) или AddUserSid(по SID) интерфейса IDiskQuotaControl. Также по этим параметрам можно реализовать поиск (FindUserName и FindUserSid). Удаляется запись функцией DeleteUser.
На этом я заканчиваю описание работы с квотами. Если будут вопросы или найдете ошибки, то пишите на flint@vtc.ru
Вот и все на сегодня.
С уважением, ведущий рассылки FliNT
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||