Traduction de dokan en delphi: un driver "user-mode" pour disques virtuels

Description

Avant toute chose, je tiens à souligner que ce code est une traduction du header d'interface pour la librairie Dokan (voir http://dokan-dev.net/en/) ainsi que du programme de démonstration Mirror.c qui est fourni avec. À quelques détails près, cela fonctionne exactement de la même façon. Avant de poser des questions sur Dokan, je vous conseille de consulter la section "about" et le Readme.txt qui est fourni avec la librairie à l'adresse plus haut.

Le principe de Dokan est de faciliter l'écriture de drivers de disques virtuels. L'idée consiste à utiliser un driver (en mode "kernel") qui communique les appels bas niveau à une DLL se chargeant de gérer le système de fichiers et les opérations I/O en mode "user" . Les intérêts pour le programmeur sont multiples:
- pas besoin de devoir écrire un driver soi-même (personnellement j'ai bloqué à la première étape, lorsqu'il a fallu installer le DDK) ni de lire la montagne de doc qui va avec
- possibilité d'utiliser n'importe quel langage (ici Delphi)
- last but not least, le débugage est EXTREMEMENT simplifié puisque le code étant exécuté dans un processus en mode "User", s'il se produit une erreur (mettons une violation d'accès) il suffit de stopper le processus qui gère le système de fichier, relire son code, corriger, recompiler et relancer. Noter que dans le cas d'un développement "classique" de driver avec le DDK, à la moindre erreur on se retrouve avec un écran bleu et il faut rebooter. Et je ne parle même pas du débogage, qui nécessite une deuxième machine à côté!

Le programme Mirror.dpr ne fournit pas de fonctionnalité révolutionnaire, il se contente de monter un répertoire sur un disque virtuel de la lettre de son choix (un peu comme la commande SUBST de MS-Dos). Il s'agit avant tout d'un exemple pour illustrer la simplicité d'utilisation de Dokan. Il s'utilise ainsi:
Usage: Mirror.exe
/R RootDirectory (e.g. /R C:\test)
/L DriveLetter (e.g. /L m)
/T ThreadCount (optional, e.g. /T 5)
/D (optional, enable debug output)
/S (optional, use stderr for output)

Par exemple:
Mirror.exe /R C:\test /L x /D
pour monter le répertoire C:\test sur le lecteur X:\

Bien sûr, pour faire fonctionner Mirror.exe, il faudra d'abord installer Dokan et redémarrer la machine. Je n'ai pas constaté de bugs, ça a l'air très stable. Merci de me dire si ça ne fonctionne pas chez vous. Il semblerait que ça fonctionne sous Vista (j'ai testé XP uniquement) mais pas encore sous Win 7.

Parmi les applications les plus immédiates de cette librarie, on peut citer:
- la possibilité de gérer un système de fichier cryptés avec la méthode de son choix. A priori, il suffirait simplement de modifier les méthodes MirrorReadFile et MirrorWriteFile!
- la possibilité de gérer de manière transparente un système de fichiers distant (FTP ou autres)
- etc...

Les concepts derrière cette librarie sont certainement de niveau "expert", mais la programmation est tellement simple que je le mets dans la catégorie "initié".

Source / Exemple :


program Mirror;

