multithreadingdelphiiisisapi

Why is file access in WebBroker-ISAPI-Module only working in separate thread?


I'm developing my first ISAPI web module with Delphi 11.1 and WebBroker, and want to write logs to the file system. For testing purposes I made the following procedure TryWriteFile, testing if I can create a file in the WebApplicationDirectory:

procedure WriteMyFile;
var
  fname: string;
  fs: TFileStream;
begin
  fname := TPath.Combine(WebApplicationDirectory, 'myfile.log');
  fs := TFileStream.Create(fname, fmCreate or fmOpenWrite or fmShareDenyNone);
  fs.Write([$54,$65,$73,$74,$0D,$0A], 6); { T e s t CR LF }
  fs.Free;
end;

Now, I try calling WriteMyFile in the DefaultHandler action, but get an "access denied" Exception:

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  WriteMyFile;  //==> exception: access denied!
  Response.Content := 'no error!';
end;

When I call WriteMyFile from a separate thread instead, it works. The file is created correctly:

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  thr: TThread;
begin
  //WriteMyFile;  //==> exception: access denied!

  thr := TThread.CreateAnonymousThread(WriteMyFile); //works, file is created!
  thr.FreeOnTerminate := False;
  thr.Start;
  thr.WaitFor;
  thr.Free;
  Response.Content := 'no error!';
end;

So my question is: Why? Does it have something to do with Windows folder permissions? And if so, how can those be different in different threads?

My configuration

IIS configuration:

  1. ISAPI restrictions set to "Allow unspecified ISAPI modules"
  2. new application pool MYPOOL
    • no managed code
    • 32bit enabled (since my dll is compiled to 32bit)
  3. new Website "My Website"
    • physical path C:\inetpub\wwwroot\MYPOOL\ with added "Modify" permission for IIS AppPool\MYPOOL
    • Handler Mappings: Execution allowed.

Both IIS and Delphi 11.1 Alexandria are running on my Windows 10 machine.


Solution

  • I found the culprit: it was an IIS configuration problem. I didn't configure Authentication for my virtual directory (which is a different thing than Authentication for the physical path).

    The original behavior is still weird, though. But that "just" seems to be an IIS quirk, not related to my code (nor Delphi).

    Update

    Thank you Peter Wolf for providing a method to confirm this! The default Authentication for my virtual directory was IUSR, which is exactly, what GetUserName spits out:

    uses Winapi.Windows;
    var  usrname: string;
    
    procedure GetMyUserName;
    var
      len: Cardinal;
    begin
      len := 100;
      SetLength(usrname, len);
      GetUserNameW(PWideChar(usrname), len);
      SetLength(usrname, len);
    end;
    
    procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    var
      thr: TThread;
    begin
      GetMyUserName;
      Response.Content := usrname;  //==> "IUSR"
    
      thr := TThread.CreateAnonymousThread(WriteMyFile);
      thr.FreeOnTerminate := False;
      thr.Start;
      thr.WaitFor;
      thr.Free;
      Response.Content := usrname;  //==> "MYPOOL"
    end;
    

    So apparently, different threads can be associated with different authentication identities.