Jump to content

[Resolvido] DeviceIoControl - Compressão NTFS (0x0009C040)


Muryllo
 Share

Recommended Posts

Boa Tarde, estou tendo problemas provavelmente de parâmetros com a função DeviceIoControl do Kernel32 LIB. Não consigo de forma alguma o retorno verdadeiro, eu necessito usar a função para comprimir um ficheiro usando a compressão NTFS padrão (Aquela que deixa o nome do arquivo em AZUL no windows explorer)

Pra isso é claro vou precisar do HANDLE, da constante de compressão (0x0009C040 ou FSCTL_SET_COMPRESSION) = 639.040 e depois definir o padrão de compressão entre Nenhum, Normal e LZ. Eu estou recebendo retorno 0 de deviceiocontrol e GetLastWin32Error me retorna INVALID_HANDLE_VALUE sendo que o handle tem todas as permissões necessárias para DeviceIoControl e retorna um handle útil.

Estou tentando dessa forma :

Imports System.IO
Imports System.Runtime.InteropServices
'
Public Class Teste
'
<[DllImport]("KERNEL32.DLL", SetLastError:=True, EntryPoint:="CreateFileA", CharSet:=CharSet.Auto)> _
	Public Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As IntPtr
End Function
'
<[DllImport]("KERNEL32.DLL", SetLastError:=True, EntryPoint:="CloseHandle", CharSet:=CharSet.Auto)> _
Public Shared Function CloseHandle(ByVal hObject As IntPtr) As Integer
End Function
'
<[DllImport]("KERNEL32.DLL", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function DeviceIoControl(ByVal hDevice As IntPtr, _
								   ByVal IoControlCode As Long, _
								   <MarshalAs(UnmanagedType.AsAny), _
								   [in]()> ByVal InBuffer As Object, _
								   ByVal nInBufferSize As UInteger, _
								   <MarshalAs(UnmanagedType.AsAny), _
								   [Out]()> ByVal OutBuffer As Object, _
								   ByVal nOutBufferSize As UInteger, _
								   ByRef pBytesReturned As UInteger, _
								   <[in]()> ByRef Overlapped As Object) As Boolean
End Function
'
Public Const INVALID_HANDLE_VALUE As Long = -1&
Public Const FSCTL_SET_COMPRESSION As Integer = &H9C040
'
Public Const FILE_OPEN_EXISTING As Long = &H3
Public Const FILE_GENERIC_READ As Long = &H80000000
Public Const FILE_GENERIC_ALL As Long = &H10000000
Public Const FILE_SHARE_READ As Long = &H1
Public Const FILE_SHARE_WRITE As Long = &H2
Public Const FILE_ANY_ACCESS As Long = &H0
Public Const FILE_READ_ACCESS As Long = &H1
Public Const FILE_WRITE_ACCESS As Long = &H2
Public Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Public Const FILE_FLAG_NO_BUFFERING As Long = &H20000000
Public Const FILE_FLAG_BACKUP_SEMANTICS As Long = &H2000000
'
Public Const COMPRESSION_FORMAT_NONE As Short = &H0
Public Const COMPRESSION_FORMAT_DEFAULT As Short = &H1
'
Public Shared Function Get_File_Handle(ByVal File_Path As String, ByVal File_Access() As Integer, ByVal File_Share_Mode() As Integer) As IntPtr
	Try
		If (File.Exists(File_Path)) = True Then
			Dim Current_File_Access As Integer = 0
			Dim Current_File_Share_Mode As Integer = 0
			If (File_Access(0)) = Nothing Then
				Current_File_Access = 0
			Else
				For Each Current_Access_Flag As Integer In File_Access
					Current_File_Access += Current_Access_Flag
				Next
			End If
			If (File_Share_Mode(0)) = Nothing Then
				Current_File_Share_Mode = 0
			Else
				For Each Current_Share_Mode_Flag As Integer In File_Share_Mode
					Current_File_Share_Mode += Current_Share_Mode_Flag
				Next
			End If
			Dim File_Handle As IntPtr = CreateFile(File_Path, Current_File_Access, Current_File_Share_Mode, IntPtr.Zero, FILE_OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_NO_BUFFERING, IntPtr.Zero)
			If (File_Handle = INVALID_HANDLE_VALUE) Then
				Return INVALID_HANDLE_VALUE
			Else
				Return File_Handle
			End If
		Else
			Return INVALID_HANDLE_VALUE
		End If
	Catch NTSTATUS As Exception
		Return INVALID_HANDLE_VALUE
	End Try
End Function
'
Public Shared Function NtSuccess(ByVal Status As Integer) As Boolean
		If (Status = &H0) Then
			Return False
		Else
			Return True
		End If
End Function
'
Public Shared Function Compress_File(ByVal File_Path As String) As Boolean
	'regras de acesso e compartilhamento
	Dim Access_Rules() As Integer = {FILE_GENERIC_ALL}
	Dim Shared_Rules() As Integer = {FILE_SHARE_READ, FILE_SHARE_WRITE}
	'create file para retornar o handle
	Dim File_Handle As IntPtr = Get_File_Handle(File_Path, Access_Rules, Shared_Rules)
	'verificar se o ponto flutuante (HANDLE) é válido.
	If (File_Handle = INVALID_HANDLE_VALUE) Then
		'caso seja inválido retornar False
		Return False
	Else
		'caso contrário
		'começa aqui após CreateFileA retornar o handle.
		'DeviceIoControl será usado junto com 0x0009C040 (FILE_SYSTEM_CONTROL_SET_COMPRESSION = FSCTL_SET_COMPRESSION)
		Dim compression As Short = COMPRESSION_FORMAT_DEFAULT 'constante equivalente a COMPRESSION_FORMAT_DEFAULT ou 0x00000001
		Dim retBytes As Integer 'buffer Int32 para retorno dos bytes da função
		'verificar GetLastWin32Error ...
		MsgBox(Marshal.GetLastWin32Error)
		'nenhum erro encontrado (0x0)
		'callback para DeviceIoControl
		If (NtSuccess(DeviceIoControl(File_Handle, FSCTL_SET_COMPRESSION, compression, 2, 0, IntPtr.Zero, retBytes, IntPtr.Zero))) = True Then
			MsgBox("estado de sucesso")
			'o salto deveria ser até aqui, porém GetLastWin32Error retorna 0x6 (INVALID_HANDLE_VALUE) wtf ???????????????
			Return True
		Else
			MsgBox("estado de falha")
			MsgBox(Marshal.GetLastWin32Error)
			'ok ... erro INVALID_HANDLE_VALUE
			'será que tem haver com SafeFileHandle ?????
			Return False
		End If
	End If
End Function
'
End Class

De Get_File_Handle para cima está ok. Talvez algum parâmetro de DeviceIoControl esteja errado pois eu modifiquei várias vezes (mais de 30 vezes). Depois de CreateFile ser chamado e retornar o handle meu GetLastWin32Error é 0.

Agradeço a quem puder ajudar pois nunca havia mexido nessa API antes... 👍

EDIT :

Já consegui resolver ... Era justamente o que eu pensava. SafeFileHandles 😛

Para quem estiver procurando a mesma coisa em Visual Basic .NET ...

Open Source :

Imports System.IO
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices
'
Public Class NTFS_Compression
'
<[DllImport]("KERNEL32.DLL", SetLastError:=True, EntryPoint:="DeviceIoControl", CharSet:=CharSet.Auto)> _
Public Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Integer, ByRef lpInBuffer As Short, ByVal pInBufferSize As Integer, ByVal lpOutBuffer As IntPtr, ByVal nOutBufferSize As Integer, ByRef lpBytesReturned As Integer, ByVal lpOverlapped As IntPtr) As Integer
End Function
'
<[DllImport]("KERNEL32.DLL", SetLastError:=True, EntryPoint:="CloseHandle", CharSet:=CharSet.Auto)> _
Public Shared Function CloseHandle(ByVal hObject As SafeFileHandle) As Integer
End Function
'
Public Const FSCTL_SET_COMPRESSION As Integer = &H9C040
'
Public Const FILE_OPEN_EXISTING As Long = &H3
Public Const FILE_GENERIC_READ As Long = &H80000000
Public Const FILE_GENERIC_ALL As Long = &H10000000
Public Const FILE_SHARE_READ As Long = &H1
Public Const FILE_SHARE_WRITE As Long = &H2
Public Const FILE_ANY_ACCESS As Long = &H0
Public Const FILE_READ_ACCESS As Long = &H1
Public Const FILE_WRITE_ACCESS As Long = &H2
Public Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Public Const FILE_FLAG_NO_BUFFERING As Long = &H20000000
Public Const FILE_FLAG_BACKUP_SEMANTICS As Long = &H2000000
'
Public Const COMPRESSION_FORMAT_NONE As Short = &H0
Public Const COMPRESSION_FORMAT_DEFAULT As Short = &H1
'
Public Shared Function NtSuccess(ByVal Status As Integer) As Boolean
 Try
	 If (Status = &H0) Then
		 Return False
	 Else
		 Return True
	 End If
 Catch NTSTATUS As Exception
	 Return False
 End Try
