intfwin.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    IntfWin – модуль, реализующий интерфейс между модулями Utils, FileIO и
    программной платформой WIN32.

    Copyright © 2017 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit IntfWin;

{$MODE DELPHI,EXTENDEDSYNTAX ON}

interface

uses
    Windows, Lang;

{$ASMMODE INTEL,CALLING REGISTER,INLINE ON,GOTO ON}
{$H+,I-,J-,M-,Q-,R-,T-}

type
    OSIntfFileOpenID = HANDLE;

    OSIntfFileFindID = class(_Object)
    public
        constructor create(); overload;
        constructor create(findID: HANDLE; const findData: WIN32_FIND_DATAW); overload;
        procedure close(); virtual; abstract;
        function findNext(): boolean; virtual; abstract;
    private
        findID: HANDLE;
        findData: WIN32_FIND_DATAW;
    end;

{$I FIOINTF.INC}

implementation

type
    OSIntfDriveFindWin32 = class(OSIntfFileFindID)
    public
        constructor create();
        procedure close(); override;
        function findNext(): boolean; override;
    end;

    OSIntfFileFindWin32 = class(OSIntfFileFindID)
    public
        constructor create(findID: HANDLE; const findData: WIN32_FIND_DATAW);
        procedure close(); override;
        function findNext(): boolean; override;
    end;

{ unit private members }

function buildOSIntfTime(year, month, day,
        hour, minute, second, millis: int): OSIntfDateTimeRecord;
begin
    result.year := year;
    result.month := month;
    result.day := day;
    result.reserved := 0;
    result.hour := hour;
    result.minute := minute;
    result.second := second;
    result.millis := millis;
end;

function systemTimeToOSIntf(const st: SYSTEMTIME): OSIntfDateTimeRecord;
begin
    result.year := st.year;
    result.month := st.month;
    result.day := st.day;
    result.reserved := 0;
    result.hour := st.hour;
    result.minute := st.minute;
    result.second := st.second;
    result.millis := st.millisecond;
end;

function fileTimeToOSIntf(const ft: FILETIME): OSIntfDateTimeRecord;
var
    st: SYSTEMTIME;
begin
    if (ft.dwLowDateTime = 0) and (ft.dwHighDateTime = 0) then begin
        result := buildOSIntfTime(0, 0, 0, 0, 0, 0, 0);
        exit;
    end;
    initialize(st);
    fileTimeToSystemTime(ft, st);
    result := systemTimeToOSIntf(st);
end;

function osintfTimeToFile(const ot: OSIntfDateTimeRecord): FILETIME;
var
    st: SYSTEMTIME;
begin
    initialize(result);
    st.year := Word(ot.year);
    st.month := Word(ot.month);
    st.dayOfWeek := Word(-1);
    st.day := Word(ot.day);
    st.hour := Word(ot.hour);
    st.minute := Word(ot.minute);
    st.second := Word(ot.second);
    st.millisecond := Word(ot.millis);
    systemTimeToFileTime(st, result);
end;

{ OSIntfFileFindID }

constructor OSIntfFileFindID.create();
begin
    inherited create();
end;

constructor OSIntfFileFindID.create(findID: HANDLE; const findData: WIN32_FIND_DATAW);
begin
    inherited create();
    self.findID := findID;
    self.findData := findData;
end;

{ OSIntfDriveFindWin32 }

constructor OSIntfDriveFindWin32.create();
begin
    inherited create();
    with findData do begin
        dwFileAttributes := OSINTF_FILE_ATTR_DIRECTORY;
        ftCreationTime.dwLowDateTime := 0;
        ftCreationTime.dwHighDateTime := 0;
        ftLastWriteTime.dwLowDateTime := 0;
        ftLastWriteTime.dwHighDateTime := 0;
        ftLastAccessTime.dwLowDateTime := 0;
        ftLastAccessTime.dwHighDateTime := 0;
        nFileSizeLow := 0;
        nFileSizeHigh := 0;
        dwReserved0 := 0;
        dwReserved1 := 0;
        cFileName[0] := uchar(int('A') - 1);
        cFileName[1] := ':';
        cFileName[2] := #$0000;
        cAlternateFileName[0] := #$0000;
    end;
    findNext();
end;

procedure OSIntfDriveFindWin32.close();
begin
end;

