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

Soyez le premier à donner votre avis sur cette source.

Vue 6 753 fois - Téléchargée 638 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

cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1 -
Ah oui j'avais oublié: il y avait un petit oubli dans la déclaration de la variable g_DokanOperations. Les champs inutilisés n'étaient pas initialisés à nil, donc ça pouvait causer des "Access violation".

C'est corrigé à présent.
cs_jfs59
Messages postés
184
Date d'inscription
mardi 29 avril 2003
Statut
Membre
Dernière intervention
2 février 2010
-
Bon le problème c'est que ça plante déjà la

i := DokanMain(g_DokanOptions, g_DokanOperations);

donc des l'entrée ... et c'est déjà dans la dll dokan donc non modifiable

sinon j'ai mis des traceurs partout du genre showmessage ! c'est un peu long mais on vois bien par ou passe le programme pour chaque opération genre lecture dossier et autres ...

et la réponse a la question précédente est oui .... pourquoi trainer des dossiers et des fichiers quand un seul fichier serait possible ! a la limite ça serait remettre en cause les iso, cue et autres nrg ...

plus besoin de daemon tools et/ou autres isobuster ... lol
Bacterius
Messages postés
3869
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
6 -
Mais est-ce vraiment nécessaire de tout stocker dans un seul fichier ?

Cordialement, Bacterius !
cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1 -
C'est possible, mais tu devras programmer tout ce qui concerne la gestion de tes fichiers. En clair: la seule chose que tu n'as pas à faire c'est le driver, c'est à dire la partie qui fait le lien entre Windows et la douzaine de fonctions "custom" que tu écris (openfile, closefile, deletefile, readfile, writefile, etc...). En particulier, c'est ce driver-là qui fait apparaitre le disque virtuel dans le poste de travail et qui fait que l'accès pour les autres applications à ton système de fichiers custom est transparent, peu importe ce que tu as utilisé derrière pour le stockage.
cs_jfs59
Messages postés
184
Date d'inscription
mardi 29 avril 2003
Statut
Membre
Dernière intervention
2 février 2010
-
donc je veux dans un fichier "énorme" mettre des centaines voire des milliers de fichiers plus petit et que le tout soit vu comme un disque avec transparence totale ...

cordialement

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.