End Function
'
Public Shared Function Get_File_Handle(ByVal File_Path As String, ByVal File_Mode As FileMode, ByVal File_Access As FileAccess, ByVal File_Share As FileShare) As SafeFileHandle
 Try
	 If (File.Exists(File_Path)) = True Then
		 Dim File_Base_Stream As FileStream = File.Open(File_Path, File_Mode, File_Access, File_Share)
		 Dim Safe_File_Handle As SafeFileHandle = File_Base_Stream.SafeFileHandle
		 Return Safe_File_Handle
	 Else
		 Return Nothing
	 End If
 Catch HANDLE_STATUS As Exception
	 Return Nothing
 End Try
End Function
'
Public Shared Function Compress_File(ByVal File_Path As String, ByVal File_Compression As Boolean) As Boolean
 Try
	 If (File.Exists(File_Path)) = True Then
		 Dim Status_Compression As Integer = 0
		 Dim Bytes_Returned As Integer = 0
		 Dim File_Handle As SafeFileHandle = Get_File_Handle(File_Path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
		 If (File_Compression = True) Then
			 Status_Compression = COMPRESSION_FORMAT_DEFAULT
		 Else
			 Status_Compression = COMPRESSION_FORMAT_NONE
		 End If
		 If (File_Handle Is Nothing) Then
			 Return False
		 Else
			 If (NtSuccess(DeviceIoControl(File_Handle, FSCTL_SET_COMPRESSION, Status_Compression, 2, IntPtr.Zero, 0, Bytes_Returned, IntPtr.Zero))) = True Then
				 CloseHandle(File_Handle)
				 Return True
			 Else
				 CloseHandle(File_Handle)
				 Return False
			 End If
		 End If
	 Else
		 Return False
	 End If
 Catch NTSTATUS As Exception
	 Return False
 End Try
End Function
'
End Class
Edited by Muryllo
Link to comment
Share on other sites

Para que tens o 2 aqui?

DeviceIoControl(File_Handle, FSCTL_SET_COMPRESSION, compression, 2, 0, IntPtr.Zero, retBytes, IntPtr.Zero))

