0
votes

I apologise if this is a trivial question, but I have searched as much as I can and cannot find any solution.

I have a Delphi/FMX application that creates a DLL for Win32 and a DYLIB for macOS (High Sierra), 32 bits.

In a unit, I was simply using GetModuleName(hInstance) to get the name of the module that used the unit, and that works fine in Win32 and Win64. For example, if the main program is using the unit it will return the main program name. If however, a DLL used by the main program uses the unit, it will return the DLL name. I'm using this to create separate log files for the main program and any DLLs it calls and using the name retuned in the log file name.

The code that works (which is in a unit used by a DYLIB (OSX)/DLL (Win32) created with FMX) in Windows is:

sModuleName := TPath.GetFileNameWithoutExtension(GetModuleName(hInstance));

GetModulename is in the System.SysUtils unit and AFAIK there is no conditional define for Windows/OSX etc. My assumption, obviously wrong, is that it should work when built for OSX. It doesn't, it hangs on that line with no error, just an '(Not Responding)' in FORCE QUIT window. I have tried this on both a macOS VM and on a hard macOS system with the same result.

Is there something (else) I need to do to get GetModuleName working with OSX?

and/or

Is there something (similar) that will retrieve the module name if I build for OSX?

2
A quick check of the source for Rio's version of SysUtils shows that GetModuleName directly calls Windows.GetModuleFilename on line 25912, so apparently it's Windows only. (There's a define in there somewhere, because changing the target to Android causes Code Insight to no longer show either GetModuleName or GetModuleFilename, but switching to Win32 as a target shows them both and the direct linkage to Windows.pas.)Ken White
@KenWhite, Thanks, then clearly that's why it's hanging. Any ideas how I a can achieve the same result on a OSX build. I could use an arbitrarily unique name, but I'd like some meaning (like the module name) in the name so the user knows which of the several log files to address eg. DyLibTest_20190103_111326795.log.Kevin Black
Unfortunately, no - that's why I posted a comment instead of an answer. I don't do any development for Apple products, so I don''t install that support. I don't see anything that jumps out at me when looking in SysUtils. There are several people that frequent the Delphi tags that are using it for OSX, so one of them may be able to help.Ken White
ISTM that a hInstance is a Windows-specific thing.Rudy Velthuis
@RudyVelthuis, It may well be, but is there a OSX equivalent function, I don't care whether it's getModuleName(hInstance) or anything else. I simply want to know which module referred me to this point (ie. the main program or one of several DLLs which use the log file unit). Alternatively, I could setup the log file in the DLL or program (removing that setup from the Logfile unit INITIALIZE section), but I 'like' where it is now?Kevin Black

2 Answers

0
votes

As noted, the problem is really that the log file Unit has no idea where it was referenced from, but the Unit/Module that did the referencing knows it's own name?

So the workaround (and it is a workaround) was to move the actual call to the function that does the log file creation out of the Initialze Block in the log file unit (ie. putting it in a function called LogFileSetup with a single parameter: the calling unit name) and put a call to that function at the beginning of the calling unit's Initialize Block. So in the calling unit, for example:

initialization

  // Setup the log file
  if not(LogFileSetup('MyModuleName')) then
    ShowMessage('Logfile setup error');

And the function in the LogFile unit (for reference I'm using Log4Pascal which is free, simple and works):

function LogFileSetup(sModuleName : string): Boolean;
begin
    try
      Result := True;
      {$IFDEF MSWINDOWS}
      sLogFolder := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'logs';
      {$ENDIF MSWINDOWS}
      {$IFDEF MACOS}
      sLogFolder := IncludeTrailingPathDelimiter(GetHomePath) + 'Library/Logs/MyAppName';
      {$ENDIF MACOS}

      if not(DirectoryExists(sLogFolder)) then
        ForceDirectories(sLogFolder);

      sLogFile := IncludeTrailingPathDelimiter(sLogFolder) + sModuleName + '_' + FormatDateTime('YYYYMMDD', now) + '_' + FormatDateTime('hhmmsszzz',now) + '.log';
      EMPLogger := TLogger.Create(sLogFile);
      EMPLogger.getLogLevel;

      // INFO log message
      EMPLogger.Info('[Log4Pascal] Logging commenced');
    except
      Result := False;
    end;
end;

Of course, if anyone has an answer to the question: Is there an OSX equivalent to:

  GetModuleName(hInstance)

That would be excellent.

0
votes

GetModuleName also exists on MacOS. I think your problem is not that the function doesn't exist, but that you should not call it from within the initialization section. In general you must avoid calling into other dlls/dylibs in the initialization section so that you don't trigger the loader lock.