Как извлечь информацию из файла
    , как на вкладке "Сводка" в Windows Explorer
  ![]()  | 
Наши проекты:
 Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту  | 
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS | 
| [216.73.216.0] | 
 
 | 
		
  | 
    
  
 MSDN Library 
 FAQ раздела 
 Поиск по разделу  
  Как правильно задавать вопросы
    Как извлечь информацию из файла
    , как на вкладке "Сводка" в Windows Explorer
  | 
         
         
         
          
           Сообщ.
           #1
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Всем привет 
        
      Разьясните пожалуйсто,как из файлов с расширением "*.msi" , с помощью API-функций из закладки "Сводка" извлечь информацию о Авторе и Теме Заранее благодарен.  | 
    
| 
         
         
         
          
           Сообщ.
           #2
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Хм красивый вопрос... Первый раз такой слышу, никто раньше не интересовался. 
        
      По сабжу: MsiGetProductInfoFromScript Вообще смотри вот сюда: http://msdn.microsoft.com/library/en-us/msi/setup/installer_function_reference.asp  | 
    
| 
         
         
         
          
           Сообщ.
           #3
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Rouse_ 
        
      кажется всё очень просто, может фаил что то и хранит внутри себя но все эти данные также есть в реестрее Как говоритсяб а ЛАРЧИК-то просто открывался !!!!!  | 
    
| 
         
         
         
          
           Сообщ.
           #4
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата ctranik @  но все эти данные также есть в реестрее Хм, я вообщето обьяснял про неустановленный MSI пакет     | 
    
