
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.30] |
![]() |
|
Сообщ.
#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, ну отзовись пожалуйста
![]() Цитата *.Экзе рэйсед экспепшен класс 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
,
|
|
|
Спасибо большое. Получилось.
|