Mete 0, e no 1º IntPtr.Zero mete 0 também, esse parametro não é pointer.

Já agora, o que é o NtSuccess aí? Se a função suceder o valor é diferente de 0, estás a igualar ao NtSucess = true, tens a certeza que não estás a fazer algo de errado?

Estás a comparar Long com IntPtr... ainda n aprendes-te a ligar a Option Strict?

Também não estás a usar CloseHandle...

Se ainda tiveres erros, mostra os parametros todos exactos com que estás a chamar DeviceIoControl.

Vê isto em qualquer dos casos, http://stackoverflow.com/questions/624125/compress-a-folder-using-ntfs-compression-in-net

Edited by He B TeMy
Link to comment
Share on other sites

O 2 é equivalente ao Len(Short), NtSuccess retorna verdadeiro para qualquer Status diferente de 0, IntPtr.Zero retorna 0 que é um Int16, Int32, Int64 UInt64 ou o que preferir. Não faz a menor diferença isso, agora eu percebi uma coisa em questão ao handle. Se ao menos 1 handle estiver aberto a próxima chamada a função no mesmo arquivo poderá falhar. Sabe por quê ? Pois o arquivo está sendo usado e o windows nunca vai deixar eu aceder ao arquivo a não ser que eu feche o handle com CloseHandle. Interessante, sim eu sei. Eu não imaginei isso mas pior é que faz todo sentido mesmo mas ok. Já não apresenta mais erros a segunda classe completa devido a essas alterações que eu fiz. FileStream.Handle agora é obsoleto, para isso SafeFileHandle é o recomendável.

EDIT :

Perdão ... o 2 ali é referente ao tamanho em bytes de Short que é usado na função. Para obter isso é :

Len(New Short)

Abraços 🙂

Edited by Muryllo
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.