На Дельфи написана ActiveX-кнопка с дополнительным свойством Command. Я хочу сохранять значение свойства в файле контейнера через IStorage, IStream или др.
К примеру, контейнером является Excel-документ (файл xls), куда вставляется моя кнопка. Как это реализовать?
Здравствуйте, GladiatorX, Вы писали:
GX>На Дельфи написана ActiveX-кнопка с дополнительным свойством Command. Я хочу сохранять значение свойства в файле контейнера через IStorage, IStream или др. GX>К примеру, контейнером является Excel-документ (файл xls), куда вставляется моя кнопка. Как это реализовать?
Нужно реализовать IPersistXXXX интерфейсы, такие как IPersistStorage, IPersistStream (IPersistStreamInit), IPersistPropertyBag etc. Контейнер запросит тот интерфейс, по которому он умеет сохраняться.
Здравствуйте, Vi2, Вы писали:
Vi2>Здравствуйте, GladiatorX, Вы писали:
GX>>На Дельфи написана ActiveX-кнопка с дополнительным свойством Command. Я хочу сохранять значение свойства в файле контейнера через IStorage, IStream или др. GX>>К примеру, контейнером является Excel-документ (файл xls), куда вставляется моя кнопка. Как это реализовать?
Vi2>Нужно реализовать IPersistXXXX интерфейсы, такие как IPersistStorage, IPersistStream (IPersistStreamInit), IPersistPropertyBag etc. Контейнер запросит тот интерфейс, по которому он умеет сохраняться.
Сделал следующее:
TButtonX = class(TActiveXControl, IButtonX, ISpecifyPropertyPages, IPersistStreamInit)
...
// IPersistStreamInit
function IPersistStreamInit.IsDirty = IPersistStreamInit_IsDirty;
function IPersistStreamInit_IsDirty: HResult; stdcall;
function IPersistStreamInit.Load = IPersistStreamInit_Load;
function IPersistStreamInit_Load(const stm: ActiveX.IStream): HResult; stdcall;
function IPersistStreamInit.Save = IPersistStreamInit_Save;
function IPersistStreamInit_Save(const stm: ActiveX.IStream; fClearDirty: BOOL): HResult; stdcall;
function IPersistStreamInit.GetSizeMax = IPersistStreamInit_GetSizeMax;
function IPersistStreamInit_GetSizeMax(out cbSize: Largeint): HResult; stdcall;
function IPersistStreamInit.InitNew = IPersistStreamInit_InitNew;
function IPersistStreamInit_InitNew: HResult; stdcall;
...
// IPersistStreamInit
function TButtonX.IPersistStreamInit_IsDirty: HResult; stdcall;
begin
result := S_OK;
end;
function TButtonX.IPersistStreamInit_Load(const stm: ActiveX.IStream): HResult; stdcall;
begin
result := S_OK;
end;
function TButtonX.IPersistStreamInit_Save(const stm: ActiveX.IStream;
fClearDirty: BOOL): HResult; stdcall;
begin
result := S_OK;
end;
...
После этого перестали сохраняться и загружаться стандартные свойства кнопки.
Как мне сохранять и считывать все свойства моей кнопки?
Здравствуйте, GladiatorX, Вы писали:
GX>После этого перестали сохраняться и загружаться стандартные свойства кнопки. GX>Как мне сохранять и считывать все свойства моей кнопки?
Что сохранишь, то и поимеешь. Каждый IPersistXXXX имеет свою собственную семантику, и какой из них лучше — хз. Иногда определяется контейнером: именно контейнер определяет тип хранилища. Иногда он сам упаковывает один тип хранилища в другое, чтобы не насиловать сам контрол.
IPersistXXXX.Load загружает из хранилища XXXX данные. Если ты оттуда читать не будешь, значит, свойства контрола будут такими, как при создании контрола. IPersistXXXX.Save сохраняет в хранилище XXXX данные. Если ты туда писать не будешь, значит, и оттуда нечего читать будет.
IPersistStream::Load(IStream *pStm), следовательно, твой контрол должен читать из pStm его методами свои данные, т.е. используя IStream::Read(void *pv, ULONG cb, ULONG *pcbRead). Что-то наподобие:
IPersistPropertyBag::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog), следовательно, твой контрол должен читать из pPropBag его методами свои свои данные, т.е. используя IPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog). Что-то наподобие:
hr = pPropBag->Read(L"coordX", &v, NULL); coordX присвоить из v;
hr = pPropBag->Read(L"coordY", &v, NULL); coordY присвоить из v;
Здравствуйте, Vi2, Вы писали:
Vi2>Здравствуйте, GladiatorX, Вы писали:
GX>>После этого перестали сохраняться и загружаться стандартные свойства кнопки. GX>>Как мне сохранять и считывать все свойства моей кнопки?
Vi2>Что сохранишь, то и поимеешь. Каждый IPersistXXXX имеет свою собственную семантику, и какой из них лучше — хз. Иногда определяется контейнером: именно контейнер определяет тип хранилища. Иногда он сам упаковывает один тип хранилища в другое, чтобы не насиловать сам контрол.
Vi2>IPersistXXXX.Load загружает из хранилища XXXX данные. Если ты оттуда читать не будешь, значит, свойства контрола будут такими, как при создании контрола. IPersistXXXX.Save сохраняет в хранилище XXXX данные. Если ты туда писать не будешь, значит, и оттуда нечего читать будет.
Vi2>IPersistStream::Load(IStream *pStm), следовательно, твой контрол должен читать из pStm его методами свои данные, т.е. используя IStream::Read(void *pv, ULONG cb, ULONG *pcbRead). Что-то наподобие: Vi2>
Vi2>IPersistPropertyBag::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog), следовательно, твой контрол должен читать из pPropBag его методами свои свои данные, т.е. используя IPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog). Что-то наподобие: Vi2>
Vi2>hr = pPropBag->Read(L"coordX", &v, NULL); coordX присвоить из v;
Vi2>hr = pPropBag->Read(L"coordY", &v, NULL); coordY присвоить из v;
Vi2>
При чтении вылетает исключение. Что я сделал не так?
type
PBtnProps = ^TBtnProps;
TBtnProps = record
Caption: string;
end;
var
BtnProps: PBtnProps;
function TButtonX.IPersistStreamInit_Load(const stm: ActiveX.IStream): HResult; stdcall;
var
BytesRead: Longint;
begin
New(BtnProps);
try
stm.Read(BtnProps, SizeOf(TBtnProps), @BytesRead);
if BytesRead > 0 then
begin
ShowMessage('Load=' + BtnProps.Caption);
end;
finally
Dispose(BtnProps)
end;
result := S_OK;
end;
function TButtonX.IPersistStreamInit_Save(const stm: ActiveX.IStream; fClearDirty: BOOL): HResult; stdcall;
var
BytesWrite: Longint;
begin
ShowMessage('Save=' + FDelphiControl.Caption);
New(BtnProps);
try
BtnProps^.Caption := FDelphiControl.Caption;
stm.Write(BtnProps, SizeOf(TBtnProps), @BytesWrite);
if BytesWrite > 0 then
begin
ShowMessage('Save=' + IntToStr(BytesWrite));
end;
finally
Dispose(BtnProps)
end;
result := S_OK;
end;
Здравствуйте, Аноним, Вы писали:
А>При чтении вылетает исключение. Что я сделал не так?
Трудно сказать. Язык Дельфи для меня незнакомый. Но навскидку, разве можно писать строки напрямую в файл или поток как в "stm.Read(BtnProps, SizeOf(TBtnProps), @BytesRead);"? В С/С++ нельзя. И структуры со строками тоже нельзя. Поскольку в структуре хранится адрес строки, а не сама строка. Соответственно, при чтении таких данных могут получиться коллизии. Я завтра спрошу у специалиста по Дельфи.
Здравствуйте, Vi2, Вы писали:
Vi2>Здравствуйте, Аноним, Вы писали:
А>>При чтении вылетает исключение. Что я сделал не так?
Vi2>Трудно сказать. Язык Дельфи для меня незнакомый. Но навскидку, разве можно писать строки напрямую в файл или поток как в "stm.Read(BtnProps, SizeOf(TBtnProps), @BytesRead);"? В С/С++ нельзя. И структуры со строками тоже нельзя. Поскольку в структуре хранится адрес строки, а не сама строка. Соответственно, при чтении таких данных могут получиться коллизии. Я завтра спрошу у специалиста по Дельфи.