Jump to content

Yaron

Members
  • Content Count

    286
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by Yaron


  1. @eivindbakkestuen:

    I have a 4 Core/8 Thread GPU and the CPU is not really tasked, I believe it's mostly disk I/O.

     

    @Fr0sT.Brutal:

    You missed the bit where I wrote that the mediainfo data is synched with a central scrape-coordination thread, so the main UI is only disturbed when all the data has been scraped. And yes, it can be analyzing 100's (even 1000's) of files, trying to do so for about 4 concurrently.

     


  2. @Remy Lebeau Debugging multiple threads is very difficult (at least in D7) as the run-point seems to jump between the threads each time you advance one line (unless there's something I'm missing?)

    This is why I use a lot of debug log entries.

     

    I believe the code I pasted above doesn't synchronize anything with the main thread, it's passing data to another (scraping) thread which works (synchronizes) just fine when doing other heavy work unrelated to mediainfo.

     


  3. Here are more details (it's somewhat big, but may help others in the future):

     

    1. I have an Idle priority information-scraping thread so the UI won't be affected by the scraping process.
    2. The scraping thread pre-creates & uses an idle priority TMediaInfoThread thread (see below).
    3. The scraping activates the mediainfo thread.
    4. This is the mediainfo's DLL pascal header file : https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfoDLL/MediaInfoDLL.pas
    5. And you can download the DLL here https://mediaarea.net/en/MediaInfo/Download/Windows

     

    Code used to activate MediaInfo data gathering thread:

       MediaInfoThread.MediaName  := MediaName;
       MediaInfoThread.ScrapeType := niVideo;
       MediaInfoThread.ResumeThread;
    
       // do other scraping related meta-data gathering (e.g. online DB lookup) while mediainfo is processing to maximize concurrently/performance.
    
       While MediaInfoThread.IsSuspended = False do Sleep(1);
       If MediaInfoThread.miResult = True then
    


    The MediaInfo thread code:

    Type
      TMediaInfoThread = Class(TThread)
        procedure execute; override;
      private
        FEvent             : THandle;
      public
        ScraperThreadIndex : Integer;
        IsSuspended        : Boolean;
        ScrapeType         : Integer;
        MediaName          : WideString;
        miResult           : Boolean;
        miWidth            : Integer;
        miHeight           : Integer;
        miDuration         : Double;
        miIcons            : WideString;
        miMediaTAGs        : TZPTAGRec;
        Closed             : PBoolean;
        procedure ResumeThread;
      end;
    
    
    procedure TMediaInfoThread.Execute;
    begin
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Thread Created, ID #'+IntToStr(ThreadID));{$ENDIF}
      FEvent := CreateEvent(nil,
                            False,    // auto reset
                            False,    // initial state = not signaled
                            nil);
    
      ResetTagData(miMediaTAGs);                      
      ScrapeType  := niVideo;
      IsSuspended := True;
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Thread Initially Suspended');{$ENDIF}
      WaitForSingleObject(FEvent, INFINITE);  // Wait to start processing
      {$IFDEF LOC1ALTRACE}
      DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Thread Initially Resumed'+CRLF);
      {$ENDIF}
      While (Terminated = False) do
      Begin
        {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','MediaInfo on "'+MediaName+'" (before)');{$ENDIF}
        miResult := MediaInfo_GetMediaInfoCache(MediaName,ScrapeType,miIcons,miWidth,miHeight,miDuration,@miMediaTAGs,ScraperThreadIndex);
        {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','MediaInfo on "'+MediaName+'" (after)');{$ENDIF}
        If Terminated = False then
        Begin
          IsSuspended := True;
          {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','MediaInfo complete, Thread Suspended'+CRLF);{$ENDIF}
          WaitForSingleObject(FEvent, INFINITE);  // No more Work
          {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Thread Resumed');{$ENDIF}
          If Terminated = False then ResetTagData(miMediaTAGs);
        End;
      End;
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Close event handle');{$ENDIF}
      CloseHandle(fEvent);
    
      If @Closed <> nil then Closed^ := True;
    
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(ScraperThreadIndex)+'.txt','Thread Terminated');{$ENDIF}
    end;
    
    
    procedure TMediaInfoThread.ResumeThread;
    begin
      SetEvent(FEvent);
      IsSuspended := False;
    end;
    
    function MediaInfo_GetMediaInfo(FileName : WideString; ScrapeType : Integer; miData : PMediaInfoDataRecord; grabThreadID : Integer) : Boolean;
    var
      miHandle       : Cardinal;
      sFormat        : WideString;
      sFormatVersion : WideString;
      sFormatString  : WideString;
      sProfile       : WideString;
      sCodecID       : WideString;
      sScanType      : WideString;
      sDuration      : WideString;
      aCount         : Integer;
      vCount         : Integer;
      I              : Integer;
      mWidth         : Integer;
      mHeight        : Integer;
      mChannels      : Integer;
      iWidth         : Integer;
      iHeight        : Integer;
      iChannels      : Integer;
      iCodec         : Integer;
      S              : String;
    begin
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','MediaInfo_GetMediaInfo (before)');{$ENDIF}
      Result := False;
      With miData^ do
      Begin
        iconFormat     := niNone;
        iconResolution := niNone;
        iconVideo      := niNone;
        iconAudio      := niNone;
        iconChannels   := niNone;
        ResetTagData(TAGs);
    
        If MediaInfoLoaded = True then
        Begin
          {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','MediaInfoLoaded = True');{$ENDIF}
          miHandle        := MediaInfo_New;
          {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','MediaInfo Handle : '+IntToHex(miHandle,8));{$ENDIF}
          If MediaInfo_Open(miHandle, PWideChar(FileName)) = 1 then
          Begin
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Open successful');{$ENDIF}
            Result := True;
    
            // Duration
            sDuration      := MediaInfo_Get(miHandle, Stream_General, 0, 'Duration'      , Info_Text, Info_Name);
            mediaDuration  := StrToFloatDef(sDuration,0) / 1000;
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Duration : '+sDuration);{$ENDIF}
    
            // File format
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','*** File Format ');{$ENDIF}
            sFormat        := MediaInfo_Get(miHandle, Stream_General, 0, 'Format'        , Info_Text, Info_Name);
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Format : '+sFormat);{$ENDIF}
            sFormatVersion := MediaInfo_Get(miHandle, Stream_General, 0, 'Format_Version', Info_Text, Info_Name);
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','FormatVersion : '+sFormatVersion);{$ENDIF}
            sProfile       := MediaInfo_Get(miHandle, Stream_General, 0, 'Format_Profile', Info_Text, Info_Name);
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Profile : '+sProfile);{$ENDIF}
            iconFormat     := MediaInfo_FormatToNavIcon(sFormat,sFormatVersion,sProfile);
    
            If ScrapeType = niVideo then
            Begin
              // Video Codec & Resolution
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','*** Video Codec & Resolution');{$ENDIF}
              mWidth  := -1;
              mHeight := -1;
              vCount := MediaInfo_Count_Get(miHandle, Stream_Video, -1);
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Video Streams : '+IntToStr(vCount));{$ENDIF}
              For I := 0 to vCount-1 do
              Begin
                iWidth         := StrToIntDef(MediaInfo_Get(miHandle, Stream_Video, I, 'Width' , Info_Text, Info_Name),-1);
                iHeight        := StrToIntDef(MediaInfo_Get(miHandle, Stream_Video, I, 'Height', Info_Text, Info_Name),-1);
    
                If (iWidth > mWidth) or (iHeight > mHeight) then
                Begin
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Video Resolution : '+IntToStr(iWidth)+'x'+IntToStr(iHeight));{$ENDIF}
                  mWidth         := iWidth;
                  mHeight        := iHeight;
                  sFormat        := MediaInfo_Get(miHandle, Stream_Video, I, 'Format'        , Info_Text, Info_Name);
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Format : '+sFormat);{$ENDIF}
                  sFormatVersion := MediaInfo_Get(miHandle, Stream_Video, I, 'Format_Version', Info_Text, Info_Name);
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. FormatVersion : '+sFormatVersion);{$ENDIF}
                  sProfile       := MediaInfo_Get(miHandle, Stream_Video, I, 'Format_Profile', Info_Text, Info_Name);
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Profile : '+sProfile);{$ENDIF}
                  sScanType      := TNT_WideLowercase(MediaInfo_Get(miHandle, Stream_Video, I, 'ScanType', Info_Text, Info_Name));
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. ScanType : '+sScanType);{$ENDIF}
                  sCodecID       := MediaInfo_Get(miHandle, Stream_Video, I, 'CodecID'       , Info_Text, Info_Name);
                  {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. CodecID : '+sCodecID);{$ENDIF}
    
                  iconResolution := MediaInfo_ResolutionToNavIcon(mWidth,mHeight,sScanType);
                  iconVideo      := MediaInfo_VideoCodecToNavIcon(sFormat,sFormatVersion,sCodecID,sProfile);
                  mediaWidth     := mWidth;
                  mediaHeight    := mHeight;
                End;
              End;
            End
              else
            Begin
              // Audio TAGs
              TAGs.tgTitle     := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Track'              , Info_Text, Info_Name));
              TAGs.tgGenre     := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Genre'              , Info_Text, Info_Name));
              TAGs.tgTrack     := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Track/Position'     , Info_Text, Info_Name));
              TAGs.tgArtist    := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Performer'          , Info_Text, Info_Name));
              TAGs.tgAlbum     := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Album'              , Info_Text, Info_Name));
              TAGs.tgYear      := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Recorded_Date'      , Info_Text, Info_Name));
              TAGs.tgAuthor    := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'WrittenBy'          , Info_Text, Info_Name));
              TAGs.tgComment   := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Comment'            , Info_Text, Info_Name));
              TAGs.tgURL       := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Track/Url'          , Info_Text, Info_Name));
              TAGs.tgCopyright := UTF8Decode(MediaInfo_Get(miHandle, Stream_General, 0, 'Copyright'          , Info_Text, Info_Name));
              S                     :=            MediaInfo_Get(miHandle, Stream_General, 0, 'Encoded_Application', Info_Text, Info_Name);
              If S = '' then S      :=            MediaInfo_Get(miHandle, Stream_General, 0, 'Encoded_Library'    , Info_Text, Info_Name);
              TAGs.tgAuthor    := UTF8Decode(S);
            End;
    
            // Audio Codec & Channels
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','*** Audio Codec & Channels');{$ENDIF}
            mChannels := -1;
            aCount    := MediaInfo_Count_Get(miHandle, Stream_Audio, -1);
            {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Audio Streams : '+IntToStr(aCount));{$ENDIF}
            For I := 0 to aCount-1 do
            Begin
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Audio Stream');{$ENDIF}
              iCodec    := -1;
              //sCommercialName := MediaInfo_Get(miHandle, Stream_Audio, I, 'Format_Commercial_IfAny', Info_Text, Info_Name);
              sFormatString := MediaInfo_Get(miHandle, Stream_Audio, I, 'Format/String' , Info_Text, Info_Name);
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Format String : '+sFormatString);{$ENDIF}
              sFormat       := MediaInfo_Get(miHandle, Stream_Audio, I, 'Format'        , Info_Text, Info_Name);
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Format : '+sFormat);{$ENDIF}
              sProfile      := MediaInfo_Get(miHandle, Stream_Audio, I, 'Format_Profile', Info_Text, Info_Name);
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Profile : '+sProfile);{$ENDIF}
              iChannels     := MediaInfo_AudioChannelCount(MediaInfo_Get(miHandle, Stream_Audio, I, 'Channel(s)', Info_Text, Info_Name));
              {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt',IntToStr(I)+'. Channels : '+IntToStr(iChannels));{$ENDIF}
    
              iCodec    := MediaInfo_AudioCodecToNavIcon(sFormat,sProfile,sFormatString);
    
              If (iChannels > mChannels) or
                 ((iconAudio = ni_codecDD       ) and (iCodec in [ni_codecDTS,ni_codecDTS_ES,ni_codecDTS_HD_MA,ni_codecDD_TrueHD] = True)) or
                 ((iconAudio = ni_codecDTS      ) and (iCodec in [ni_codecDTS_ES,ni_codecDTS_HD_MA,ni_codecDD_TrueHD] = True)) or
                 ((iconAudio = ni_codecDTS_ES   ) and (iCodec in [ni_codecDTS_HD_MA,ni_codecDD_TrueHD] = True)) or
                 ((iconAudio = ni_codecDD_TrueHD) and (iCodec in [ni_codecDTS_HD_MA] = True)) then
              Begin
                mChannels    := iChannels;
                iconChannels := MediaInfo_AudioChannelsToNavIcon(mChannels);
                iconAudio    := MediaInfo_AudioCodecToNavIcon(sFormat,sProfile,sFormatString);
              End;
            End;
          End;
          {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','Close Handle');{$ENDIF}
          MediaInfo_Close(miHandle);
        End;
      End;
      {$IFDEF LOCALTRACE}DebugMsgFT('c:\log\MediaInfoThread_'+IntToStr(grabThreadID)+'.txt','MediaInfo_GetMediaInfo (after)'+CRLF);{$ENDIF}
    end;
    
    
    function MediaInfo_ProccessTAGdata(FileName : WideString; mRec : TZPFileClass; var albumArt : TBitmap) : Boolean;
    var
      miHandle       : Cardinal;
      sImage         : String;
      sDecoded       : String;
      fStream        : TMemoryStream;
      S              : String;
    
    begin
      Result         := False;
    
      If MediaInfoLoaded = True then
      Begin
        miHandle        := MediaInfo_New;
        If MediaInfo_Open(miHandle, PWideChar(FileName)) = 1 then
        Begin
          sImage := MediaInfo_Get(miHandle, Stream_General, 0, 'Cover_Data'  , Info_Text, Info_Name);
    
          If sImage <> '' then
          Begin
            sDecoded := Base64DecodeString(sImage);
            Result   := True;
            fStream  := TMemoryStream.Create;
            fStream.Write(sDecoded[1],Length(sDecoded));
            fStream.Position := 0;
            LoadDetectedImage(fStream,albumArt);
            fStream.Free;
          End;
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Track'  , Info_Text, Info_Name);
          If S = '' then S := MediaInfo_Get(miHandle, Stream_General, 0, 'Movie'  , Info_Text, Info_Name);
    
          If mRec.zplTAG.tgTitle = '' then
          Begin
            If S = '' then mRec.zplTAG.tgTitle := mRec.zplName else mRec.zplTAG.tgTitle := UTF8Decode(S);
          End;
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Genre'              , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgGenre     = '') then mRec.zplTAG.tgGenre     := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Track/Position'     , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgTrack     = '') then mRec.zplTAG.tgTrack     := FillZero(S,2);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Performer'          , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgArtist    = '') then mRec.zplTAG.tgArtist    := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Album'              , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgAlbum     = '') then mRec.zplTAG.tgAlbum     := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Recorded_Date'      , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgYear      = '') then mRec.zplTAG.tgYear      := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'WrittenBy'          , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgAuthor    = '') then mRec.zplTAG.tgAuthor    := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Encoded_Application', Info_Text, Info_Name);
          If S = '' then
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Encoded_Library'    , Info_Text, Info_Name);
    
          If (S <> '') and (mRec.zplTAG.tgEncoder   = '') then mRec.zplTAG.tgEncoder   := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Comment'            , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgComment   = '') then mRec.zplTAG.tgComment   := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Track/Url'          , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgURL       = '') then mRec.zplTAG.tgURL       := UTF8Decode(S);
    
          S := MediaInfo_Get(miHandle, Stream_General, 0, 'Copyright'          , Info_Text, Info_Name);
          If (S <> '') and (mRec.zplTAG.tgCopyright = '') then mRec.zplTAG.tgCopyright := UTF8Decode(S);
    
          SendLCDs(2400,strNoMoreData);
        End;
        MediaInfo_Close(miHandle);
      End;
    end;
    
    


  4. I'm the author of Zoom Player a Windows media player/home theater application.

    One of Zoom Player's features is that it scrapes information on the video file using multiple mechanism.

     

    Two of these mechanisms are somehow severely lagging my user interface even though I'm running the code in separate threads with Idle priority:

    1. Running MediaInfo.dll on the media file.

    2. Using a non-visual background directshow graph using LAV Filters (directshow filters) to extract a video frame from the video.

     

    Other than moving this functionality to completely separate processes, is there a method to ensure my UI (running in the main thread) remains responsive?


  5. I managed to debug the SSL DLL's LoadLibrary's GetLastError value and it's #193, which according to microsoft is "ERROR_BAD_EXE_FORMAT".

     

    I don't belive the DLLs were corrupted, but just in case I downloaded the latest versions of the DLLs (v1.0.2.21) and no more errors on my local Win10 machine, but the Win2019 server is still triggering the same exceptions.


  6. I did further testing and it's also failing outside of IIS, when running as a normal process on my local Windows 10 machine (which I also recently upgraded from Windows 7 where the code worked fine).

     

    So is this issue possibly specific to Windows 10/2019?


  7. @Remy Lebeau

    I upgraded to a new server (Win2008 to Win2019) and now I'm getting the same issue (trying to send eMail via Amazon SES).

     

    This is inside an ISAPI 32bit dll that worked just fine when running under Windows 2008, but fails to send email when running under Windows 2019.

    I literally use the same code, same DLLs as the Win2008 machine, all DLLs (my DLL & SSL) are 32bit, everything else in the code works fine except for sending eMail using TLS (SSL).

    Inside IIS's application pool, the DLL is defined as 32bit, classic and "no managed code".

     

    When calling "smtp.send" (TIdSMTP), I get the following exception and additional info:

    
    3/22/2020 10:00:49 AM [       11854 ms] : SMTP Send Exception : SSL negotiation failed.
    
    3/22/2020 10:00:49 AM [       11854 ms] : Inner Exception : Could not load SSL library.
    3/22/2020 10:00:49 AM [       11855 ms] : WhichFailedToLoad : "Failed to load libeay32.dll."
    
    

     

    The DLLs are in the same folder as the ISAPI DLL, I also tried placing them in the Windows/System32 folder but it made no difference.

    Both "libeay32.dll" and "ssleay32.dll" are v1.0.2.19 and I'm using Delphi 10.3.3.

     


  8. I recently upgrade my main development machine from Windows 7 to Windows 10.

     

    The major thing I noticed is that executing my app under the debugger is slower and file access (on an SSD drive) also seems impacted even though benchmarks show that read/write/io speeds are identical to Windows 7.

     

    Is this something anyone else encountered and possibly found a solution?


  9. What is the best approach you would recommend for creating log files with output to multiple files and input coming from multiple threads (some threads writing into the same log file) with a volume that can reach 1000's of entries per second?

     

    Everything is running in a single process, so Critical section locks can be used and log entries must be flushed so they won't be lost in RAM if the process crashes.


  10. I prepared to post a question on how I can do this, but I actually reached a solution on my own while contemplating on how to best phrase the question and code.

     

    Since I searched and couldn't find good documentation on how to do this, I decided to post my solution here:

     

    Imagine a table with columns: NAME , TAGS , etc ...

    The TAGS column contains a string with multiple unique identifiers, for example "tag0001|tag0002|tag0003|tag0004".

     

    I'm only getting rows with a TAGS column that contains a sub-string I'm looking for (a specific tag) :

       sTAGUID := 'tag0002';
       dbQuery.SQL.Text := 'SELECT * FROM CARDS_TABLE WHERE (POSITION(:taguid,TAGS) > 0) ORDER BY LOWER(NAME) ASC;';
       Try
         dbQuery.Prepare;
         dbQuery.ParamByName('taguid').AsString := sTAGUID;
         dbQuery.Open;
         If dbQuery.RecordCount > 0 then While dbQuery.Eof = False do
         Begin
           New(nEntry);
           ExtractCardQuery(dbQuery,nEntry^);
           cardList.Add(nEntry);
           dbQuery.Next;
         End;
       finally
         dbQuery.free
       end;

     


  11. I would like to integrate Ad support for my Delphi 7 desktop application (I also have Delphi 10.3.3 which I could possibly use to create a DLL which would integrate into the D7 app).

     

    Has anyone here ever create a Delphi desktop app that's supported by advertising?

    This model is very prevalent in the mobile world, but on the desktop, I haven't found a lot of information.

     

    I could easily integrate google ads, but I believe they do not allow ads within desktop applications.

    Microsoft seems to allow Ads (https://developer.microsoft.com/en-us/store/monetize/ads-in-apps), but the API is designed for UWP apps (am I mistaken?) and I'm not sure how to go about the integration process.

    Do you suggest other Ad networks that would integrate nicely with Delphi?


  12. Using Delphi 10.3.3:

    My goal is to have a stable client/server remote control over TCP/IP that can automatically recover in cases where the network disconnects occasionally.

     

    I use a structure Remi suggested of creating a reading thread that tries to readln with a 1sec time out:

        FClient.OnDisconnected := OnDisconnect;
        FClient.IOHandler.ReadTimeout := 1000; // lets us exit the read operation after 1sec of inactivity
    
        While Terminated = False do
        Begin
          If FClient.Connected = True then
          Try
            FData := FClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
          Except
            // catch exception & update UI
            Try
              If FClient.Connected = True then FClient.Disconnect;
            except
              on E: Exception do
              Begin
                // Catch disconnect exception
              End;
            end;
          End;
        End;  

    I have a client PC connected to a router via cable and the server PC connected to the same router via WiFi.

     

    After connecting the client to the server, I disconnect the server's wifi access and absolutely nothing happens (I waited over a minute).

    The OnDisconnected event isn't triggered, calling FClient.IOHandler.ReadLn doesn't return any exception and FClient.Connected remains True.

     

    If I then try to call FClient.IOHandler.WriteLn, within about 10 seconds a "Socket Error # 10054 - Connection reset by peer" exception is raised on the ReadLn code about (not on the WriteLn function).

    In that exception, I call "If FClient.Connected = True then FClient.Disconnect" which raises another "Socket Error # 10054 - Connection reset by peer" exception.

    Finally, my code loops to the beginning and then the same exception is triggered on the initial "If FClient.Connected = True" line.

     

    Obviously, I'm not handling things correctly, I wasn't expecting "If FClient.Connected" to raise exceptions and I was hoping the OnDisconnect event to trigger, but are there other pitfalls I might be missing?


  13. On Android, RTL text (e.g. Hebrew) appears in reverse order (letter order is reversed).

    Using the same code, & input (YouTube DATA API 3) and the text appears correctly in windows.

     

    I searched the web and the only reference I was able to find is a post from five years ago that says RTL is not supported and there is no roadmap entry for future support:

    https://stackoverflow.com/questions/28494354/right-to-left-language-in-delphi-xe7-mobile-android

     

    Has anything changed since?


  14. For some reason, even when setting the TListView's control to not show the "Details" section, the text within the control is cut at about 80% of the width.

     

    I have "ListView.ItemAppearanceObject.ItemObjects.Detail.Visible = False" and "ListView.ItemAppearanceObject.ItemEditObjects.Detail.Visible = False".

    And "ListView.ItemAppearance" set to "ListItem" (which adds a ">" at the end of the line for some reason).

     


  15. I am sharing a video's URL from YouTube to my Android app.

     

    I do this using Android Intents by modifying the "AndroidManifest.template.xml", adding:

    <intent-filter>  
      <action android:name="android.intent.action.SEND" />
      <category android:name="android.intent.category.DEFAULT" />
      <data android:mimeType="text/plain" />
    </intent-filter>

     

    Then in my app's form OnCreate event, I use:

      if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, AppEventService) then
        AppEventService.SetApplicationEventHandler(HandleAppEvent);
    
      MainActivity.registerIntentAction(TJIntent.JavaClass.ACTION_SEND);
      TMessageManager.DefaultManager.SubscribeToMessage(TMessageReceivedNotification, HandleActivityMessage);

    Then HandleAppEvent looks like this:

    function TMainForm.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
    var
      StartupIntent: JIntent;
    begin
      Result := False;
      Case AAppEvent of
        TApplicationEvent.BecameActive:
        Begin
          clientAndroidBecameActive := True;
          If clientAndroidActivated = False then
          Begin
            clientAndroidActivated := True;
            StartupIntent := MainActivity.getIntent;
            if StartupIntent <> nil then
            Begin
              HandleIntentAction(StartupIntent);
              StartupIntent := nil;
            End;
          End;
        End;
      end;
    end;

    And HandleActivityMessage looks like:

    procedure TMainForm.HandleActivityMessage(const Sender: TObject; const M: TMessage);
    begin
      if M is TMessageReceivedNotification then HandleIntentAction(TMessageReceivedNotification(M).Value);
    end;

    And finally, HandleIntentAction:

    function TMainForm.HandleIntentAction(const Data: JIntent): Boolean;
    var
      Extras : JBundle;
      sURL   : String;
    begin
      Result := False;
      if Data <> nil then
      begin
        Extras := Data.getExtras;
        if Extras <> nil then
        Begin
          sURL := JStringToString(Extras.getString(TJIntent.JavaClass.EXTRA_TEXT));
          // Do something useful with the URL
          Extras := nil;
        End
      end
    end;

    My problem is there are occasions after sharing where an intent is duplicated, triggering once on the share and again when opening the app later on.

     

    Does anyone have experience with something similar?

     

     


  16. If anyone else is wondering how it's done, you need to add "[Context] marsRequest: TWebRequest;" and then the client's IP is available in "marsRequest.RemoteIP".

      [Path('projectX')]
      TProjectXServicesResource = class
      protected
        [Context] marsRequest: TWebRequest;
      public
        [GET, Produces(TMediaType.TEXT_HTML)]
        function xHome([QueryParam] lang : String) : String;
    
        [POST, Consumes(TMediaType.MULTIPART_FORM_DATA), Produces(TMediaType.TEXT_HTML)]
        function xAction([FormParams] AParams: TArray<TFormParam>): String;
      end;
    
    function TProjectXServicesResource.xHome([QueryParam] lang : String) : String;
    begin
      ShowMessage(marsRequest.RemoteIP);
    end;
    
    function TProjectXServicesResource.xAction([FormParams] AParams: TArray<TFormParam>) : String;
    begin
      ShowMessage(marsRequest.RemoteIP);
    end;

     

×