function OSIntfDriveFindWin32.findNext(): boolean;
var
    driveName: PWideChar;
    driveType: DWORD;
begin
    driveName := @(findData.cFileName);
    repeat
        inc(driveName[0]);
        if driveName[0] > 'Z' then begin
            result := false;
            exit;
        end;
        driveType := getDriveTypeW(PWideChar(UnicodeString(driveName) + DIRECTORY_SEPARATOR));
        if (driveType <> DRIVE_UNKNOWN) and (driveType <> DRIVE_NO_ROOT_DIR) then begin
            result := true;
            exit;
        end;
    until false;
end;

{ OSIntfFileFindWin32 }

constructor OSIntfFileFindWin32.create(findID: HANDLE; const findData: WIN32_FIND_DATAW);
begin
    inherited create(findID, findData);
end;

procedure OSIntfFileFindWin32.close();
begin
    findClose(findID);
end;

function OSIntfFileFindWin32.findNext(): boolean;
begin
    result := findNextFileW(findID, @findData);
end;

{ functions }

{ date & time }

function osintfGetCurrentUTCOffsetInMillis(): int;
var
    tzInfo: TIME_ZONE_INFORMATION;
begin
    tzInfo.bias := int(0);
    getTimeZoneInformation(@tzInfo);
    result := -60000 * tzInfo.bias;
end;

function osintfGetCurrentUTCTime(): OSIntfDateTimeRecord;
var
    st: SYSTEMTIME;
begin
    initialize(st);
    getSystemTime(@st);
    result := systemTimeToOSIntf(st);
end;

{ file system }

function osintfFileNameMaximumLength(): int;
begin
    result := MAX_PATH;
end;

function osintfFileNameCaseSensitive(): boolean;
begin
    result := false;
end;

function osintfFileNameValid(const osFileName: UnicodeString): boolean;
var
    i: int;
    len: int;
    c: uchar;
begin
    len := length(osFileName);
    if len = 0 then begin
        result := true;
        exit;
    end;
    if (len < 2) or (len > MAX_PATH) then begin
        result := false;
        exit;
    end;
    c := osFileName[1];
    if ((c < 'A') or (c > 'Z')) and ((c < 'a') or (c > 'z')) or (osFileName[2] <> ':') or
            (len >= 3) and (osFileName[3] <> DIRECTORY_SEPARATOR) then begin
        result := false;
        exit;
    end;
    for i := 4 to len do begin
        case osFileName[i] of
        #$0000, '/', ':', '*', '?', '"', '<', '>', '|': begin
            result := false;
            exit;
        end;
        '\': begin
            if osFileName[i - 1] = '\' then begin
                result := false;
                exit;
            end;
        end;
        end;
    end;
    result := true;
end;

function osintfFileNameToVFS(const osFileName: UnicodeString): UnicodeString;
label
    label0;
var
    i: int;
    j: int;
    cc: int;
    len: int;
    c: uchar;
begin
    result := stringCopy(osFileName);
    len := length(result);
    i := 2;
    while i <= len do begin
        c := result[i];
        case c of
        '$': begin
            if i < len - 1 then begin
                cc := 0;
                for j := 1 to 2 do begin
                    c := result[i + j];
                    case c of
                    '0'..'9': begin
                        cc := (cc shl 4) + (int(c) - int('0'));
                    end;
                    'A'..'F': begin
                        cc := (cc shl 4) + (int(c) - (int('A') - $0a));
                    end;
                    'a'..'f': begin
                        cc := (cc shl 4) + (int(c) - (int('a') - $0a));
                    end;
                    else
                        goto label0;
                    end;
                end;
                result[i] := uchar(cc);
                delete(result, i + 1, 2);
                dec(len, 2);
            end;
        end;
        '\': begin
            result[i] := '/';
        end;
        end;
        label0:
        inc(i);
    end;
end;

function osintfFileNameToOS(const vfsFileName: UnicodeString): UnicodeString;
var
    i: int;
    len: int;
    c: uchar;
