A google search shows a few examples on how to download a file in Delphi but most are buggy and half of the time don't work in my experience.
I'm looking for a simple robust solution which will let me download a single exe (for updating my app) and will hold the execution of the current update thread until the download is done or errors out. The process is already threaded so the download code should hold execution until it's done (hopefully).
Here's two implementations, both seem very complicated
1. http://www.scalabium.com/faq/dct0116.htm
2. http://delphi.about.com/od/internetintranet/a/get_file_net.htm
Why not make use of Indy? If you use the TIdHTTP component, it's simple:
procedure TMyForm.DownloadFile;
var
IdHTTP1: TIdHTTP;
Stream: TMemoryStream;
Url, FileName: String;
begin
Url := 'http://www.rejbrand.se';
Filename := 'download.htm';
IdHTTP1 := TIdHTTP.Create(Self);
Stream := TMemoryStream.Create;
try
IdHTTP1.Get(Url, Stream);
Stream.SaveToFile(FileName);
finally
Stream.Free;
IdHTTP1.Free;
end;
end;
You can even add a progress bar by using the OnWork and OnWorkBegin events:
procedure TMyForm.IdHTTPWorkBegin(ASender: TObject; AWorkMode: TWorkMode;AWorkCountMax: Int64);
begin
ProgressBar.Max := AWorkCountMax;
ProgressBar.Position := 0;
end;
procedure TMyForm.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
ProgressBar.Position := AWorkCount;
end;
procedure TMyForm.DownloadFile;
var
IdHTTP1: TIdHTTP;
Stream: TMemoryStream;
Url, FileName: String;
begin
Url := 'http://www.rejbrand.se';
Filename := 'download.htm';
IdHTTP1 := TIdHTTP.Create(Self);
Stream := TMemoryStream.Create;
try
IdHTTP1.OnWorkBegin := IdHTTPWorkBegin;
IdHTTP1.OnWork := IdHTTPWork;
IdHTTP1.Get(Url, Stream);
Stream.SaveToFile(FileName);
finally
Stream.Free;
IdHTTP1.Free;
end;
end;
I'm not sure if these events fire in the context of the main thread, so any updates done to VCL components may have to be done using the TIdNotify component to avoid threading issues. Maybe someone else can check that.
The second approach is the standard way of using Internet resources using WinINet, a part of Windows API. I have used it a lot, and it has always worked well. The first approach I have never tried. (Neither is "very complicated". There will always be a few additional steps when using the Windows API.)
If you want a very simple method, you could simply call UrlMon.URLDownloadToFile. You will not get any fine control (at all!) about the download, but it is very simple.
Example:
URLDownloadToFile(nil,
'http://www.rejbrand.se',
PChar(ExtractFilePath(Application.ExeName) + 'download.htm'),
0,
nil);
For people that has later version of delphi, you can use this:
var
http : TNetHTTPClient;
url : string;
stream: TMemoryStream;
begin
http := TNetHTTPClient.Create(nil);
stream := TMemoryStream.Create;
try
url := YOUR_URL_TO_DOWNLOAD;
http.Get(url, stream);
stream.SaveToFile('D:\Temporary\1.zip');
finally
stream.Free;
http.Free;
end;
end;
Using URLMon.
errcode := URLMon.URLDownloadToFile(nil,
PChar('http://www.vbforums.com/showthread.php?345726-DELPHI-Download-Files'),
PChar( 'a:\download.htm'),
0,
nil);
if errcode > 0 then
showmessage('Error while downloading: ' + inttostr(errcode));
Related
I am currently making a little Program in Delphi 10.3 Community Version 26.0.34749.6593. No additional components.
Essentially I draw on TPaintBox which is fitted in a Panel. Everything works fine so far, but when the objects are repainted via "PaintBox1.Repaint" the Objects got the wrong BrushStyle (bsSolid when they should have bsClear e.g.) Of course I tried to pin it down, but I got no luck. But I found out that at the following Point something doesn't work:
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
i: Integer;
fig : ^TFigure;
apen: TPenStyle;
abrush: TBrushStyle;
color1,color2: TColor;
begin
aPen := PaintBox1.Canvas.Pen.Style;
aBrush := bsStyle;
color1 := PaintBox1.Canvas.Brush.Color;
color2 := PaintBox1.Canvas.Pen.Color;
for I:=0 to List.Count-1 do
begin
fig := List.Items[i];
case fig.Typ of
f_Kreis : begin
with Paintbox1.Canvas do
begin
pen.Style := fig.Pen;
Brush.Style := fig.Brush;
pen.Color := fig.PenColor;
brush.Color := fig.BrushColor;
Ellipse(fig.X,fig.Y,fig.X2,fig.Y2);
end;
end;
f_Rechteck : begin
with PaintBox1.Canvas do
begin
Pen.Style := fig.Pen;
Brush.Style := fig.Brush;
Pen.Color := fig.PenColor;
Brush.Color := fig.BrushColor;
Rectangle(fig.X,fig.Y,fig.X2,fig.Y2);
end;
end;
f_Line : begin
with PaintBox1.Canvas do
begin
pen.Style := fig.Pen;
brush.Style := fig.Brush;
pen.Color := fig.PenColor;
brush.Color := fig.BrushColor;
MoveTo(fig.X,Fig.Y);
LineTo(fig.X2,fig.Y2);
end;
end;
end;
end;
PaintBox1.Canvas.Pen.Style := aPen;
bsStyle := aBrush;
PaintBox1.Canvas.Brush.Color := color1;
PaintBox1.Canvas.Pen.Color := color2;
end;
So when the "Brush.Style := fig.Brush;"-Line is called, nothing happens. I went step by step and after these Line "Brush.Style" is still "bsSolid" even when "fig.Brush" is "bsClear"
For explanation: TFigure is my own class. It houses information about a drawing, such as a rectangle. It is the parent class.
Do I miss something. I really am out of Ideas. Can anyone tell me, why nothing happens?
Edit:
For testing I added the lines:
if Brush.Style <> fig.Brush then
ShowMessage('Warnung!');
under
Brush.Style := fig.Brush;
and it actually wont set it on false, though Brush.Style is bsSolid and fig.Brush is bsClear.
You have declared fig : ^TFigure;, but class instances are already references (pointers). Thus you are creating a pointer to reference, and using that pointer as if it were the reference.
Remove the pointer operator and declare
fig: TFigure;
I can't verify whether there are other errors in your code
i am asking about the possibility of compressing TIdMemoryBufferStream Using TIdCompressorZLib how can it be done properly using the following code
procedure TClientThread.SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
IdCompressorZLib : TIdCompressorZLib;
begin
Strm := TIdMemoryBufferStream.Create(PByte(Buffer), BufferSize);
IdCompressorZLib := TIdCompressorZLib.Create(nil);
try
// then can't figure what the right process to do
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
FreeAndNil(Strm);
FreeAndNil(IdCompressorZLib);
end;
end;
i am not sure about the right process that needs to be done
as example should i create another variable as StrmB then calling the compress to it ?
The TIdCompressorZLib component is intended to be used only with the Compressor property of the TIdHTTP and TIdFTP components.
For general purpose compression over a TCP connection, you can assign a TIdCompressionIntercept component to the TCP connection's IOHandler.Intercept property, eg:
var
Compressor : TIdCompressionIntercept;
begin
Compressor := TIdCompressionIntercept.Create(nil);
try
Compressor.CompressionLevel := 9;
FTCP.Socket.Intercept := Compressor;
try
// any data written here will be compressed....
finally
FTCP.Socket.Intercept := nil;
end;
finally
Compressor.Free;
end;
end;
However, since you are sending the byte size of the compressed data (by setting the AWriteByteCount parameter to True in TIdIOHandler.Write(TStream)), that will not work with the compression intercept.
Indy's IdZLib unit has a TCompressionStream class, and various ...CompressStream/Ex() functions, that you can use instead, eg:
procedure TClientThread.SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
var
Strm: TMemoryStream;
Compressor : TCompressionStream;
begin
Strm := TMemoryStream.Create;
try
Compressor := TCompressionStream.Create(clMax, Strm, False);
try
WriteTIdBytesToStream(Compressor, Buffer, BufferSize);
finally
Compressor.Free;
end;
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
end;
Or:
procedure TClientThread.SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
var
InStrm: TIdMemoryBufferStream;
OutStrm: TMemoryStream;
begin
OutStrm := TMemoryStream.Create;
try
InStrm := TIdMemoryBufferStream.Create(PByte(Buffer), BufferSize);
try
CompressStreamEx(InStrm, OutStrm, clMax, zsZLib);
finally
InStrm.Free;
end;
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(OutStrm, 0, True);
finally
OutStrm.Free;
end;
end;
Delphi 2010
I'm Transferring Image via custom TCP Socket control uses UTF-8
Client Side
var
TSS: TStringStream;
STR :String;
JPG:TJPEGImage;
BMP:TBitmap;
begin
Try
BMP.LoadFromFile('C:\1.bmp');
JPG.Assign(BMP);
JPG.CompressionQuality:=80;
JPG.Compress;
TSS:=TStringStream.Create;
JPG.SaveToStream(TSS);
STR:=TSS.DataString;
MyTCPSocket.SendString(STR);
finally
BMP.free;
JPG.free;
TSS.free;
end;
end;
Server Side
Var
TSS: TStringStream;
TSS:=TStringStream.Create;
TSS.WriteString(STR);
TSS.SaveToFile('C:\2.jpg');
This code working on the same pc great.
The problem when I send the image to other pc that uses different encoding
it receive the image but I see many wrong characters in the data "?????"
I think when TStringStream writes the bytes to the file it fails to convert the unicode characters to bytes so it appears like "?"
Any idea is much appreciated
You are trying to send binary data as if it were UTF-8 encoded text. It is not, so do not try to do that! Send the binary data in its original binary form, eg:
var
MS: TMemoryStream;
JPG: TJPEGImage;
BMP: TBitmap;
begin
MS := TMemoryStream.Create;
try
JPG := TJPEGImage.Create;
try
BMP := TBitmap.Create;
try
BMP.LoadFromFile('C:\1.bmp');
JPG.Assign(BMP);
finally
BMP.Free;
end;
JPG.CompressionQuality := 80;
JPG.Compress;
JPG.SaveToStream(MS);
finally
JPG.Free;
end;
MS.Position := 0;
MyTCPSocket.SendStream(MS);
finally
MS.free;
end;
end;
var
MS: TMemoryStream;
begin
MS := TMemoryStream.Create;
try
MyTCPSocket.ReadStream(MS);
MS.Position := 0;
MS.SaveToFile('C:\2.jpg');
finally
MS.Free;
end;
end;
If you must send binary data as text, you need to encode the data using a real binary-to-text encoding algorithm, such as base64 or yEnc, not UTF-8 (which is designed for only encoding Unicode text, not binary data), eg:
// TIdEncoderMIME and TIdDecoderMIME are part of Indy,
// which ships with Delphi, but you can use whatever
// you want...
uses
..., IdCoderMIME;
var
S: String;
MS: TMemoryStream;
JPG: TJPEGImage;
BMP: TBitmap;
begin
MS := TMemoryStream.Create;
try
JPG := TJPEGImage.Create;
try
BMP := TBitmap.Create;
try
BMP.LoadFromFile('C:\1.bmp');
JPG.Assign(BMP);
finally
BMP.Free;
end;
JPG.CompressionQuality := 80;
JPG.Compress;
JPG.SaveToStream(MS);
finally
JPG.Free;
end;
MS.Position := 0;
S := TIdEncoderMIME.EncodeStream(MS);
finally
MS.free;
end;
MyTCPSocket.SendString(S);
end;
uses
..., IdCoderMIME;
var
S: string;
MS: TMemoryStream;
begin
S := MyTCPSocket.ReadString;
MS := TMemoryStream.Create;
try
TIdDecoderMIME.DecodeStream(S, MS);
MS.Position := 0;
MS.SaveToFile('C:\2.jpg');
finally
MS.Free;
end;
end;
I finished controlling mplayer using pipe in Windows with -slave command (ftp://ftp2.mplayerhq.hu/MPlayer/DOCS/tech/slave.txt) but I don't know how to do that in MacOS.
Because MacOS developing is new for me, so, I still have Windows concept in my mind. So, I decided to use Pipe in OSX also.
This is my code to create Pipe:
uses
Macapi.ObjectiveC,Macapi.ObjCRuntime,
Macapi.Foundation;
var
LArray: array of Pointer;
LArgs: NSArray;
LTask: NSTask;
LPipe: NSPipe;
fileHandler: NSFileHandle;
function RawStr(const Str: string): Pointer;
begin
Result := TNSString.OCClass.stringWithUTF8String(PAnsiChar(UTF8String(Str)));
end;
procedure runInMacOSX;
var
data : NSData;
nsText : NSString;
begin
LArgs := TNSArray.Create;
setlength(LArray, 2);
LArray[0] := RawStr('/test.avi');
LArray[1] := RawStr('-slave');
LArgs := TNSArray.Wrap(TNSArray.Alloc.initWithObjects(#(LArray[0]), 2));
LTask := TNSTask.Wrap(TNSTask.Alloc.init);
LPipe := TNSPipe.Create;
fileHandler := TNSFileHandle.Create;
LTask.setLaunchPath(NSStr('/mplayer'));
LTask.setArguments(LArgs);
LTask.setStandardOutput((LPipe as ILocalObject).GetObjectID);
fileHandler := LPipe.fileHandleForReading;
LTask.launch;
end;
In button 1, I execute runInMacOSX: "/mplayer -slave /test.avi" and it executed correctly.
But in button 2, I'd like to pause the mplayer by sending "pause" command. I got stuck:
var
a: NSData;
str: NSString;
begin
str := TNSString.Wrap(TNSString.OCClass.stringWithUTF8String(PAnsiChar('pause')));
a := str.dataUsingEncoding(NSUTF8StringEncoding);
LPipe.fileHandleForWriting.writeData(a);
How can I solve this problem?
Thank you
I need to create new windows user as administrator using Delphi
Thanks
you can use the NetUserAdd and NetUserSetGroups functions declarated in the JEDI Headers.
see this simple sample.
program ProjectAddNewUser;
{$APPTYPE CONSOLE}
uses
JclWin32,//Jedi Library
Windows,
SysUtils;
function CreateWinUser(const wServer, wUsername, wPassword, wGroup:WideString): Boolean;
var
Buf : USER_INFO_2;//Buf for the new user info
Err : NET_API_STATUS;
ParmErr : DWORD;
GrpUsrInfo: USER_INFO_0;//Buf for the group
wDummyStr : WideString;
begin
wDummyStr:='';
FillChar (Buf, SizeOf(USER_INFO_2), 0);
with Buf do
begin
usri2_name := PWideChar(wUsername);
usri2_full_name := PWideChar(wUsername);//You can add a more descriptive name here
usri2_password := PWideChar(wPassword);
usri2_comment := PWideChar(wDummyStr);
usri2_priv := USER_PRIV_USER;
usri2_flags := UF_SCRIPT OR UF_DONT_EXPIRE_PASSWD;
usri2_script_path := PWideChar(wDummyStr);
usri2_home_dir := PWideChar(wDummyStr);
usri2_acct_expires:= TIMEQ_FOREVER;
end;
GrpUsrInfo.usri0_name:=PWideChar(wGroup);
Err := NetUserAdd(PWideChar(wServer), 1, #Buf, #ParmErr);
Result := (Err = NERR_SUCCESS);
if Result then //NOw you must set the group for the new user
begin
Err := NetUserSetGroups(PWideChar(wServer),PWideChar(wGroup),0,#GrpUsrInfo,1);
Result := (Err = NERR_SUCCESS);
end;
end;
begin
if CreateWinUser('localhost', 'MyNewUser','ThePassword','MyWindowsGroup') then
Writeln('Ok')
else
Writeln('False');
Readln;
end.
I think the API call you need is NetUserAdd.
First, check if Delphi provides a wrapper for this call. If not, you'll have to write your own. If you don't know how to make Windows API calls from Delphi, you have some more research to do.