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

Soyez le premier à donner votre avis sur cette source.

Vue 7 379 fois - Téléchargée 704 fois

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

Ajouter un commentaire Commentaires
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
18 juil. 2009 à 19:14
Quels risques la création d'un disque virtuel comporte-elle ? Peut-on, après test de ta source, détruire ce disque ?

Cordialement, Bacterius !
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
18 juil. 2009 à 19:24
Ah je vois le disque existe pendant la durée du processus "mirror.exe". Ca a l'air très intéressant, donc j'ai appelé la commande suivante :

"...\mirror.exe" /R C:\Test /L m"

Ca me crée bien un disque local "Dokan (M:)", qui pointe vers le dossier "C:\Test". Mais dans quel but peut-on utiliser cette relation ? Tout ce que tu as dit sur le kernel et le débogage dans ta description, moi ça me dépasse lol :/

Cordialement, Bacterius !
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
18 juil. 2009 à 19:34
Oui, bien sûr le disque peut être très facilement enlevé. En fait, il n'existe que tant que le programme Mirror.exe tourne. Dès que le programme s'arrête, le disque disparait (comme un lecteur USB qu'on aurait retiré). En cas de bug sérieux (si j'ai mal traduit un morceau du programme par exemple) il se peut que tu doives redémarrer, mais le cas ne s'est jamais présenté pour moi, même quand j'étais en train de traduire le prog et qu'il était encore bourré de bugs.

Le problème le plus sérieux que j'aie eu jusque là, c'est après une Access violation (due à une erreur de pointeur dans mon code pendant la phase de traduction) et après avoir quitté le programme et relancé il était impossible de monter le lecteur Dokan et j'ai été obligé de redémarrer, mais c'était tout à fait anodin en ce qui concerne la stabilité de Windows.

J'ai oublié de le préciser: il y a trois façons de démonter le disque une fois que le programme fonctionne:
- soit en utilisant l'utilitaire dokanctl.exe fourni avec Dokan, par exemple
dokanctl /u m
si le lecteur virtuel s'appelle M:\
- en utilisant la fonction DokanUnmount dans le programme
- ou encore tout simplement en terminant le programme Mirror.exe (Ctrl + C ou Ctrl + Break ou en fermant la console)

Si tu veux être absolument certain de ne pas perdre de données, je te suggère d'enregistrer tous tes documents ouverts avant de lancer Mirror.exe, au cas où tu devrais rebooter juste après.

J'ai aussi oublié de le préciser: toutes les opérations que tu fais sur le lecteur Dokan (Modification, suppression de fichiers) affecte le répertoire RootDirectory! Donc attention à ne pas supprimer un fichier important!
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
18 juil. 2009 à 19:38
Le programme Mirror n'est pas très utile en soi. En fait, la commande MS-Dos SUBST fait exactement la même chose. C'est juste un exemple "canonique" de FileSystem.

Une possibilité d'amélioration simple à réaliser serait d'implémenter un cryptage des données. Il faudrait modifier les fonctions MirrorReadFile et MirrorWriteFile pour respectivement décrypter et crypter le Buffer. Dans ces conditions, les fichiers physiques qui sont stockés dans le RootDirectory seraient cryptés (donc pas lisible par quelqu'un qui te volerait ton disque dur par exemple :-) mais du point de vue des programmes qui utiliseraient ces fichiers par l'intermédiaire du lecteur virtuel, il s'agirait de fichiers "normaux" qui s'utilisent de manière totalement standard.
cs_jfs59 Messages postés 178 Date d'inscription mardi 29 avril 2003 Statut Membre Dernière intervention 2 février 2010
19 août 2009 à 13:35
Bonjour,

pendant un instant j'ai cru avoir trouvé ce que je cherche (mal peut être) depuis longtemps.
Après avoir essayer le programme, l'avoir modifié pour le faire fonctionner sans ligne de commande, j'ai essayé de "monter" un fichier utilisable en disque virtuel ! et la évidemment déception ça marche pas ! on ne peux monter que des dossiers ....

Pour ce qui est de l'adaptation en tous cas félicitation pour le travail effectué !

si l'un d'entre vous a déjà vu ce que je cherche je suis preneur d'un lien ...

je résume : un truc en delphi, modifiable, permettant de créer et de lire/écrire un fichier et de le monter en disque virtuel.

un truc du genre Realcrypt par exemple ....

j'en profite pour noter ... 10/10 même si j'ai cherché un peu pour comprendre comment ça marche ... lol

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.