This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Delphi: Selecting a directory with TOpenDialog
I need to open a specific folder on my project. When I use opendialog1, I can only open a file. How about opening a folder ?
PS : I use Delphi 2010
On Vista and up you can show a more modern looking dialog using TFileOpenDialog.
var
OpenDialog: TFileOpenDialog;
SelectedFolder: string;
.....
OpenDialog := TFileOpenDialog.Create(MainForm);
try
OpenDialog.Options := OpenDialog.Options + [fdoPickFolders];
if not OpenDialog.Execute then
Abort;
SelectedFolder := OpenDialog.FileName;
finally
OpenDialog.Free;
end;
which looks like this:
You're looking for SelectDirectory in the FileCtrl unit. It has two overloaded versions:
function SelectDirectory(var Directory: string;
Options: TSelectDirOpts; HelpCtx: Longint): Boolean;
function SelectDirectory(const Caption: string; const Root: WideString;
var Directory: string; Options: TSelectDirExtOpts; Parent: TWinControl): Boolean;
The one you want to use depends on the version of Delphi you're using, and the specific appearance and functionality you're looking for; I( usually find the second version works perfectly for modern versions of Delphi and Windows, and users seem happy with the "normally expected appearance and functionality".
You also can use TBrowseForFolder action class (stdActns.pas):
var
dir: string;
begin
with TBrowseForFolder.Create(nil) do try
RootDir := 'C:\';
if Execute then
dir := Folder;
finally
Free;
end;
end;
or use WinApi function - SHBrowseForFolder directly (second SelectDirectory overload uses it, instead of first overload, which creates own delphi-window with all controls at runtime):
var
dir : PChar;
bfi : TBrowseInfo;
pidl : PItemIDList;
begin
ZeroMemory(#bfi, sizeof(bfi));
pidl := SHBrowseForFolder(bfi);
if pidl <> nil then try
GetMem(dir, MAX_PATH + 1);
try
if SHGetPathFromIDList(pidl, dir) then begin
// use dir
end;
finally
FreeMem(dir);
end;
finally
CoTaskMemFree(pidl);
end;
end;
Related
I have embedded several resources into the executable, for instance language (text) files.
Below you can see the contents of Languages.rc file:
Language_English RCDATA Languages\English.ini
Language_German RCDATA Languages\German.ini
Language_Czech RCDATA Languages\Czech.ini
I found this answer, which definitely helps, however I have rather hard time implementing it.
Suppose you want to get the list of those resources as a EOL-delimited string, then the first step would be defining EnumRCDataProc function:
function EnumRCDataProc(hModule: HMODULE; lpszType, lpszName: PChar; lParam: NativeInt): BOOL; stdcall;
begin
TStrings(lParam).Add(lpszName);
Result := True;
end;
Once we have that done, we can get to work:
function EnumerateRCDataResourceNames: string;
var
ExecutableHandle: HMODULE;
ResourcesList: TStringList;
begin
ExecutableHandle := LoadLibraryEx(PChar(Application.ExeName), 0, LOAD_LIBRARY_AS_DATAFILE);
try
ResourcesList := TStringList.Create;
try
EnumResourceNames(ExecutableHandle, RT_RCDATA, #EnumRCDataProc, NativeInt(ResourcesList));
Result := ResourcesList.Text;
finally
ResourcesList.Free;
end;
finally
FreeLibrary(ExecutableHandle);
end;
end;
Remarks:
As is in the original answer (see question), it is not possible to use LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE or LOAD_LIBRARY_AS_IMAGE_RESOURCE as these types are no longer defined in Delphi XE6, at least AFAIK.
You can, however, define those constants, according to MSDN:
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = $00000040
LOAD_LIBRARY_AS_IMAGE_RESOURCE = $00000020
Is it possible to make readonly source code (eg., .pas & .dfm) writable from within the Delphi IDE? The right click option to make files Readonly/Writable within the IDE doesn't change the properties on the file system. Is there an IDE extension or similar that can achieve this?
A way to do this without having to integrate a source control system would be preferable. I am using Delphi XE and Delphi 6.
Thanks!
sse
This is how I would do it.
Create a new package which will be installed into the IDE at designtime. If you have an existing package handy then you could carry on using it. Make sure the package requires the designide package. You can do this in the project manager, or just by viewing the project source and adding designide to the requires clause.
Now add the following unit to your package.
unit MakeEditable;
interface
procedure Register;
implementation
uses
Windows, SysUtils, Menus, ToolsAPI;
type
TMakeEditable = class(TObject)
private
FEditorServices: IOTAEditorServices;
FFileMenu: TMenuItem;
FMakeEditable: TMenuItem;
function MenuItemWithCaptionLike(const Menu: TMenuItem; const DesiredCaption: string): TMenuItem;
procedure MakeEditableClick(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
end;
constructor TMakeEditable.Create;
var
Index: Integer;
PreviousMenuItem: TMenuItem;
begin
inherited;
FEditorServices := (BorlandIDEServices as IOTAEditorServices);
FFileMenu := MenuItemWithCaptionLike((BorlandIDEServices as INTAServices40).MainMenu.Items, 'File');
if Assigned(FFileMenu) then begin
PreviousMenuItem := MenuItemWithCaptionLike(FFileMenu, 'Reopen');
if Assigned(PreviousMenuItem) then begin
Index := PreviousMenuItem.MenuIndex;
if Index>=0 then begin
FMakeEditable := TMenuItem.Create(FFileMenu);
FMakeEditable.Caption := 'Ma&ke Editable';
FMakeEditable.OnClick := MakeEditableClick;
FFileMenu.Insert(Index, FMakeEditable);
end;
end;
end;
end;
destructor TMakeEditable.Destroy;
begin
FMakeEditable.Free;
inherited;
end;
function TMakeEditable.MenuItemWithCaptionLike(const Menu: TMenuItem; const DesiredCaption: string): TMenuItem;
var
i: Integer;
Target, Found: string;
begin
Target := StringReplace(LowerCase(Trim(DesiredCaption)), '&', '', [rfReplaceAll, rfIgnoreCase]);
for i := 0 to Menu.Count-1 do begin
Result := Menu.Items[i];
Found := StringReplace(LowerCase(Trim(Result.Caption)), '&', '', [rfReplaceAll, rfIgnoreCase]);
if Pos(Target, Found)>0 then begin
exit;
end;
end;
Result := nil;
end;
procedure TMakeEditable.MakeEditableClick(Sender: TObject);
procedure MakeFileEditable(const FileName: string);
var
Attributes: DWORD;
begin
Attributes := GetFileAttributes(PChar(FileName));
SetFileAttributes(PChar(FileName), Attributes and not FILE_ATTRIBUTE_READONLY);
end;
var
FileName: string;
FileExt: string;
LinkedFileName: string;
EditBuffer: IOTAEditBuffer;
begin
EditBuffer := FEditorServices.TopBuffer;
FileName := EditBuffer.FileName;
if FileExists(FileName) then begin
MakeFileEditable(FileName);
EditBuffer.IsReadOnly := False;
FileExt := ExtractFileExt(FileName);
if SameText(FileExt,'.dfm') then begin
LinkedFileName := ChangeFileExt(FileName, '.pas');
end else if SameText(FileExt,'.pas') then begin
LinkedFileName := ChangeFileExt(FileName, '.dfm');
end else begin
LinkedFileName := '';
end;
if (LinkedFileName<>'') and FileExists(LinkedFileName) then begin
MakeFileEditable(LinkedFileName);
end;
end;
end;
var
MakeEditableInstance: TMakeEditable;
procedure Register;
begin
MakeEditableInstance := TMakeEditable.Create;
end;
initialization
finalization
MakeEditableInstance.Free;
end.
When you compile and install this package you will now have a new menu item on the File menu which both clears the read-only flag in the input buffer and makes the file writeable.
You can call a .bat file from the tools menu. So you could write a .bat file to do the work for you, and call it with $EDNAME as the parameter.
Your .bat file should see the filename as %1. Then you'd need a little bit of logic to change the read-only flag (attrib command?) and then see if there is a .dfm and do that one as well.
You could also (obviously) make a Delphi command-line app to do this, if you're not comfortable with .bat files.
This idea inspired by this article that talks about how to use a .bat file to integrate SVN commands with the Delphi tools menu:
http://delphi.wikia.com/wiki/Adding_TortoiseSVN_to_the_Tools_menu
Another idea: Just add an option to the tools menu: "open command prompt here". It lets you do all sorts of things from the command line, such as running the attrib command.
Add a new entry to the Tools menu, and use these settings:
Title: Open Command Prompt Here
Program: cmd.exe
Working Dir (leave blank)
Parameters: cd $PATH($EDNAME)
Also, make yourself an "Open Folder Here" entry:
Title: Open Folder Here
Program: explorer.exe
Working Dir (leave blank)
Parameters: $PATH($EDNAME)
What is a new way to get current file that is being worked on in Experts for Delphi XE
Previously in Delphi 5-7 we used ToolServices.getCurrentFile
Perhaps the deprecated units ToolIntf, ExptIntf etc. are no longer working. You can use IOTAModuleServices.CurrentModule instead. Here's a quick example:
function GetCurrentEditorFileName: string;
var
Module: IOTAModule;
Editor: IOTAEditor;
begin
Result := '';
Module := (BorlandIDEServices as IOTAModuleServices).CurrentModule;
if Assigned(Module) then
begin
Editor := Module.CurrentEditor;
if Assigned(Editor) then
Result := Editor.FileName;
end;
end;
An alternate method is to pass the "name of file in the editor" to your tool as a parameter. $EDNAME
Since at least D2007 a project file can have a main source file with differing base name. The DevExpress demos make use of this: E.g. there is a single dpr file UnboundListDemo.dpr which serves as the main source for both UnboundListDemoD11.dproj and UnboundListDemoD12.dproj.
Now if I have a Project: IOTAProject then Project.FileName returns the dproj file name. I couldn't find an "official" way to get the dpr's file name. Is there any? One can get it from parsing the dproj file (see here) but I'd prefer a ToolsAPI method.
Edit: I came up with this code based on Jon's answer:
function IsProjectSource(const FileName: string): Boolean;
begin
Result := IsDpr(FileName) or IsBpr(FileName) or IsPackage(FileName);
end;
function GxOtaGetProjectFileName2(Project: IOTAProject; NormalizeBdsProj: Boolean = False): string;
var
i: Integer;
Module: IOTAModule;
Editor: IOTAEditor;
begin
Result := '';
if Assigned(Project) then begin
Result := Project.FileName;
if NormalizeBdsProj and IsBdsprojOrDproj(Result) then begin
Module := Project as IOTAModule;
for i := 0 to Module.ModuleFileCount - 1 do
begin
Editor := Module.ModuleFileEditors[i];
if IsProjectSource(Editor.FileName) then begin
Result := Editor.FileName;
Exit;
end;
end;
end;
end;
end;
where the Is... routines are from GX_GenericUtils.
Edit 2: How to create one of these situations:
Create new VCL application.
Save as MyProject.dproj.
Close project in IDE.
In Windows explorer, rename MyProject.dproj to MyProjectD11.dproj.
From now on be sure to open MyProjectD11.dproj, not MyProject.dpr!
If you iterate the editors on the IOTAProject instance, you'll probably find the dpr.
var
Module: IOTAModule;
Project: IOTAProject;
Editor: IOTAEditor;
begin
// Set Project Here....
Module := Project as IOTAModule;
for I := 0 to Module.ModuleFileCount - 1 do
begin
Editor := Module.ModuleFileEditors[I];
end;
end;
In this question is mentioned the wcrypt2.
What I need is simply calculate the MD5 of a file. It would be perfect if I could calculate it without having to save it because it is a downloaded file in stream format.
I would like to have the most straightforward way to do that.
Thanks!
Here is a working code for Indy 10:
function MD5File(const FileName: string): string;
var
IdMD5: TIdHashMessageDigest5;
FS: TFileStream;
begin
IdMD5 := TIdHashMessageDigest5.Create;
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := IdMD5.HashStreamAsHex(FS)
finally
FS.Free;
IdMD5.Free;
end;
end;
Regards,
OscaR1
Based on #dummzeuch answere I wrote this function:
function getMD5checksum(s: TStream): string;
var
md5: TIdHashMessageDigest5;
hash : T4x4LongWordRecord;
begin
md5 := TIdHashMessageDigest5.Create;
s.Seek(0,0);
hash := md5.HashValue(s);
result := IntToHex(Integer(hash[0]), 4) +
IntToHex(Integer(hash[1]), 4) +
IntToHex(Integer(hash[2]), 4) +
IntToHex(Integer(hash[3]), 4);
end;
Indy comes with functions for calculating several hashes, MD5 is one of them. Indy is included in all versions of Delphi since at least Delphi 2006 and available as a free download for older versions.
What about:
function GetFileMD5(const Stream: TStream): String; overload;
var MD5: TIdHashMessageDigest5;
begin
MD5 := TIdHashMessageDigest5.Create;
try
Result := MD5.HashStreamAsHex(Stream);
finally
MD5.Free;
end;
end;
function GetFileMD5(const Filename: String): String; overload;
var FileStream: TFileStream;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := GetFileMD5(FileStream);
finally
FileStream.Free;
end;
end;
As you mentioned, the post you linked to talks about wcrypt2, which is a library of cryptographic routines, including MD5. The post you linked to also seems to indicate that it is available for Delphi 7 since the asker includes output labeled "Delphi 7." You have tagged this question delphi7, so I assume that's the version you're using, too. So what's stopping you from using wcrypt2?
The question links to a copy of wcrypt2.pas, and the copyright dates in that file appear to indicate that the unit was available by the time Delphi 7 was released. Check your installation; you might already have it. If not, then the unit also says that it was obtained via Project Jedi, so you could try looking there for the unit as well.
The answers to your referenced question include example Delphi code and the names of units that come with Delphi for doing MD5. They come with Delphi 2009, so you should check whether they're also available for your version.
Take a look at this implementation of MD5SUM in Delphi. It requires a string for input, but I imagine you can easily make it work with a stream.
MessageDigest_5 would work for this as well.
I use the following function in Delphi 7 with Indy 10.1.5
uses IdHashMessageDigest, idHash, Classes;
...
function cc_MD5File(const p_fileName : string) : string;
//returns MD5 has for a file
var
v_idmd5 : TIdHashMessageDigest5;
v_fs : TFileStream;
v_hash : T4x4LongWordRecord;
begin
v_idmd5 := TIdHashMessageDigest5.Create;
v_fs := TFileStream.Create(p_fileName, fmOpenRead OR fmShareDenyWrite) ;
try
v_hash := v_idmd5.HashValue(v_fs);
result := v_idmd5.AsHex(v_hash);
finally
v_fs.Free;
v_idmd5.Free;
end;
end;
If you use Overbyte http://www.overbyte.eu/frame_index.html just add unit and call function FileMD5 with name of file
uses OverbyteIcsMd5;
....
function GetMd5File:String;
begin
Result := FileMD5(FileName);
end;