| 
         
         
         
          
           Сообщ.
           #5
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Вообще, *.msi - это обычные compound-файлы (или structured storage - файлы хранения структуированых данных) и они, помимо прочего, хранят в себе т.н. наборы cвойств, т.е. информацию о документе (которую видно на той же вкладке "Сводка"). Все эти св-ва можно прочитать напрямую из файла. Для этого в винде предназначены COM-интерфейсы IPropertySetStorage, IPropertyStorage, etc.  
        
      Вот небольшой примерчик считывания некоторых св-в из любых compound файлов, а так же св-ва обычных файлов (которые отображаются на вкладке "Сводка" диалогового окна "Свойства:"): ![]() ![]() unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,   Dialogs, StdCtrls, ComCtrls; type   TForm1 = class(TForm)     Button1: TButton;     ListView1: TListView;     OpenDialog1: TOpenDialog;     procedure Button1Click(Sender: TObject);     procedure FormCreate(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; var   Form1: TForm1; implementation {$R *.dfm} uses ActiveX, ComObj; function StgOpenStorageEx(const pwcsName : POleStr; grfMode : LongInt; stgfmt : DWORD;                            grfAttrs : DWORD;  pStgOptions : Pointer; reserved2 : Pointer;                             riid : PGUID; out stgOpen : IStorage ) : HResult; stdcall; external 'ole32.dll'; const // GUID для набора св-в SummaryInformation  FMTID_SummaryInformation  : TGUID = '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';  IID_IPropertySetStorage   : TGUID = '{0000013A-0000-0000-C000-000000000046}';   STGFMT_FILE = 3;   STGFMT_ANY = 4; type  PPropSpecArray=^TPropSpecArray;  // массив спецификаций св-в  TPropSpecArray=array[0..999] of TPropSpec;   PPropVariantArray=^TPropVariantArray; // массив - приемник, куда будут помещены значения нужных св-в  TPropVariantArray=array[0..999] of TPropVariant; // Этот код в стиле VB чисто для примера. Узнать, какая FS в системе можно и через WinAPI function IsNTFS(AFileName : string) : boolean; var  fso, drv : OleVariant; begin fso := CreateOleObject('Scripting.FileSystemObject'); drv := fso.GetDrive(fso.GetDriveName(AFileName)); Result:=drv.FileSystem = 'NTFS' end; procedure TForm1.Button1Click(Sender: TObject); var  stgRoot:IStorage;  stgPS:IPropertySetStorage;  stgP:IPropertyStorage;  ps:PPropSpecArray;  pv:PPropVariantArray;  lit:TListItem;  // значения св-в записываем в ListView begin  if (not OpenDialog1.Execute) then exit;  // выбираем файл  // Проверяем: если это не compound file и система - не NTFS, тогда выходим.  // (для compound файлов FS не важна)  if (StgIsStorageFile(StringToOleStr(OpenDialog1.FileName))<>S_OK) then    if (not IsNTFS(OpenDialog1.FileName)) then   begin    MessageBox(Handle,'NTFS needed for non-compound files','Error',MB_ICONERROR);    exit;   end;  ListView1.Clear;   if (StgOpenStorageEx(StringToOleStr(OpenDialog1.FileName), STGM_READ or STGM_SHARE_DENY_WRITE,                         STGFMT_ANY, 0, nil,  nil,                         @IID_IPropertySetStorage, stgRoot)<>S_OK) then  // открываем его   begin    MessageBox(Handle,'Can not open file','Error',MB_ICONERROR);    exit;   end;  stgPS:=stgRoot as IPropertySetStorage;  // ссылаемся на нужный интерфейс  if (stgPS.Open(FMTID_SummaryInformation,STGM_READ or STGM_SHARE_EXCLUSIVE,stgP)<>S_OK) then // открываем набор св-в  begin   MessageBox(Handle,'Can not open property set','Error',MB_ICONERROR);   exit;  end;  ps:=nil;  pv:=nil;  try    GetMem(ps,SizeOf(TPropSpec));    GetMem(pv,SizeOf(TPropVariant));    ps[0].ulKind:=PRSPEC_PROPID;   // считываем св-ва по их идентификатору (см. ActiveX.pas) //  Прочитаем для примера несколько св-в. //  Для наглядности читаем по одному св-ву из потока (можно за один раз прочитать и больше), //  поэтому каждый раз значение будет лежать в pv[0]. //  В данном случае читаем только строковые св-ва.    ps[0].propid:=PIDSI_TITLE;     // заголовок    lit:=ListView1.Items.Add;    lit.Caption:='Title';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then  // читаем св-во    lit.SubItems.Add(pv[0].pszVal) // добавляем в ListView    else lit.SubItems.Add('');    ps[0].propid:=PIDSI_SUBJECT;   // тема    lit:=ListView1.Items.Add;    lit.Caption:='Subject';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then    lit.SubItems.Add(pv[0].pszVal)    else lit.SubItems.Add('');    ps[0].propid:=PIDSI_AUTHOR;   // автор    lit:=ListView1.Items.Add;    lit.Caption:='Author';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then    lit.SubItems.Add(pv[0].pszVal)    else lit.SubItems.Add('');    ps[0].propid:=PIDSI_COMMENTS; // комментарий    lit:=ListView1.Items.Add;    lit.Caption:='Comment';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then    lit.SubItems.Add(pv[0].pszVal)    else lit.SubItems.Add('');    ps[0].propid:=PIDSI_REVNUMBER; // номер редакции    lit:=ListView1.Items.Add;    lit.Caption:='Revision';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then    lit.SubItems.Add(pv[0].pszVal)    else lit.SubItems.Add('');    ps[0].propid:=PIDSI_APPNAME;   // приложение, создавшее пакет    lit:=ListView1.Items.Add;    lit.Caption:='Application';    if (stgP.ReadMultiple(1, @ps[0], @pv[0])=S_OK) then    lit.SubItems.Add(pv[0].pszVal)    else lit.SubItems.Add('');  finally    if assigned(ps) then FreeMem(ps);    if assigned(pv) then FreeMem(pv);    stgP:=nil;    stgPS:=nil;    stgRoot:=nil;  end; end; procedure TForm1.FormCreate(Sender: TObject); begin  // наборы св-в могут храниться не только в *.msi, но и в любых compound-файлах (например в документах MS Office)  OpenDialog1.Filter:='MSI Databases (*.msi)|*.msi|MS Office Documents (*.doc;*.xls)|*.doc; *.xls|All Files (*.*)|*.*' end; end. В наборе SummaryInformation есть еще несколько св-в (не все из них строковые). Также compound-файлы могут содержать и другие наборы св-в, например DocumentSummaryInformation, UserDefinedProperties, а также какие-нибудь произвольно определенные пользователем. Вся инфа - в MSDN. Прототипы, константы, структуры - в ActiveX.pas  | 
    
| 
         
         
         
          
           Сообщ.
           #6
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Krid, замечательный пример !  
        
      Меня смущает одно. Если эту прогу запустить дважды, то на второй раз она скажет "Error - файл уже используется". И это будет пока не перезагрузишься.    Cдается мне что файлик то надо закрывать ... Так как здесь есть Open ![]() ![]() OleCheck(StgOpenStorage(StringToOleStr(OpenDialog1.FileName),nil,                          STGM_READ or STGM_SHARE_EXCLUSIVE,nil,0,stgRoot));   // открываем его , то должен быть и Close . Кстати, не подскажешь как его вызвать то ?  | 
    
| 
         
         
         
          
           Сообщ.
           #7
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Еще у меня вопрос... Я не понял что же дает STGM_SHARE_EXCLUSIVE  ? 
        
      В MSDN написано Цитата  STGM_SHARE_EXCLUSIVE  Prevents others from subsequently opening the object in any mode. Note that this value is not a simple bitwise OR operation of the STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE values. In transacted mode, sharing of STGM_SHARE_DENY_WRITE or STGM_SHARE_EXCLUSIVE can significantly improve performance since they don't require snapshotting. See the following Remarks section for more information about transactioning. Обратите внимание на выделенно синим. Не это ли мешает открывать файл впоследствии?  | 
    
| 
         | 
    |
| 
         | 
      
          Народ, ну, что серьезно никто не знает как решается проблема то ?   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #9
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Krid, ну отзовись пожалуйста   
        
         . При выборе pdf или вомг - файла прога пишет :Цитата  *.Экзе рэйсед экспепшен класс EOleSysError whith message "%1 уже существует" Чего это она ?  | 
    
| 
         
         
         
          
           Сообщ.
           #10
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата SexGenius @  Если эту прогу запустить дважды, то на второй раз она скажет "Error - файл уже используется". И это будет пока не перезагрузишься. Ну правильно, это из-за STGM_SHARE_EXCLUSIVE. Этот флаг задает монопольный доступ к файлу (или секции/хранилищу/набору св-в в нем) для одной программы. Просто, в то время как ты читаешь из файла, другая прога может в него писать (например поменять св-во). А это не есть гуд. Ну тут можно либо закрыть файл - т.е., после всех операций написать ![]() ![]()  stgRoot:=nil; Цитата SexGenius @  При выборе pdf или вомг - файла прога пишет : Да это чего-то OleCheck мудрит. Я бы ее вообще убрал и написал что-то типа ![]() ![]() if (StgOpenStorage(StringToOleStr(OpenDialog1.FileName),nil,                    STGM_READ or STGM_SHARE_EXCLUSIVE,nil,0,stgRoot)<>S_OK) then   begin    MessageBox(Handle,'Can not open doc file','Error',MB_ICONERROR);    exit;   end; И кстати, везде убрал бы эту OleCheck (как-то она жисть осложняеть  ) и все бы делал ручками.Вобщем, код подправил. Должно работать нормально. А так - эксперементируй и не ищи легких путей     | 
    
| 
         
         
         
          
           Сообщ.
           #11
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Krid, 05.10.2005, 19:32:12, 876404 Да это чего-то OleCheck мудрит. Я бы ее вообще убрал и написал что-то типа Тады при открытии djvu или pdf-файла прога пишет, как ты догадался, следующее: Цитата    Can not open doc file  | 
    
| 
         
         
         
          
           Сообщ.
           #12
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Дык, естественно! pdf - это не compound (doc/structured storage / хранилище структуированых данных). Они и не должны так открываться - у них совсем другой формат. Еще раз скажу, что таким макаром, как в примере могут открываться только compound файлы. А pdf и djvu к ним никакого отношения не имеют   
        
          | 
    
| 
         
         
         
          
           Сообщ.
           #13
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Krid, 05.10.2005, 20:44:11, 876474 Еще раз скажу, что таким макаром, как в примере могут открываться только compound файлы.  А есть-ли какой-нить универсальный макар, которым можно считывать информацию со вкладки Сводка для любых файлов NTFS . Может какие-нить константы повтыкать другие в функции ?  | 
    
| 
         
         
         
          
           Сообщ.
           #14
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Ах вон ты чего хочешь   
        
        Ну это тоже можно сделать. Дело в том, что NTFS позволяет для каждого файла создавать т.н. файловые потоки (files stream). По сути это те же файлы, только их не видно и они имеют "особые" атрибуты в ФС. С ними можно работать так же, как и с обычными файлами: создавать, удалять, записывать в них инфу (можно юзать почти все файловые API ф-ции). Когда ты открываешь вкладку "Свойства" и пишешь там чего-нить, винда создает для этого файла несколько потоков, в которые записывает то, что ты ввел. С потоками можно работать, например из ком. строки в консоли: ![]() ![]() Создание файла с потоком: type nul > somefile.txt:Stream Запись в поток: echo "Something" >> somefile.txt:Stream Чтение из потока: more < somefile:Stream Копирование содержимого существующего файла в поток: type file1.txt >> somefile.txt:Stream Копирование содержимого потока в файл: more < somefile.txt:Stream >> file2.txt Удаление потоков выполняется через DeleteFile. а можно и в программе (пример создания потока): ![]() ![]() procedure TForm1.Button1Click(Sender: TObject); var  dwRet:DWORD;  hFile,hStream:THandle; begin // создаем файл hFile:=CreateFile( 'C:\testfile.txt', GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_ALWAYS, 0, 0); CloseHandle(hFile); // создаем в нем поток и пишем в него чего-нибудь. hStream:=CreateFile( 'C:\testfile.txt:mystream', GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_ALWAYS, 0, 0); WriteFile( hStream, 'This is a stream', 17, dwRet, nil ); CloseHandle(hStream); end; Соотв. читается инфа из потока ф-цией ReadFile. Т.е., по идее, нужно открыть один из потоков, созданных виндой при записи св-в, прочитать его и выудить оттуда нужную инфу. Но в Win2K появилась ф-ция StgOpenStorageEx, которая сама всем этим занимается. Причем в ней можно задать флаги так, что она будет одинаково работать и с compound файлами (содержащими св-ва внутри себя) и с обычными файлами, св-ва которых находятся в отдельном потоке - винда сама определит, что за файл. Короче пример в посте #5 я изменил - теперь он может читать св-ва как из compound, так и из обычных файлов (если, конечно у этих файлов есть набор св-в).  | 
    
| 
         
         
         
          
           Сообщ.
           #15
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Krid, огромадное спасибо !!! [+] !!!!   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #16
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Господа программисты, помогите! 
        
      Имею биологическое образование. Третью неделю лажу по интернету, одно понял - без вашей помощи мне не обойтись. Мне нужно получать свойства (автор, ключевые слова) из не compound файлов (в частности TIFF, JPEG). Как я понял, на этом форуме говорится именно об этом. Но разговор окончился на том, что Krid изменил код на подходящий, и... все. Выручайте! Опубликуйте исходник.  | 
    
| 
         
         
         
          
           Сообщ.
           #17
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата к.б.н. @  Мне нужно получать свойства (автор, ключевые слова) из не compound файлов (в частности TIFF, JPEG).  Вот тебе пример. Снимает то, что тебе нужно и немного еще (из дополнительных свойств нет времени писать демку полного разбора) ![]() ![]() uses   GDIPOBJ,   GDIPAPI,   GDIPUTIL,   ActiveX; {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject);   function GetAdvTagData(const ID: Integer): String;   const     TAG_Title     = 40091;     TAG_Comments  = 40092;     TAG_Author    = 40093;     TAG_KeyWords  = 40094;     TAG_Subject   = 40095;   begin     case ID of       TAG_Title:    Result := 'Title';       TAG_Comments: Result := 'Comments';       TAG_Author:   Result := 'Author';       TAG_KeyWords: Result := 'KeyWords';       TAG_Subject:  Result := 'Subject';     else       Result := '';     end;   end; var   GDIImage: TGPImage;   I, ACount, PropSize: Integer;   PropertyesList: array of TPropId;   PropertyItem: PPropertyItem;   PropName: String;   PropValue: WideString;   ByteValue: DWORD; begin   if OpenDialog1.Execute then   begin     Memo1.Clear;     GDIImage := TGPImage.Create(OpenDialog1.FileName);     try       ACount := GDIImage.GetPropertyCount;       SetLength(PropertyesList, ACount);       GDIImage.GetPropertyIdList(ACount, @PropertyesList[0]);       for I := 0 to ACount - 1 do       begin         PropSize := GDIImage.GetPropertyItemSize(PropertyesList[I]);         GetMem(PropertyItem, PropSize);         try           GDIImage.GetPropertyItem(PropertyesList[I], PropSize, PropertyItem);           PropName := GetAdvTagData(PropertyesList[I]);           if PropName <> '' then           begin             SetLength(PropValue, PropertyItem^.length);             Move(PropertyItem^.value^, PropValue[1], PropertyItem^.length);           end           else           begin             PropName := GetMetaDataIDString(PropertyItem^.id);             ByteValue := 0;             if PropertyItem^.type_ in               [PropertyTagTypeByte, PropertyTagTypeShort,                PropertyTagTypeLong] then             begin               if PropertyItem^.length <= 4 then               begin                 Move(PropertyItem^.value^, ByteValue, PropertyItem^.length);                 PropValue := IntToStr(ByteValue);               end               else               begin                 SetLength(PropValue, PropertyItem^.length);                 Move(PropertyItem^.value^, PropValue[1], PropertyItem^.length);               end;             end;           end;           Memo1.Lines.Add(PropName + ' - ' + PropValue);         finally           FreeMem(PropertyItem);         end;       end;     finally       GDIImage.Free;     end;   end; end;  | 
    
| 
         
         
         
          
           Сообщ.
           #18
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата к.б.н. @  Мне нужно получать свойства (автор, ключевые слова) из не compound файлов (в частности TIFF, JPEG). Как я понял, на этом форуме говорится именно об этом. Не правильно понял. Читай внимательно предыдущие посты и введение к коду из фака (как извлечь "Сводку" свойств файла). Там говорится о стандартных св-вах, хранящихся либо внутри compound файлов либо в файловых потоках, ассоциированных с файлом. А получение специфических св-в файлов определённого типа - это совсем из другой оперы. Тут используются свои наборы API (см. например, код Rouse_ - там юзаются ф-ции из gdiplus.dll)/свои интерфейсы/прямое чтение "тэгов" из файла/etc. Вобщем, твой вопрос (о св-вах TIFF, JPEG) скорее для форума Delphi: Multimedia  | 
    
| 
         
         
         
          
           Сообщ.
           #19
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Благодарю! 
        
      Отличный код. Все работает. Правда, чтобы получить только необходимое (название, коммент., автор...), я его несколько укоротил. Для таких же тупых как я, прокомментирую: Здесь используется новый интерфейс GDI+. Работает на Windows XP и .NET Server, на более ранних ПО не работает (без определенной доработки). Чтобы с этим кодом заработал Delphi, в него надо установить соответствующие заголовочные файлы (подробности http://www.delphikingdom.com/asp/viewitem.asp?catalogid=772 )  | 
    
| 
         
         
         
          
           Сообщ.
           #20
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          MSI файлы - это наборы таблиц и комманд для интерпретатора msiexec. Разумеется вы можете опросить таблицы внутри msi на предмет количесва названия файлов всего что вы пожелаете, есть даже SQL подобный язык. Для интересующихся - скачайте Microsoft MSI Orca. (http://www.technipages.com/downloadview-details-44-Orca_MSI_Editor.html). 
        
      Примерно код для прочтения какого либо свойтсва из MSI: ![]() ![]() const msilib = 'msi.dll'; type   MSIHANDLE = DWORD;       TMsiHandle = MSIHANDLE; function MsiCloseHandle(hAny: MSIHANDLE):UINT;stdcall;external msilib name 'MsiCloseHandle'; function MsiOpenProduct(szProduct:LPCSTR;var hProduct:MSIHANDLE):UINT;stdcall;external msilib name 'MsiOpenProductA'; function MsiGetProductProperty(hProduct:MSIHANDLE;szProperty:LPCSTR;lpValueBuf:LPSTR;pcchValueBuf:LPDWORD):UINT;stdcall; external msilib name 'MsiGetProductPropertyA'; function MsiSetInternalUI(dwUILevel:INSTALLUILEVEL;phWnd:LPHWND):INSTALLUILEVEL;stdcall; external msilib name 'MsiSetInternalUI'; function GetMSIProperty(aProductCode:string):string; var   msi:TMSIHandle;  t:string;  function _getmsiproperty(_name:string):string;  var   txt:PChar;   sz:DWORD;  begin   sz:=MAX_PATH;   txt:=AllocMem(sz+1);   if MsiGetProductProperty(msi,PChar(_name),txt,@sz)=ERROR_MORE_DATA then    begin     ReAllocMem(txt,sz+1);     MsiGetProductProperty(msi,PChar(_name),txt,@sz);    end;   SetString(Result,txt,sz);   FreeMem(txt,sz+1);  end; begin  MsiSetInternalUI(2,nil); // скрываем GUI  if MsiOpenProduct(PChar(aProductCode),msi)=ERROR_SUCCESS then   begin    t:=_getmsiproperty('ARPPRODUCTICON'); // главная иконка приложения        if t='' then t:=_getmsiproperty('ProductIcon');        if t='' then t:=_getmsiproperty('CompleteSetupIcon');        if t='' then t:=_getmsiproperty('CustomSetupIcon');        if t='' then t:=_getmsiproperty('InfoIcon');        if t='' then t:=_getmsiproperty('InstallerIcon');        if t='' then t:=_getmsiproperty('RemoveIcon');        if t='' then t:=_getmsiproperty('RepairIcon');        Result:=t;        MsiCloseHandle(msi);   end; end;  | 
    
| 
         
         
         
          
           Сообщ.
           #21
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          работаю на builder c++, 
        
      но к сожалению, у меня при вызове hr = pPropSetStg->Open(PropSetfmtid, STGM_READ, &pPropStg ); получаю hr равную -2147286785, т.е. возникает ошибка, для doc файлов такая ошибка не возникает вот весь участок кода: ![]() ![]() FMTID PropSetfmtid={0xf29f85e0,0x4ff9,0x1068,{0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 }}; HRESULT hr = S_OK; IPropertyStorage *pPropStg = NULL; IPropertySetStorage *pPropSetStg = NULL; IStorage *stgRoot = NULL; PROPSPEC propspec; PROPVARIANT propRead; if (StgOpenStorageEx(StringToOleStr(FileName), STGM_READ | STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0, NULL, NULL, IID_IPropertySetStorage, reinterpret_cast<void**>(&pPropSetStg)) != S_OK) return; //if (pPropSetStg->Open(PropSetfmtid, STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READ, &pPropStg ) != S_OK) // return; hr = pPropSetStg->Open(PropSetfmtid, STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READ, &pPropStg ); propspec.ulKind = PRSPEC_PROPID; propspec.propid = 0x00000002;  pPropStg->ReadMultiple(1, &propspec, &propRead); AnsiString Title = propRead.pszVal; подскажите пожалуйста как можно считать свойства из pdf и djvu файла? почему возникает ошибка?  | 
    
| 
         
         
         
          
           Сообщ.
           #22
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Доброго времени суток. 
        
      Подскажите пожалуйста. Использую за основу код из сообщения #5 от Krid. Но я хочу получить пользовательское свойство из .DOC файла. Вот такой код получился. ![]() ![]() procedure TForm1.Button1Click(Sender: TObject); var   stgRoot: IStorage;   stgPS: IPropertySetStorage;   stgP: IPropertyStorage;   ps: ^TPropSpec;   pv: ^TPropVariant;   ReturnCode: Integer; begin // выбираем файл   if (not OpenDialog1.Execute) then     exit;   // Проверяем: если это не compound file и система - не NTFS, тогда выходим.   // (для compound файлов FS не важна)   if (StgIsStorageFile(StringToOleStr(OpenDialog1.FileName))<>S_OK) then     if (not IsNTFS(OpenDialog1.FileName)) then begin       MessageBox(Handle,'NTFS needed for non-compound files','Error',MB_ICONERROR);       exit;     end;   if (StgOpenStorageEx(StringToOleStr(OpenDialog1.FileName), STGM_READ or STGM_SHARE_DENY_WRITE,                         STGFMT_ANY, 0, nil,  nil,                        @IID_IPropertySetStorage, stgRoot)<>S_OK) then  // открываем его   begin     MessageBox(Handle,'Can not open file','Error',MB_ICONERROR);     exit;   end;   stgPS := stgRoot as IPropertySetStorage;  // ссылаемся на нужный интерфейс   if (stgPS.Open(FMTID_SummaryInformation, STGM_READ or STGM_SHARE_EXCLUSIVE, stgP) <> S_OK) then // открываем набор св-в   begin     MessageBox(Handle,'Can not open property set','Error',MB_ICONERROR);     exit;   end;   ps:=nil;   pv:=nil;   try     GetMem(ps,SizeOf(TPropSpec));     GetMem(pv,SizeOf(TPropVariant));     // свойство по номеру //    ps.ulKind := PRSPEC_PROPID; //    ps.propid := PIDSI_TITLE;     // свойство по имени     ps.ulKind := PRSPEC_LPWSTR;     ps.lpwstr := 'QWERTY';     // читаем св-во     ReturnCode := stgP.ReadMultiple(1, ps, pv);     if ReturnCode = S_OK then       ShowMessage(pv.pszVal)     else       ShowMessage('Error!!!');   finally     if assigned(ps) then       FreeMem(ps);     if assigned(pv) then       FreeMem(pv);     stgP := nil;     stgPS := nil;     stgRoot := nil;   end; end; В тестируемом DOC файле создал свойство QWERTY при чтении свойство по имени всегда S_FALSE. Чтение стандартных свойств по номеру работает. Подскажите, пожалуйста что я не правильно делаю.  | 
    
| 
         
         
         
          
           Сообщ.
           #23
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Пользовательские св-ва надо считывать из набора UserDefinedProperties, а ты открываешь SummaryInformation. 
        
      В stgPS.Open вместо FMTID_SummaryInformation юзай FMTID_UserDefinedProperties ({D5CDD505-2E9C-101B-9397-08002B2CF9AE})  | 
    
| 
         
         
         
          
           Сообщ.
           #24
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Спасибо большое. Получилось.   
        
       |