begin
    result := stringCopy(vfsFileName);
    len := length(result);
    if (len > 0) and (result[1] = '/') then begin
        delete(result, 1, 1);
        dec(len);
    end;
    i := 1;
    while i <= len do begin
        c := result[i];
        case c of
        #$0000..#$001f, '$', '\', ':', '*', '?', '"', '<', '>', '|': begin
            if (i <> 2) or (c <> ':') then begin
                result[i] := '$';
                insert(stringToUTF16(DIGITS[((int(c) shr 4) and $0f) + 1] +
                        DIGITS[(int(c) and $0f) + 1]), result, i + 1);
                inc(i, 2);
            end;
        end;
        '/': begin
            result[i] := '\';
        end;
        end;
        inc(i);
    end;
end;

function osintfFileWriteAttributes(const osFileName: UnicodeString; const attributes: int;
        const creationTime, lastWriteTime, lastAccessTime: OSIntfDateTimeRecord): boolean;
var
    fileID: HANDLE;
    realAttr: DWORD;
    crTime: FILETIME;
    lwTime: FILETIME;
    laTime: FILETIME;
begin
    realAttr := getFileAttributesW(PWideChar(osFileName));
    if int(realAttr) = -1 then begin
        result := false;
        exit;
    end;
    realAttr := (realAttr and not DWORD(OSINTF_FILE_ATTR_MASK)) or
            DWORD(attributes and OSINTF_FILE_ATTR_MASK);
    crTime := osintfTimeToFile(creationTime);
    lwTime := osintfTimeToFile(lastWriteTime);
    laTime := osintfTimeToFile(lastAccessTime);
    if not setFileAttributesW(PWideChar(osFileName), realAttr) then begin
        result := false;
        exit;
    end;
    fileID := createFileW(PWideChar(osFileName), GENERIC_WRITE, FILE_SHARE_WRITE, nil,
            OPEN_EXISTING, realAttr or FILE_FLAG_BACKUP_SEMANTICS, 0);
    if fileID = INVALID_HANDLE_VALUE then begin
        result := false;
        exit;
    end;
    try
        if not setFileTime(fileID, @crTime, @laTime, @lwTime) then begin
            result := false;
            exit;
        end;
    finally
        closeHandle(fileID);
    end;
    result := true;
end;

function osintfFileReadAttributes(const osFileName: UnicodeString; out attributes: int;
        out creationTime, lastWriteTime, lastAccessTime: OSIntfDateTimeRecord): boolean;
var
    fileID: HANDLE;
    realAttr: DWORD;
    crTime: FILETIME;
    lwTime: FILETIME;
    laTime: FILETIME;
begin
    realAttr := getFileAttributesW(PWideChar(osFileName));
    if int(realAttr) = -1 then begin
        result := false;
        exit;
    end;
    fileID := createFileW(PWideChar(osFileName), GENERIC_READ, FILE_SHARE_READ, nil,
            OPEN_EXISTING, realAttr or FILE_FLAG_BACKUP_SEMANTICS, 0);
    if fileID = INVALID_HANDLE_VALUE then begin
        result := false;
        exit;
    end;
    try
        if not getFileTime(fileID, @crTime, @laTime, @lwTime) then begin
            result := false;
            exit;
        end;
    finally
        closeHandle(fileID);
    end;
    attributes := int(realAttr) and OSINTF_FILE_ATTR_MASK;
    creationTime := fileTimeToOSIntf(crTime);
    lastWriteTime := fileTimeToOSIntf(lwTime);
    lastAccessTime := fileTimeToOSIntf(laTime);
    result := true;
end;

function osintfFileFindFirst(const osBasePath: UnicodeString;
        out findInfo: OSIntfFileFindInfo): boolean;
var
    c: uchar;
    len: int;
    driveType: DWORD;
    findID: HANDLE;
    findData: WIN32_FIND_DATAW;
    findObject: OSIntfFileFindID;
    defaultTime: OSIntfDateTimeRecord;