(*******************************************************************************
 *

  • Copyright (c) 2007, 2008 Hiroki Asakawa info@dokan-dev.net
*
  • Delphi translation by Vincent Forman (vincent.forman@gmail.com)
*
  • Permission is hereby granted, free of charge, to any person obtaining a copy
  • of this software and associated documentation files (the "Software"), to deal
  • in the Software without restriction, including without limitation the rights
  • to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  • copies of the Software, and to permit persons to whom the Software is
  • furnished to do so, subject to the following conditions:
*
  • The above copyright notice and this permission notice shall be included in
  • all copies or substantial portions of the Software.
*
  • THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  • IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  • FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  • AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  • LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  • OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  • THE SOFTWARE.
*
                                                                                                                                                              • )
{$APPTYPE CONSOLE} uses Windows, SysUtils, Dokan in 'Dokan.pas'; // Not available in Windows.pas function SetFilePointerEx(hFile: THandle; lDistanceToMove: LARGE_INTEGER; lpNewFilePointer: Pointer; dwMoveMethod: DWORD): BOOL; stdcall; external kernel32; // Some additional Win32 flags const FILE_READ_DATA = $00000001; FILE_WRITE_DATA = $00000002; FILE_APPEND_DATA = $00000004; FILE_READ_EA = $00000008; FILE_WRITE_EA = $00000010; FILE_EXECUTE = $00000020; FILE_READ_ATTRIBUTES = $00000080; FILE_WRITE_ATTRIBUTES = $00000100; FILE_ATTRIBUTE_ENCRYPTED = $00000040; FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $00002000; FILE_FLAG_OPEN_NO_RECALL = $00100000; FILE_FLAG_OPEN_REPARSE_POINT = $00200000; STATUS_DIRECTORY_NOT_EMPTY = $C0000101; INVALID_SET_FILE_POINTER = $FFFFFFFF; // Utilities routines, to be defined later procedure DbgPrint(const Message: string); overload; forward; procedure DbgPrint(const Format: string; const Args: array of const); overload; forward; function MirrorConvertPath(FileName: PWideChar): string; forward; // Output the value of a flag by searching amongst an array of value/name pairs procedure CheckFlag(const Flag: Cardinal; Values: array of Cardinal; Names: array of string); var i:Integer; begin for i:=Low(Values) to High(Values) do if Values[i]=Flag then DbgPrint(' %s',[Names[i]]); end; type EDokanMainError = class(Exception) public constructor Create(DokanErrorCode: Integer); end; constructor EDokanMainError.Create(DokanErrorCode: Integer); var s:string; begin case DokanErrorCode of DOKAN_SUCCESS: s := 'Success'; DOKAN_ERROR: s := 'Generic error'; DOKAN_DRIVE_LETTER_ERROR: s := 'Bad drive letter'; DOKAN_DRIVER_INSTALL_ERROR: s := 'Cannot install driver'; DOKAN_START_ERROR: s := 'Cannot start driver'; DOKAN_MOUNT_ERROR: s := 'Cannot mount on the specified drive letter'; else s := 'Unknown error'; end; inherited CreateFmt('Dokan Error. Code: %d.'+sLineBreak+'%s',[DokanErrorCode,s]); end; // Dokan callbacks function MirrorCreateFile(FileName: PWideChar; AccessMode, ShareMode, CreationDisposition, FlagsAndAttributes: Cardinal; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; const AccessModeValues: array[1..19] of Cardinal = ( GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE, _DELETE, FILE_READ_DATA, FILE_READ_ATTRIBUTES, FILE_READ_EA, READ_CONTROL, FILE_WRITE_DATA, FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, FILE_APPEND_DATA, WRITE_DAC, WRITE_OWNER, SYNCHRONIZE, FILE_EXECUTE, STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, STANDARD_RIGHTS_EXECUTE ); AccessModeNames: array[1..19] of string = ( 'GENERIC_READ', 'GENERIC_WRITE', 'GENERIC_EXECUTE', 'DELETE', 'FILE_READ_DATA', 'FILE_READ_ATTRIBUTES', 'FILE_READ_EA', 'READ_CONTROL', 'FILE_WRITE_DATA', 'FILE_WRITE_ATTRIBUTES', 'FILE_WRITE_EA', 'FILE_APPEND_DATA', 'WRITE_DAC', 'WRITE_OWNER', 'SYNCHRONIZE', 'FILE_EXECUTE', 'STANDARD_RIGHTS_READ', 'STANDARD_RIGHTS_WRITE', 'STANDARD_RIGHTS_EXECUTE' ); ShareModeValues: array[1..3] of Cardinal = ( FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE ); ShareModeNames: array[1..3] of string = ( 'FILE_SHARE_READ', 'FILE_SHARE_WRITE', 'FILE_SHARE_DELETE' ); CreationDispositionValues: array[1..5] of Cardinal = ( CREATE_NEW, OPEN_ALWAYS, CREATE_ALWAYS, OPEN_EXISTING, TRUNCATE_EXISTING ); CreationDispositionNames: array[1..5] of string = ( 'CREATE_NEW', 'OPEN_ALWAYS', 'CREATE_ALWAYS', 'OPEN_EXISTING', 'TRUNCATE_EXISTING' ); FlagsAndAttributesValues: array[1..26] of Cardinal = ( FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ENCRYPTED, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY, FILE_FLAG_WRITE_THROUGH, FILE_FLAG_OVERLAPPED, FILE_FLAG_NO_BUFFERING, FILE_FLAG_RANDOM_ACCESS, FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_POSIX_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_OPEN_NO_RECALL, SECURITY_ANONYMOUS, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, SECURITY_DELEGATION, SECURITY_CONTEXT_TRACKING, SECURITY_EFFECTIVE_ONLY, SECURITY_SQOS_PRESENT ); FlagsAndAttributesNames: array[1..26] of string = ( 'FILE_ATTRIBUTE_ARCHIVE', 'FILE_ATTRIBUTE_ENCRYPTED', 'FILE_ATTRIBUTE_HIDDEN', 'FILE_ATTRIBUTE_NORMAL', 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED', 'FILE_ATTRIBUTE_OFFLINE', 'FILE_ATTRIBUTE_READONLY', 'FILE_ATTRIBUTE_SYSTEM', 'FILE_ATTRIBUTE_TEMPORARY', 'FILE_FLAG_WRITE_THROUGH', 'FILE_FLAG_OVERLAPPED', 'FILE_FLAG_NO_BUFFERING', 'FILE_FLAG_RANDOM_ACCESS', 'FILE_FLAG_SEQUENTIAL_SCAN', 'FILE_FLAG_DELETE_ON_CLOSE', 'FILE_FLAG_BACKUP_SEMANTICS', 'FILE_FLAG_POSIX_SEMANTICS', 'FILE_FLAG_OPEN_REPARSE_POINT', 'FILE_FLAG_OPEN_NO_RECALL', 'SECURITY_ANONYMOUS', 'SECURITY_IDENTIFICATION', 'SECURITY_IMPERSONATION', 'SECURITY_DELEGATION', 'SECURITY_CONTEXT_TRACKING', 'SECURITY_EFFECTIVE_ONLY', 'SECURITY_SQOS_PRESENT' ); begin FilePath := MirrorConvertPath(FileName); DbgPrint('CreateFile: %s', [filePath]); (* if (ShareMode = 0) and ((AccessMode and FILE_WRITE_DATA) <> 0) then ShareMode := FILE_SHARE_WRITE else if ShareMode = 0 then ShareMode := FILE_SHARE_READ;
  • )
