I added some FTP servers via network shortcuts to windows.
How can I get the FTP address via the WinAPI?
With SHGetFolderPath / CSIDL_NETHOOD, I can get the location of the target.lnk file. But how can I get the FTP URL of that file?
A "normal" .lnk file, I can resolve with this:
ShellLink := CreateComObject(CLSID_ShellLink) as IShellLink;
ShellLink.QueryInterface(IPersistFile, PersistFile);
PersistFile.Load('C:\Test.lnk', STGM_READ);
ShellLink.Resolve(WindowHandle, 0);
Filename[0] := #0;
ShellLink.GetPath(PChar(#Filename[0]), Length(Filename), pfd, 0);
... but this does not work for the Target.lnk files of the network shortcuts to ftp://host/ adresses.
IShellLink::GetPath is used to retrieve a file system path (only drive letter or UNC roots). This is not clear from the documentation but it uses SHGetPathFromIDListEx internally and MSDN has this to say about that function:
Converts an item identifier list to a file system path
If you want the raw shortcut target your best bet is usually IShellLink::GetIDList. You can get the parsing name by using SHGetNameFromIDList(..., SIGDN_DESKTOPABSOLUTEPARSING, ...) on the id-list.
Related
I am working on a project where I need to play a video from a file in Delphi. I often work from home and school, and I have the problem that at home, my USB is drive 'J' and at school my USB is drive 'D'.
I manually go and change it every time. Is there a way for Delphi to automatically get the video from where ever it is?
Each sector has an image component laid over it for selecting the sector.
*Note, I know I can search for a specific file's location in Delphi, but I have over 24 different places where I need to play different videos, so searching would probably be my last resort, unless I use a procedure and set constants for each sector to differenciate between them.
The code currently looks as follows:
procedure TtForm.imgSector1Click(Sender: TObject);
begin
//Variables,this is for initializing them when I create them later.
//Procedures
SectorDeselect; //Procedure to turn all sector borders white
// Video
WindowsMediaPlayer1.Controls.stop;
WindowsMediaPlayer1.URL := 'J:\IT\PAT\phase 2\Videos\Footage1.mp4'; //Where my problem lies
WindowsMediaPlayer1.Controls.Play;
// Sector Info. The memos and Rich edits
redSectorInfo.Lines.Clear;
redSectorInfo.Lines.Add('');
// Sector. Highlighting the sector borders surrounding the sector
SectorBordr1.Brush.Color := clGreen;
SectorBorder10.Brush.Color := clGreen;
end;
I would suggest adding a TEdit control in your app's UI to let you specify the base drive/path for the files on the machine the app is currently running on. Your code can then construct individual file paths at runtime that are relative to that base path. Don't use hard-code paths in your code.
You can then save that base path into the Windows Registry in a new key you create, ie HKEY_CURRENT_USER\Software\MyApp. Or, you can save the path in a configuration file (INI, XML, JSON, etc) created in a subfolder in your Windows user profile, like %APPDATA%\MyApp. Your code can then read in that base path each time the app is run.
If the files are stored on a USB drive, an alternative solution would be to simply enumerate the available drives at runtime, such as with GetLogicalDriveStrings(). For each drive, append a relative path for a given file onto the end of it, and then check if that file exists, such as with FileExists(). If so, you now know which drive to use for all of the files until the next time your app is run (you can save the drive path between runs, as described above). If the file is not found, move on to the next drive.
What about adding a parameter on the CommandLine?
Start
D:\myfolder\myfile D
OR
Start
J:\myfolder\myfile J
GUI files can accept a parameter. Capture it with code like:
DriveLetter := ParamStr(1);
I need to guess the mime type with the purpose of fill the "Content-Type" header for some file uploads.
I fail to found a solution for it.
I wish to call something like: GetMimeType('C:File.jpg') and get back image/jpg.
Best if is multiplataform (for win/osx) but will be enough if only for windows.
Try using the FindMimeFromData Function.
FindMimeFromData contains hard-coded tests for (currently 26) separate
MIME types (see Known MIME Types). This means that if a given buffer
contains data in the format of one of these MIME types, a test exists
in FindMimeFromData that is designed (by scanning through the buffer
contents) to recognize the corresponding MIME type.
from urlmon.pas
function FindMimeFromData(
pBC: IBindCtx; // bind context - can be nil
pwzUrl: LPCWSTR; // url - can be nil
pBuffer: Pointer; // buffer with data to sniff - can be nil (pwzUrl must be valid)
cbSize: DWORD; // size of buffer
pwzMimeProposed: LPCWSTR; // proposed mime if - can be nil
dwMimeFlags: DWORD; // will be defined
out ppwzMimeOut: LPWSTR; // the suggested mime
dwReserved: DWORD // must be 0
): HResult; stdcall;
Also this article to see hot it works MIME Type Detection in Internet Explorer
IE uses GetClassFileOrMime and FindMimeFromData API to detect the mime type of data/files (UrlMon unit in Delphi).
MIME Type Detection in Internet Explorer
The CLSID returned from GetClassFileOrMime could be located in the registry under HKEY_CLASSES_ROOT\CLSID\<clsid>\MimeTypes. (also FileExtensions, FriendlyName).
The registry also holds a MIME database under: HKEY_CLASSES_ROOT\MIME\Database\Content Type.
But since the list of known MIME types is relatively small you could hold that as a resource XML (or whatever) and simply fetch it from there. This will supprt both Windows and OSX.
For file upload operation you can simply always use application/octet-stream.
Indy has TIdMimeTable class (IdGlobal) and it uses a fixed list plus inspecting the registry HKEY_CLASSES_ROOT (see FillMimeTable). If no match is found application/octet-stream is used.
You should probably want to inspect the file content at the server side once the file is uploaded, and not rely on the headers because the ContentType could be tampered at the client side, and also not match with the registry at the server-side.
Internet Direct (Indy) contains a class for this:
class TIdThreadSafeMimeTable
in unit IdCustomHTTPServer
Code example in a HTTP server application:
Response.ContentType :=
Response.HTTPServer.MIMETable.GetFileMIMEType(FileName);
How many file types are you talking about? Maybe you can just create a list of types you want to support.
I can imagine these lists to be available on the internet as well, for instance
here: http://www.webmaster-toolkit.com/mime-types.shtml
here: http://webdesign.about.com/od/multimedia/a/mime-types-by-file-extension.htm
here: List of ALL MimeTypes on the Planet, mapped to File Extensions?
here: http://hul.harvard.edu/ois/systems/wax/wax-public-help/mimetypes.htm
Just get the file extension using ExtractFileExt and check it agains one of those lists. A file doesn't have a mime type in it, so you'll need to use either this list of file extensions, or determine the type by reading the file headers, which is harder and just as unreliable.
How can I make a single executable package that contains DLL and Image Resource Files?
Then how do I extract them from my Executable at Runtime?
Option 1 using the IDE (Delphi 2007 or Higher):
You can click the Project menu, then select Resources..., which you can load any file into. For your purpose this would be RC_DATA.
Option 2 without the IDE
If you do not have the above option, you will need to use the BRCC32 (Borland Resource Compiler) to create a .RES file from RC file, which you then link to your Application. To link Resource files without using the IDE, try the following:
Lets say for example we want to add a a couple of DLL files, and the name of the DLL files are MyLib1.dll and MyLib2.dll, to add this open Notepad, and type the following:
MYLIB1 RCDATA "..\MyLib1.dll"
MYLIB2 RCDATA "..\MyLib2.dll"
Make sure the ..\xxx.dll paths are correct, so obviously you need to edit that.
Now you need to save this as a .rc file, so File>Save As..(make sure the dropdown filter is All Files .) and name it MyResources.rc. Now you need to use the Resource Compiler to generate the Res file, using this console command:
BRCC32 MyResources.RC
You can write that command by using the Command Prompt, Start Menu > Run > cmd.exe, alternatively you can find the BRCC32.exe inside the bin folder of your Delphi setup and drag the MyResource.RC file onto.
This will create a Res file named MyResources.RES which you can include inside the Main Delphi form of your Application, like so:
{$R *.dfm}
{$R MyResources.res}
you can extract the resources by using something like this:
procedure ExtractResource(ResName: String; Filename: String);
var
ResStream: TResourceStream;
begin
ResStream:= TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
ResStream.Position:= 0;
ResStream.SaveToFile(Filename);
finally
ResStream.Free;
end;
end;
What I've found out to be convenient, is to use a .zip container.
Then you'll have two implementations:
Append some .zip content to an existing .exe, and the .exe code will retrieve the .zip content on request;
Embed the .zip content as a resource, then extract on request each content.
Solution 1 will add the .zip content after compilation. Whereas 2 will add the .zip content at compilation. For a setup program, I think solution 1 makes sense to me. For a way of retrieving some needed files (libraries, and even bitmaps or text) which are linked to a particular exe release, solution 2 could be envisaged.
Using .zip as format make it easy to parse the content, and allow compression. Using a tool like TotalCommander, you can even read the .zip file content with Ctrl+PgDown over the .exe. Very convenient.
You'll find in this link how you implement solution 1, and in this link (same page, but another post) how to use the TZipRead.Create() constructor to directly access to a .zip bundled as resource. You'll find in our repository how it works with working applications: e.g. how we embedded icons, textual content and graphviz + spell-checker libraries in the SynProject executable.
About performance, there is no difference between the two solutions, at least with our code. Both use memory mapped files to access the content, so it will be more or less identical: very fast.
We use Indy and we need SSL eMail support in our app., however we need to have our application in a single .Exe.
We know that the default Indy handler requires to have the dlls in the path. Extracting the Dlls from one of the EXE's resources would be the last resort.
Any better ideas?
Try SSLBlackBox.
TOndrey gave you a good answer. I use SecureBlackBox as well.
You may consider some other third party components:
StreamSec
SecureBridge from DevArt
Be aware: if you add SSL/TLS support inside your executable, it might become restricted for export. If you're in the USA, this could mean that your application cannot be sold or given to people outside the USA. This is why these DLL's aren't part of Indy or Delphi themselves.
The libraries that Delphi uses are actually compiled DLL's from the OpenSSL project. But if you have a good knowledge of C then you should be able to compile the source to .obj files and link them with your Delphi code instead. You would probably need to modify part of the Indy code for this too. Of course, others could have done this too, but that makes the export of those Indy components (or even Delphi itself) more complex, because of those export restrictions.
Funnily enough, source code is protected by the first amendment which basically allows you to print the code in a book and then send it to some rogue nation. While if you'd send it in digital form (compiled or not) then you're committing a federal offence and probably will have to be careful when picking up the soap in the shower for at least a year... No one claimed that laws make sense. They can just be a pain in the [beep]...
Other SSL solutions don't work together with the Indy components, which would mean you'd have to rewrite part of your code to support those other solutions.
This link tells how you can load a DLL from memory, so you don't need to have it on disk. It's an alternate solution which I haven't tried. I don't think it will work, since the two DLL's depend on each other, but it might be worth a try...
Is the "Single EXE" requirement for distribution purposes or must it also be a single .EXE file when running on the client's machine?
If it's only for distribution purposes, you can append the DLL files to the end of your .EXE file and then - when the program starts - extract them from the .EXE file and store them locally as .DLL files, something like this:
VAR F,O : FILE;
VAR BUF : ARRAY[1..<MaxSizeOfDLLs>] OF BYTE;
ASSIGN(F,ParamStr(0)); RESET(F,1);
SEEK(F,<OriginalExeSize>);
BLOCKREAD(F,BUF,<FirstDllSize>);
ASSIGN(O,<NameOfFirstDLL>); REWRITE(O,1);
BLOCKWRITE(O,BUF,<FirstDllSize>); CLOSE(O);
BLOCKREAD(F,BUF,<SecondDllSize>);
ASSIGN(O,<NameOfSecondDLL>); REWRITE(O,1);
BLOCKWRITE(O,BUF,<SecondDllSize>); CLOSE(O);
SEEK(F,<OriginalExeSize>); TRUNCATE(F); CLOSE(F)
Quick'n'Dirty, not properly formatted, etc., but should give you the basic idea.
Have you tried compiling the OpenSLL source yourself and importing the object files into Delphi?
Recommended reading: Using C object files in Delphi - explains how to create a program that does not need a DLL, and can be deployed in one piece
I use Microsoft's CAPICOM for SSl3 and it solved my needs... It's freely redistributable but discontinued
If you try other components maybe you should look to SYNAPSE(at http://synapse.ararat.cz/) (I also use) it can work with StreamSec(and others) to send emails over ssl. Its free and easy to work.
Const
cdoSendUsingMethod = 'http://schemas.microsoft.com/cdo/configuration/sendusing';
cdoSMTPServer = 'http://schemas.microsoft.com/cdo/configuration/smtpserver';
cdoSMTPServerPort = 'http://schemas.microsoft.com/cdo/configuration/smtpserverport';
cdoSendServerPort = '25';
cdoSendUsingPort = 2;
cdoSMTPConnectionTimeout = 'http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout';
cdoSMTPAuthenticate = 'http://schemas.microsoft.com/cdo/configuration/smtpauthenticate';
cdoAnonymous = '0';
cdoBasic = '1';
cdoSMTPUseSSL = 'http://schemas.microsoft.com/cdo/configuration/smtpusessl';
cdoSendUserName = 'http://schemas.microsoft.com/cdo/configuration/sendusername';
cdoSendPassword = 'http://schemas.microsoft.com/cdo/configuration/sendpassword';
cdoURLGetLatestVersion = 'http://schemas.microsoft.com/cdo/configuration/urlgetlatestversion';
...
function SensCDOMail (ASubject, AFrom, ATo, ABody, ASmtpServer : WideString): String;
var
cdoMessage:OleVariant;
cdoConfiguration: OleVariant;
begin
//Configuration Object
cdoMessage:= CreateOleObject('CDO.Message');
cdoConfiguration:= CreateOleObject('CDO.Configuration');
try
cdoConfiguration.Fields(cdoSendUsingMethod):= cdoSendUsingPort;
cdoConfiguration.Fields(cdoSMTPServer):= ASmtpServer;
cdoConfiguration.Fields(cdoSMTPServerPort):= cdoSendServerPort;
cdoConfiguration.Fields(cdoSMTPAuthenticate):= cdoAnonymous;
cdoConfiguration.Fields(cdoSMTPUseSSL ):= True; // use SSL
cdoConfiguration.Fields.Update;
cdoMessage.Configuration:= cdoConfiguration;
cdoMessage.To := ATo;
cdoMessage.From := AFrom;
cdoMessage.Subject := ASubject;
//cdoMessage.HTMLBody := ABody; //Want to send in Html format
cdoMessage.TextBody := ABody; //Want to send in text format
cdoMessage.Send;
finally
VarClear(cdoMessage);
VarClear(cdoConfiguration);
end;
end;
It is possible to include these DLLs into the program's executable as resources and either export them to files when used or even use them without exporting them first by relocating the code and searching the entry points in memory. I have got code somewhere for doing the latter....
(Using Delphi 5)
I am attempting to open a log file using the following code:
// The result of this is:
// C:\Program Files\MyProgram\whatever\..\Blah\logs\mylog.log
fileName := ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log';
// The file exists check passes
if (FileExists(fileName)) then
begin
logs := TStringList.Create();
// An exception is thrown here: 'unable to open file'
logs.LoadFromFile(fileName);
end;
If I relocate the log file to C:\mylog.log the code works perfectly. I'm thinking that the spaces in the file path are messing things up. Does anyone know if this is normal behavior for Delphi 5? If it is, is there a function to escape the space or transform the path into a windows 8.3 path?
I'm pretty sure that Delphi 5 handles spaces in filenames ok but it has been a very long time since I have used that specific version. Is the file currently open by another process? It also could be a permissions issue. Can you instead of loading it into a tStringList, try opening it with a tFileStream with the filemode set to "fmOpenRead or fmShareDenyNone".
fStm := tFileStream.Create( filename, fmOpenRead or fmShareDenyNone );
then load your tStringlist from the stream:
Logs.LoadFromStream ( fStm );
Are you sure its not the "..\" thats causing the problem rather than the spaces. Have you tried to see if it works at
c:\My\Path\nospaces\
If so and you are always using the ..\ path, maybe write a simple function to remove the last folder from your application path and create a full correct pathname.
It's odd that Delphi 5 would throw errors about this. I know of an issue with FileExists failing on files with an invalid last-modified-date (since it internally uses FileAge), but it's the opposite here. Instead of using "..\" I would consider risking the current path, and loading from a relative path: LoadFromFile('..\Something\Something.log'); especially for smaller applications, or by calling ExtractFilePath twice: ExtractFilePath(ExtractFilePath(Application.ExeName))
I'm pretty sure Delphi has always handled spaces so I doubt that is the issue.
You don't show the full path. Any chance it is really long? For example I could believe an issue with paths longer than 255 characters.
It's also a bad idea to put log files under Program Files. Often normal users are not given permission to write to anything under Program Files.
Delphi 5 can open files with spaces - that is certainly not the problem. To prove it, try copying it to c:\my log.log- it should open fine.
Is there any more information in the error message you receive? The most likely thing is that someone else (perhaps your own program) is still writing to the log.
The spaces are not a problem. While the '..' could be a problem in Delphi 5, mosts probably the file is locked by the process that writes to it. If you have control of it, make sure it opens the file with fmShareDenyWrite and not fmShareExclusive or fmShareCompat (which is the default).
Also, you can use:
fileName := ExpandFileName(ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log');
to obtain the absolute path from a relative path.
Also, as others have said, it is not good idea to write anything in Program Files. Regular users (that are not Administrators or Power Users) do not have rights to write there (although in Vista is will be virtualized, is is still not a good idea). Use the appropriate Application Data folder for the user (or all users). This folder can be obtained using:
SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,#path[0])
where folder is either CSIDL_COMMON_APPDATA or CSIDL_LOCAL_APPDATA. See this delphi.about.com article for an example.
Simple :
// if log file = "C:\Program files\mylog.log"
// you'll get :
// »»»»» fileName = 'C:\Program files..\Blah\logs\mylog.log'
// if log file = "C:\mylog.log"
// you'll get :
// »»»»» fileName = 'C:..\Blah\logs\mylog.log'
Try this code instead, I'm pretty sure it will fit your needs :
fileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName))
+ '..\Blah\logs\mylog.log';
Regards,
Olivier
Delphi 5 has never had a problem opening files with spaces and I am still using it since it is uber stable and works great for older XP apps. You need to check your code closely.