begin
    len := length(osBasePath);
    if len = 0 then begin
        findObject := OSIntfDriveFindWin32.create();
        defaultTime := buildOSIntfTime(0, 0, 0, 0, 0, 0, 0);
        findInfo.attributes := OSINTF_FILE_ATTR_DIRECTORY;
        findInfo.creationTime := defaultTime;
        findInfo.lastWriteTime := defaultTime;
        findInfo.lastAccessTime := defaultTime;
        findInfo.size := 0;
        findInfo.name := findObject.findData.cFileName;
        findInfo.findID := findObject;
        result := true;
        exit;
    end;
    if len = 2 then begin
        c := osBasePath[1];
        if ((c >= 'A') and (c <= 'Z') or (c >= 'a') and (c <= 'z')) and
                (osBasePath[2] = ':') then begin
            driveType := getDriveTypeW(PWideChar(osBasePath + DIRECTORY_SEPARATOR));
            if (driveType <> DRIVE_UNKNOWN) and (driveType <> DRIVE_NO_ROOT_DIR) then begin
                defaultTime := buildOSIntfTime(0, 0, 0, 0, 0, 0, 0);
                findInfo.attributes := OSINTF_FILE_ATTR_DIRECTORY;
                findInfo.creationTime := defaultTime;
                findInfo.lastWriteTime := defaultTime;
                findInfo.lastAccessTime := defaultTime;
                findInfo.size := 0;
                findInfo.name := stringToUpperCase(c) + ':';
                findInfo.findID := nil;
                result := true;
                exit;
            end;
        end;
    end;
    if osintfFileNameValid(osBasePath) then begin
        initialize(findData);
        if osBasePath[len] <> DIRECTORY_SEPARATOR then begin
            findID := findFirstFileW(PWideChar(osBasePath), @findData);
            if findID <> INVALID_HANDLE_VALUE then begin
                closeHandle(findID);
                findInfo.attributes := int(findData.dwFileAttributes) and OSINTF_FILE_ATTR_MASK;
                findInfo.creationTime := fileTimeToOSIntf(findData.ftCreationTime);
                findInfo.lastWriteTime := fileTimeToOSIntf(findData.ftLastWriteTime);
                findInfo.lastAccessTime := fileTimeToOSIntf(findData.ftLastAccessTime);
                findInfo.size := longBuild(int(findData.nFileSizeHigh),
                        int(findData.nFileSizeLow));
                findInfo.name := findData.cFileName;
                findInfo.findID := nil;
                result := true;
                exit;
            end;
        end else begin
            findID := findFirstFileW(PWideChar(osBasePath + '*.*'), @findData);
            if findID <> INVALID_HANDLE_VALUE then begin
                findInfo.attributes := int(findData.dwFileAttributes) and OSINTF_FILE_ATTR_MASK;
                findInfo.creationTime := fileTimeToOSIntf(findData.ftCreationTime);
                findInfo.lastWriteTime := fileTimeToOSIntf(findData.ftLastWriteTime);
                findInfo.lastAccessTime := fileTimeToOSIntf(findData.ftLastAccessTime);
                findInfo.size := longBuild(int(findData.nFileSizeHigh),
                        int(findData.nFileSizeLow));
                findInfo.name := findData.cFileName;
                findInfo.findID := OSIntfFileFindWin32.create(findID, findData);
                result := true;
                exit;
            end;
        end;
    end;
    defaultTime := buildOSIntfTime(0, 0, 0, 0, 0, 0, 0);
    findInfo.attributes := 0;
    findInfo.creationTime := defaultTime;
    findInfo.lastWriteTime := defaultTime;
    findInfo.lastAccessTime := defaultTime;
    findInfo.size := 0;
    findInfo.name := '';
    findInfo.findID := nil;
    result := false;
end;

function osintfFileFindNext(var findInfo: OSIntfFileFindInfo): boolean;
var
    findID: OSIntfFileFindID;
begin
    findID := findInfo.findID;
    if (findID <> nil) and findID.findNext() then begin
        with findID.findData do begin
            findInfo.attributes := int(dwFileAttributes) and OSINTF_FILE_ATTR_MASK;
            findInfo.creationTime := fileTimeToOSIntf(ftCreationTime);
            findInfo.lastWriteTime := fileTimeToOSIntf(ftLastWriteTime);
            findInfo.lastAccessTime := fileTimeToOSIntf(ftLastAccessTime);
            findInfo.size := longBuild(int(nFileSizeHigh), int(nFileSizeLow));
            findInfo.name := cFileName;
        end;
        result := true;
        exit;
    end;
    result := false;
end;

function osintfFileFindClose(var findInfo: OSIntfFileFindInfo): boolean;
var
    findID: OSIntfFileFindID;
