Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» Вопросы по Delphi (до версии 2009) - часть 6

Автор: DmitryKz
Дата сообщения: 17.08.2011 19:14

Цитата:
Только структуры должны быть упакованы

А почему, объясните, пожалуйста? Это не снизит время доступа к полям?
Автор: Frodo_Torbins
Дата сообщения: 17.08.2011 19:26
DmitryKz
В файле они наверняка будут упакованы, так что у вас нету выбора.
Автор: DmitryKz
Дата сообщения: 17.08.2011 19:29
Понял, спасибо...

Задам ещё один вопрос:
даных много, секции имеют древовидную структуру, подразделяясь на подсекции и далее... мне все данные не нужны, но интерес к вопросу не только теоретический:

мой код (нет-нет, это не для г...кода выглядит так:


Код: function по заполнению структуры(File: TFileStream): TStruct;
var
struct: TStuct; // структура здесь, в функции
bf: array of Byte; // массив байтов (буфер)
lbf: Int64; // длина буфера
//одно поле структуры получаю так:
lbf := sizeof(struct.Id);
SetLength(bf, lbf);
File.Read(bf, lbf);
struct.Id := DWORD(&bf);
... // далее по тому же способу получаю остальные поля

Result := struct;
Автор: Frodo_Torbins
Дата сообщения: 17.08.2011 20:05
DmitryKz
А зачем читать отдельные поля? Можно ведь сразу всю структуру: File.Read(struct, SizeOf(struct)) по крайней мере если у вас с упаковкой и выравниванием все в порядке.
Автор: XPerformer
Дата сообщения: 17.08.2011 20:18
И в плане оптимизации: SetLength(bf, lbf) не обязательно делать каждый раз при чтении очередной записи, достаточно один раз выставить размер буфера по максимуму
Автор: DmitryKz
Дата сообщения: 17.08.2011 20:25
Frodo_Torbins
Я не знаю, как это сделать:
отдельные поля структуры имеют идентификатор и в зависимости от идентификатора содержат данные, не совпадающие с данными других структур...
а ещё есть файлы с таким же строением, но с другими структурами...

то есть принцип построения файлов один и тот же. Примерно он выглядит так:

//все параметры описаны так - param: offset, length, type of data
//заголовок файла:
парам1...
...
парам4 - кол-во разделов
//конец заголовка файла

//начало описания списка указателей разделов
парам1 = тип (от которого и зависит разный набор данных)
парам2 = кол-во подсекций
парам3 = офсет от начала файла
парам4 = длина раздела
//конец описания списка разделов

//разделы:
//могут иметь разные параметры с разными типами, зависит от парам1 (см. выше описание разделов)
//также в свою очередь могут иметь подсекции, а те - свои подсекции

ну то есть нету никакой унификации...

Добавлено:

Цитата:
достаточно один раз выставить размер буфера по максимуму

а если надо выполнять приведение типов? Скажем, сначала прочитали тип WORD, затем double, затем DWORD. При приведении данных из буфера будут учитываться только первые 2, 8, 4 байта соответственно?
Автор: XPerformer
Дата сообщения: 17.08.2011 20:31
DmitryKz

Цитата:
а если надо выполнять приведение типов? Скажем, сначала прочитали тип WORD, затем double, затем DWORD. При приведении данных из буфера будут учитываться только первые 2, 8, 4 байта соответственно?

При такой форме записи - да
struct.Id := DWORD(&bf);
тут указан конкретный тип, размер которого и определяет, сколько байт будет учитываться
Автор: DmitryKz
Дата сообщения: 17.08.2011 20:33

Цитата:
При такой форме записи - да

Вы не представляете, какую заботу Вы сейчас с меня сняли, спасибо
А есть ещё другие способы приведения байтов, прочитанных из файла в буфер?
...
Ещё бы разобраться со всем этим разветвлением...........
Автор: XPerformer
Дата сообщения: 17.08.2011 20:45
DmitryKz
в таком случае предлагаю
bf: array of Byte;
заменить на
bf: array [0..1000] of Byte;
Динамические массивы - это особые объекты в делфи, у которых есть дополнительные поля (например, хранится длина по отрицательному смещению), соответственно, накладные расходы выше.
Для быстродействия лучше использовать статические массивы, выделив память один раз по максимуму.

Поскольку секции вложены - то процедура чтения должна быть рекурсивной, то есть читать верхний объект, а затем вызывать себя для подобъектов рекурсивно.

>> все параметры описаны так - param: offset, length, type of data
это очень напоминает TStream.ReadComponent, есть смысл посмотреть исходники этой процедуры

Добавлено:

Цитата:
А есть ещё другие способы приведения байтов, прочитанных из файла в буфер?

Вопрос не понял - зачем?
в делфи 2 способа приведения, для последовательности байтов подходит этот
Автор: DmitryKz
Дата сообщения: 17.08.2011 20:56
XPerformer
Спасибо за помощь.
Автор: Frodo_Torbins
Дата сообщения: 17.08.2011 21:13
DmitryKz
Для всех типов секций в файле у вас должны быть свои типы рекордов в коде:
[more]
Код: [no]TSection = packed record
Type: Integer;
InnerSections: Integer;
Ofset: Integer;
Length: Integer;
end;

//Читаем заголовок в цикле
for i := 1 to SectionsCount do
File.Read(Sections[i], SizeOf(TSection));

//Секция с картинкой
TImageSection = packed record
Format: String[3];
Name: String[40];
Length: Integer;
//Data - картинка может иметь переменную длину, поэтому ее читаем отдельно
end;
//Таких типов столько, сколько у вас может быть типов секций

//Читаем картинку
File.Read(Image, SizeOf(TImageSection));
SetLength(Buf, Image.Length);
File.Read(Buf[0], Length(Buf));//Data[/no]
Автор: DmitryKz
Дата сообщения: 17.08.2011 21:52
Frodo_Torbins
Секций там не один десяток, а если учесть ещё параметры в них, то это займёт время... Я оформил пока записи для интересующих меня данных.
Если честно, я из кода не понял, что он должен делать. Похоже на работу с типизированными файлами (я, кстати, думал о том, типизирован ли мой файл и решил, что не типизирован, хотя может потому, что испугался глубины вложенностей структур/рекордов), только непонятны вот эти операторы:
File.Read(Sections[i], SizeOf(TSection));
File.Read(Image, SizeOf(TImageSection));
File.Read(Buf[0], Length(Buf))
Выделенное красным, это что - поле записи (структуры)?
Я не работал с классом File, а его метод Read видел только в примерах о типизированных файлах, но там на первом месте после скобок был хэндл файла, назначенный AssignFile. В справке я тоже не нашёл других примеров.

XPerformer

Цитата:
в таком случае предлагаю
bf: array of Byte;
заменить на
bf: array [0..1000] of Byte;

В этом случае начинает ругаться на инвалидное приведение типов...
Автор: Frodo_Torbins
Дата сообщения: 17.08.2011 22:04
DmitryKz
Код: File: TFileStream;
Sections: array of TSection;
Image: TImageSection;
Buf: array of Byte;
Автор: DmitryKz
Дата сообщения: 17.08.2011 22:09
Ага, теперь ясно, а то имя File озадачило первоначально...
Автор: Man_Without_Face
Дата сообщения: 19.08.2011 11:55
Доброго времени суток. Делаю экспорт в dbf при помощи TAdoCommand:
cmd.ConnectionString := 'Provider=VFPOLEDB.msm;Data Source='+LMDBEexport.text+'\;Password="";Collating Sequence=RUSSIAN';
Заказчику не понравилась версия dbf. Походу у него старый foxpro. Говорит нужно в формате dbs4 или 2.6.
Сделал так:
cmd.ConnectionString := 'Provider=VFPOLEDB.msm;Data Source='+LMDBEexport.text+'\;Password="";Extended Properties=dBase IV;Collating Sequence=RUSSIAN';
Наши мегапрограммисты сказали, что начальные байты присланного заказчиком dbf файла и моим должны совпадать. Совпадения нет, как переделать под dbs4 или 2.6?
Автор: Coltrain
Дата сообщения: 19.08.2011 15:45
Man_Without_Face
Насколько критично делать экспорт через ADO? Если не сильно, то можно попробовать сделать экспорт при помощи компонент Halcyon. Пока жалоб не было...
Автор: Man_Without_Face
Дата сообщения: 19.08.2011 16:03
Coltrain
Halcon использовал раньше, с удовольствием от него избавился. У меня
стоял вроде как последний, он не поддерживал поля datetime, еще я не
мог null значения записать. Да и работал он в десять раз медленнее.

В моей программе я так понимаю нужно провайдер другой, какой то старый.
Автор: Coltrain
Дата сообщения: 19.08.2011 18:14
Man_Without_Face
Понятно. Тогда Apollo предлагать не буду .
Насколько я понял, сам dbf-файл для экспорта создается из программы. Про более старый провайдер для FoxPro я ничего не знаю, поэтому предложу такой вариант. Создать пустой dbf-файл нужной структуры в таком же FoxPro как и у заказчика, сохранить в "укромном" месте, а потом просто копировать его и заполнять экспортными данными.
Автор: XPerformer
Дата сообщения: 19.08.2011 18:40
Coltrain

Цитата:
Создать пустой dbf-файл нужной структуры в таком же FoxPro как и у заказчика, сохранить в "укромном" месте, а потом просто копировать его и заполнять экспортными данными.


Поддерживаю - лучший вариант для достижения совместимости.
А для доступа к dbf рекомендую легкую и быструю библиотеку TDbf в исходниках
http://sourceforge.net/projects/tdbf/files/
Долгое время беспроблемно использовал для экспорта/импорта ее версию 4.0
Автор: Maks150988
Дата сообщения: 20.08.2011 18:01
Доброго времени суток. Расширил функционал ListBox. Создал свою структуру для каждого элемента наподобие ListView.

type
tagLISTBOXEXITEMW = packed record
mask : UINT;
state : UINT;
pszText : LPWSTR;
cchTextMax: Integer;
iImage : Integer;
end;
PListBoxExItemW = ^TListBoxExItemW;
TListBoxExItemW = tagLISTBOXEXITEMW;


Обрабатываю сообщения по вставке/удалению строк.

function CtrlWndProc_LbAddString(pcp: P_CTRL_PRO; hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
var
iCount: Integer;
bRet : Boolean;
begin
Result := CallWindowProcW(@pcp.CtrlProc, hWnd, uMsg, wParam, lParam);
bRet := (Result <> LB_ERR) and (Result <> LB_ERRSPACE);
if bRet then
begin
iCount := SendMessageW(hWnd, LB_GETCOUNT, 0, 0);
SetLength(pcp.ItemData, iCount);
pcp.ItemData[Result].state := 0;
pcp.ItemData[Result].iImage := I_IMAGENONE;
pcp.ItemData[Result].pszText := '';
pcp.ItemData[Result].cchTextMax := 0;
RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ERASE);
end;
end;

function CtrlWndProc_LbInsertString(pcp: P_CTRL_PRO; hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
var
iCount: Integer;
bRet : Boolean;
begin
Result := CallWindowProcW(@pcp.CtrlProc, hWnd, uMsg, wParam, lParam);
bRet := (Result <> LB_ERR) and (Result <> LB_ERRSPACE);
if bRet then
begin
iCount := SendMessageW(hWnd, LB_GETCOUNT, 0, 0);
SetLength(pcp.ItemData, iCount);
if (Result < (iCount - 1)) then
CopyMemory(
@pcp.ItemData[Result + 1],
@pcp.ItemData[Result],
(iCount - 1 - Result) * SizeOf(pcp.ItemData[0])
);
pcp.ItemData[Result].state := 0;
pcp.ItemData[Result].iImage := I_IMAGENONE;
pcp.ItemData[Result].pszText := '';
pcp.ItemData[Result].cchTextMax := 0;
RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ERASE);
end;
end;

function CtrlWndProc_LbDeleteString(pcp: P_CTRL_PRO; hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
Result := CallWindowProcW(@pcp.CtrlProc, hWnd, uMsg, wParam, lParam);
if (Result <> LB_ERR) then
begin
if (wParam < Result) then
CopyMemory(
@pcp.ItemData[wParam],
@pcp.ItemData[wParam + 1],
(Result - wParam) * SizeOf(pcp.ItemData[0])
);
SetLength(pcp.ItemData, Result);
RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ERASE);
end;
end;

function CtrlWndProc_LbResetContent(pcp: P_CTRL_PRO; hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
Result := CallWindowProcW(@pcp.CtrlProc, hWnd, uMsg, wParam, lParam);
SetLength(pcp.ItemData, 0);
RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ERASE);
end;


Теперь необходимо вставить в элемент ListBox свою структуру.

pszText := 'SendMessageW(GetDlgItem(hWnd, IDC_LISTBOX), LB_EX_SETITEMINFO, i, Integer(@info))';
i := SendMessageW(GetDlgItem(hWnd, IDC_LISTBOX), LB_ADDSTRING, 0, Integer(LPWSTR(pszText)));
ZeroMemory(@info, SizeOf(TListBoxExItemW));
info.mask := LBIF_TEXT or LBIF_IMAGE or LBIF_STATE;
info.iImage := 2;
info.state := LBIS_UNCHECKED;
info.pszText := LPWSTR(pszText);
info.cchTextMax := lstrlenW(info.pszText) + 1;
SendMessageW(GetDlgItem(hWnd, IDC_LISTBOX), LB_EX_SETITEMINFO, i, Integer(@info))


Пытаюсь получить содержимое буфера.

SetLength(pszText, MAX_PATH);
ZeroMemory(@info, SizeOf(TListBoxExItemW));
info.mask := LBIF_TEXT;
info.pszText := LPWSTR(pszText);
info.cchTextMax := MAX_PATH;
SendMessageW(GetDlgItem(hWnd, IDC_LISTBOX), LB_EX_GETITEMINFO, iItem, Integer(@info));
MessageBoxW(hWnd, LPWSTR(psztext), nil, MB_OK);


Получается хрень какая-то. Собственно не понимаю как с пойнтерами работать. Мне нужно присваивать указатели или выделять под них память? Вобщем подскажите как правильнее надо. Ну и если память выделять то GetMem или что-нибудь из WinApi типа ClobalLock + до кучи.
Автор: Man_Without_Face
Дата сообщения: 22.08.2011 14:56
Coltrain
XPerformer

Цитата:
сохранить в "укромном" месте, а потом просто копировать его и заполнять экспортными данными

Спасибо, так и сделал.

Другой вопрос появился: использую TLMDBrowseEdit для указания пути для копирования файла:
CopyFile('file.DBF', LMDBEexport.text+'\file.DBF',true);
Ошибка:
E2010 Incompatible types: 'string' and 'Pointer'
В хелпе написано:
destination
Required. Character string destination where the file or files from source are to be copied. Wildcard characters are not allowed.

Как по другому файл скопировать с указанием заранее не известной директории?

Автор: Gnom3
Дата сообщения: 22.08.2011 15:04

Цитата:
Подскажите пожалуйста, как задать цвет окна через HWND?
Смысл такой - есть вызываемая функция, которая обрабатывет окно именно через хендл, нужно внутри этой-же функции к тому-же окну задать цвет. Насколько это возможно?

Нашел ответ - создаю оконную процедуру для моего окна и в нем перерисовываю фон:

Код:
WM_ERASEBKGND:
begin
GetWindowRect(hDlg, DlgRect);
DlgRect.Right := DlgRect.Right - DlgRect.Left;
DlgRect.Left := 0;
DlgRect.Bottom := DlgRect.Bottom - DlgRect.Top;
DlgRect.Top := 0;
BRUSH := CreateSolidBrush($FF0000);
try
FillRect(wParam, DlgRect, BRUSH);
finally
DeleteObject(BRUSH);
end;
Result := 1;
end;
Автор: Czechoslovak
Дата сообщения: 22.08.2011 15:40
Man_Without_Face
CopyFile('file.DBF', PChar(LMDBEexport.text+'\file.DBF'),true);
Автор: Maks150988
Дата сообщения: 23.08.2011 10:17
Gnom3
Навскидку, BitBlt с координат верхнего левого угла кнопки на родителе из HDC, полученном через GetDC(GetParent(ЭЛЕМЕНТ)).
Кстати, заместо GetWindowRect можно использовать GetClientRect без дополнительного подсчета правой и нижней границы.

Кто бы мне подсказал бы еще с моим вопросом... =)
Автор: Gnom3
Дата сообщения: 23.08.2011 16:57
Я, видимо, не совсем корректно задал вопрос Прошу не судить за это строго. В общем, на окне находятся несколько объектов типа TNewNotebook и одного типа TPanel. Мне нужно поймать эти объекты и закрасить в цвет окна-родителя.
Пробую выловить с помощью EnumWindows(FillRect(wParam, RC1, BRUSH),0); ругается - [Error] Incompatible types: 'Integer' and 'Pointer'

