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...
18 juil. 2009 à 19:14
Cordialement, Bacterius !
18 juil. 2009 à 19:24
"...\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 !
18 juil. 2009 à 19:34
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!
18 juil. 2009 à 19:38
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.
19 août 2009 à 13:35
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.