begin
    findID := findInfo.findID;
    if findID <> nil then begin
        findID.close();
        findID.free();
        findInfo.findID := nil;
        result := true;
        exit;
    end;
    result := false;
end;

function osintfFileMove(const osFileSrcName, osFileDstName: UnicodeString): boolean;
begin
    result := moveFileW(PWideChar(osFileSrcName), PWideChar(osFileDstName));
end;

function osintfFileDelete(const osFileName: UnicodeString): boolean;
begin
    result := deleteFileW(PWideChar(osFileName));
end;

function osintfDirectoryCreate(const osDirName: UnicodeString): boolean;
begin
    result := createDirectoryW(PWideChar(osDirName), nil);
end;

function osintfDirectoryDelete(const osDirName: UnicodeString): boolean;
begin
    result := removeDirectoryW(PWideChar(osDirName));
end;

{ file operation }

function osintfFileCreate(const osFileName: UnicodeString;
        out fileID: OSIntfFileOpenID): boolean;
begin
    fileID := createFileW(PWideChar(osFileName), GENERIC_WRITE, 0, nil,
            CREATE_ALWAYS, 0, 0);
    result := fileID <> INVALID_HANDLE_VALUE;
end;

function osintfFileOpenForReadOnly(const osFileName: UnicodeString;
        out fileID: OSIntfFileOpenID): boolean;
begin
    fileID := createFileW(PWideChar(osFileName), GENERIC_READ, FILE_SHARE_READ, nil,
            OPEN_EXISTING, getFileAttributesW(PWideChar(osFileName)), 0);
    result := fileID <> INVALID_HANDLE_VALUE;
end;

function osintfFileOpenForReadWrite(const osFileName: UnicodeString;
        out fileID: OSIntfFileOpenID): boolean;
begin
    fileID := createFileW(PWideChar(osFileName), GENERIC_READ + GENERIC_WRITE, 0, nil,
            OPEN_EXISTING, getFileAttributesW(PWideChar(osFileName)), 0);
    result := fileID <> INVALID_HANDLE_VALUE;
end;

function osintfFileOpenForAppending(const osFileName: UnicodeString;
        out fileID: OSIntfFileOpenID): boolean;
var
    pos: long;
begin
    fileID := createFileW(PWideChar(osFileName), GENERIC_WRITE, 0, nil,
            OPEN_EXISTING, getFileAttributesW(PWideChar(osFileName)), 0);
    result := fileID <> INVALID_HANDLE_VALUE;
    if result then begin
        pos := 0;
        setFilePointer(fileID, pos.ints[0], @(pos.ints[1]), FILE_END);
    end;
end;

function osintfFileClose(var fileID: OSIntfFileOpenID): boolean;
begin
    result := closeHandle(fileID);
    if result then begin
        fileID := 0;
    end;
end;

function osintfFileSetSize(const fileID: OSIntfFileOpenID): boolean;
begin
    result := setEndOfFile(fileID);
end;

function osintfFileGetSize(const fileID: OSIntfFileOpenID): long;
begin
    result := 0;
    result.ints[0] := int(getFileSize(fileID, @(result.ints[1])));
end;

function osintfFileSetPos(const fileID: OSIntfFileOpenID; pos: long): long;
begin
    result := pos;
    result.ints[0] := int(setFilePointer(fileID, pos.ints[0], @(result.ints[1]), FILE_BEGIN));
end;

function osintfFileSeek(const fileID: OSIntfFileOpenID; delta: long): long;
begin
    result := 0;
    result.ints[1] := delta.ints[1];
    result.ints[0] := int(setFilePointer(fileID, delta.ints[0], @(result.ints[1]), FILE_CURRENT));
end;

function osintfFileRead(const fileID: OSIntfFileOpenID; dst: byte_ArraySimple; length: int): int;
var
    readed: DWORD;
begin
    readed := 0;
    if readFile(fileID, dst^, length, readed, nil) then begin
        result := int(readed);
        exit;
    end;
    result := 0;
end;

function osintfFileWrite(const fileID: OSIntfFileOpenID; src: byte_ArraySimple; length: int): int;
var
    writed: DWORD;
begin
    writed := 0;
    if writeFile(fileID, src^, length, writed, nil) then begin
        result := int(writed);
        exit;
    end;
    result := 0;
end;

end.