I need to monitor real time if a new file is created on a folder. System.IO.FileSystemWatcher seems a perfect solution. But on Delphi 11 it is reporting
[dcc32 Fatal Error] F2613 Unit 'System.IO' not found.
Do I have to download something to have the .pas unit?
P.S. I have explored using the windows API FindFirstChangeNotification but this does not provide the filename created.
System.IO.FileSystemWatcher is a .net class and not part of the Delphi RTL. Therefore you won't find it anywhere.
I think the API function you need is ReadDirectoryChangesW.
You can also use DDNRuntime(Delphi .NET Framework/.NET Core Runtime)
https://github.com/ying32/DDNRuntime-examples
It can help you call .net fuctions easyly
Just like
procedure TestMemoryStream;
var
LMem: DNMemoryStream;
LBytes: TArray<Byte>;
B: Byte;
LReadLen: Integer;
begin
LMem := TDNMemoryStream.Create;
LMem.Write([1,2,3,4,5], 0, 5);
LMem.WriteByte(121);
LMem.Flush;
LMem.Position := 0;
Writeln('data Length: ', LMem.Length);
SetLength(LBytes, LMem.Length);
LReadLen := LMem.Read(LBytes, 0, Length(LBytes));
Writeln('len: ', LReadLen);
for b in LBytes do
Write(b, ' ');
Writeln;
end;
Related
I have been asked to correct an issue (not related to this question) in a legacy Delphi program. After fixing some issues with missing components, I am now stuck with some GDI Plus functionality, which stops me from compiling the program. One of the functions where this is used is:
function TDownLoadItem.LoadRawBitmapFromStream(var bm: TBitmap): Boolean;
var
image: TGPBitmap;
begin
Result := False;
if Content.Size = 0 then
exit;
// NOTE: Content is a TMemoryStream, declared globally.
image := GDIPlusHelper.LoadBitmapFromStream(Content); // <== This is where the problem is....
try
bm.Width := image.GetWidth;
bm.Height := image.GetHeight;
with TGPGraphics.Create(bm.Canvas.Handle) do
try
DrawImage(image, 0, 0, image.GetWidth, image.GetHeight);
Result := True;
finally
Free;
end;
finally
image.Free;
end;
end;
I think (not sure) the last Delphi version used was 2006, I am on Delphi Rio 10.3.
Online I have managed to find GDI+ 1.2, but this does not solve the problem. The procedure LoadBitmapFromStream does not exit in these libraries. GDIPlusHelper was apparently renamed to GDIPlusHelpers and most code has changed from classes to interfaces. I suspect an older edition of the GDI Plus libraries were used, but I cannot find these.
Reworking the code would be too complex as it would require Content to be an IStream instead of a TMemoryStream. Also, simply using a TBitmap is not feasible either as other code (not shown) uses functionality specific to TGPBitmap (e.g. RotateFlip).
Any suggestions on how to fix/work around this? Thanks in advance!
The following code tries to load the OpenGL glGenBuffers method but fails to do so. Could you help to comment what is the reason and how to work around ?
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
StrUtils, SysUtils, Windows;
var
Library_OpenGL: HMODULE;
begin
try
Library_OpenGL := LoadLibrary('opengl32.dll');
Writeln(Ifthen(GetProcAddress(Library_OpenGL, 'glGenBuffers') <> nil, ' glGenBuffers Success', ' glGenBuffers Failed'));
Writeln(Ifthen(GetProcAddress(Library_OpenGL, 'glGenBuffersARB') <> nil, ' glGenBuffersARB Success', ' glGenBuffersARB Failed'));
FreeLibrary(Library_OpenGL);
Library_OpenGL := LoadLibrary('atioglxx.dll'); // ATI 4850
Writeln(Ifthen(GetProcAddress(Library_OpenGL, 'glGenBuffers') <> nil, ' glGenBuffers Success', ' glGenBuffers Failed'));
Writeln(Ifthen(GetProcAddress(Library_OpenGL, 'glGenBuffersARB') <> nil, ' glGenBuffersARB Success', ' glGenBuffersARB Failed'));
FreeLibrary(Library_OpenGL);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
If we properly check the return of the API, instead of dumping if it fails or not, we get the answer to why it fails pretty quick.
Replace
Writeln(Ifthen(GetProcAddress(Library_OpenGL, 'glGenBuffers') <> nil, ' glGenBuffers Success', ' glGenBuffers Failed'));
with
Win32Check(GetProcAddress(Library_OpenGL, 'glGenBuffers') <> nil);
and we have an exception:
EOSError: System Error. Code: 127.
The specified procedure could not be found
So the reason GetProcAddress fails is that the opengl library does not export a glGenBuffers function. A check with "depends" confirms that.
A quick search about missing functions in opengl leads us to wglGetProcAddress:
The wglGetProcAddress function returns the address of an OpenGL
extension function for use with the current OpenGL rendering context.
If you read the documentation throughly, you'll notice that simply calling gwlGetProcAddress supplying glGenBuffers will fail. You need a rendering context for it to return the address of the function.
Below is my shortest project which succeeds in getting the function's address. It use a GUI application since a rendering context requires a window, and a default VCL form suits the requirements (see "remarks" in SetPixelFormat). (Disclaimer: I don't know anything about opengl, code below is not necessarily correct, it also omits error checking which you should be really avoiding).
uses
opengl;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Library_OpenGL: HMODULE;
DC: HDC;
PxFmt: TPixelFormatDescriptor;
GLRC: HGLRC;
glGenBuffers: procedure(n: GLsizei; buffers: PGLuint); cdecl;
begin
Library_OpenGL := LoadLibrary('opengl32.dll');
DC := GetDC(Handle);
ZeroMemory(#PxFmt, SizeOf(PxFmt));
PxFmt.nSize := SizeOf(PxFmt);
PxFmt.nVersion := 1;
PxFmt.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
PxFmt.iPixelType := PFD_TYPE_RGBA;
PxFmt.cColorBits := 32;
PxFmt.cDepthBits := 24;
PxFmt.cStencilBits := 8;
SetPixelFormat(DC, ChoosePixelFormat(DC, #PxFmt), #PxFmt);
GLRC := wglCreateContext(DC);
wglMakeCurrent(DC, GLRC);
#glGenBuffers := wglGetProcAddress('glGenBuffers');
Assert(#glGenBuffers <> nil);
...
end;
While probably too late to be useful to the original questioner, the following might help others who find this question...
Delphi (possibly even very early versions) includes an OpenGL unit which provides basic functionality. I understand this is limited to functions present in OpenGL 1.1.
Delphi XE6 (released in April 2014 according to Wikipedia) added an OpenGLExt unit, including functionality up to OpenGL 4.3. This unit provides glGenBuffers and many other extension functions, but the extension functions are only loaded if your program explicitly calls InitOpenGLext (which itself just makes many wglGetProcAddress calls).
Using the two units together can save substantial effort.
I just got a simple tutorial working (just drawing a white triangle) using GLFW along with Delphi's OpenGL and OpenGLExt units.
Using GLFW in Delphi requires a unit to bind the GLFW functions, along with compiled GLFW DLLs. The following Github repository includes such a unit and DLLs together (though this isn't the one I used): https://github.com/neslib/DelphiGlfw
The essential calls before you can call extension functions are as follows. I've omitted error checking, but comments in the code should help with that.
uses Winapi.OpenGL, Winapi.OpenGLExt,
glfw; //The glfw unit name may be different, depending where you obtained it.
var window:pGLFWwindow;
begin
glfwInit; //Returns 0 if it fails.
window:=glfwCreateWindow(640,480,pchar(utf8encode('My Test Window')),
nil,nil); //Returns nil if it fails.
glfwMakeContextCurrent(window);
// Next line assigns function pointers for extension functions, so glGenBuffers will work.
InitOpenGLext;
// Add any other setup and your rendering loop here, and clean up after the loop.
// ...
end;
Im facing this issue when i try to extract a methode in the if statement. i couldnt find any reported bug on it.
procedure TForm1.BitBtn3Click(Sender: TObject);
var
x: integer;
b: boolean;
begin
if true then
x := 8 //********************** i try to extract this line
else
x := 6;
showmessage(inttostr(x));
end;
the result i get is:
procedure TForm1.BitBtn3Click(Sender: TObject);
var
x: integer;
b: boolean;
begin
if true then
newMethode
else
x := 6;
showmessage(inttostr(x));
end;
and the new Methode is:
procedure TForm1.newMethode;
var
x: Integer;
begin
x := 8;
end;
Can anybody check how is the behaviour on Delphi XE? anybody knows if it was reported?
This is a bug in the "Extract Method" refactoring.
As an alternative, you might want to use the "Extract Method" refactoring from ModelMaker Code Explorer refactoring tool. At EUR 99, it is a relatively cheap tool that works from Delphi 5 onward, and the recent 9.0.5 updates have vastly improved their Extract Method refactoring so much that I haven't used the Delphi built-in one for quite a while.
Two great benefits:
it launches the method-editor dialog where you can change and reorder parameters, which are then reflected in the extracted and calling code
it leaves the original code in a (* *) comment just in case something fails, or you need to reference it
In addition, it places bookmarks (numbered 7, 8 and 9) in the code for easy navigation between the extracted code and the call site.
Highly recommended.
You can find your answer about extract method at Oracle Blogs Entry
I had some code before I moved to Unicode and Delphi 2009 that appended some text to a log file a line at a time:
procedure AppendToLogFile(S: string);
// this function adds our log line to our shared log file
// Doing it this way allows Wordpad to open it at the same time.
var F, C1 : dword;
begin
if LogFileName <> '' then begin
F := CreateFileA(Pchar(LogFileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
if F <> 0 then begin
SetFilePointer(F, 0, nil, FILE_END);
S := S + #13#10;
WriteFile(F, Pchar(S)^, Length(S), C1, nil);
CloseHandle(F);
end;
end;
end;
But CreateFileA and WriteFile are binary file handlers and are not appropriate for Unicode.
I need to get something to do the equivalent under Delphi 2009 and be able to handle Unicode.
The reason why I'm opening and writing and then closing the file for each line is simply so that other programs (such as WordPad) can open the file and read it while the log is being written.
I have been experimenting with TFileStream and TextWriter but there is very little documentation on them and few examples.
Specifically, I'm not sure if they're appropriate for this constant opening and closing of the file. Also I'm not sure if they can make the file available for reading while they have it opened for writing.
Does anyone know of a how I can do this in Delphi 2009 or later?
Conclusion:
Ryan's answer was the simplest and the one that led me to my solution. With his solution, you also have to write the BOM and convert the string to UTF8 (as in my comment to his answer) and then that worked just fine.
But then I went one step further and investigated TStreamWriter. That is the equivalent of the .NET function of the same name. It understands Unicode and provides very clean code.
My final code is:
procedure AppendToLogFile(S: string);
// this function adds our log line to our shared log file
// Doing it this way allows Wordpad to open it at the same time.
var F: TStreamWriter;
begin
if LogFileName <> '' then begin
F := TStreamWriter.Create(LogFileName, true, TEncoding.UTF8);
try
F.WriteLine(S);
finally
F.Free;
end;
end;
Finally, the other aspect I discovered is if you are appending a lot of lines (e.g. 1000 or more), then the appending to the file takes longer and longer and it becomes quite inefficient.
So I ended up not recreating and freeing the LogFile each time. Instead I keep it open and then it is very fast. The only thing I can't seem to do is allow viewing of the file with notepad while it is being created.
For logging purposes why use Streams at all?
Why not use TextFiles? Here is a very simple example of one of my logging routines.
procedure LogToFile(Data:string);
var
wLogFile: TextFile;
begin
AssignFile(wLogFile, 'C:\MyTextFile.Log');
{$I-}
if FileExists('C:\MyTextFile.Log') then
Append(wLogFile)
else
ReWrite(wLogFile);
WriteLn(wLogfile, S);
CloseFile(wLogFile);
{$I+}
IOResult; //Used to clear any possible remaining I/O errors
end;
I actually have a fairly extensive logging unit that uses critical sections for thread safety, can optionally be used for internal logging via the OutputDebugString command as well as logging specified sections of code through the use of sectional identifiers.
If anyone is interested I'll gladly share the code unit here.
Char and string are Wide since D2009. Thus you should use CreateFile instead of CreateFileA!
If you werite the string you shoudl use Length( s ) * sizeof( Char ) as the byte length and not only Length( s ). because of the widechar issue. If you want to write ansi chars, you should define s as AnsiString or UTF8String and use sizeof( AnsiChar ) as a multiplier.
Why are you using the Windows API function instead of TFileStream defined in classes.pas?
Try this little function I whipped up just for you.
procedure AppendToLog(filename,line:String);
var
fs:TFileStream;
ansiline:AnsiString;
amode:Integer;
begin
if not FileExists(filename) then
amode := fmCreate
else
amode := fmOpenReadWrite;
fs := TFileStream.Create(filename,{mode}amode);
try
if (amode<>fmCreate) then
fs.Seek(fs.Size,0); {go to the end, append}
ansiline := AnsiString(line)+AnsiChar(#13)+AnsiChar(#10);
fs.WriteBuffer(PAnsiChar(ansiline)^,Length(ansiline));
finally
fs.Free;
end;
Also, try this UTF8 version:
procedure AppendToLogUTF8(filename, line: UnicodeString);
var
fs: TFileStream;
preamble:TBytes;
outpututf8: RawByteString;
amode: Integer;
begin
if not FileExists(filename) then
amode := fmCreate
else
amode := fmOpenReadWrite;
fs := TFileStream.Create(filename, { mode } amode, fmShareDenyWrite);
{ sharing mode allows read during our writes }
try
{internal Char (UTF16) codepoint, to UTF8 encoding conversion:}
outpututf8 := Utf8Encode(line); // this converts UnicodeString to WideString, sadly.
if (amode = fmCreate) then
begin
preamble := TEncoding.UTF8.GetPreamble;
fs.WriteBuffer( PAnsiChar(preamble)^, Length(preamble));
end
else
begin
fs.Seek(fs.Size, 0); { go to the end, append }
end;
outpututf8 := outpututf8 + AnsiChar(#13) + AnsiChar(#10);
fs.WriteBuffer(PAnsiChar(outpututf8)^, Length(outpututf8));
finally
fs.Free;
end;
end;
If you try to use text file or Object Pascal typed/untyped files in a multithreaded application you gonna have a bad time.
No kidding - the (Object) Pascal standard file I/O uses global variables to set file mode and sharing. If your application runs in more than one thread (or fiber if anyone still use them) using standard file operations could result in access violations and unpredictable behavior.
Since one of the main purposes of logging is debugging a multithreaded application, consider using other means of file I/O: Streams and Windows API.
(And yes, I know it is not really an answer to the original question, but I do not wish to log in - therefor I do not have the reputation score to comment on Ryan J. Mills's practically wrong answer.)
Delphi 2010 has a nice set of new file access functions in IOUtils.pas (I especially like the UTC versions of the date-related functions). What I miss so far is something like
TFile.GetSize (const Path : String)
What is the Delphi 2010-way to get the size of a file? Do I have to go back and use FindFirst to access TSearchRec.FindData?
Thanks.
I'm not sure if there's a "Delphi 2010" way, but there is a Windows way that doesn't involve FindFirst and all that jazz.
I threw together this Delphi conversion of that routine (and in the process modified it to handle > 4GB size files, should you need that).
uses
WinApi.Windows;
function FileSize(const aFilename: String): Int64;
var
info: TWin32FileAttributeData;
begin
result := -1;
if NOT GetFileAttributesEx(PChar(aFileName), GetFileExInfoStandard, #info) then
EXIT;
result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
end;
You could actually just use GetFileSize() but this requires a file HANDLE, not just a file name, and similar to the GetCompressedFileSize() suggestion, this requires two variables to call. Both GetFileSize() and GetCompressedFileSize() overload their return value, so testing for success and ensuring a valid result is just that little bit more awkward.
GetFileSizeEx() avoids the nitty gritty of handling > 4GB file sizes and detecting valid results, but also requires a file HANDLE, rather than a name, and (as of Delphi 2009 at least, I haven't checked 2010) isn't declared for you in the VCL anywhere, you would have to provide your own import declaration.
Using an Indy unit:
uses IdGlobalProtocols;
function FileSizeByName(const AFilename: TIdFileName): Int64;
You can also use DSiFileSize from DSiWin32. Works in "all" Delphis. Internally it calls CreateFile and GetFileSize.
function DSiFileSize(const fileName: string): int64;
var
fHandle: DWORD;
begin
fHandle := CreateFile(PChar(fileName), 0, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if fHandle = INVALID_HANDLE_VALUE then
Result := -1
else try
Int64Rec(Result).Lo := GetFileSize(fHandle, #Int64Rec(Result).Hi);
finally CloseHandle(fHandle); end;
end; { DSiFileSize }
I'd like to mention few Pure Delphi ways. Though i think Deltics made a most speed-effective answer for Windows platform, yet sometimes you want just rely on RTL and also make portable code that would work in Delphi for MacOS or in FreePascal/Virtual Pascal/whatever.
There is FileSize function left from Turbo Pascal days.
http://turbopascal.org/system-functions-filepos-and-filesize
http://docwiki.embarcadero.com/CodeExamples/XE2/en/SystemFileSize_(Delphi)
http://docwiki.embarcadero.com/Libraries/XE2/en/System.FileSize
The sample above lacks "read-only" mode setting. You would require that to open r/o file such as one on CD-ROM media or in folder with ACLs set to r/o. Before calling ReSet there should be zero assigned to FileMode global var.
http://docwiki.embarcadero.com/Libraries/XE2/en/System.FileMode
It would not work on files above 2GB size (maybe with negative to cardinal cast - up to 4GB) but is "out of the box" one.
There is one more approach, that you may be familiar if you ever did ASM programming for MS-DOS. You Seek file pointer to 1st byte, then to last byte, and check the difference.
I can't say exactly which Delphi version introduced those, but i think it was already in some ancient version like D5 or D7, though that is just common sense and i cannot check it.
That would take you an extra THandle variable and try-finally block to always close the handle after size was obtained.
Sample of getting length and such
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.FileOpen
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.FileSeek
Aside from 1st approach this is int64-capable.
It is also compatible with FreePascal, though with some limitations
http://www.freepascal.org/docs-html/rtl/sysutils/fileopen.html
You can also create and use TFileStream-typed object - which was the primary, officially blessed avenue for file operations since Delphi 1.0
http://www.freepascal.org/docs-html/rtl/classes/tfilestream.create.html
http://www.freepascal.org/docs-html/rtl/classes/tstream.size.html
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TFileStream.Create
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TStream.Size
As a side note, this avenue is of course integrated with aforementioned IOUtils unit.
http://docwiki.embarcadero.com/Libraries/XE3/en/System.IOUtils.TFile.OpenRead
This is a short solution using FileSize that does the job:
function GetFileSize(p_sFilePath : string) : Int64;
var
oFile : file of Byte;
begin
Result := -1;
AssignFile(oFile, p_sFilePath);
try
Reset(oFile);
Result := FileSize(oFile);
finally
CloseFile(oFile);
end;
end;
From what I know, FileSize is available only from XE2.
uses
System.Classes, System.IOUtils;
function GetFileSize(const FileName : string) : Int64;
var
Reader: TFileStream;
begin
Reader := TFile.OpenRead(FileName);
try
result := Reader.Size;
finally
Reader.Free;
end;
end;