views:

1675

answers:

7

I have a delphi (Win32) web application that can run either as a CGI app, ISAPI or Apache DLL. I want to be able to generate a unique filename prefix (unique for all current requests at a given moment), and figure that the best way to do this would be to use processID (to handle CGI mode) as well as threadID (to handle dll mode).

How would I get a unique Process ID and Thread ID in Delphi?

Will these be unique in a Multi-Core/Multi-Processor situation (on a single webserver machine)?

Edit: please note that I was advised against this approach, and thus the accepted answer uses a different method to generate temporary filenames

+4  A: 

Could you not use a GUID instead?

Edit: Should have said first time around, check out the following two functions

CreateGuid
GuidToString
Jamie
+3  A: 

Process IDs are not guaranteed to be unique on windows. They are certainly unique for the life of the process, but once a process dies its id can be immediately reused. I am not certain about ThreadIDs. If these are temporary files you could use something equivalent to tmpfile or tmpnam (C functions, but I assume Delphi has an equivalent).

As Jamie posted a GUID may be better.

grieve
"Unique for all current requests at a given moment" - reuse of Process IDs would be fine, as the file will only exist for the life of a request. I want to be sure however, that on a multi-core system, they will be unique at a given moment. And that adding thread ID will account for multithreading
Graza
This implies the files are temporary (at least it seems to), in which case I would just use tmpfile or its Delphic equivalent.
grieve
+2  A: 

Better than either of of those options, you should be using the system function _tempnam. It returns a random file name in the directory for a file that does not exist. If you want to, you can supply a prefix to _tempnam so that the file you create is recognizably yours. If you are providing a unique prefix, there is shouldn't be any worry about someone opening your file. There is another solution, however.

_tempnam is only good if you want to put the file into an arbitrary directory. If you don't care that the directory is the system temporary directory, use tempfile_s instead. It will also create the file for you, so no worry about race conditions... Errors will only occur if you try to open more temp files than the system can handle. The big downside to tempfile_s is that the file will disappear once you fclose it.

EDIT: I've gotten a downvote because this is a C function. You have access to the C runtime by importing them into delphi. Have a look at some examples with msvcrt.dll here.

function _tempnam(const Dir: PChar, const Prefix: PChar): PChar; cdecl;
  external 'msvcrt.dll' name '_tempnam';
Douglas Mayle
Does it also create an empty file to "get a lock on" the name? - that is, is there *any* chance of a clash if there is a split second delay between generating the name and creating the file (if I need to create the file myself)
Graza
Not sure if this is available in delphi by default. The MSDN documentation for GetTempFileName does mention using a GUID
Jamie
+1  A: 

Others all gave you a good and reasonable ideas, but still - if you're using files for temporary storage and if those files will always be created first (it doesn't matter if there is a leftover file with a same name already on the disk as you'll overwrite it anyway) then processid_threadid approach is completely valid.

Use GetCurrentProcessID and GetCurrentThreadID Win32 calls to access those two IDs.

gabr
+5  A: 

you have many good ideas presented here.

Does it also create an empty file to "get a lock on" the name?

no; i believe we rely on Windows to ensure the same temp file name is never given twice on the same computer since boot time.

is there any chance of a clash if there is a split second delay between generating the name and creating the file (if I need to create the file myself).

no; that'd be a pretty bad thing.

here's a routine i've been using for getting a temp file.

function GetTemporaryFileName:string;
var
  Path, FileName: array[0..MAX_PATH] of Char;
begin
  Win32Check(GetTempPath(MAX_PATH, Path) <> 0);
  Win32Check(GetTempFileName(Path, '~EX', 0, FileName) <> 0);
  Result:=String(Filename);
end;

you could instead use FileGetTempName( ) from JclFileUtils.pas in JCL.

X-Ray
I went with this approach as it turned out to be the simplest, as well as being more "correct" from the POV that temp files get put in the system temp folder.I thought my process+thread ID idea worked, but I'd also have needed to add code to account for a thread creating more than one file..
Graza
+3  A: 

1) How to get a unique Process ID & ThreadID in Delphi:

Answer:
NOTE: Ensure to add 'windows' to your uses clause in the implementation section
NOTE: Cardinals are unsigned 32-bit integers ranging from 0 to 4294967295

implementation
uses Windows;

procedure MySolution();
var
  myThreadID:Cardinal;  
  myProcessID:Cardinal;
begin
  myThreadID := windows.GetCurrentThreadID;
  myProcessID := windows.GetCurrentProcessId;
end;

2) Will these be unique in a Multi-Core/Multi-Processor situation (on a single webserver machine)?
Answer: Yes.

The process identifier is valid from the time the process is created until the process has been terminated and is unique throughout the system. (Not unique to processor)

Until the thread terminates, the thread identifier uniquely identifies the thread throughout the system. (Again, system wide, not unique to processor)

Darian Miller
+5  A: 

Windows provides functionality for creating guaranteed unique file names. No need for creating your own:

Here's a Delphi wrapper around that functionality:

function CreateTempFileName(aPrefix: string): string;
var
  Buf: array[0..MAX_PATH] of Char;
  Temp: array[0..MAX_PATH] of Char;
begin
  GetTempPath(MAX_PATH, Buf);
  if GetTempFilename(Buf, PChar(aPrefix), 0, Temp) = 0 then
  begin
    raise Exception.CreateFmt(sWin32Error, [GetLastError, SysErrorMessage(GetLastError)]);
  end;
  Result := string(Temp);
end;
Nick Hodges
I would go with this.Be aware that the MSDN docs say- "Due to the algorithm used to generate filenames, GetTempFileName can perform poorly when creating a large number of files with the same prefix. In such cases, it is recommended that you construct unique filenames based on GUID"
Jamie