Я рою инет пока, и второй день не нахожу подходящего примера. По сути, нужно получить хендлы дочерних объектов определенного типа и применить к ним определенную функцию.
Автор: akaGM
Дата сообщения: 23.08.2011 17:58
Gnom3
а статически через свойства не подходит, нужна обязательно динамическая закраска?
и кол-во контролов наперёд неизвестно что ли?
и вообще лучше не через АПИ делать, а через vcl-ные
Components[], ComponentCount
Автор: Gnom3
Дата сообщения: 23.08.2011 18:06

Цитата:
а статически через свойства не подходит, нужна обязательно динамическая закраска?

Статически и делаю сейчас. Просто оформляю готовое окно программы из подключаемой длл, и половину подготовкм приходится сейчас делать из программы. Просто хочется избавиться от этого. Контролы известны, но это опять-же - дополнительные обращения из программы к библиотеке.
Автор: ShIvADeSt
Дата сообщения: 24.08.2011 02:11
Maks150988

Цитата:
Кто бы мне подсказал бы еще с моим вопросом... =)

Самое простое - посмотри как это сделано в Дельфи (там элементам листбокса можно дату устанавливать), тут проблема в том, что если удаляешь элемент из листбокса, то нужно чистить за ним память.
Автор: smirnvlad
Дата сообщения: 24.08.2011 07:05
Maks150988
в CtrlWndProc_LbDeleteString указатель на строку pszText затирается, а память занятая строкой перед этим не освобождается, так же и в CtrlWndProc_LbResetContent

