Short Version
How to perform svn cat in Delphi's subversion api wrapper?
Long Version
I want to get the contents of a file in subversion.
Here's a random public repository that lets you cat (i.e. display the contents of) a file:
>svn cat http://svn.code.sf.net/p/unicon/code/trunk/unicon/README
Unicon 13.x README
This is the Unicon distribution. Please tell us
where it compiles, and when and how it doesn't.
...snip...
So that works.
Now how to do it in Delphi?
How do i read the contents (i.e. cat) a file in Delphi's Subversion API wrapper?
Based on this Stackoverflow answer, i try:
SvnItem: TSvnItem;
SvnItem := TSvnItem.Create(SvnClient, nil, 'http://svn.code.sf.net/p/unicon/code/trunk/unicon/README');
Unfortunately the call to create a TSvnItem throws an exception:
EAprError: The given path is misformatted or contained invalid characters
So what am i doing wrong?
For The Lazy
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, SvnClient;
procedure Main;
var
SvnClient: TSvnClient;
SvnItem: TSvnItem;
url: string;
begin
// Set the global variable where the subversion DLLs can be found.
BaseDllDir := ExtractFilePath(ParamStr(0)); //Setting a global variable (yes, seriously)
SvnClient := TSvnClient.Create;
SvnClient.Initialize;
url := 'http://svn.code.sf.net/p/unicon/code/trunk/unicon/README';
SvnItem := TSvnItem.Create(SvnClient, nil, url);
end;
begin
try
Main;
except
on E: Exception do
begin
ExitCode := 1;
Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
end;
end;
end.
See also
https://sourceforge.net/projects/radstudioverins/
Programmatically adding, deleting and committing files into a subversion repository using Delphi
reading SVN:externals from working copy
It's not an answer; but it is a workaround for the next guy.
Thing that should work (but doesn't)
function GetSubversionFileContents(Client: TSvnClient; Url: string): TStream;
var
item: TSvnItem;
buffer: TBytes;
ms: TMemoryStream;
begin
//WARNING: This code doesn't work.
item := TSvnItem.Create(Client, nil, Url); //throws exception
buffer := item.GetBaseFile;
item.Free;
ms := TMemoryStream.Create;
ms.Size := Length(buffer);
if Length(buffer) > 0 then
ms.Write(buffer[0], Length(buffer));
ms.Seek(0, soFromBeginning);
end;
Hack workaround
function GetSubversionFileContents(Client: TSvnClient; Url: string): TStream;
var
http: IWinHttpRequest;
stm: IStream;
begin
// Issue the WebDAV HTTP GET ourselves.
// (SVN is just a wrapper around WebDAV)
http := CoWinHttpRequest.Create;
http.Open('GET', url, False);
if Client.UserName <> '' then
http.SetCredentials(Client.UserName, Client.Password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER);
http.Send(EmptyParam);
stm := IUnknown(http.ResponseStream) as IStream;
Result := TOleStream.Create(stm);
//TODO: Re-write the rest of the Subversion API into something modern (i.e. post-1995).
end;
Subversion is just an implementation of WebDAV - which is just REST but without the cool name. Which means you can issue a GET to the url to get the contents yourself directly.
I have a url of a website. It looks something like this: http://www.example.com/downloads/file/4789/download?
I would like to save the file to my system, but I do not know how to get the file name of the download triggered by the URL in my example. Some files are pdf others are doc and rtf etc.
If someone can please point me in a direction of the filename problem and also what components to use, I would really appreciate it.
to get the filename from a url you can retrieve the HEAD information and check Content Disposition header field. For this task you can use the TIdHTTP indy component. if the Content Disposition doesn't have the file name you can try parsing the url.
Try this sample .
{$APPTYPE CONSOLE}
{$R *.res}
uses
IdURI,
IdHttp,
SysUtils;
function GetRemoteFileName (const URI: string) : string;
var
LHttp: TIdHTTP;
begin
LHttp := TIdHTTP.Create(nil);
try
LHttp.Head(URI);
Result:= LHTTP.Response.RawHeaders.Params['Content-Disposition', 'filename'];
if Result = '' then
with TIdURI.Create(URI) do
try
Result := Document;
finally
Free;
end;
finally
LHttp.Free;
end;
end;
begin
try
Writeln(GetRemoteFileName('http://dl.dropbox.com/u/12733424/Blog/Delphi%20Wmi%20Code%20Creator/Setup_WmiDelphiCodeCreator.exe'));
Writeln(GetRemoteFileName('http://studiostyl.es/settings/downloadScheme/1305'));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
If I would like to save a IXMLDOMDocument3 in runtime to a file on my harddrive, what is the syntax for that?
E.g. like IXMLDOMDocument3.save('c:\test.xml')
Or is it even possible?
Best regards!
the sample code below demonstrates how to load and save IXMLDomDocument3 XML at runtime. It uses msxml header file from Delphi-2010. IXMLDomDocument3 inherits from IXMLDomDocument and has Save method (as you wrote in your question). If method parameter is a string, then it specifies file name (it creates or replaces target file).
program Project3;
{$APPTYPE CONSOLE}
uses SysUtils, msxml, comObj, activex;
procedure LoadAndSaveXML(LoadFile, SaveFile : string);
var xml : IXMLDOMDocument3;
tn : IXMLDOMElement;
begin
xml := CreateComObject(CLASS_DOMDocument60) as IXMLDOMDocument3;
xml.load(LoadFile);
xml.save(SaveFile);
end;
begin
try
CoInitialize(nil);
try
LoadAndSaveXML('D:\in.xml', 'D:\out.xml');
finally
CoUninitialize();
end;
except
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
readln;
end;
end;
end.
I'm attempting to use a local .sdf file as a means of temporary storage should the main database be unreachable. I have the .sdf file, but when I try to set it to the file it seems to not at all know if the .sdf exists. The current connection string I have currently is:
Driver={SQL Native Client};Data Source=C::\users\username\desktop\file\MyData.sdf;Persist Security Info=False
and for the Provider it generated for me:
Provider=Microsoft.SQLSERVER.CE.OLEDB.3.5
When I try to use the connection, I get a "Provider cannot be found. It may not be properly installed." The .sdf is most definitely in the folder. I also had/have a problem with it wanting a username and/or password, neither of which I had to specify when creating the database.
The Question: Is there something wrong with my connection string? Is it reasonable to use ADO connections to access SQL Compact Databases? Might there be an easier way to query/retrieve data from a temporary storage (I would prefer doing it with SQL though)?
Most documentation seems to be from 2003/2005, which is unhelpful.
I used "connectionstrings.com" for help making the string. Any advice would be helpful, thanks
First to open the sdf file you must use a provider compatible with the version of the sdf file. since you mention in your comments the version 3.5 you must use this provider Microsoft.SQLSERVER.CE.OLEDB.3.5
Then you must ensure which the provider is installed
Try this code to list the OLEDB providers installed in your system
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
Registry,
Classes,
SysUtils;
procedure ListOLEDBProviders;
var
LRegistry: TRegistry;
LIndex: Integer;
SubKeys,Values: TStrings;
CurKey, CurSubKey: string;
begin
LRegistry := TRegistry.Create;
try
LRegistry.RootKey := HKEY_CLASSES_ROOT;
if LRegistry.OpenKeyReadOnly('CLSID') then
begin
SubKeys := TStringList.Create;
try
LRegistry.GetKeyNames(SubKeys);
LRegistry.CloseKey;
for LIndex := 0 to SubKeys.Count - 1 do
begin
CurKey := 'CLSID\' + SubKeys[LIndex];
if LRegistry.KeyExists(CurKey) then
begin
if LRegistry.OpenKeyReadOnly(CurKey) then
begin
Values:=TStringList.Create;
try
LRegistry.GetValueNames(Values);
LRegistry.CloseKey;
for CurSubKey in Values do
if SameText(CurSubKey, 'OLEDB_SERVICES') then
if LRegistry.OpenKeyReadOnly(CurKey+'\ProgID') then
begin
Writeln(LRegistry.ReadString(''));
LRegistry.CloseKey;
if LRegistry.OpenKeyReadOnly(CurKey+'\OLE DB Provider') then
begin
Writeln(' '+LRegistry.ReadString(''));
LRegistry.CloseKey;
end;
end;
finally
Values.Free;
end;
end;
end;
end;
finally
SubKeys.Free;
end;
LRegistry.CloseKey;
end;
finally
LRegistry.Free;
end;
end;
begin
try
ListOLEDBProviders;
except
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
Now this is a basic sample to connect to a Sql Server compact file.
{$APPTYPE CONSOLE}
{$R *.res}
uses
ActiveX,
ComObj,
AdoDb,
SysUtils;
procedure Test;
Var
AdoQuery : TADOQuery;
begin
AdoQuery:=TADOQuery.Create(nil);
try
AdoQuery.ConnectionString:='Provider=Microsoft.SQLSERVER.CE.OLEDB.3.5;Data Source=C:\Datos\Northwind.sdf';
AdoQuery.SQL.Text:='Select * from Customers';
AdoQuery.Open;
While not AdoQuery.eof do
begin
Writeln(Format('%s %s',[AdoQuery.FieldByName('Customer ID').AsString,AdoQuery.FieldByName('Company Name').AsString]));
AdoQuery.Next;
end;
finally
AdoQuery.Free;
end;
end;
begin
try
CoInitialize(nil);
try
Test;
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
also check this wich helps with protected databases
"Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data
source=\NorthWind.sdf; SSCE:Database Password="
I am using Delphi 2010, and when I created a console application that prints "Hello World", it takes 111 kb. If I want to query WMI with Delphi, I add WBEMScripting_TLB, ActiveX, and Variants units to my project. If I perform a simple WMI query, my executable size jumps to 810 kb. I
Is there anyway to query WMI without such a large addition to the size of the file? Forgive my ignorance, but why do I not have this issue with C++?
Here is my code:
program WMITest;
{$APPTYPE CONSOLE}
uses
SysUtils,
WBEMScripting_TLB,
ActiveX,
Variants;
function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string;
var
Services: ISWbemServices;
SObject: ISWbemObject;
ObjSet: ISWbemObjectSet;
SProp: ISWbemProperty;
Enum: IEnumVariant;
Value: Cardinal;
TempObj: OLEVariant;
loc: TSWbemLocator;
SN: string;
i: integer;
begin
Result := '';
i := 0;
try
loc := TSWbemLocator.Create(nil);
Services := Loc.ConnectServer(wmiHost, root {'root\cimv2'}, '', '', '', '',
0, nil);
ObjSet := Services.ExecQuery('SELECT * FROM ' + wmiClass, 'WQL',
wbemFlagReturnImmediately and wbemFlagForwardOnly, nil);
Enum := (ObjSet._NewEnum) as IEnumVariant;
if not VarIsNull(Enum) then
try
while Enum.Next(1, TempObj, Value) = S_OK do
begin
try
SObject := IUnknown(TempObj) as ISWBemObject;
except SObject := nil;
end;
TempObj := Unassigned;
if SObject <> nil then
begin
SProp := SObject.Properties_.Item(wmiProperty, 0);
SN := SProp.Get_Value;
if not VarIsNull(SN) then
begin
if varisarray(SN) then
begin
for i := vararraylowbound(SN, 1) to vararrayhighbound(SN, 1) do
result := vartostr(SN[i]);
end
else
Result := SN;
Break;
end;
end;
end;
SProp := nil;
except
Result := '';
end
else
Result := '';
Enum := nil;
Services := nil;
ObjSet := nil;
except
on E: Exception do
Result := e.message;
end;
end;
begin
try
WriteLn('hello world');
WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem',
'Caption'));
WriteLn('done');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
UPDATE:
When I compile the following sample from MSDN with Microsoft Visual C++ 2008 (console application), it is 76 kb.
#Mick, you can access the WMI without import the WBEMScripting from Delphi, using the IBindCtx and IMoniker interfaces.
Check this simple code (Tested in Delphi 2010 and Windows 7), the exe file size is 174 kb.
program WmiTest;
{$APPTYPE CONSOLE}
uses
SysUtils
,ActiveX
,ComObj
,Variants;
function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string;
var
objWMIService : OLEVariant;
colItems : OLEVariant;
colItem : OLEVariant;
oEnum : IEnumvariant;
iValue : LongWord;
function GetWMIObject(const objectName: String): IDispatch;
var
chEaten: Integer;
BindCtx: IBindCtx;//for access to a bind context
Moniker: IMoniker;//Enables you to use a moniker object
begin
OleCheck(CreateBindCtx(0, bindCtx));
OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));//Converts a string into a moniker that identifies the object named by the string
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));//Binds to the specified object
end;
begin
objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',[wmiHost,root]));
colItems := objWMIService.ExecQuery(Format('SELECT * FROM %s',[wmiClass]),'WQL',0);
oEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
while oEnum.Next(1, colItem, iValue) = 0 do
begin
Result:=colItem.Properties_.Item(wmiProperty, 0); //you can improve this code ;) , storing the results in an TString.
end;
end;
begin
try
CoInitialize(nil);
try
WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem','Caption'));
Readln;
finally
CoUninitialize;
end;
except
on E:Exception do
Begin
Writeln(E.Classname, ': ', E.Message);
Readln;
End;
end;
end.
ActiveX and/or Variants would add 36KB at most.
It's WBEMScripting_TLB that adds about 650KB to your project.
It's not huge in lines of code but more than declaring quite a few classes, interfaces and constants, it includes OleServer in its uses.
And THAT brings the whole Controls unit with its heavy baggage.
when delphi builds an executable, it statically links in the delphi runtime libraries. this results in a larger executable, however as the rtl is statically linked, deployment is easier, and there's an element of future proofing.
you can configure delphi to use runtime packages by enabling Build with runtime packages in the Project / Options. however you'll have to ensure the delphi rtl packages are available, and you may encounter issues with debugging.
this static vs runtime linking behaviour probably explains the differences you're seeing between delphi and c++.
The difference you're seeing, in part anyway, is because VC++ uses dynamically linked runtime libraries by default; the runtime libraries are loaded from DLLs when the app runs, and therefore the code isn't present in the executable.
Delphi, OTOH, by default links in all of the runtime library code unless you build with runtime packages enabled. This difference in default configurations will account for the majority of the size differences between the executables.
Well, I don't know about WBEMScripting_TLB, but ActiveX.pas is a pretty huge unit. It's almost 7000 lines on my D2010 install. If you have to bring any significant amount of that into your code, then you can expect it will add a few hundred K to your EXE size.
How big is the TLB, by the way?