О нумерации версий Delphi
Блог Чорнага кашака
История развивается по спирали, а пони бегает по кругу… |
Сначала разработчики Delphi нумеровали версии просто: 1, 2, 3, 4, 5, 6, 7, 8. Затем, с подачи маркетологов Microsoft перешли на нумерацию версий по годам: 2005, 2006, 2007, 2009, 2010. В 2010 году Embarcadero придумала для всех своих продуктов новый бренд — «XE». «XE» — это не «Express Edition» как у Oracle, а два понятия: «X» — гетерогенность платформы (например, доступ к различным базам данных без дополнительных затрат) и «E» — скромное «Embarcadero». Поэтому с 2010 года началась новая эра в нумерации версий Delphi: Delphi XE, Delphi XE2… Что будет дальше? А дальше:
- Delphi XE3
- Delphi XE4
- Delphi XE5
- Delphi XE6
- Delphi XE7
- Delphi XE8
- Delphi XE9
- Delphi XEA
- Delphi XEB
- Delphi XEC
- Delphi XED
- Delphi XEE
О, XEE! Легкой перестановкой букв мы получаем название первой версии Delphi — «Delphi.EXE». Круг замкнулся… 🙂
Конвертирование XLS/XLSX/XLSM в XLSB
Блог Чорнага кашакаС появлением MS Excel 2007 на смену привычного XLS-файла пришли сразу три формата:
- XLSX — стандартный формат файлов Excel 2007-2010 на основе XML;
- XLSM — формат Excel 2007-2010 на основе XML с поддержкой макросов (в отличие от XLSX он позволяет сохранять код макросов MS Visual Basic для приложений (VBA) и листы макросов MS Excel 4.0 (XLM));
- XLSB — формат двоичных файлов Excel 2007-2010 (BIFF12).
Наиболее интересным из них является формат XLSB — Excel Binary Workbook. В отличие от других форматов Excel 2007-2010, он хранит данные не в виде XML, а является двоичным. Это дает существенные преимущества при работе с большими таблицами, т.к. бинарные файлы занимают меньше места на диске и читаются/записываются быстрее.
Ни одна из библиотек для работы с файлами Excel в Delphi не поддерживает XLSB-формат. Например, XLSReadWrite поддерживает только XLSX, а авторы TMS FlexCel вообще не могут сказать, когда у них будет поддержка формата Excel 2007 и будет ли вообще. Поэтому, что бы конвертировать файлы Excel в XLSB-формат необходимо использовать OLE:
Var
xls: OleVariant;
…
Const
// formats in Excel 2007-2010
xlExcel12 = 50; // XLSB
xlOpenXMLWorkbook = 51; // XLSX
xlOpenXMLWorkbookMacroEnabled = 52; // XLSM
xlExcel8 = 56; // XLS (export to Excel 97-2003)
…
Procedure XYZ.ConvertFilesToXLSB(slFiles: TStringList);
Var
iFile: Integer;
begin
Try
Try
xls := CreateOleObject(‘Excel.Application’);
xls.DisplayAlerts := False;
If StrToFloat(StringReplace(xls.Version, ‘.’, DecimalSeparator, [])) < 12
then WriteToLog(‘Error! Requires Excel 2007/2010’)
else for iFile := 0 to slFiles.Count-1 do
ConvertToXLSB(slFiles[iFile]);
Finally
xls.Quit;
xls := UnAssigned;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Где ConvertToXLSB:
Procedure XYZ.ConvertToXLSB(sFileName: String);
Var
sFileNameTo: String;
begin
sFileName := ExpandFileName(sFileName);
sFileNameTo := ChangeFileExt(sFileName, ‘.xlsb’);
Try
Try
xls.Workbooks.Open(sFileName);
xls.ActiveWorkbook.SaveAs(Filename := sFileNameTo, FileFormat := xlExcel12);
Finally
xls.Workbooks.Close;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Думаю, что код простой и комментировать нечего. Остановлюсь только на строке проверки версии MS Excel. xls.Version возвращает номер версии MS Excel в виде строки, где цифры разделены точкой (например, «11.0» для Excel 2003, «12.0» для Excel 2007…), поэтому, чтобы получить номер версии в виде числа его необходимо преобразовать следующим способом:
StrToFloat(StringReplace(xls.Version, ‘.’, DecimalSeparator, []))
IE9 in edit Mode & TWebBrowser = EZeroDivide
Семен Хмелевvar
CW: word;
procedure TForm1.FormCreate(Sender: TObject);
begin
CW := Get8087CW(); // Запоминаем предыдущее состояние регистра
System.Set8087CW($133f); // Выключаем исключения
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
System.Set8087CW(CW); // Восстанавливаем предыдущее значение регистра
end;
Но данный способ приемлем если у вас форма с TWebBrowser открыта модально или приложение не большое. В случае же если приложение MDI, большое и пользователь может динамически выполнять скрипты, то ни как не хотелось бы отключать генерацию исключений на все время работы приложения. Но как сделать так, что бы исключения были выключены только для TWebBrowser?К счастью для меня TWebBrowser используется у нас для отображения статической HTML разметки и проблема была замечены только когда пользователь трогал полосу прокрутки, потому было решено менять значение управляющего регистра только когда мышь появляется над TWebBrowser и восстанавливать обратно, когда покидает границы компонента.
TEventObject = class(TInterfacedObject,IDispatch)
private
FOnEvent: TProc;
protected
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
public
constructor Create(const OnEvent: TProc);
property OnEvent: TProc read FOnEvent write FOnEvent;
end;
var
webBrowser: IWebBrowser;
iDocument2: IHTMLDocument2;
eoMouseLeave, eoMouseEnter: TEventObject;
iElement2: IHTMLElement2;
j: Integer;
CW: Word;
implementation
function TEventObject.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
ArgErr: Pointer): HResult;
begin
if (Dispid = DISPID_VALUE) then
begin
if Assigned(FOnEvent) then
FOnEvent; // Вызываем обработчик события
Result := S_OK;
end
else Result := E_NOTIMPL;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{Создание объектов обработчиков реализующих IDispatch}
eoMouseEnter := TEventObject.Create(self.OnMouseEnter);
eoMouseLeave := TEventObject.Create(self.OnMouseLeave);
end;
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
begin
{Устанавливаем обработчики на события после загрузки страницы}
webBrowser := pDisp as IWebBrowser;
if Assigned(webBrowser.Document) then
begin
iDocument2 := webBrowser.Document as IHTMLDocument2;
iDocument2.DesignMode := 'On';
for j:=0 to iDocument2.All.Length-1 do
begin
iElement2 := iDocument2.All.item(j,EmptyParam) as IHTMLElement2;
iElement2.AttachEvent('onmouseenter', eomouseEnter);
iElement2.AttachEvent('onmouseleave', eoMouseLeave);
end;
end;
end;
procedure TForm1.WebBrowser1BeforeNavigate2(Sender: TObject;
const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
Headers: OleVariant; var Cancel: WordBool);
begin
{отсоединяем обработчики от событий, перед загрузкой новой страницы}
if Assigned(webBrowser.Document) then
begin
iDocument2 := webBrowser.Document as IHTMLDocument2;
for j:=0 to iDocument2.All.Length-1 do
begin
iElement2:=All.item(i,EmptyParam) as IHTMLElement2;
iElement2.detachEvent('onmouseenter', eoMouseEnter);
iElement2.detachEvent('onmouseleave', eoMouseLeave);
end;
end;
end;
procedure TForm1.OnMouseEnter;
begin
CW := Get8087CW();
System.Set8087CW($133f); // Отключаем исключения
end;
procedure TForm1.OnMouseLeave;
begin
System.Set8087CW(CW); // Восстанавливаем значение регистра
end;Здесь мы вешаем обработчики на события OnMouseEnter и OnMouseLeave, т.к. MSDN говорит нам, что события не всплывающие(не проходят по всей иерархии DOM до верхнего уровня), то обработчики устанавливаем на все объекты документа. В качестве обработчика метод IHTMLDocument.AttachEvent просит объект с IDispatch, для этого реализуем TEventObject. И не забываем, что eoMouseEnter, eoMouseleave надо удалить перед закрытием формы.