I have written some code in Delphi/Pascal to upload a file to a website. To accomplish it, I run the code below in a thread immediately before I click the upload button. The thread looks for the windows upload file dialog and fills it in.
It works most of the time. Occasionally but with some frequency, it does not work. What is happening is that I am entering the full filename with path into the upload dialog. For some reason, sometimes windows will remove the path from the filename. Since the given filename (without the path) does not exist in the directory that windows is currently in, the upload fails.
For example, I am uploading "c:\uploads10\123.pdf", Windows will (sometimes) truncate the filename to "123.pdf" which does not exist in whatever directory the upload dialog defaults to, so the upload fails.
This has been driving me crazy for a while. Can someone tell me how to fix it?
for Looper := 1 to 2 do
begin
UploadDialogHandle := FindWindow('#32770', 'Choose File to Upload');
if UploadDialogHandle = 0 then
Sleep(100)
else
begin
SetFileUploadDialogPosition(UploadDialogHandle);
break;
end;
end;
if UploadDialogHandle = 0 then
exit;
for Looper := 1 to 1 do
begin
// Find the child ComboBoxEx32 window
UploadDialogFileEditHandle := FindWindowEx(FileUploadDialogHandle, 0,
'ComboBoxEx32', '');
if UploadDialogFileEditHandle = 0 then
Sleep(100)
else
break;
end;
if UploadDialogFileEditHandle = 0 then
exit;
for Looper := 1 to 1 do
begin
// Find the child ComboBox window
UploadDialogFileEditHandle := FindWindowEx(UploadDialogFileEditHandle, 0,
'ComboBox', '');
if UploadDialogFileEditHandle = 0 then
Sleep(100)
else
break;
end;
for Looper := 1 to 1 do
begin
// Find the child Edit window
UploadDialogFileEditHandle := FindWindowEx(UploadDialogFileEditHandle, 0,
'Edit', '');
if UploadDialogFileEditHandle = 0 then
Sleep(100)
else
break;
end;
if UploadDialogFileEditHandle = 0 then
exit;
Result := False;
if (UploadDialogHandle = 0) or (UploadDialogFileEditHandle = 0) or
(FileUploadButtonHandle = 0) then
exit;
StrPCopy(aUploadFileName, FileNameString);
SendMessage(UploadDialogFileEditHandle, WM_SETTEXT, SizeOf(aUploadFileName),
Integer(#aUploadFileName));
Sleep(200);
SendMessage(UploadDialogFileEditHandle, WM_GETTEXT, SizeOf(FileEditContents),
Integer(#FileEditContents));
if (StrComp(aUploadFileName, FileEditContents) = 0) then
SendMessage(FileUploadButtonHandle, BM_CLICK, 0, 0);
Related
In Delphi program I use ExtractFileDir function for getting parent folder and this code works correctly:
Fmain.frxReport1.LoadFromFile(ExtractFileDir(ExtractFileDir(ExtractFileDir(ExtractFilePath(ParamStr(0)))))+'\FastReports\report1.fr3');
How can I modify it and get parent folder's path with using "\.." as it is realised in Delphi sample program
Update:
I wrote this code:
setcurrentdir('../../');
s1:=getcurrentdir();
frxReport1.LoadFromFile(s1+'\FastReports\report1.fr3');
but I want one-line(as my code with ExtractFileDir ) and well-readable code to replace my code.
I have a function which absolutize a relative path.
To absolutize a path, you need to know the base path.
I the case of Delphi you show, the paths are relative to the project directory.
Once you have an absolute path, you can apply ExtractFilePath several times to go up in the directory levels.
Let's take an example: You have a relative path "....\SomeFile.txt". This path is relative to the base path "C:\ProgramData\Acme\Project\Demo". The complete path is: "C:\ProgramData\Acme\Project\Demo....\SomeFile.txt". Then the absolute path result of AbsolutizePath will be "C:\ProgramData\Acme\SomeFile.txt".
Note that Absolutize path take care of ".." (parent directory), "." (Current directory) and drive specification (Such as C:).
function AbsolutizePath(
const RelPath : String;
const BasePath : String = '') : String;
var
I, J : Integer;
begin
if RelPath = '' then
Result := BasePath
else if RelPath[1] <> '\' then begin
if (BasePath = '') or
((Length(RelPath) > 1) and (RelPath[2] = ':')) then
Result := RelPath
else
Result := IncludeTrailingPathDelimiter(BasePath) + RelPath;
end
else
Result := RelPath;
// If there is no drive in the result, add the one from base if any
if (Length(Result) > 1) and (Result[2] <> ':') and
(Length(BasePath) > 1) and (BasePath[2] = ':') then
Insert(Copy(BasePath, 1, 2), Result, 1);
// Delete "current dir" marker
I := 1;
while TRUE do begin
I := Pos('\.\', Result, I);
if I <= 0 then
break;
Delete(Result, I, 2);
end;
// Process "up one level" marker
I := 1;
while TRUE do begin
I := Pos('\..\', Result, I);
if I <= 0 then
break;
J := I - 1;
while (J > 0) and (not CharInSet(Result[J], ['\', ':'])) do
Dec(J);
// Check if we hit drive delimiter and fix it
if (J = 2) and (Result[J] = ':') then
J := 3;
Delete(Result, J + 1, I - J + 3);
I := J;
end;
end;
For a project, I need to read a name inside a TrueType font file (.ttf). I written a code to do that, inspirated from a c++ example. Here is the code:
TWByteArray = array of Byte;
TWAnsiCharArray = array of AnsiChar;
...
//---------------------------------------------------------------------------
class function TWStringHelper.ByteToStr(const bytes: TWByteArray): string;
begin
SetLength(Result, Length(bytes));
if Length(Result) > 0 then
Move(bytes[0], Result[1], Length(bytes));
end;
//---------------------------------------------------------------------------
class function TWStringHelper.UniStrToByte(const str: UnicodeString): TWByteArray;
begin
SetLength(Result, Length(str) * SizeOf(WideChar));
if (Length(Result) > 0) then
Move(str[1], Result[0], Length(Result));
end;
//---------------------------------------------------------------------------
class function TWStringHelper.BytesToUniStr(const bytes: TWByteArray): UnicodeString;
begin
SetLength(Result, Length(bytes) div SizeOf(WideChar));
if Length(Result) > 0 then
Move(bytes[0], Result[1], Length(bytes));
end;
//---------------------------------------------------------------------------
...
//---------------------------------------------------------------------------
class function TWControlFont.SwapWord(value: Word): Word;
begin
Result := MakeWord(HiByte(value), LoByte(value));
end;
//---------------------------------------------------------------------------
class function TWControlFont.SwapLong(value: LongInt): LongInt;
begin
Result := MakeLong(SwapWord(HiWord(value)), SwapWord(LoWord(value)));
end;
//---------------------------------------------------------------------------
class function TWControlFont.GetFontNameFromFile(const fileName: UnicodeString): UnicodeString;
var
pFile: TFileStream;
offsetTable: ITTFOffsetTable;
dirTable: ITTFDirectoryTable;
nameHeader: ITTFNameTableHeader;
nameRecord: ITTFNameRecord;
nameBuffer: TWByteArray;//TWAnsiCharArray;
i: USHORT;
found: Boolean;
test2: string;
test3: UnicodeString;
test: Integer;
const name: array [0..3] of Byte = (Ord('n'), Ord('a'), Ord('m'), Ord('e'));
begin
// open font file
pFile := TFileStream.Create(fileName, fmOpenRead);
// succeeded?
if (not Assigned(pFile)) then
Exit;
try
pFile.Seek(0, soFromBeginning);
// read TTF offset table
if (pFile.Read(offsetTable, SizeOf(ITTFOffsetTable)) <> SizeOf(ITTFOffsetTable)) then
Exit;
offsetTable.m_NumOfTables := SwapWord(offsetTable.m_NumOfTables);
offsetTable.m_MajorVersion := SwapWord(offsetTable.m_MajorVersion);
offsetTable.m_MinorVersion := SwapWord(offsetTable.m_MinorVersion);
// is truetype font and version is 1.0?
if ((offsetTable.m_MajorVersion <> 1) or (offsetTable.m_MinorVersion <> 0)) then
Exit;
found := False;
// iterate through file tables
if (offsetTable.m_NumOfTables > 0) then
for i := 0 to offsetTable.m_NumOfTables - 1 do
begin
// read table
if (pFile.Read(dirTable, SizeOf(ITTFDirectoryTable)) <> SizeOf(ITTFDirectoryTable)) then
Exit;
// found name table?
if (CompareMem(#dirTable.m_Tag, #name, 4) = True) then
begin
found := True;
dirTable.m_Length := SwapLong(dirTable.m_Length);
dirTable.m_Offset := SwapLong(dirTable.m_Offset);
break;
end;
end;
// found name table?
if (not found) then
Exit;
// seek to name location
pFile.Position := dirTable.m_Offset;
// read name table header
if (pFile.Read(nameHeader, SizeOf(ITTFNameTableHeader)) <> SizeOf(ITTFNameTableHeader)) then
Exit;
nameHeader.m_NRCount := SwapWord(nameHeader.m_NRCount);
nameHeader.m_StorageOffset := SwapWord(nameHeader.m_StorageOffset);
// iterate through name records
if (nameHeader.m_NRCount > 0) then
for i := 0 to nameHeader.m_NRCount - 1 do
begin
// read name record
if (pFile.Read(nameRecord, SizeOf(ITTFNameRecord)) <> SizeOf(ITTFNameRecord)) then
Exit;
nameRecord.m_NameID := SwapWord(nameRecord.m_NameID);
// found font name?
if (nameRecord.m_NameID = 1) then
begin
// get font name length and offset
nameRecord.m_StringLength := SwapWord(nameRecord.m_StringLength);
nameRecord.m_StringOffset := SwapWord(nameRecord.m_StringOffset);
if (nameRecord.m_StringLength = 0) then
continue;
// calculate and seek to font name offset
pFile.Position := dirTable.m_Offset + nameRecord.m_StringOffset + nameHeader.m_StorageOffset;
try
SetLength(nameBuffer, nameRecord.m_StringLength + 1);
//REM FillChar(nameBuffer[0], nameRecord.m_StringLength + 1, $0);
// read font name from file
if (pFile.Read(nameBuffer[0], nameRecord.m_StringLength)
<> nameRecord.m_StringLength)
then
Exit;
nameBuffer[nameRecord.m_StringLength] := $0;
//OutputDebugString(PChar(nameBuffer));
//TWMemoryHelper.SwapBytes(nameBuffer[0], nameRecord.m_StringLength);
//OutputDebugString(PChar(nameBuffer));
//test := StringElementSize(RawByteString(#nameBuffer[0]));
//Result := TWStringHelper.BytesToUniStr(nameBuffer);
//Result := UnicodeString(AnsiString(TWStringHelper.ByteToStr(nameBuffer)));
//REM Result := UnicodeString(nameBuffer);
test2 := TWStringHelper.ByteToStr(nameBuffer);
OutputDebugStringA(PAnsiChar(test2));
test3 := UnicodeString(PAnsiChar(test2));
OutputDebugStringW(PWideChar(test3));
Result := test3;
OutputDebugStringW(PWideChar(test3));
finally
SetLength(nameBuffer, 0);
end;
break;
end;
end;
finally
pFile.Free;
end;
end;
//---------------------------------------------------------------------------
This code works well until the final part of the GetFontNameFromFile() function. There, things start to get complicated. Indeed, I'm unable to convert the nameBuffer byte array to a string in a correct manner.
The first problem I met is that the nameBuffer may be a simple ASCII string, or an UTF16 string, depend on file (I tried with the emoji.ttf available in FireFox, that returns an ASCII string, and Tahoma.ttf from my Win installation, that returns a UTF16 string). I need a way to determine that, and I don't know if there is a function or class in the VCL to do that.
The second problem is the conversion itself. The above code works more or less, but I feel that is not a correct solution. When I try to convert to an UnicodeString directly from nameBuffer, I get some strange crashes. If I try to convert nameBuffer to an AnsiString, the conversion seems success, however a conversion like UnicodeString(AnsiString(nameBuffer)) fails.
And the code seems to be full of memory issues. As I'm new with Delphi, I'm not very comfortable with the memory usage. For example, I suspect several issues with the byte array when I activate the
FillChar(nameBuffer[0], nameRecord.m_StringLength + 1, $0);
line.
So anybody can analyse this code and points me what I doing wrong?
Thanks in advance,
Regards
i'm actually programming an app to see what files are opened .
He is a part of a code that is not by me , i'm trying to using it but i don't understand it ...
I'm trying to get the file names opened by the process , but the function is always resulting like : /Default or /Sessions/1/Windows ... Something like that.Please help me and sorry for my bad english
const
SystemHandleInformation = $10;
STATUS_SUCCESS = $00000000;
STATUS_BUFFER_OVERFLOW = $80000005;
STATUS_INFO_LENGTH_MISMATCH = $C0000004;
DefaulBUFFERSIZE = $100000;
type
OBJECT_INFORMATION_CLASS = (ObjectBasicInformation, ObjectNameInformation,
ObjectTypeInformation, ObjectAllTypesInformation, ObjectHandleInformation);
SYSTEM_HANDLE = packed record
uIdProcess: ULONG;
ObjectType: UCHAR;
Flags: UCHAR;
Handle: Word;
pObject: Pointer;
GrantedAccess: ACCESS_MASK;
end;
PSYSTEM_HANDLE = ^SYSTEM_HANDLE;
SYSTEM_HANDLE_ARRAY = Array [0 .. 0] of SYSTEM_HANDLE;
PSYSTEM_HANDLE_ARRAY = ^SYSTEM_HANDLE_ARRAY;
SYSTEM_HANDLE_INFORMATION = packed record
uCount: ULONG;
Handles: SYSTEM_HANDLE_ARRAY;
end;
PSYSTEM_HANDLE_INFORMATION = ^SYSTEM_HANDLE_INFORMATION;
TNtQuerySystemInformation = function(SystemInformationClass: DWORD;
SystemInformation: Pointer; SystemInformationLength: DWORD;
ReturnLength: PDWORD): THandle; stdcall;
TNtQueryObject = function(ObjectHandle: cardinal;
ObjectInformationClass: OBJECT_INFORMATION_CLASS;
ObjectInformation: Pointer; Length: ULONG; ResultLength: PDWORD)
: THandle; stdcall;
UNICODE_STRING = packed record
Length: Word;
MaximumLength: Word;
Buffer: PWideChar;
end;
OBJECT_NAME_INFORMATION = UNICODE_STRING;
POBJECT_NAME_INFORMATION = ^OBJECT_NAME_INFORMATION;
Var
NTQueryObject: TNtQueryObject;
NTQuerySystemInformation: TNtQuerySystemInformation;
Procedure EnumerateOpenFiles();
var
sDummy: string;
hProcess: THandle;
hObject: THandle;
ResultLength: DWORD;
aBufferSize: DWORD;
aIndex: Integer;
pHandleInfo: PSYSTEM_HANDLE_INFORMATION;
HDummy: THandle;
lpwsName: PWideChar;
lpwsType: PWideChar;
lpszProcess: pchar;
begin
aBufferSize := DefaulBUFFERSIZE;
pHandleInfo := AllocMem(aBufferSize);
HDummy := NTQuerySystemInformation(DWORD(SystemHandleInformation),
pHandleInfo, aBufferSize, #ResultLength); // Get the list of handles
if (HDummy = STATUS_SUCCESS) then // If no error continue
begin
for aIndex := 0 to pHandleInfo^.uCount - 1 do // iterate the list
begin
hProcess := OpenProcess(PROCESS_DUP_HANDLE or PROCESS_QUERY_INFORMATION or
PROCESS_VM_READ, False, pHandleInfo.Handles[aIndex].uIdProcess);
// open the process to get aditional info
if (hProcess <> INVALID_HANDLE_VALUE) then // Check valid handle
begin
hObject := 0;
if DuplicateHandle(hProcess, pHandleInfo.Handles[aIndex].Handle,
GetCurrentProcess, #hObject, STANDARD_RIGHTS_REQUIRED, False, 0) then
// Get a copy of the original handle
begin
lpwsName := GetObjectInfo(hObject, ObjectTypeInformation);
// Get the filename linked to the handle
if (lpwsName <> nil) then
begin
lpwsType := GetObjectInfo(hObject, ObjectNameInformation);
lpszProcess := AllocMem(MAX_PATH);
if GetModuleFileNameEx(hProcess, 0, lpszProcess, MAX_PATH) <> 0 then
// get the name of the process
sDummy := ExtractFileName(lpszProcess)
else
sDummy := 'System Process';
with MainForm.UsedFilesListView.Items.add do
begin
// Ajout
Caption := sDummy;
ImageIndex := -1;
SubItems.add(lpwsName);
end;
// Writeln('PID ', pHandleInfo.Handles[aIndex].uIdProcess);
// Writeln('Handle ', pHandleInfo.Handles[aIndex].Handle);
// Writeln('Process ', sDummy);
// Writeln('FileName ', string(lpwsName));
// Writeln;
FreeMem(lpwsName);
FreeMem(lpwsType);
FreeMem(lpszProcess);
end;
CloseHandle(hObject);
end;
CloseHandle(hProcess);
end;
end;
end;
FreeMem(pHandleInfo);
end;
First of all, you failed to provide SSCCE in your question, which greatly reduces chances for someone to take a look and try to fix your code. Because we would need to think about all missing declarations and what units to include to make compilable code, and yeah, thats boring.
Second, copy and paste programming is bad practice and it won't make your programming skills to improve. Try to consult MSDN about what certain APIs do and how to use them, then try to fiddle with the code by using informations you gathered through Google/MSDN.
About question itself, it's a tricky one, and widely undocumented.
Check this useful post on SysInternals forums which roughly explains what you have to do: HOWTO: Enumerate handles.
After acquiring file paths, you have to replace MS-DOS device paths with their mapped paths (e.g. \Device\HarddiskVolume1 > C:\). You can do that with GetLogicalDriveStrings and QueryDosDevice APIs.
Now the code itself. You would need JEDI API library to compile it. Tested on XE2:
{$APPTYPE CONSOLE}
program FileHandles;
uses
Winapi.Windows,
System.Classes,
JwaNative,
JwaNtStatus,
JwaWinternl;
procedure EnumerateDevicePaths(const ADeviceNames, ADevicePaths: TStringList);
var
drives : array[0..4095] of Char;
pdrive : PChar;
drive : String;
drive_path : array[0..4095] of Char;
sdrive_path: String;
begin
ADeviceNames.Clear;
ADevicePaths.Clear;
if GetLogicalDriveStrings(SizeOf(drives), drives) = 0 then
Exit;
pdrive := drives;
while pdrive^ <> #0 do
begin
drive := Copy(pdrive, 0, 4);
if drive <> '' then
begin
if drive[Length(drive)] = '\' then
Delete(drive, Length(drive), 1);
QueryDosDevice(PChar(drive), drive_path, SizeOf(drive_path));
sdrive_path := drive_path;
ADeviceNames.Add(drive);
ADevicePaths.Add(sdrive_path);
end;
Inc(pdrive, 4);
end;
end;
function EnumerateOpenFiles: Integer;
const
HANDLE_BUFFER_INCREASE_CHUNK = 16 * 1024; // increase handles buffer by 16kb
type
// this struct is missing in JEDI declarations (?)
TSystemHandleInformations = record
HandleCount: ULONG;
Handles : array[0..0] of TSystemHandleInformation;
end;
PSystemHandleInformations = ^TSystemHandleInformations;
var
phandles_info : PSystemHandleInformations;
phandles_size : DWORD;
retcode : DWORD;
C1, C2 : Integer;
phandle_info : PSystemHandleInformation;
process_handle: THandle;
dup_handle : THandle;
obj_name_info : PObjectNameInformation;
obj_name_size : DWORD;
fname : String;
device_names : TStringList;
device_paths : TStringList;
begin
device_names := TStringList.Create;
try
device_paths := TStringList.Create;
try
EnumerateDevicePaths(device_names, device_paths); // enumerate devices list, so we can use these later on to replace MS-DOS paths with mapped ones
phandles_size := HANDLE_BUFFER_INCREASE_CHUNK; // start with HANDLE_BUFFER_INCREASE_CHUNK value
phandles_info := AllocMem(phandles_size);
try
retcode := NtQuerySystemInformation(DWORD(SystemHandleInformation), phandles_info, phandles_size, nil);
while retcode = STATUS_INFO_LENGTH_MISMATCH do // realloc handles buffer memory until it's big enough to accept all handles data
begin
Inc(phandles_size, HANDLE_BUFFER_INCREASE_CHUNK);
ReallocMem(phandles_info, phandles_size);
retcode := NtQuerySystemInformation(DWORD(SystemHandleInformation), phandles_info, phandles_size, nil);
end;
if retcode <> STATUS_SUCCESS then
Exit(retcode);
// iterate through opened handles
for C1 := 0 to phandles_info^.HandleCount do
begin
phandle_info := pointer(Integer(#phandles_info^.Handles) + C1 * SizeOf(TSystemHandleInformation)); // get pointer to C1 handle info structure
// if ObjectType is not file, or if handle is named pipe (which would make Nt*() function to block), we skip to the next handle
// GrantedAccess mask here is very cryptic, I've been unable to find more information about it on Google, all codes use static hex numbers for check
if (phandle_info^.ObjectTypeNumber <> 28) or
(phandle_info^.GrantedAccess = $0012019F) or
(phandle_info^.GrantedAccess = $001A019F) or
(phandle_info^.GrantedAccess = $00120189) then
Continue;
process_handle := OpenProcess(PROCESS_DUP_HANDLE, FALSE, phandle_info^.ProcessId);
if process_handle <> 0 then
try
if DuplicateHandle(process_handle, phandle_info^.Handle, GetCurrentProcess, #dup_handle, 0, FALSE, 0) then
try
obj_name_size := SizeOf(TObjectNameInformation);
obj_name_info := AllocMem(obj_name_size);
try
// get path to the file
retcode := NtQueryObject(dup_handle, ObjectNameInformation, obj_name_info, obj_name_size, #obj_name_size);
if retcode <> STATUS_SUCCESS then
begin
ReallocMem(obj_name_info, obj_name_size);
retcode := NtQueryObject(dup_handle, ObjectNameInformation, obj_name_info, obj_name_size, nil);
end;
if retcode <> STATUS_SUCCESS then
Continue;
fname := obj_name_info^.Name.Buffer;
// replace MS-DOS device names with their mappings
for C2 := 0 to device_paths.Count - 1 do
if Copy(fname, 1, Length(device_paths[C2])) = device_paths[C2] then
begin
Delete(fname, 1, Length(device_paths[C2]));
fname := device_names[C2] + fname;
Break;
end;
// do necessary processing with fname here
WriteLn(phandle_info^.ProcessId, ': ', fname);
finally
FreeMem(obj_name_info, obj_name_size);
end;
finally
CloseHandle(dup_handle);
end;
finally
CloseHandle(process_handle);
end;
end;
finally
FreeMem(phandles_info, phandles_size);
end;
Exit(STATUS_SUCCESS);
finally
device_paths.Free;
end;
finally
device_names.Free;
end;
end;
begin
EnumerateOpenFiles;
Write('Done!');
ReadLn;
end.
This code can be improved in several more ways, but I gave you enough to start. For example, one of the optimizations would be to avoid opening same process multiple times by sorting handle list by PID, then opening process only once to check the handle group with those same PIDs.
It appears that you are using code from here: Delphi - get what files are opened by an application. This code claims to:
list all open handles from all processes
In other words it lists handles that are associated with objects other than file objects. The file names that you see that do not look like file names are indeed so. They are the names of objects other than files, to which the process has handles.
when using the Delphi code provided as answer on this page,
Delphi - get what files are opened by an application
I get nice results on Windows 8 but an error on XP
in the provided function "GetObjectInfo" :
NTQueryObject called at line 4 is returning STATUS_INVALID_INFO_CLASS (3221225475).
It seems that the class ObjectNameInformation is not a valid info class.
I use Delphi 7.
program ListAllHandles;
{$APPTYPE CONSOLE}
uses
PSApi,
Windows,
SysUtils;
const
SystemHandleInformation = $10;
STATUS_SUCCESS = $00000000;
STATUS_BUFFER_OVERFLOW = $80000005;
STATUS_INFO_LENGTH_MISMATCH = $C0000004;
DefaulBUFFERSIZE = $100000;
type
OBJECT_INFORMATION_CLASS = (ObjectBasicInformation,ObjectNameInformation,ObjectTypeInformation,ObjectAllTypesInformation,ObjectHandleInformation );
SYSTEM_HANDLE=packed record
uIdProcess:ULONG;
ObjectType:UCHAR;
Flags :UCHAR;
Handle :Word;
pObject :Pointer;
GrantedAccess:ACCESS_MASK;
end;
PSYSTEM_HANDLE = ^SYSTEM_HANDLE;
SYSTEM_HANDLE_ARRAY = Array[0..0] of SYSTEM_HANDLE;
PSYSTEM_HANDLE_ARRAY= ^SYSTEM_HANDLE_ARRAY;
SYSTEM_HANDLE_INFORMATION=packed record
uCount:ULONG;
Handles:SYSTEM_HANDLE_ARRAY;
end;
PSYSTEM_HANDLE_INFORMATION=^SYSTEM_HANDLE_INFORMATION;
TNtQuerySystemInformation=function (SystemInformationClass:DWORD; SystemInformation:pointer; SystemInformationLength:DWORD; ReturnLength:PDWORD):THandle; stdcall;
TNtQueryObject =function (ObjectHandle:cardinal; ObjectInformationClass:OBJECT_INFORMATION_CLASS; ObjectInformation:pointer; Length:ULONG;ResultLength:PDWORD):THandle;stdcall;
UNICODE_STRING=packed record
Length :Word;
MaximumLength:Word;
Buffer :PWideChar;
end;
OBJECT_NAME_INFORMATION=UNICODE_STRING;
POBJECT_NAME_INFORMATION=^OBJECT_NAME_INFORMATION;
Var
NTQueryObject :TNtQueryObject;
NTQuerySystemInformation:TNTQuerySystemInformation;
function GetObjectInfo(hObject:cardinal; objInfoClass:OBJECT_INFORMATION_CLASS):LPWSTR;
var
pObjectInfo:POBJECT_NAME_INFORMATION;
HDummy :THandle;
dwSize :DWORD;
begin
Result:=nil;
dwSize := sizeof(OBJECT_NAME_INFORMATION);
pObjectInfo := AllocMem(dwSize);
HDummy := NTQueryObject(hObject, objInfoClass, pObjectInfo,dwSize, #dwSize);
if((HDummy = STATUS_BUFFER_OVERFLOW) or (HDummy = STATUS_INFO_LENGTH_MISMATCH)) then
begin
FreeMem(pObjectInfo);
pObjectInfo := AllocMem(dwSize);
HDummy := NTQueryObject(hObject, objInfoClass, pObjectInfo,dwSize, #dwSize);
end;
if((HDummy >= STATUS_SUCCESS) and (pObjectInfo.Buffer <> nil)) then
begin
Result := AllocMem(pObjectInfo.Length + sizeof(WCHAR));
CopyMemory(result, pObjectInfo.Buffer, pObjectInfo.Length);
end;
FreeMem(pObjectInfo);
end;
Procedure EnumerateOpenFiles();
var
sDummy : string;
hProcess : THandle;
hObject : THandle;
ResultLength: DWORD;
aBufferSize : DWORD;
aIndex : Integer;
pHandleInfo : PSYSTEM_HANDLE_INFORMATION;
HDummy : THandle;
lpwsName : PWideChar;
lpwsType : PWideChar;
lpszProcess : PAnsiChar;
begin
AbufferSize := DefaulBUFFERSIZE;
pHandleInfo := AllocMem(AbufferSize);
HDummy := NTQuerySystemInformation(DWORD(SystemHandleInformation), pHandleInfo,AbufferSize, #ResultLength); //Get the list of handles
if(HDummy = STATUS_SUCCESS) then //If no error continue
begin
for aIndex:=0 to pHandleInfo^.uCount-1 do //iterate the list
begin
hProcess := OpenProcess(PROCESS_DUP_HANDLE or PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, pHandleInfo.Handles[aIndex].uIdProcess); //open the process to get aditional info
if(hProcess <> INVALID_HANDLE_VALUE) then //Check valid handle
begin
hObject := 0;
if DuplicateHandle(hProcess, pHandleInfo.Handles[aIndex].Handle,GetCurrentProcess(), #hObject, STANDARD_RIGHTS_REQUIRED,FALSE, 0) then //Get a copy of the original handle
begin
lpwsName := GetObjectInfo(hObject, ObjectNameInformation); //Get the filename linked to the handle
if (lpwsName <> nil) then
begin
lpwsType := GetObjectInfo(hObject, ObjectTypeInformation);
lpszProcess := AllocMem(MAX_PATH);
if GetModuleFileNameEx(hProcess, 0,lpszProcess, MAX_PATH)<>0 then //get the name of the process
sDummy:=ExtractFileName(lpszProcess)
else
sDummy:= 'System Process';
Writeln('PID ',pHandleInfo.Handles[aIndex].uIdProcess);
Writeln('Handle ',pHandleInfo.Handles[aIndex].Handle);
Writeln('Process ',sDummy);
Writeln('FileName ',string(lpwsName));
Writeln;
FreeMem(lpwsName);
FreeMem(lpwsType);
FreeMem(lpszProcess);
end;
CloseHandle(hObject);
end;
CloseHandle(hProcess);
end;
end;
end;
FreeMem(pHandleInfo);
Any help ?
There are a number of errors in your translation:
In C an enum has a minimum size of 4 bytes, so you should set {$ENUMSIZE 4}
Records should not be packed but aligned
NtQueryObject doesn't return a handle but an NTSTATUS
My advise is to use the existing translations in the Jedi Apilib since they are time tested. You need the JwaNative unit.
On my blog you can find a complete example on enumerating all handles: http://www.remkoweijnen.nl/blog/2012/03/07/running-multiple-instances-of-lync-howto/
So my current task involves taking a given string of text (Ex: ABC123) and encrypting it using LockBox3's EncryptString(source, target) function. I can successfully encrypt the string and get the output to save to a .txt file.
The next step in this process is to then use LockBox3's EncryptFile(source, target) function to take the .txt containing my already-encrypted string and encrypting said file using AES-128 (same as the string encryption but with diff password)
Basically, I can get the string to encrypt correctly and output to a .txt file. I then request that the user grab the .txt, and bring it into my program. The program then attempts to take that file and encrypt it further. When I do this, I get a file to output.. however when I go to decrypt said file the resulting .txt does not contain the original text.. or any text for that matter. I am basically confused as to how I should be going about encrypting the .txt file. Any suggestions? I apologize if this question/code is not specific enough. Please let me know what else, if anything I need to make clear about the situation in order to better help you guys understand what I'm struggling with! Thanks!
EDIT1:
Alright everyone, thanks for the suggestions. To clarify:
The stream I have in the decryption process is to be used later, so that after I have decrypted the file, I can read from it and decrypt the remaining encrypted (from the first step) string.
To clarify further:
My codec (Codec1) for encrypting the string is using AES-128 with CBC, with a tag of "0" and an AsymetricKeySize of 1024 (which, Im pretty sure is irrelevant for this type of encryption correct?) My codec for encrypting FILES (Codec2 above) has the same settings, however the passwords for Codec1 and Codec2 are different. Basically, I am using Codec1 to encrypt a string and write it to a .txt, and then I am using Codec2 to encrypt said file.. eventually decrypt it and use the Stream to read from said file and decrypt that string using Codec1 again.
my file encryption/decryption code:
String Encryption:
procedure TForm1.Button1Click(Sender: TObject);
begin
codec1.Password := WORD_1;
//Begin encryption
sPlainText := Serial_number.Number; //Store Serial Number of machine
codec1.EncryptString(sPlainText,CipherText); //Encrypt (base64)
listbox2.Clear;
listbox2.AddItem(Ciphertext, AnsiCipher);
end;
Write encrypted string to a file and save it:
saveDialog := TSaveDialog.Create(self);
saveDialog.Title := 'Choose location to save Authentication Code';
saveDialog.InitialDir := 'C:\';
saveDialog.DefaultExt := '';
saveDialog.FilterIndex := 1;
saveDialog.Execute();
glb_fileName1 := saveDialog.FileName;
//open stream and write cipher to a .txt of chosen location
try
Stream := TFileStream.Create(saveDialog.GetNamePath + saveDialog.FileName + '.txt', fmOpenReadWrite);
except
Stream := TFileStream.Create(saveDialog.GetNamePath + saveDialog.FileName + '.txt', fmCreate);
end;
for k := 1 to (Length(CipherText)) do
buff[k] := byte(CipherText[k]);
ptr := #buff[1];
Stream.WriteBuffer(ptr^, Length(CipherText));
Stream.Free;
saveDialog.Free;
Grab location of .txt for file encryption:
procedure TForm1.Button4Click(Sender: TObject);
var
fileName : string;
holder_obj : TSerial_number;
begin
holder_obj := Tserial_number.Create;
listbox4.Clear;
if OpenTextFileDialog1.Execute() then
fileName := OpenTextFileDialog1.FileName;
listbox4.AddItem(filename, holder_obj);
end;
File Encryption:
Codec2.Password := WORD_2;
sCrypt := glb_fileName1 + '_enc.txt';
Codec2.EncryptFile(glb_fileName1+'.txt', sCrypt);
Grab Encrypted File for decryption:
procedure TForm1.Button3Click(Sender: TObject);
var
holder_obj : TSerial_number;
begin
holder_obj := Tserial_number.Create;
listbox3.Clear;
if OpenTextFileDialog1.Execute() then
glb_fileName2 := OpenTextFileDialog1.FileName;
listbox3.AddItem(glb_filename2, holder_obj);
end;
File Decryption (opening a stream to read from the decrypted file once I have it, so that I can decrypt the encrypted string it contains):
procedure TForm1.Button5Click(Sender: TObject);
var
saveDialog : TSaveDialog;
begin
saveDialog := TSaveDialog.Create(self);
saveDialog.Title := 'Choose location to save Decrypted Authentication Code';
saveDialog.InitialDir := 'C:\';
saveDialog.DefaultExt := '';
saveDialog.Execute();
glb_fileName1:= saveDialog.FileName;
//open stream and write cipher to a .txt of chosen location
try
Stream := TFileStream.Create(saveDialog.GetNamePath + saveDialog.FileName + '.txt', fmOpenReadWrite);
except
Stream := TFileStream.Create(saveDialog.GetNamePath + saveDialog.FileName + '.txt', fmCreate);
end;
Stream.Free;
Codec2.Password := WORD_2;
Codec2.DecryptFile(glb_fileName2, saveDialog.FileName + '.txt');
saveDialog.Free;
end;
The code you provided is way to complicated to try and see what is going wrong. If you are just trying to see if the encoding/decoding works you should only need simple code like the code below. Just put a test file on your drive and hardcode the names. That will let you know that the encoding/decoding works if the InputFile.txt and Un-EncryptedFile.text are the same.
Once you have the working then you can start to build up your full routines. The code you have posted is really confusing with the globals being used between the button clicks and just named 1, 2, etc. You have streams created that do nothing and only confuse the issue more. Strip things back to the basics and get that working first.
procedure TestEncodeDecode();
begin
Codec2.Password := WORD_2;
Codec2.EncryptFile('c:\InputFile.txt', 'c:\EncryptedFile.txt');
Codec2.DecryptFile('c:\EncryptedFile.txt', 'c:\Un-EncryptedFile.txt');
end;
I too am confused about what your question is asking. At the risk of misinterpreting your question, I have assumed that you are trying:
Encrypt a string;
Store the encrypted string in a file
Encrypt the file (double encryption)
Reverse the previous steps to reconstruct the original string.
The selftest() method proves that this works.
If this interpretation is correct, please consider something like the following solution. (Tested in Delphi 2010. Not test in XE2)
unit uDoubleEncrypt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, uTPLb_CryptographicLibrary, uTPLb_BaseNonVisualComponent,
uTPLb_Codec;
type
TmfmDoubleEncrypt = class(TForm)
Codec1: TCodec;
Codec2: TCodec;
CryptographicLibrary1: TCryptographicLibrary;
btnGo: TButton;
memoLog: TMemo;
dlgSave1: TSaveDialog;
dlgOpen1: TOpenDialog;
procedure btnGoClick(Sender: TObject);
private
FFileName_Plain, FFileName_Cipher: string;
sSerial: string;
function EncryptStringWithCodec1( const sPlaintext: string): ansistring;
function GetFileName( dlgOpenX: TOpenDialog; var sFN: string): boolean;
procedure SaveAnsiStringToFile( const sFN: string; const sSerialCipherText: AnsiString);
function ReconstructSerial: string;
public
procedure Put( const LineFmt: string; const Args: array of const);
procedure Button1Click;
procedure Button4Click;
function SelfTest: boolean;
end;
var
mfmDoubleEncrypt: TmfmDoubleEncrypt;
implementation
{$R *.dfm}
procedure TmfmDoubleEncrypt.btnGoClick( Sender: TObject);
var
WORD_1, WORD_2: string;
begin
WORD_1 := 'red';
WORD_2 := 'blue';
sSerial := '123'; // Serial_number.Number; // Store Serial Number of machine
Codec1.Password := WORD_1;
Codec2.Password := WORD_2;
// Run the self test.
SelfTest;
// Clean up.
Codec1.Burn;
Codec2.Burn
// You may also want to delete temporary files here.
end;
function TmfmDoubleEncrypt.EncryptStringWithCodec1(
const sPlaintext: string): ansistring;
begin
// Assume Codec1 properties already set-up:
// 1. Password
// 2. CryptoLibrary
// 3. Cipher (at design-time)
// 4. Chain-mode
Codec1.Reset; // Normally not necessary. A defence agains the codec being left in a corrupt state.
Codec1.EncryptString( sPlaintext, result)
end;
function TmfmDoubleEncrypt.GetFileName(
dlgOpenX: TOpenDialog; var sFN: string): boolean;
begin
result := dlgOpenX.Execute;
if result then
sFN := dlgOpenX.FileName
end;
procedure TmfmDoubleEncrypt.Put(
const LineFmt: string; const Args: array of const);
begin
memoLog.Lines.Add( Format( LineFmt, Args))
end;
procedure TmfmDoubleEncrypt.SaveAnsiStringToFile(
const sFN: string; const sSerialCipherText: AnsiString);
const
Modes: array[boolean] of word = (fmCreate, fmOpenReadWrite);
var
SaveStream: TStream;
begin
SaveStream := TFileStream.Create( sFN, Modes[ FileExists( sFN)]);
try
SaveStream.Size := 0;
if sSerialCipherText <> '' then
SaveStream.WriteBuffer( sSerialCipherText[1], Length( sSerialCipherText))
finally
SaveStream.Free
end
end;
procedure TmfmDoubleEncrypt.Button1Click;
// This method is equivalent to gEdit101's Button1Click()
var
sPlainText: string;
sSerialCipherText: AnsiString;
sFN: string;
begin
sPlainText := sSerial;
sSerialCipherText := EncryptStringWithCodec1( sPlainText);
Put( 'Encrypted serial number is %s', [sSerialCipherText]);
if GetFileName( dlgOpen1, sFN) then
begin
SaveAnsiStringToFile( sFN, sSerialCipherText);
FFileName_Plain := sFN; // Store for Button4Click()
Put('encrypted serial number save to file "%s".',[sFN])
end;
end;
procedure TmfmDoubleEncrypt.Button4Click;
// This method is equivalent to gEdit101's Button4Click()
var
sPlainText: string;
sSerialCipherText: AnsiString;
sFN: string;
begin
Codec2.Reset;
FFileName_Cipher := FFileName_Plain + '_enc.dat'; // Not a text file. + '_enc.txt' would be wrong.
Codec2.EncryptFile( FFileName_Plain, FFileName_Cipher);
Put( 'Double Encrypted serial number is now stored in file "%s"', [FFileName_Cipher]);
end;
function TmfmDoubleEncrypt.ReconstructSerial: string;
var
CipherStream, PlainStream: TStream;
sEncryptedSerial: AnsiString;
begin
CipherStream := TFileStream.Create( FFileName_Cipher, fmOpenRead);
PlainStream := TMemoryStream.Create;
try
Codec2.Reset;
Codec2.DecryptStream( PlainStream, CipherStream);
PlainStream.Position := 0;
SetLength( sEncryptedSerial, PlainStream.Size);
if Length( sEncryptedSerial) > 0 then
PlainStream.ReadBuffer( sEncryptedSerial[1], Length( sEncryptedSerial));
Codec1.Reset;
Codec1.DecryptString( result, sEncryptedSerial)
finally
CipherStream.Free;
PlainStream.Free
end
end;
function TmfmDoubleEncrypt.SelfTest: boolean;
var
sRecon: string;
begin
Put('Commencing self test...',[]);
try
Button1Click; // 1st encryption
Button4Click; // 2nd encryption
sRecon := ReconstructSerial; // Reconstruction
result := sSerial = sRecon
finally
Put('Finished self test. Result = %s',[BoolToStr( result, True)]);
end;
end;
end.
The dfm for this unit is ...
object mfmDoubleEncrypt: TmfmDoubleEncrypt
Left = 0
Top = 0
Caption = 'Double Encrypt'
ClientHeight = 304
ClientWidth = 643
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
643
304)
PixelsPerInch = 96
TextHeight = 13
object btnGo: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Go'
TabOrder = 0
OnClick = btnGoClick
end
object memoLog: TMemo
Left = 8
Top = 39
Width = 627
Height = 257
Anchors = [akLeft, akTop, akRight, akBottom]
Color = clInfoBk
ReadOnly = True
ScrollBars = ssVertical
TabOrder = 1
end
object Codec1: TCodec
AsymetricKeySizeInBits = 1024
AdvancedOptions2 = []
CryptoLibrary = CryptographicLibrary1
Left = 440
Top = 112
StreamCipherId = 'native.StreamToBlock'
BlockCipherId = 'native.AES-128'
ChainId = 'native.CBC'
end
object Codec2: TCodec
AsymetricKeySizeInBits = 1024
AdvancedOptions2 = []
CryptoLibrary = CryptographicLibrary1
Left = 536
Top = 112
StreamCipherId = 'native.StreamToBlock'
BlockCipherId = 'native.AES-128'
ChainId = 'native.CBC'
end
object CryptographicLibrary1: TCryptographicLibrary
Left = 480
Top = 48
end
object dlgSave1: TSaveDialog
InitialDir = 'C:\Temp'
Title = 'Choose location to save Authentication Code'
Left = 440
Top = 176
end
object dlgOpen1: TOpenDialog
InitialDir = 'C:\'
Left = 536
Top = 176
end
end