I have the process ID (PID), how can I get its full command line (path and arguments)?
I recently had to do this; needed to terminate known applications, perform a task, then restart them with the same command line parameters they started with.
Of all the methods that I explored that can achieve getting command line parameters for each running process, WMI proved to be the safest and least painful way.
Add the 2 units pasted below, then call GetProcessList() and loop through the objects in the TObjectList. In your application, ensure you call CoInitialize(nil) on start up, and CoUninitialize() before it's closed. Code tested in Delphi 6.
unit uWmi;
interface
uses
Classes, Contnrs,
Variants, ActiveX, ComObj;
const
WMI_RESULT_OK = 0;
WMI_RESULT_NO_RECORDS = 1;
WMI_RESULT_NULL = 2;
WMI_RESULT_INVALID_PROPERTY = 3;
WMI_RESULT_ERROR_EXEC_QUERY = 4;
WMI_RESULT_UNKNOWN_ERROR = 5;
WMI_RESULT_STRINGS: array [0..5] of string = (
'OK',
'No records',
'Property value is null',
'Invalid property',
'Error executing query',
'Unknown error'
);
type
TWmi = class
private
FService: OleVariant;
function ExecWmiQuery(const AWMIQuery: string; var AItems: OleVariant; var AEnum: IEnumVariant): Boolean;
public
constructor Create;
class function GetWmiObject(const objectName: string): IDispatch;
class function GetWmiSelectQuery(const AWMIClass: string; const ASelectAll: Boolean; const AWMIProperties: TStrings = nil; const AWMIProperty: string = ''): string;
class function GetWmiPropertyValue(const AItem: OleVariant; const AProperty: string; var AValue: string): Integer;
class procedure AddWmiPropertyValueToList(const AValue: string; AResult: Integer; AValues: TStrings); overload;
class procedure AddWmiPropertyValueToList(const AItem: OleVariant; const AProperty: string; AValues: TStrings); overload;
function GetFirstRecordSinglePropertyValue(const AWMIClass, AWMIProperty: string; var AValue: string;
const ASelectAll: Boolean): Integer; overload;
function GetFirstRecordSinglePropertyValue(const AWMIProperty: string; var AValue: string;
const AWMIQuery: string): Integer; overload;
function GetAllRecordsSinglePropertyValues(const AWMIClass, AWMIProperty: string; AValues: TStrings;
const ASelectAll: Boolean): Integer; overload;
function GetAllRecordsSinglePropertyValues(const AWMIProperty: string; AValues: TStrings;
const AWMIQuery: string): Integer; overload;
function GetAllRecordsMultiplePropertiesValues(const AWMIClass: string; AProperties: TStrings;
ARecords: TObjectList; const ASelectAll: Boolean): Integer; overload;
function GetAllRecordsMultiplePropertiesValues(AProperties: TStrings; ARecords: TObjectList;
const AWMIQuery: string): Integer; overload;
function GetAllRecordsMultiplePropertiesValues(const AWMIClass: string; const AProperties: array of string;
ARecords: TObjectList; const ASelectAll: Boolean): Integer; overload;
function GetAllRecordsMultiplePropertiesValues(const AProperties: array of string;
ARecords: TObjectList; const AWMIQuery: string): Integer; overload;
end;
implementation
uses
SysUtils, uStringUtils;
const
wbemFlagForwardOnly = $00000020;
{ TWmi }
function TWmi.ExecWmiQuery(const AWMIQuery: string; var AItems: OleVariant; var AEnum: IEnumVariant): Boolean;
begin
try
AItems := FService.ExecQuery(AWMIQuery, 'WQL', wbemFlagForwardOnly);
AEnum := IUnknown(AItems._NewEnum) as IEnumVariant;
Result := True;
except
Result := False;
end;
end;
constructor TWmi.Create;
{$IFDEF USE_LOCATOR}
const
USER = '';
PASSWORD = '';
COMPUTER = 'localhost';
var
locator: OleVariant;
{$ENDIF}
begin
{$IFDEF USE_LOCATOR}
locator := CreateOleObject('WbemScripting.SWbemLocator');
FService := locator.ConnectServer(COMPUTER, 'root\CIMV2', USER, PASSWORD);
{$ELSE}
FService := GetWmiObject('winmgmts:\\localhost\root\cimv2');
{$ENDIF}
end;
class function TWmi.GetWmiObject(const objectName: string): IDispatch;
var
chEaten: Integer;
BindCtx: IBindCtx;
Moniker: IMoniker;
begin
OleCheck(CreateBindCtx(0, bindCtx));
OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;
class function TWmi.GetWmiSelectQuery(const AWMIClass: string; const ASelectAll: Boolean;
const AWMIProperties: TStrings = nil; const AWMIProperty: string = ''): string;
var
props: string;
i: Integer;
begin
if ASelectAll then
props := '*'
else
if AWMIProperties = nil then
props := AWMIProperty
else
for i := 0 to AWMIProperties.Count - 1 do
if props = '' then
props := AWMIProperties[i]
else
props := props + ',' + AWMIProperties[i];
Result := Format('SELECT %s FROM %s', [props, AWMIClass]);
end;
class function TWmi.GetWmiPropertyValue(const AItem: OleVariant; const AProperty: string; var AValue: string): Integer;
var
v: OleVariant;
begin
try
v := AItem.Properties_.Item(AProperty).Value;
except
Result := WMI_RESULT_INVALID_PROPERTY;
AValue := '';
Exit;
end;
if VarIsNull(v) then
begin
Result := WMI_RESULT_NULL;
AValue := '';
Exit;
end;
AValue := Trim(v);
Result := WMI_RESULT_OK;
end;
class procedure TWmi.AddWmiPropertyValueToList(const AValue: string; AResult: Integer; AValues: TStrings);
begin
AValues.AddObject(AValue, TObject(AResult));
end;
class procedure TWmi.AddWmiPropertyValueToList(const AItem: OleVariant; const AProperty: string; AValues: TStrings);
var
value: string;
r: Integer;
begin
r := GetWmiPropertyValue(AItem, AProperty, value);
AddWmiPropertyValueToList(value, r, AValues);
end;
function TWmi.GetFirstRecordSinglePropertyValue(const AWMIClass, AWMIProperty: string; var AValue: string;
const ASelectAll: Boolean): Integer;
begin
Result := GetFirstRecordSinglePropertyValue(AWMIProperty, AValue,
GetWmiSelectQuery(AWMIClass, ASelectAll, nil, AWMIProperty));
end;
function TWmi.GetFirstRecordSinglePropertyValue(const AWMIProperty: string; var AValue: string;
const AWMIQuery: string): Integer;
var
items, item: OleVariant;
enum: IEnumVariant;
value: Cardinal;
begin
try
if not ExecWmiQuery(AWMIQuery, items, enum) then
begin
Result := WMI_RESULT_ERROR_EXEC_QUERY;
Exit;
end;
if enum.Next(1, item, value) <> 0 then
Result := WMI_RESULT_NO_RECORDS
else
try
Result := GetWmiPropertyValue(item, AWMIProperty, AValue);
finally
item := Unassigned;
end;
except {on: EOleException do Writeln(e.Source, ' ', e.ErrorCode, ' ', e.Message);}
Result := WMI_RESULT_UNKNOWN_ERROR;
end;
end;
function TWmi.GetAllRecordsSinglePropertyValues(const AWMIClass, AWMIProperty: string; AValues: TStrings;
const ASelectAll: Boolean): Integer;
begin
Result := GetAllRecordsSinglePropertyValues(AWMIProperty, AValues,
GetWmiSelectQuery(AWMIClass, ASelectAll, nil, AWMIProperty));
end;
function TWmi.GetAllRecordsSinglePropertyValues(const AWMIProperty: string; AValues: TStrings; const AWMIQuery: string): Integer;
var
items, item: OleVariant;
enum: IEnumVariant;
value: Cardinal;
begin
try
if not ExecWmiQuery(AWMIQuery, items, enum) then
begin
Result := WMI_RESULT_ERROR_EXEC_QUERY;
Exit;
end;
AValues.Clear;
while enum.Next(1, item, value) = 0 do
try
AddWmiPropertyValueToList(item, AWMIProperty, AValues);
finally
item := Unassigned;
end;
if AValues.Count = 0 then
Result := WMI_RESULT_NO_RECORDS
else
Result := WMI_RESULT_OK;
except {on: EOleException do Writeln(e.Source, ' ', e.ErrorCode, ' ', e.Message);}
Result := WMI_RESULT_UNKNOWN_ERROR;
AValues.Clear;
end;
end;
function TWmi.GetAllRecordsMultiplePropertiesValues(const AWMIClass: string; AProperties: TStrings;
ARecords: TObjectList; const ASelectAll: Boolean): Integer;
begin
Result := GetAllRecordsMultiplePropertiesValues(AProperties, ARecords,
GetWmiSelectQuery(AWMIClass, ASelectAll, AProperties));
end;
function TWmi.GetAllRecordsMultiplePropertiesValues(AProperties: TStrings; ARecords: TObjectList;
const AWMIQuery: string): Integer;
var
items, item: OleVariant;
enum: IEnumVariant;
value: Cardinal;
values: TStrings;
i: Integer;
begin
try
if not ExecWmiQuery(AWMIQuery, items, enum) then
begin
Result := WMI_RESULT_ERROR_EXEC_QUERY;
Exit;
end;
ARecords.Clear;
while enum.Next(1, item, value) = 0 do
try
values := TStringList.Create;
ARecords.Add(values);
for i := 0 to AProperties.Count - 1 do
AddWmiPropertyValueToList(item, AProperties[i], values);
finally
item := Unassigned;
end;
if ARecords.Count = 0 then
Result := WMI_RESULT_NO_RECORDS
else
Result := WMI_RESULT_OK;
except {on: EOleException do Writeln(e.Source, ' ', e.ErrorCode, ' ', e.Message);}
Result := WMI_RESULT_UNKNOWN_ERROR;
ARecords.Clear;
end;
end;
function TWmi.GetAllRecordsMultiplePropertiesValues(const AWMIClass: string; const AProperties: array of string;
ARecords: TObjectList; const ASelectAll: Boolean): Integer;
var
props: TStringList;
begin
props := CreateStringList(AProperties);
try
Result := GetAllRecordsMultiplePropertiesValues(AWMIClass, props, ARecords, ASelectAll);
finally
props.Free;
end;
end;
function TWmi.GetAllRecordsMultiplePropertiesValues(const AProperties: array of string;
ARecords: TObjectList; const AWMIQuery: string): Integer;
var
props: TStrings;
begin
props := CreateStringList(AProperties);
try
Result := GetAllRecordsMultiplePropertiesValues(props, ARecords, AWMIQuery);
finally
props.Free;
end;
end;
{initialization
CoInitialize(nil);
finalization
CoUninitialize;}
end.
unit uWmiProcess;
interface
uses
Contnrs;
type
TProcessInfo = class
public
Name: string;
ProcessId: Cardinal;
ExecutablePath: string;
CommandLine: string;
SessionId: Integer;
CreationDate: string;
end;
function GetProcessList: TObjectList{<TProcessInfo>};
implementation
uses
SysUtils, Classes, uWmi;
function GetProcessList: TObjectList{<TProcessInfo>};
var
wmi: TWmi;
processInfo: TProcessInfo;
records: TObjectList;
values: TStringList;
i: Integer;
function CallWmi(const AProps: array of string): Boolean;
begin
Result := wmi.GetAllRecordsMultiplePropertiesValues('Win32_Process', AProps, records, False) = uWmi.WMI_RESULT_OK;
end;
begin
Result := TObjectList.Create(True);
try
records := TObjectList.Create(True);
try
wmi := TWmi.Create;
try
if not CallWmi(['Name', 'ProcessId', 'ExecutablePath', 'CommandLine', 'SessionId', 'CreationDate']) then
Exit;
for i := 0 to records.Count - 1 do
begin
processInfo := TProcessInfo.Create;
Result.Add(processInfo);
values := TStringList(records[i]);
processInfo.Name := values[0];
processInfo.ProcessId := StrToInt(values[1]);
processInfo.ExecutablePath := values[2];
processInfo.CommandLine := values[3];
processInfo.SessionId := StrToInt(values[4]);
processInfo.CreationDate := values[5];
end;
finally
wmi.Free;
end;
finally
records.Free;
end;
except
//FreeAndNil(Result);
Result.Free;
raise;
end;
end;
end.
unit uStringUtils;
interface
uses
Classes;
procedure SplitString(const ASource: string; const ASeparator: Char; AValues: TStrings); overload;
function SplitString(const ASource: string; const ASeparator: Char): TStringList; overload;
function JoinStrings(const ASeparator: string; AValues: TStrings): string;
function CopyRange(const ASource: string; const AIndexFrom, AIndexTo: Integer): string;
type
TStringsHelper = class //poor man's helper :) ToDo should be other way around, naked routines calling the static class?
public
class procedure SplitString(const ASource: string; const ASeparator: Char; AValues: TStrings); overload;
class function SplitString(const ASource: string; const ASeparator: Char): TStringList; overload;
class function JoinStrings(const ASeparator: string; AValues: TStrings): string;
end;
type
TStringArray = array of string;
procedure FillStringList(const AValues: array of string; AStrings: TStrings);
function CreateStringList(const AValues: array of string): TStringList;
function CreateStringArray(const AStrings: array of string): TStringArray;
implementation
function CopyRange(const ASource: string; const AIndexFrom, AIndexTo: Integer): string;
begin
Result := Copy(ASource, AIndexFrom, AIndexTo - AIndexFrom + 1);
end;
procedure SplitString(const ASource: string; const ASeparator: Char; AValues: TStrings);
var
i, lastDelimPos: Integer;
begin
AValues.Clear;
lastDelimPos := 0;
for i := 1 to Length(ASource) do
if ASource[i] = ASeparator then
begin
if lastDelimPos = 0 then
AValues.Add(CopyRange(ASource, 1, i - 1))
else
AValues.Add(CopyRange(ASource, lastDelimPos + 1, i - 1));
lastDelimPos := i;
end;
if lastDelimPos = 0 then
AValues.Add(ASource)
else
AValues.Add(CopyRange(ASource, lastDelimPos + 1, MaxInt));
end;
function SplitString(const ASource: string; const ASeparator: Char): TStringList;
begin
Result := TStringList.Create;
SplitString(ASource, ASeparator, Result);
end;
function JoinStrings(const ASeparator: string; AValues: TStrings): string;
var
s: string;
i, len: Integer;
p: PChar;
begin
case AValues.Count of
0:
Result := '';
1:
Result := AValues[0];
else
len := (AValues.Count - 1) * Length(ASeparator);
for i := 0 to AValues.Count - 1 do
Inc(len, Length(AValues[i]));
SetLength(Result, len);
p := PChar(Result);
for i := 0 to AValues.Count - 1 do
begin
if i = 0 then
s := AValues[i]
else
s := ASeparator + AValues[i];
Move(PChar(s)^, p^, Length(s));
Inc(p, Length(s));
end;
end;
end;
{ TStringsHelper }
class procedure TStringsHelper.SplitString(const ASource: string;
const ASeparator: Char; AValues: TStrings);
begin
uStringUtils.SplitString(ASource, ASeparator, AValues); //Note the explicit unit reference
end;
class function TStringsHelper.SplitString(const ASource: string;
const ASeparator: Char): TStringList;
begin
Result := uStringUtils.SplitString(ASource, ASeparator); //Note the explicit unit reference
end;
class function TStringsHelper.JoinStrings(const ASeparator: string;
AValues: TStrings): string;
begin
Result := uStringUtils.JoinStrings(ASeparator, AValues); //Note the explicit unit reference
end;
procedure FillStringList(const AValues: array of string; AStrings: TStrings);
var
i: Integer;
begin
AStrings.Clear;
AStrings.Capacity := Length(AValues);
for i := 0 to Length(AValues) - 1 do
AStrings.Add(AValues[i]);
end;
function CreateStringList(const AValues: array of string): TStringList;
begin
Result := TStringList.Create;
FillStringList(AValues, Result);
end;
function CreateStringArray(const AStrings: array of string): TStringArray;
var
i: Integer;
begin
SetLength(Result, Length(AStrings));
for i := 0 to Length(AStrings) - 1 do
Result[i] := AStrings[i];
end;
end.
Related
I imported the WSDL file into the DLL, but when I try to send the file, I get the "Cannot read that as a ZipFile" error. When sending the file, it sends as Base64, I think the problem stems here. Is it possible to send the zip file directly as BinaryData?
Function code:
function EBookSender(kullaniciAdi, parola, dosyaYolu, yil: String;
periyod: Integer): String;
var
myService: IeBookService;
sessionCode: string;
files: TransferDocument;
Zip: TZipFile;
zipArray: TArray<System.Byte>;
loginResult: Boolean;
documentResult: Boolean;
documentNumber: String;
begin
try
try
myService := eBookService.GetIeBookService();
Zip := TZipFile.Create;
Zip.Open('C:\Temp\' + ExtractFileName(dosyaYolu).Split(['.'])[0] +
'.zip', zmWrite);
Zip.Add(dosyaYolu);
Zip.Close;
Zip.Open('C:\Temp\' + ExtractFileName(dosyaYolu).Split(['.'])[0] +
'.zip', zmRead);
Zip.Read(0, zipArray);
myService.Login(kullaniciAdi, parola, loginResult, sessionCode);
files := TransferDocument.Create;
files.Period := periyod;
files.Year := yil;
files.FileName := ExtractFileName(dosyaYolu).Split(['.'])[0] + '.zip';
files.BinaryData := zipArray;
myService.Transfer(sessionCode, files, documentResult, documentNumber);
// Result := sessionCode;
Result := loginResult.ToString();
except
on exp: Exception do
begin
Result := exp.Message;
end;
//
end;
finally
FreeAndNil(Zip);
end;
end;
I imported the WSDL file belonging to the SOAP service. The codes generated after importing are as follows:
unit eBookService;
interface
uses Soap.InvokeRegistry, Soap.SOAPHTTPClient, System.Types, Soap.XSBuiltIns;
const
IS_OPTN = $0001;
IS_NLBL = $0004;
IS_REF = $0080;
type
TransferDocument2 = class; { "http://schemas.datacontract.org/2004/07/eBook.WebService.Models"[GblCplx] }
TransferDocument = class; { "http://schemas.datacontract.org/2004/07/eBook.WebService.Models"[GblElm] }
DataModel2 = class; { "http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"[GblCplx] }
DataModel = class; { "http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"[GblElm] }
ConnectorAccountRights2 = class; { "http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"[GblCplx] }
ConnectorAccountRights = class; { "http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"[GblElm] }
{$SCOPEDENUMS ON}
{ "http://schemas.datacontract.org/2004/07/eBook.Common.Base"[GblSmpl] }
Enums_BookType = (Journal, Ledger);
{$SCOPEDENUMS OFF}
TransferDocument2 = class(TRemotable)
private
FBinaryData: TArray<System.Byte>;
FBinaryData_Specified: boolean;
FFileName: string;
FFileName_Specified: boolean;
FPeriod: Integer;
FPeriod_Specified: boolean;
FYear: string;
FYear_Specified: boolean;
function GetBinaryData(Index: Integer): TArray<System.Byte>;
procedure SetBinaryData(Index: Integer; const ATArray: TArray<System.Byte>);
function BinaryData_Specified(Index: Integer): boolean;
function GetFileName(Index: Integer): string;
procedure SetFileName(Index: Integer; const Astring: string);
function FileName_Specified(Index: Integer): boolean;
function GetPeriod(Index: Integer): Integer;
procedure SetPeriod(Index: Integer; const AInteger: Integer);
function Period_Specified(Index: Integer): boolean;
function GetYear(Index: Integer): string;
procedure SetYear(Index: Integer; const Astring: string);
function Year_Specified(Index: Integer): boolean;
published
property BinaryData: TArray<System.Byte> Index (IS_OPTN or IS_NLBL) read GetBinaryData write SetBinaryData stored BinaryData_Specified;
property FileName: string Index (IS_OPTN or IS_NLBL) read GetFileName write SetFileName stored FileName_Specified;
property Period: Integer Index (IS_OPTN) read GetPeriod write SetPeriod stored Period_Specified;
property Year: string Index (IS_OPTN or IS_NLBL) read GetYear write SetYear stored Year_Specified;
end;
TransferDocument = class(TransferDocument2)
private
published
end;
DataModel2 = class(TRemotable)
private
FData: TArray<System.Byte>;
FData_Specified: boolean;
FUniqueId: string;
FUniqueId_Specified: boolean;
function GetData(Index: Integer): TArray<System.Byte>;
procedure SetData(Index: Integer; const ATArray: TArray<System.Byte>);
function Data_Specified(Index: Integer): boolean;
function GetUniqueId(Index: Integer): string;
procedure SetUniqueId(Index: Integer; const Astring: string);
function UniqueId_Specified(Index: Integer): boolean;
published
property Data: TArray<System.Byte> Index (IS_OPTN or IS_NLBL) read GetData write SetData stored Data_Specified;
property UniqueId: string Index (IS_OPTN or IS_NLBL) read GetUniqueId write SetUniqueId stored UniqueId_Specified;
end;
DataModel = class(DataModel2)
private
published
end;
ConnectorAccountRights2 = class(TRemotable)
private
FAllowToUseEDefter: Boolean;
FAllowToUseEDefter_Specified: boolean;
FAllowToUseEDenetim: Boolean;
FAllowToUseEDenetim_Specified: boolean;
function GetAllowToUseEDefter(Index: Integer): Boolean;
procedure SetAllowToUseEDefter(Index: Integer; const ABoolean: Boolean);
function AllowToUseEDefter_Specified(Index: Integer): boolean;
function GetAllowToUseEDenetim(Index: Integer): Boolean;
procedure SetAllowToUseEDenetim(Index: Integer; const ABoolean: Boolean);
function AllowToUseEDenetim_Specified(Index: Integer): boolean;
published
property AllowToUseEDefter: Boolean Index (IS_OPTN) read GetAllowToUseEDefter write SetAllowToUseEDefter stored AllowToUseEDefter_Specified;
property AllowToUseEDenetim: Boolean Index (IS_OPTN) read GetAllowToUseEDenetim write SetAllowToUseEDenetim stored AllowToUseEDenetim_Specified;
end;
ConnectorAccountRights = class(ConnectorAccountRights2)
private
published
end;
IeBookService = interface(IInvokable)
['{2FAB8159-6B26-4D5E-B364-EA454B0B248A}']
procedure Login(const userName: string; const password: string; out LoginResult: Boolean; out sessionId: string); stdcall;
procedure Transfer(const sessionId: string; const document: TransferDocument2; out TransferResult: Boolean; out documentNumber: string); stdcall;
procedure TransferWithDivisionDays(const sessionId: string; const document: TransferDocument2; const startDay: Byte; const endDay: Byte; out TransferWithDivisionDaysResult: Boolean; out documentNumber: string
); stdcall;
function GetBookFiles(const sessionId: string; const BookType: Enums_BookType; const Period: Integer; const Year: string; out GetBookFilesResult: Boolean): DataModel2; stdcall;
function GetConnectorAccountRights(const sessionId: string): ConnectorAccountRights2; stdcall;
end;
function GetIeBookService(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): IeBookService;
implementation
uses System.SysUtils;
function GetIeBookService(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): IeBookService;
const
defWSDL = 'http://xxxx.com/eBookService.svc?wsdl';
defURL = 'http://xxxx.com/eBookService.svc';
defSvc = 'eBookService';
defPrt = 'CustomBinding_IeBookService';
var
RIO: THTTPRIO;
begin
Result := nil;
if (Addr = '') then
begin
if UseWSDL then
Addr := defWSDL
else
Addr := defURL;
end;
if HTTPRIO = nil then
RIO := THTTPRIO.Create(nil)
else
RIO := HTTPRIO;
try
Result := (RIO as IeBookService);
if UseWSDL then
begin
RIO.WSDLLocation := Addr;
RIO.Service := defSvc;
RIO.Port := defPrt;
end else
RIO.URL := Addr;
finally
if (Result = nil) and (HTTPRIO = nil) then
RIO.Free;
end;
end;
function TransferDocument2.GetBinaryData(Index: Integer): TArray<System.Byte>;
begin
Result := FBinaryData;
end;
procedure TransferDocument2.SetBinaryData(Index: Integer; const ATArray: TArray<System.Byte>);
begin
FBinaryData := ATArray;
FBinaryData_Specified := True;
end;
function TransferDocument2.BinaryData_Specified(Index: Integer): boolean;
begin
Result := FBinaryData_Specified;
end;
function TransferDocument2.GetFileName(Index: Integer): string;
begin
Result := FFileName;
end;
procedure TransferDocument2.SetFileName(Index: Integer; const Astring: string);
begin
FFileName := Astring;
FFileName_Specified := True;
end;
function TransferDocument2.FileName_Specified(Index: Integer): boolean;
begin
Result := FFileName_Specified;
end;
function TransferDocument2.GetPeriod(Index: Integer): Integer;
begin
Result := FPeriod;
end;
procedure TransferDocument2.SetPeriod(Index: Integer; const AInteger: Integer);
begin
FPeriod := AInteger;
FPeriod_Specified := True;
end;
function TransferDocument2.Period_Specified(Index: Integer): boolean;
begin
Result := FPeriod_Specified;
end;
function TransferDocument2.GetYear(Index: Integer): string;
begin
Result := FYear;
end;
procedure TransferDocument2.SetYear(Index: Integer; const Astring: string);
begin
FYear := Astring;
FYear_Specified := True;
end;
function TransferDocument2.Year_Specified(Index: Integer): boolean;
begin
Result := FYear_Specified;
end;
function DataModel2.GetData(Index: Integer): TArray<System.Byte>;
begin
Result := FData;
end;
procedure DataModel2.SetData(Index: Integer; const ATArray: TArray<System.Byte>);
begin
FData := ATArray;
FData_Specified := True;
end;
function DataModel2.Data_Specified(Index: Integer): boolean;
begin
Result := FData_Specified;
end;
function DataModel2.GetUniqueId(Index: Integer): string;
begin
Result := FUniqueId;
end;
procedure DataModel2.SetUniqueId(Index: Integer; const Astring: string);
begin
FUniqueId := Astring;
FUniqueId_Specified := True;
end;
function DataModel2.UniqueId_Specified(Index: Integer): boolean;
begin
Result := FUniqueId_Specified;
end;
function ConnectorAccountRights2.GetAllowToUseEDefter(Index: Integer): Boolean;
begin
Result := FAllowToUseEDefter;
end;
procedure ConnectorAccountRights2.SetAllowToUseEDefter(Index: Integer; const ABoolean: Boolean);
begin
FAllowToUseEDefter := ABoolean;
FAllowToUseEDefter_Specified := True;
end;
function ConnectorAccountRights2.AllowToUseEDefter_Specified(Index: Integer): boolean;
begin
Result := FAllowToUseEDefter_Specified;
end;
function ConnectorAccountRights2.GetAllowToUseEDenetim(Index: Integer): Boolean;
begin
Result := FAllowToUseEDenetim;
end;
procedure ConnectorAccountRights2.SetAllowToUseEDenetim(Index: Integer; const ABoolean: Boolean);
begin
FAllowToUseEDenetim := ABoolean;
FAllowToUseEDenetim_Specified := True;
end;
function ConnectorAccountRights2.AllowToUseEDenetim_Specified(Index: Integer): boolean;
begin
Result := FAllowToUseEDenetim_Specified;
end;
initialization
{ IeBookService }
InvRegistry.RegisterInterface(TypeInfo(IeBookService), 'http://tempuri.org/', 'utf-8');
InvRegistry.RegisterDefaultSOAPAction(TypeInfo(IeBookService), 'http://tempuri.org/IeBookService/%operationName%');
InvRegistry.RegisterInvokeOptions(TypeInfo(IeBookService), ioDocument);
InvRegistry.RegisterInvokeOptions(TypeInfo(IeBookService), ioSOAP12);
{ IeBookService.Login }
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Login', 'userName', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Login', 'password', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Login', 'sessionId', '',
'', IS_NLBL);
{ IeBookService.Transfer }
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Transfer', 'sessionId', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Transfer', 'document', '',
'[Namespace="http://schemas.datacontract.org/2004/07/eBook.WebService.Models"]', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'Transfer', 'documentNumber', '',
'', IS_NLBL);
{ IeBookService.TransferWithDivisionDays }
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'TransferWithDivisionDays', 'sessionId', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'TransferWithDivisionDays', 'document', '',
'[Namespace="http://schemas.datacontract.org/2004/07/eBook.WebService.Models"]', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'TransferWithDivisionDays', 'documentNumber', '',
'', IS_NLBL);
{ IeBookService.GetBookFiles }
InvRegistry.RegisterMethodInfo(TypeInfo(IeBookService), 'GetBookFiles', '',
'[ReturnName="result"]', IS_OPTN or IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetBookFiles', 'sessionId', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetBookFiles', 'BookType', '',
'[Namespace="http://schemas.datacontract.org/2004/07/eBook.Common.Base"]');
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetBookFiles', 'Year', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetBookFiles', 'result', '',
'[Namespace="http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"]', IS_NLBL);
{ IeBookService.GetConnectorAccountRights }
InvRegistry.RegisterMethodInfo(TypeInfo(IeBookService), 'GetConnectorAccountRights', '',
'[ReturnName="GetConnectorAccountRightsResult"]', IS_OPTN or IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetConnectorAccountRights', 'sessionId', '',
'', IS_NLBL);
InvRegistry.RegisterParamInfo(TypeInfo(IeBookService), 'GetConnectorAccountRights', 'GetConnectorAccountRightsResult', '',
'[Namespace="http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models"]', IS_NLBL);
RemClassRegistry.RegisterXSClass(TransferDocument2, 'http://schemas.datacontract.org/2004/07/eBook.WebService.Models', 'TransferDocument2', 'TransferDocument');
RemClassRegistry.RegisterXSClass(TransferDocument, 'http://schemas.datacontract.org/2004/07/eBook.WebService.Models', 'TransferDocument');
RemClassRegistry.RegisterXSInfo(TypeInfo(Enums_BookType), 'http://schemas.datacontract.org/2004/07/eBook.Common.Base', 'Enums_BookType', 'Enums.BookType');
RemClassRegistry.RegisterXSClass(DataModel2, 'http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models', 'DataModel2', 'DataModel');
RemClassRegistry.RegisterXSClass(DataModel, 'http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models', 'DataModel');
RemClassRegistry.RegisterXSClass(ConnectorAccountRights2, 'http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models', 'ConnectorAccountRights2', 'ConnectorAccountRights');
RemClassRegistry.RegisterXSClass(ConnectorAccountRights, 'http://schemas.datacontract.org/2004/07/eBook.Services.Core.Models', 'ConnectorAccountRights');
end.
Why do we need so much code just to tell the debugger to call .ToString on some objects?
I looked at the included examples under C:\Program Files (x86)\Embarcadero\Studio\19.0\source\Visualizers and adapted them for one of my objects - it works well.
Is there an easier way?
unit uMyObjectDebugVisualizer;
interface
procedure Register;
implementation
uses
Classes, Forms, SysUtils, ToolsAPI;
type
TMyObjectDebugVisualizer = class(TInterfacedObject,
IOTADebuggerVisualizer, IOTADebuggerVisualizerValueReplacer, IOTAThreadNotifier, IOTAThreadNotifier160)
private
FNotifierIndex: Integer;
FCompleted: Boolean;
FDeferredResult: string;
public
{ IOTADebuggerVisualizer }
function GetSupportedTypeCount: Integer;
procedure GetSupportedType(Index: Integer; var TypeName: string;
var AllDescendants: Boolean);
function GetVisualizerIdentifier: string;
function GetVisualizerName: string;
function GetVisualizerDescription: string;
{ IOTADebuggerVisualizerValueReplacer }
function GetReplacementValue(const Expression, TypeName, EvalResult: string): string;
{ IOTAThreadNotifier }
procedure EvaluteComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: Cardinal; ResultSize: Cardinal;
ReturnCode: Integer);
procedure ModifyComplete(const ExprStr: string; const ResultStr: string;
ReturnCode: Integer);
procedure ThreadNotify(Reason: TOTANotifyReason);
procedure AfterSave;
procedure BeforeSave;
procedure Destroyed;
procedure Modified;
{ IOTAThreadNotifier160 }
procedure EvaluateComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
end;
{ TMyObjectDebugVisualizer }
procedure TMyObjectDebugVisualizer.AfterSave;
begin
// don't care about this notification
end;
procedure TMyObjectDebugVisualizer.BeforeSave;
begin
// don't care about this notification
end;
procedure TMyObjectDebugVisualizer.Destroyed;
begin
// don't care about this notification
end;
procedure TMyObjectDebugVisualizer.Modified;
begin
// don't care about this notification
end;
procedure TMyObjectDebugVisualizer.ModifyComplete(const ExprStr, ResultStr: string; ReturnCode: Integer);
begin
// don't care about this notification
end;
procedure TMyObjectDebugVisualizer.EvaluteComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress, ResultSize: Cardinal;
ReturnCode: Integer);
begin
EvaluateComplete(ExprStr, ResultStr, CanModify, TOTAAddress(ResultAddress),
LongWord(ResultSize), ReturnCode);
end;
procedure TMyObjectDebugVisualizer.EvaluateComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
begin
FCompleted:= True;
if ReturnCode = 0 then
FDeferredResult:= ResultStr;
end;
procedure TMyObjectDebugVisualizer.ThreadNotify(Reason: TOTANotifyReason);
begin
// don't care about this notification
end;
function TMyObjectDebugVisualizer.GetReplacementValue(const Expression, TypeName, EvalResult: string): string;
var
CurProcess: IOTAProcess;
CurThread: IOTAThread;
ResultStr: array[0..255] of Char;
CanModify: Boolean;
ResultAddr, ResultSize, ResultVal: LongWord;
EvalRes: TOTAEvaluateResult;
DebugSvcs: IOTADebuggerServices;
Done: Boolean;
begin
Result:= EvalResult;
if Supports(BorlandIDEServices, IOTADebuggerServices, DebugSvcs) then CurProcess:= DebugSvcs.CurrentProcess;
if (CurProcess <> nil) and (CurProcess.GetProcessType <> optOSX32) then begin
CurThread:= CurProcess.CurrentThread;
if CurThread <> nil then repeat
Done:= True;
EvalRes:= CurThread.Evaluate(Expression + '.ToString', #ResultStr, Length(ResultStr),
CanModify, eseAll, '', ResultAddr, ResultSize, ResultVal, '', 0);
case EvalRes of
erOK: Result:= ResultStr;
erDeferred: begin
FCompleted:= False;
FDeferredResult:= '';
FNotifierIndex:= CurThread.AddNotifier(Self);
while not FCompleted do DebugSvcs.ProcessDebugEvents;
CurThread.RemoveNotifier(FNotifierIndex);
FNotifierIndex:= -1;
if FDeferredResult <> '' then Result:= FDeferredResult
else Result:= EvalResult;
end;
erBusy: begin
DebugSvcs.ProcessDebugEvents;
Done:= False;
end;
end;
until Done;
end;
end;
function TMyObjectDebugVisualizer.GetSupportedTypeCount: Integer;
begin
Result:= 1;
end;
procedure TMyObjectDebugVisualizer.GetSupportedType(Index: Integer; var TypeName: string; var AllDescendants: Boolean);
begin
AllDescendants:= True;
TypeName:= 'TMyObject';
end;
function TMyObjectDebugVisualizer.GetVisualizerDescription: string;
begin
Result:= 'Displays TMyObject objects';
end;
function TMyObjectDebugVisualizer.GetVisualizerIdentifier: string;
begin
Result:= ClassName;
end;
function TMyObjectDebugVisualizer.GetVisualizerName: string;
begin
Result:= 'TMyObject Visualizer for Delphi';
end;
var
MyObjectVis: IOTADebuggerVisualizer;
procedure Register;
begin
MyObjectVis:= TMyObjectDebugVisualizer.Create;
(BorlandIDEServices as IOTADebuggerServices).RegisterDebugVisualizer(MyObjectVis);
end;
procedure RemoveVisualizer;
var
DebuggerServices: IOTADebuggerServices;
begin
if Supports(BorlandIDEServices, IOTADebuggerServices, DebuggerServices) then begin
DebuggerServices.UnregisterDebugVisualizer(MyObjectVis);
MyObjectVis:= nil;
end;
end;
initialization
finalization
RemoveVisualizer;
end.
To get this working, you have to add it to a package. Also add "designide.dcp" to the required packages.
I have a working debugger visualizer that helps visualize variables of type TIntTime.
TIntTime = type integer;
The visualizer replaces a number of seconds since midnight with a time string HH:MM:SS. This works fine on variables of type TIntTime during a debug session, but not on functions. For example if I place GetiTime in a watch
function GetiTime: TIntTime;
begin
Result:=30000;
end;
the watch will show 30000. The expected replaced value is '08:20:00'. The visualizer does not intercept function return values of type TIntTime and this is the problem.
I am using Delphi 10 Seattle. My visualizer is based on DateTimeVisualizer.pas found in Delphi 10\source\Visualizers. The DateTimeVisualizer suggests that function return values are intercepted by using the type name string 'function: TIntTime' in GetSupportedType. I have tried
'function: TIntTime'
'function:TIntTime'
'function::TIntTime'
without luck. I suspect it is a question of getting this type name string correct, but haven't been able to find information about the formatting on the internet.
If I instead place GetDateTime in a watch it shows '14-02-2018 13:20:30' as expected. If I switch the TDateTime/TDate/TTime visualizer off in options the watch shows 43145.5559... This tells me that it is possible to intercept function return values with a visualizer.
function GetDateTime: TDateTime;
begin
Result:=EncodeDateTime(2018,2,14,13,20,30,0);
end;
In my case it is not an option to use the TDateTime data type. So my question is: how can I get my visualizer to intercept function return values of type TIntTime?
Below is the source for the TIntTime visualizer
unit IntTimeVisualizer;
interface
procedure Register;
implementation
uses
Classes, Forms, SysUtils, ToolsAPI;
resourcestring
sIntTimeVisualizerName = 'TIntTime Visualizer for Delphi';
sIntTimeVisualizerDescription = 'Displays TIntTime instances in a human-readable time format rather than as an integer value';
type
TDebuggerIntTimeVisualizer = class(TInterfacedObject, IOTADebuggerVisualizer,
IOTADebuggerVisualizerValueReplacer, IOTAThreadNotifier, IOTAThreadNotifier160)
private
FCompleted: Boolean;
FDeferredResult: string;
public
{ IOTADebuggerVisualizer }
function GetSupportedTypeCount: Integer;
procedure GetSupportedType(Index: Integer; var TypeName: string;
var AllDescendants: Boolean);
function GetVisualizerIdentifier: string;
function GetVisualizerName: string;
function GetVisualizerDescription: string;
{ IOTADebuggerVisualizerValueReplacer }
function GetReplacementValue(const Expression, TypeName, EvalResult: string): string;
{ IOTAThreadNotifier }
procedure EvaluteComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: Cardinal; ResultSize: Cardinal;
ReturnCode: Integer);
procedure ModifyComplete(const ExprStr: string; const ResultStr: string;
ReturnCode: Integer);
procedure ThreadNotify(Reason: TOTANotifyReason);
procedure AfterSave;
procedure BeforeSave;
procedure Destroyed;
procedure Modified;
{ IOTAThreadNotifier160 }
procedure EvaluateComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
end;
TIntTimeType = (dttIntTime);
TIntTimeVisualizerType = record
TypeName: string;
TimeType: TIntTimeType;
end;
const
IntTimeVisualizerTypes: array[0..1] of TIntTimeVisualizerType =
(
(TypeName: 'TIntTime'; TimeType: dttIntTime;), //<-- This type is working fine
(TypeName: 'function: TIntTime'; TimeType: dttIntTime;) //<-- This type is not working
);
{ TDebuggerIntTimeVisualizer }
procedure TDebuggerIntTimeVisualizer.AfterSave;
begin
// don't care about this notification
end;
procedure TDebuggerIntTimeVisualizer.BeforeSave;
begin
// don't care about this notification
end;
procedure TDebuggerIntTimeVisualizer.Destroyed;
begin
// don't care about this notification
end;
procedure TDebuggerIntTimeVisualizer.Modified;
begin
// don't care about this notification
end;
procedure TDebuggerIntTimeVisualizer.ModifyComplete(const ExprStr,
ResultStr: string; ReturnCode: Integer);
begin
// don't care about this notification
end;
procedure TDebuggerIntTimeVisualizer.EvaluteComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress, ResultSize: Cardinal;
ReturnCode: Integer);
begin
EvaluateComplete(ExprStr, ResultStr, CanModify, TOTAAddress(ResultAddress),
LongWord(ResultSize), ReturnCode);
end;
procedure TDebuggerIntTimeVisualizer.EvaluateComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
begin
FCompleted := True;
if ReturnCode = 0 then
FDeferredResult := ResultStr;
end;
procedure TDebuggerIntTimeVisualizer.ThreadNotify(Reason: TOTANotifyReason);
begin
// don't care about this notification
end;
function TDebuggerIntTimeVisualizer.GetReplacementValue(
const Expression, TypeName, EvalResult: string): string;
var
TimeType: TIntTimeType;
I: Integer;
function IntTimeToStr(s: Integer): string;
var
hh, mm, ss: integer;
begin
hh:=s div 3600;
mm:=(s div 60)-hh*60;
ss:=s mod 60;
Result:=Format('%.2d:%.2d:%.2d',[hh,mm,ss]);
end;
function FormatResult(const LEvalResult: string; DTType: TIntTimeType; out ResStr: string): Boolean;
var
IntValue: integer;
begin
Result := True;
try
if not TryStrToInt(LEvalResult, IntValue) then
Result:=false
else
case DTType of
dttIntTime: ResStr:=IntTimeToStr(IntValue);
end;
except
Result := False;
end;
end;
begin
TimeType := TIntTimeType(-1);
for I := Low(IntTimeVisualizerTypes) to High(IntTimeVisualizerTypes) do begin
if TypeName = IntTimeVisualizerTypes[I].TypeName then begin
TimeType:=IntTimeVisualizerTypes[I].TimeType;
Break;
end;
end;
if not FormatResult(EvalResult, TimeType, Result) then
Result := EvalResult;
end;
function TDebuggerIntTimeVisualizer.GetSupportedTypeCount: Integer;
begin
Result := Length(IntTimeVisualizerTypes);
end;
procedure TDebuggerIntTimeVisualizer.GetSupportedType(Index: Integer; var TypeName: string;
var AllDescendants: Boolean);
begin
AllDescendants := false;
TypeName := IntTimeVisualizerTypes[Index].TypeName;
end;
function TDebuggerIntTimeVisualizer.GetVisualizerDescription: string;
begin
Result := sIntTimeVisualizerDescription;
end;
function TDebuggerIntTimeVisualizer.GetVisualizerIdentifier: string;
begin
Result := ClassName;
end;
function TDebuggerIntTimeVisualizer.GetVisualizerName: string;
begin
Result := sIntTimeVisualizerName;
end;
var
IntTimeVis: IOTADebuggerVisualizer;
procedure Register;
begin
IntTimeVis:=TDebuggerIntTimeVisualizer.Create;
(BorlandIDEServices as IOTADebuggerServices).RegisterDebugVisualizer(IntTimeVis);
end;
procedure RemoveVisualizer;
var
DebuggerServices: IOTADebuggerServices;
begin
if Supports(BorlandIDEServices, IOTADebuggerServices, DebuggerServices) then begin
DebuggerServices.UnregisterDebugVisualizer(IntTimeVis);
IntTimeVis:=nil;
end;
end;
initialization
finalization
RemoveVisualizer;
end.
I use UploadObject in unit Data.Cloud.AmazonAPI to upload some files to the AWS S3,and it works,but how can I get the progress information while uploading or downloading? It's my code:
function TForm1.UploadFile(LocalFilePath: string; RemoteFileName: string; Bucket: string): Boolean;
var
Service: TAmazonStorageService;
ConAmazon: TAmazonConnectionInfo;
FS: TFileStream;
Content: TBytes;
begin
try
ConAmazon := TAmazonConnectionInfo.Create(nil);
ConAmazon.AccountKey := 'MtJqIM7WyjJA*********************';
ConAmazon.AccountName := 'AKIAIXVAH*********';
ConAmazon.QueueEndpoint := 'queue.amazonaws.com';
ConAmazon.StorageEndpoint := 's3-eu-west-1.amazonaws.com';
ConAmazon.TableEndpoint := 'sdb.amazonaws.com';
ConAmazon.UseDefaultEndpoints := True;
Service := TAmazonStorageService.Create(ConAmazon);
if FileExists(LocalFilePath) then
begin
FS := TFileStream.Create(LocalFilePath, fmOpenRead);
Content := ByteContent(FS);
FS.Free;
Result := Service.UploadObject(Bucket, RemoteFileName, Content, True, nil, nil, amzbaPrivate, nil, OnProgress);
end
else
Result := False;
finally
ConAmazon.Free;
Service.Free;
end;
end;
For downloading check this question Can I monitor the progress of an S3 download using the Cloud.AmazonAPI?
For uploading it is similar but you'll need to create TAmazonStorageService subclass as following
type
TProgressAmazonStorageService = class(TAmazonStorageService)
function IssuePutRequest(URL: string; Headers: TStringList;
QueryParameters: TStringList; const QueryPrefix: string;
ResponseInfo: TCloudResponseInfo;
Content: TStream; out ResponseString: string): TCloudHTTP; overload; override;
end;
function TProgressAmazonStorageService.IssuePutRequest(URL: string; Headers: TStringList;
QueryParameters: TStringList; const QueryPrefix: string;
ResponseInfo: TCloudResponseInfo;
Content: TStream; out ResponseString: string): TCloudHTTP;
var
ProgressStream: TProgressStream;
begin
Result := PrepareRequest('PUT', Headers, QueryParameters, QueryPrefix, URL);
try
ProgressStream := TProgressStream.Create(Content);
try
ProgressStream.OnProgress := Form1.OnProgress;
Form1.ProgressBar1.Max := Content.Size;
Form1.ProgressBar1.Value := 0;
if Content <> nil then
ResponseString := Result.Put(URL, ProgressStream)
else
ResponseString := Result.Put(URL);
finally
ProgressStream.Free;
end;
PopulateResponseInfo(Result, ResponseInfo);
except
on E: Exception do
begin
Result.Free;
Raise;
end;
end;
end;
progress function looks like this
procedure TForm1.OnProgress(const ACount: Int64);
begin
Form1.ProgressBar1.Value := Form1.ProgressBar1.Value + ACount;
Application.ProcessMessages;
end;
and TProgressStream like this
type
TOnProgressEvent = procedure(const ACount: Int64) of object;
TProgressStream = class(TStream)
strict private
FStream: TStream;
protected
function GetSize: Int64; override;
procedure SetSize(NewSize: Longint); overload; override;
procedure SetSize(const NewSize: Int64); overload; override;
public
OnProgress: TOnProgressEvent;
function Read(var Buffer; Count: Longint): Longint; overload; override;
function Write(const Buffer; Count: Longint): Longint; overload; override;
function Read(Buffer: TBytes; Offset, Count: Longint): Longint; overload; override;
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; overload; override;
function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
constructor Create(const AStream: TStream);
end;
constructor TProgressStream.Create(const AStream: TStream);
begin
FStream := AStream;
end;
function TProgressStream.GetSize: Int64;
begin
Result := FStream.Size;
end;
procedure TProgressStream.SetSize(NewSize: Longint);
begin
FStream.Size := NewSize;
end;
procedure TProgressStream.SetSize(const NewSize: Int64);
begin
FStream.Size := NewSize;
end;
function TProgressStream.Read(var Buffer; Count: Longint): Longint;
begin
Result := FStream.Read(Buffer, Count);
end;
function TProgressStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := FStream.Write(Buffer, Count);
end;
function TProgressStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint;
begin
if Assigned(OnProgress) then
begin
OnProgress(Count);
end;
Result := FStream.Read(Buffer, Offset, Count);
end;
function TProgressStream.Write(const Buffer: TBytes; Offset, Count: Longint): Longint;
begin
Result := FStream.Write(Buffer, Offset, Count);
end;
function TProgressStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
Result := FStream.Seek(Offset, Origin);
end;
function TProgressStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
Result := FStream.Seek(Offset, Origin);
end;
Here a unit i can't get working properly on Delphi 2009. I give you the original code that correctly transmit data when compiled with Delphi 2007. Ansifying the code for Delphi 2009 gives me a connection to the server but no data is transmitted and no feedback). Thanks.
unit SMTP_Connections2007;
// *********************************************************************
// Unit Name : SMTP_Connections *
// Author : Melih SARICA (Non ZERO) *
// Date : 01/17/2004 *
//**********************************************************************
interface
uses
Classes, StdCtrls;
const
WinSock = 'wsock32.dll';
Internet = 2;
Stream = 1;
fIoNbRead = $4004667F;
WinSMTP = $0001;
LinuxSMTP = $0002;
type
TWSAData = packed record
wVersion: Word;
wHighVersion: Word;
szDescription: array[0..256] of Char;
szSystemStatus: array[0..128] of Char;
iMaxSockets: Word;
iMaxUdpDg: Word;
lpVendorInfo: PChar;
end;
PHost = ^THost;
THost = packed record
Name: PChar;
aliases: ^PChar;
addrtype: Smallint;
Length: Smallint;
addr: ^Pointer;
end;
TSockAddr = packed record
Family: Word;
Port: Word;
Addr: Longint;
Zeros: array[0..7] of Byte;
end;
function WSAStartup(Version:word; Var Data:TwsaData):integer; stdcall; far; external winsock;
function socket(Family,Kind,Protocol:integer):integer; stdcall; far; external winsock;
function shutdown(Socket,How:Integer):integer; stdcall; far; external winsock;
function closesocket(socket:Integer):integer; stdcall; far; external winsock;
function WSACleanup:integer; stdcall; far; external winsock;
function bind(Socket:Integer; Var SockAddr:TSockAddr; AddrLen:integer):integer; stdcall; far; external winsock;
function listen(socket,flags:Integer):integer; stdcall; far; external winsock;
function connect(socket:Integer; Var SockAddr:TSockAddr; AddrLen:integer):integer; stdcall; far; external winsock;
function accept(socket:Integer; Var SockAddr:TSockAddr; Var AddrLen:Integer):integer; stdcall; far; external winsock;
function WSAGetLastError:integer; stdcall; far; external winsock;
function recv(socket:integer; data:pchar; datalen,flags:integer):integer; stdcall; far; external winsock;
function send(socket:integer; var data; datalen,flags:integer):integer; stdcall; far; external winsock;
function gethostbyname(HostName:PChar):PHost; stdcall; far; external winsock;
function WSAIsBlocking:boolean; stdcall; far; external winsock;
function WSACancelBlockingCall:integer; stdcall; far; external winsock;
function ioctlsocket(socket:integer; cmd: Longint; var arg: longint): Integer; stdcall; far; external winsock;
function gethostname(name:pchar; size:integer):integer; stdcall; far; external winsock;
procedure _authSendMail(MailServer,uname,upass,mFrom,mFromName,mToName,Subject:string;mto,mbody:TStringList);
function ConnectServer(mhost:string;mport:integer):integer;
function ConnectServerwin(mhost:string;mport:integer):integer;
function DisConnectServer:integer;
function Stat: string;
function SendCommand(Command: String): string;
function SendData(Command: String): string;
function SendCommandWin(Command: String): string;
function ReadCommand: string;
function encryptB64(s:string):string;
var
mconnHandle: Integer;
mFin, mFOut: Textfile;
EofSock: Boolean;
mactive: Boolean;
mSMTPErrCode: Integer;
mSMTPErrText: string;
mMemo: TMemo;
implementation
uses
SysUtils, Sockets, IdBaseComponent,
IdCoder, IdCoder3to4, IdCoderMIME, IniFiles,Unit1;
var
mClient: TTcpClient;
procedure _authSendMail(MailServer, uname, upass, mFrom, mFromName,
mToName, Subject: string; mto, mbody: TStringList);
var
tmpstr: string;
cnt: Integer;
mstrlist: TStrings;
RecipientCount: Integer;
begin
if ConnectServerWin(Mailserver, 587) = 250 then //port is 587
begin
Sendcommandwin('AUTH LOGIN ');
SendcommandWin(encryptB64(uname));
SendcommandWin(encryptB64(upass));
SendcommandWin('MAIL FROM: ' + mfrom);
for cnt := 0 to mto.Count - 1 do
SendcommandWin('RCPT TO: ' + mto[cnt]);
Sendcommandwin('DATA');
SendData('Subject: ' + Subject);
SendData('From: "' + mFromName + '" <' + mfrom + '>');
SendData('To: ' + mToName);
SendData('Mime-Version: 1.0');
SendData('Content-Type: multipart/related; boundary="Esales-Order";');
SendData(' type="text/html"');
SendData('');
SendData('--Esales-Order');
SendData('Content-Type: text/html;');
SendData(' charset="iso-8859-9"');
SendData('Content-Transfer-Encoding: QUOTED-PRINTABLE');
SendData('');
for cnt := 0 to mbody.Count - 1 do
SendData(mbody[cnt]);
Senddata('');
SendData('--Esales-Order--');
Senddata(' ');
mSMTPErrText := SendCommand(crlf + '.' + crlf);
try
mSMTPErrCode := StrToInt(Copy(mSMTPErrText, 1, 3));
except
end;
SendData('QUIT');
DisConnectServer;
end;
end;
function Stat: string;
var
s: string;
begin
s := ReadCommand;
Result := s;
end;
function EchoCommand(Command: string): string;
begin
SendCommand(Command);
Result := ReadCommand;
end;
function ReadCommand: string;
var
tmp: string;
begin
repeat
ReadLn(mfin, tmp);
if Assigned(mmemo) then
mmemo.Lines.Add(tmp);
until (Length(tmp) < 4) or (tmp[4] <> '-');
Result := tmp
end;
function SendData(Command: string): string;
begin
Writeln(mfout, Command);
end;
function SendCommand(Command: string): string;
begin
Writeln(mfout, Command);
Result := stat;
end;
function SendCommandWin(Command: string): string;
begin
Writeln(mfout, Command + #13);
Result := stat;
end;
function FillBlank(Source: string; number: Integer): string;
var
a: Integer;
begin
Result := '';
for a := Length(trim(Source)) to number do
Result := Result + ' ';
end;
function IpToLong(ip: string): Longint;
var
x, i: Byte;
ipx: array[0..3] of Byte;
v: Integer;
begin
Result := 0;
Longint(ipx) := 0;
i := 0;
for x := 1 to Length(ip) do
if ip[x] = '.' then
begin
Inc(i);
if i = 4 then Exit;
end
else
begin
if not (ip[x] in ['0'..'9']) then Exit;
v := ipx[i] * 10 + Ord(ip[x]) - Ord('0');
if v > 255 then Exit;
ipx[i] := v;
end;
Result := Longint(ipx);
end;
function HostToLong(AHost: string): Longint;
var
Host: PHost;
begin
Result := IpToLong(AHost);
if Result = 0 then
begin
Host := GetHostByName(PChar(AHost));
if Host <> nil then Result := Longint(Host^.Addr^^);
end;
end;
function LongToIp(Long: Longint): string;
var
ipx: array[0..3] of Byte;
i: Byte;
begin
Longint(ipx) := long;
Result := '';
for i := 0 to 3 do Result := Result + IntToStr(ipx[i]) + '.';
SetLength(Result, Length(Result) - 1);
end;
procedure Disconnect(Socket: Integer);
begin
ShutDown(Socket, 1);
CloseSocket(Socket);
end;
function CallServer(Server: string; Port: Word): Integer;
var
SockAddr: TSockAddr;
begin
Result := socket(Internet, Stream, 0);
if Result = -1 then Exit;
FillChar(SockAddr, SizeOf(SockAddr), 0);
SockAddr.Family := Internet;
SockAddr.Port := swap(Port);
SockAddr.Addr := HostToLong(Server);
if Connect(Result, SockAddr, SizeOf(SockAddr)) <> 0 then
begin
Disconnect(Result);
Result := -1;
end;
end;
function OutputSock(var F: TTextRec): Integer; far;
begin
if F.BufPos <> 0 then
begin
Send(F.Handle, F.BufPtr^, F.BufPos, 0);
F.BufPos := 0;
end;
Result := 0;
end;
function InputSock(var F: TTextRec): Integer; far;
var
Size: Longint;
begin
F.BufEnd := 0;
F.BufPos := 0;
Result := 0;
repeat
if (IoctlSocket(F.Handle, fIoNbRead, Size) < 0) then
begin
EofSock := True;
Exit;
end;
until (Size >= 0);
F.BufEnd := Recv(F.Handle, F.BufPtr, F.BufSize, 0);
EofSock := (F.Bufend = 0);
end;
function CloseSock(var F: TTextRec): Integer; far;
begin
Disconnect(F.Handle);
F.Handle := -1;
Result := 0;
end;
function OpenSock(var F: TTextRec): Integer; far;
begin
if F.Mode = fmInput then
begin
EofSock := False;
F.BufPos := 0;
F.BufEnd := 0;
F.InOutFunc := #InputSock;
F.FlushFunc := nil;
end
else
begin
F.Mode := fmOutput;
F.InOutFunc := #OutputSock;
F.FlushFunc := #OutputSock;
end;
F.CloseFunc := #CloseSock;
Result := 0;
end;
procedure AssignCrtSock(Socket:integer; Var Input,Output:TextFile);
begin
with TTextRec(Input) do
begin
Handle := Socket;
Mode := fmClosed;
BufSize := SizeOf(Buffer);
BufPtr := #Buffer;
OpenFunc := #OpenSock;
end;
with TTextRec(Output) do
begin
Handle := Socket;
Mode := fmClosed;
BufSize := SizeOf(Buffer);
BufPtr := #Buffer;
OpenFunc := #OpenSock;
end;
Reset(Input);
Rewrite(Output);
end;
function ConnectServer(mhost: string; mport: Integer): Integer;
var
tmp: string;
begin
mClient := TTcpClient.Create(nil);
mClient.RemoteHost := mhost;
mClient.RemotePort := IntToStr(mport);
mClient.Connect;
mconnhandle := callserver(mhost, mport);
if (mconnHandle<>-1) then
begin
AssignCrtSock(mconnHandle, mFin, MFout);
tmp := stat;
tmp := SendCommand('HELO bellona.com.tr');
if Copy(tmp, 1, 3) = '250' then
begin
Result := StrToInt(Copy(tmp, 1, 3));
end;
end;
end;
function ConnectServerWin(mhost: string; mport: Integer): Integer;
var
tmp: string;
begin
mClient := TTcpClient.Create(nil);
mClient.RemoteHost := mhost;
mClient.RemotePort := IntToStr(mport);
mClient.Connect;
mconnhandle := callserver(mhost, mport);
if (mconnHandle<>-1) then
begin
AssignCrtSock(mconnHandle, mFin, MFout);
tmp := stat;
tmp := SendCommandWin('HELO bellona.com.tr');
if Copy(tmp, 1, 3) = '250' then
begin
Result := StrToInt(Copy(tmp, 1, 3));
end;
end;
end;
function DisConnectServer: Integer;
begin
closesocket(mconnhandle);
mClient.Disconnect;
mclient.Free;
end;
function encryptB64(s: string): string;
var
hash1: TIdEncoderMIME;
p: string;
begin
if s <> '' then
begin
hash1 := TIdEncoderMIME.Create(nil);
p := hash1.Encode(s);
hash1.Free;
end;
Result := p;
end;
end.
Here some code to give it a try:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
// Button1: TButton;
// Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
SMTP_Connections2007;
procedure TForm1.Button1Click(Sender: TObject);
var
mto, mbody: TStringList;
MailServer, uname, upass, mFrom, mFromName,
mToName, Subject: string;
begin
mMemo := Memo1; // to output server feedback
//..........................
MailServer := 'somename.servername';
uname := 'username';
upass := 'password';
mFrom := 'someuser#xyz.net';
mFromName := 'forename surname';
mToName := '';
Subject := 'Your Subject';
//..........................
mto := TStringList.Create;
mbody := TStringList.Create;
try
mto.Add('destination_emailaddress');
mbody.Add('Test Mail');
//Send Mail.................
_authSendMail(MailServer, uname, upass, mFrom, mFromName, mToName, Subject, mto, mbody);
//..........................
finally
mto.Free;
mbody.Free;
end;
end;
end.
I ansified your code, and tested it with Delphi2009, it works without any problem. I've managed to send email from gmx.com to mail.google.com.
I did change string to AnsiString, Char to AnsiChar, and PChar to PAnsiChar.
Maybe you simply forgot to ansify Char or PChar?
One thing to consider would be the TCP/IP library Synapse, of which the latest development version in SVN compiles and runs against Delphi 2009 with Unicode and
has all of the functionality in your unit and can easily perform the steps of your test program.