DbgPrint(' AccessMode = 0x%x', [AccessMode]); CheckFlag(AccessMode, AccessModeValues, AccessModeNames); DbgPrint(' ShareMode = 0x%x', [ShareMode]); CheckFlag(ShareMode, ShareModeValues, ShareModeNames); DbgPrint(' CreationDisposition = 0x%x', [ShareMode]); CheckFlag(CreationDisposition, CreationDispositionValues, CreationDispositionNames); // Check if FilePath is a directory if (GetFileAttributes(PChar(FilePath)) and FILE_ATTRIBUTE_DIRECTORY) <> 0 then FlagsAndAttributes := FlagsAndAttributes or FILE_FLAG_BACKUP_SEMANTICS; DbgPrint(' FlagsAndAttributes = 0x%x', [FlagsAndAttributes]); CheckFlag(FlagsAndAttributes, FlagsAndAttributesValues, FlagsAndAttributesNames); // Save the file handle in Context DokanFileInfo.Context := CreateFile(PChar(FilePath), AccessMode, ShareMode, nil, CreationDisposition, FlagsAndAttributes, 0); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin // Error codes are negated value of Win32 error codes Result := -GetLastError; DbgPrint('CreateFile failed, error code = %d', [-Result]); end else Result := 0; DbgPrint(''); end; function MirrorOpenDirectory(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('OpenDirectory: %s', [FilePath]); DokanFileInfo.Context := CreateFile(PChar(FilePath), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -GetLastError; DbgPrint('CreateFile failed, error code = %d', [-Result]); end else Result := 0; DbgPrint(''); end; function MirrorCreateDirectory(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('CreateDirectory: %s', [FilePath]); if not CreateDirectory(PChar(FilePath), nil) then begin Result := -GetLastError; DbgPrint('CreateDirectory failed, error code = %d', [-Result]); end else Result := 0; DbgPrint(''); end; function MirrorCleanup(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('Cleanup: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('Error: invalid handle', [FilePath]); end else begin Result := 0; CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; if DokanFileInfo.DeleteOnClose then begin if DokanFileInfo.IsDirectory then begin DbgPrint('DeleteOnClose -> RemoveDirectory'); if not RemoveDirectory(PChar(FilePath)) then DbgPrint('RemoveDirectory failed, error code = %d', [GetLastError]); end else begin DbgPrint('DeleteOnClose -> DeleteFile'); if not DeleteFile(PChar(FIlePath)) then DbgPrint('DeleteFile failed, error code = %d', [GetLastError]); end; end; end; DbgPrint(''); end; function MirrorCloseFile(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin Result := 0; FilePath := MirrorConvertPath(FileName); DbgPrint('CloseFile: %s', [FilePath]); if DokanFileInfo.Context <> INVALID_HANDLE_VALUE then begin DbgPrint('Error: file was not closed during cleanup'); CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; end; DbgPrint(''); end; function MirrorReadFile(FileName: PWideChar; var Buffer; NumberOfBytesToRead: Cardinal; var NumberOfBytesRead: Cardinal; Offset: Int64; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; Opened: Boolean; begin FilePath := MirrorConvertPath(FileName); DbgPrint('ReadFile: %s (Offset: %d, Length: %d)', [FilePath, Offset, NumberOfBytesToRead]); Opened := DokanFileInfo.Context = INVALID_HANDLE_VALUE; if Opened then begin DbgPrint('Invalid handle (maybe passed through cleanup?), creating new one'); DokanFileInfo.Context := CreateFile(PChar(FilePath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); end; if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -GetLastError; DbgPrint('CreateFile failed, error code = %d', [-Result]); end else try if SetFilePointerEx(DokanFileInfo.Context, LARGE_INTEGER(Offset), nil, FILE_BEGIN) then begin if ReadFile(DokanFileInfo.Context, Buffer, NumberOfBytesToRead, NumberOfBytesRead, nil) then begin Result := 0; DbgPrint('Read: %d', [NumberOfBytesRead]); end else begin Result := -GetLastError; DbgPrint('ReadFile failed, error code = %d', [-Result]); end; end else begin Result := -GetLastError; DbgPrint('Seek failed, error code = %d', [-Result]); end; finally if Opened then begin CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; end; end; DbgPrint(''); end; function MirrorWriteFile(FileName: PWideChar; var Buffer; NumberOfBytesToWrite: Cardinal; var NumberOfBytesWritten: Cardinal; Offset: Int64; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; Opened: Boolean; begin FilePath := MirrorConvertPath(FileName); DbgPrint('WriteFile: %s (Offset: %d, Length: %d)', [FilePath, Offset, NumberOfBytesToWrite]); Opened := DokanFileInfo.Context = INVALID_HANDLE_VALUE; if Opened then begin DbgPrint('Invalid handle (maybe passed through cleanup?), creating new one'); DokanFileInfo.Context := CreateFile(PChar(FilePath), GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); end; if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -GetLastError; DbgPrint('CreateFile failed, error code = %d', [-Result]); end else try if SetFilePointerEx(DokanFileInfo.Context, LARGE_INTEGER(Offset), nil, FILE_BEGIN) then begin if WriteFile(DokanFileInfo.Context, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, nil) then begin Result := 0; DbgPrint('Written: %d', [NumberOfBytesWritten]); end else begin Result := -GetLastError; DbgPrint('ReadFile failed, error code = %d', [-Result]); end; end else begin Result := -GetLastError; DbgPrint('Seek failed, error code = %d', [-Result]); end; finally if Opened then begin CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; end; end; DbgPrint(''); end; function MirrorFlushFileBuffers(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('FlushFileBuffers: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('Error: invalid handle') end else begin if FlushFileBuffers(DokanFileInfo.Context) then Result := 0 else begin Result := -GetLastError; DbgPrint('FlushFileBuffers failed, error code = %d', [-Result]); end; end; DbgPrint(''); end; function MirrorGetFileInformation(FileName: PWideChar; FileInformation: PByHandleFileInformation; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; Opened: Boolean; FindData: WIN32_FIND_DATAA; FindHandle: THandle; begin FilePath := MirrorConvertPath(FileName); DbgPrint('GetFileInformation: %s', [FilePath]); Opened := DokanFileInfo.Context = INVALID_HANDLE_VALUE; if Opened then begin DbgPrint('Invalid handle (maybe passed through cleanup?), creating new one'); DokanFileInfo.Context := CreateFile(PChar(FilePath), GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); end; if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('CreateFile failed, error code = %d', [GetLastError]); end else try if GetFileInformationByHandle(DokanFileInfo.Context, FileInformation^) then Result := 0 else begin DbgPrint('GetFileInformationByHandle failed, error code = %d', [GetLastError]); if Length(FileName) = 1 then begin Result := 0; FileInformation.dwFileAttributes := GetFileAttributes(PChar(FilePath)); end else begin ZeroMemory(@FindData, SizeOf(FindData)); FindHandle := FindFirstFile(PChar(FilePath), FindData); if FindHandle = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('FindFirstFile failed, error code = %d', [GetLastError]); end else begin Result := 0; FileInformation.dwFileAttributes := FindData.dwFileAttributes; FileInformation.ftCreationTime := FindData.ftCreationTime; FileInformation.ftLastAccessTime := FindData.ftLastAccessTime; FileInformation.ftLastWriteTime := FindData.ftLastWriteTime; FileInformation.nFileSizeHigh := FindData.nFileSizeHigh; FileInformation.nFileSizeLow := FindData.nFileSizeLow; Windows.FindClose(FindHandle); end; end; end; finally if Opened then begin CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; end; end; DbgPrint(''); end; function MirrorFindFiles(PathName: PWideChar; FillFindDataCallback: TDokanFillFindData; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: widestring; FindData: WIN32_FIND_DATAW; FindHandle: THandle; begin FilePath := MirrorConvertPath(PathName) + '\*'; DbgPrint('GetFileInformation: %s', [FilePath]); FindHandle := FindFirstFileW(PWideChar(FilePath), FindData); if FindHandle = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('FindFirstFile failed, error code = %d', [GetLastError]); end else begin Result := 0; try FillFindDataCallback(FindData, DokanFileInfo); while FindNextFileW(FindHandle, FindData) do FillFindDataCallback(FindData, DokanFileInfo); finally Windows.FindClose(FindHandle); end; end; DbgPrint(''); end; function MirrorSetFileAttributes(FileName: PWideChar; FileAttributes: Cardinal; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('SetFileAttributes: %s', [FilePath]); if SetFileAttributes(PChar(FilePath), FileAttributes) then Result := 0 else begin Result := -GetLastError; DbgPrint('SetFileAttributes failed, error code = %d', [-Result]); end; DbgPrint(''); end; function MirrorSetFileTime(FileName: PWideChar; CreationTime, LastAccessTime, LastWriteTime: PFileTime; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('SetFileTime: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('Error: invalid handle'); end else begin if SetFileTime(DokanFileInfo.Context, CreationTime, LastAccessTime, LastWriteTime) then Result := 0 else begin Result := -GetLastError; DbgPrint('SetFileTime failed, error code = %d', [-Result]); end; end; DbgPrint(''); end; function MirrorDeleteFile(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin Result := 0; FilePath := MirrorConvertPath(FileName); DbgPrint('DeleteFile: %s', [FilePath]); DbgPrint(''); end; function MirrorDeleteDirectory(FileName: PWideChar; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; FindData: WIN32_FIND_DATAA; FindHandle: THandle; begin FilePath := MirrorConvertPath(FileName); DbgPrint('DeleteDirectory: %s', [FilePath]); FindHandle := FindFirstFile(PChar(FilePath), FindData); if FindHandle = INVALID_HANDLE_VALUE then begin Result := -GetLastError; if Result = -ERROR_NO_MORE_FILES then Result := 0 else DbgPrint('FindFirstFile failed, error code = %d', [-Result]); end else begin Cardinal(Result) := STATUS_DIRECTORY_NOT_EMPTY; Result := -Result; Windows.FindClose(FindHandle); end; DbgPrint(''); end; function MirrorMoveFile(ExistingFileName, NewFileName: PWideChar; ReplaceExisiting: LongBool; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var ExistingFilePath, NewFilePath: string; Status: Boolean; begin ExistingFilePath := MirrorConvertPath(ExistingFileName); NewFilePath := MirrorConvertPath(NewFileName); DbgPrint('MoveFile: %s -> %s', [ExistingFilePath, NewFilePath]); if DokanFileInfo.Context <> INVALID_HANDLE_VALUE then begin CloseHandle(DokanFileInfo.Context); DokanFileInfo.Context := INVALID_HANDLE_VALUE; end; if ReplaceExisiting then Status := MoveFileEx(PChar(ExistingFilePath), PChar(NewFilePath), MOVEFILE_REPLACE_EXISTING) else Status := MoveFile(PChar(ExistingFilePath), PChar(NewFilePath)); if Status then Result := 0 else begin Result := -GetLastError; DbgPrint('MoveFile failed, error code = %d', [-Result]); end; DbgPrint(''); end; function MirrorSetEndOfFile(FileName: PWideChar; Length: Int64; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('SetEndOfFile: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin Result := -1; DbgPrint('Invalid handle'); end else begin if SetFilePointerEx(DokanFileInfo.Context, LARGE_INTEGER(Length), nil, FILE_BEGIN) then begin if SetEndOfFile(DokanFileInfo.Context) then Result := 0 else begin Result := -GetLastError; DbgPrint('SetEndOfFile failed, error code = %d', [-Result]); end; end else begin Result := -GetLastError; DbgPrint('Seek failed, error code = %d', [-Result]); end; end; DbgPrint(''); end; function MirrorLockFile(FileName: PWideChar; Offset, Length: Int64; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('LockFile: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin DbgPrint('Invalid handle'); Result := -1; end else begin if LockFile(DokanFileInfo.Context, LARGE_INTEGER(Offset).LowPart, LARGE_INTEGER(Offset).HighPart, LARGE_INTEGER(Length).LowPart, LARGE_INTEGER(Length).HighPart) then Result := 0 else begin Result := -GetLastError; DbgPrint('LockFile failed, error code = %d', [-Result]); end; end; DbgPrint(''); end; function MirrorUnlockFile(FileName: PWideChar; Offset, Length: Int64; var DokanFileInfo: TDokanFileInfo): Integer; stdcall; var FilePath: string; begin FilePath := MirrorConvertPath(FileName); DbgPrint('LockFile: %s', [FilePath]); if DokanFileInfo.Context = INVALID_HANDLE_VALUE then begin DbgPrint('Invalid handle'); Result := -1; end else begin if UnlockFile(DokanFileInfo.Context, LARGE_INTEGER(Offset).LowPart, LARGE_INTEGER(Offset).HighPart, LARGE_INTEGER(Length).LowPart, LARGE_INTEGER(Length).HighPart) then Result := 0 else begin Result := -GetLastError; DbgPrint('UnlockFile failed, error code = %d', [-Result]); end; end; DbgPrint(''); end; function MirrorUnmount(var DokanFileInfo: TDokanFileInfo): Integer; stdcall; begin Result := 0; DbgPrint('Unmount'); DbgPrint(''); end; // Global vars var g_RootDirectory: string = ''; g_DokanOperations: TDokanOperations = ( CreateFile: MirrorCreateFile; OpenDirectory: MirrorOpenDirectory; CreateDirectory: MirrorCreateDirectory; Cleanup: MirrorCleanup; CloseFile: MirrorCloseFile; ReadFile: MirrorReadFile; WriteFile: MirrorWriteFile; FlushFileBuffers: MirrorFlushFileBuffers; GetFileInformation: MirrorGetFileInformation; FindFiles: MirrorFindFiles; FindFilesWithPattern: nil; SetFileAttributes: MirrorSetFileAttributes; SetFileTime: MirrorSetFileTime; DeleteFile: MirrorDeleteFile; DeleteDirectory: MirrorDeleteDirectory; MoveFile: MirrorMoveFile; SetEndOfFile: MirrorSetEndOfFile; LockFile: MirrorLockFile; UnlockFile: MirrorUnlockFile; GetDiskFreeSpace: nil; GetVolumeInformation: nil; Unmount: MirrorUnmount ); g_DokanOptions: TDokanOptions = ( DriveLetter: #0; ThreadCount: 0; DebugMode: False; UseStdErr: False; UseAltStream: False; UseKeepAlive: False; GlobalContext: 0; ); // Utilities routines procedure DbgPrint(const Message: string); overload; begin if g_DokanOptions.DebugMode then begin if g_DokanOptions.UseStdErr then Writeln(ErrOutput,Message) else Writeln(Message) end; end; procedure DbgPrint(const Format: string; const Args: array of const); overload; begin if g_DokanOptions.DebugMode then begin if g_DokanOptions.UseStdErr then Writeln(ErrOutput,SysUtils.Format(Format,Args)) else Writeln(SysUtils.Format(Format,Args)) end; end; function MirrorConvertPath(FileName: PWideChar): string; begin if FileName = nil then begin WriteLn('Null filename'); Result := g_RootDirectory end else Result := g_RootDirectory + FileName; end; // Main procedure procedure Main; var i: Integer; function FindSwitch(const s: string; t: array of Char): Integer; var i: Integer; c: Char; begin if (Length(s) = 2) and (s[1] in ['/','-','\']) then begin c := UpCase(s[2]); for i:=Low(t) to High(t) do if t[i] = c then begin Result := i; Exit; end; end; Result := Low(t) - 1; end; begin IsMultiThread := True; i := 1; while i <= ParamCount do begin case FindSwitch(ParamStr(i), ['R','L','T','D','S']) of 0: begin if (i = ParamCount) or (ParamStr(i+1) = '') then raise Exception.Create('Missing root directory after /R'); Inc(i); g_RootDirectory := ParamStr(i); end; 1: begin if (i = ParamCount) or (Length(ParamStr(i+1)) <> 1) then raise Exception.Create('Missing drive letter after /L'); Inc(i); g_DokanOptions.DriveLetter := WideString(ParamStr(i))[1]; end; 2: begin if (i = ParamCount) or (ParamStr(i+1) = '') then raise Exception.Create('Missing thread count after /T'); Inc(i); g_DokanOptions.ThreadCount := StrToInt(ParamStr(i)); end; 3: g_DokanOptions.DebugMode := True; 4: g_DokanOptions.UseStdErr := True; end; Inc(i); end; if (g_RootDirectory = '') or (g_DokanOptions.DriveLetter = #0) then begin WriteLn('Usage: ',ExtractFileName(ParamStr(0))); WriteLn(' /R RootDirectory (e.g. /R C:\test)'); WriteLn(' /L DriveLetter (e.g. /L m)'); WriteLn(' /T ThreadCount (optional, e.g. /T 5)'); WriteLn(' /D (optional, enable debug output)'); WriteLn(' /S (optional, use stderr for output)'); end else begin i := DokanMain(g_DokanOptions, g_DokanOperations); if i <> DOKAN_SUCCESS then raise EDokanMainError.Create(i); end; end; begin try Main; except on e: Exception do WriteLn('Error (',e.ClassName,'): ',e.Message); else WriteLn('Unspecified error'); end; end.

Conclusion :


L'auteur de la librairie Dokan est Hiroki Asakawa. Je pense que comme moi vous ne pourrez que saluer la qualité de son travail. Pour information, une solution professionnelle pour des fonctionnalités similaires est le "Callback File System", qui est vendu à une modique somme de plusieurs milliers d'euros...

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.