а как выглядят функции для LB_EX_SETITEMINFO и LB_EX_GETITEMINFO

в LB_EX_SETITEMINFO надо копировать строку из переданного указателя в свой, а не присваивать указатель

в LB_EX_GETITEMINFO нельзя выделять память под возвращаемую строку, её выделяет тот кто шлет сообщение, а раз размер строки заранее не известен, то при передачи nil вместо указателя на строку, должен заполняться размер строки, и только вторым сообщением копирование строки в переданный указатель
Автор: Maks150988
Дата сообщения: 24.08.2011 17:20
smirnvlad

Да это потом сделаю освобождение памяти. Я просто вот чего не пойму. Выполняю заполнение списка через CtrlWndProc_LbAddString два раза. В LB_EX_SETITEMINFO код такой.


Код: if ((plbi.mask and LBIF_TEXT) <> 0) then
begin
bRet := IsBadWritePtr(plbi.pszText, plbi.cchTextMax);
if not bRet then
begin
// GetMem(pcp.ItemData[wParam].pszText, plbi.cchTextMax);
// ZeroMemory(pcp.ItemData[wParam].pszText, plbi.cchTextMax);
pcp.ItemData[wParam].pszText := LPWSTR(GlobalAlloc(GPTR, plbi.cchTextMax));
lstrcpynW(@pcp.ItemData[wParam].pszText, @plbi.pszText, plbi.cchTextMax);
end;
end;

Страницы: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374

Предыдущая тема: MPO File


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.