Jump to content

NamoRamana

Members
  • Content Count

    28
  • Joined

  • Last visited

Posts posted by NamoRamana


  1. Yes, I tried that as well.. For all the interfaces, the addresses come out at 0s though...

     

    I also tried the solution suggested by Remy here: https://stackoverflow.com/questions/23563932/getadaptersinfo-not-working-on-delphi-xe6. This works fine.

     

    The code posted in my original post has been part of the production system. Looks like if I don't find any solution to fix that code (or any reasonable explanation as to why it works on few machines and why it isn't on others), then I might have to with alternate API suggested here. 

     

    Thank you.


  2. I am running the following NetAPI32.dll > NetWkstaTransportEnum API (through JEDI JwaLM library) to get MAC address of a local machine... The API gives correct MAC addresses on somewhat older windows 10 machines.. It fails to return any MAC address on newer windows 10 machines (lEntriesRead comes out 0).. Tried both the scenarios through LAN and Wifi connectivity.

     

    Any ideas?

     

    function TForm1.GetNetAPIMacAddress: string;
    const
      NERR_Success = 0;
    var
      lStatus : NET_API_STATUS;
      lEntriesRead : DWORD;
      lEntriesTotal: DWORD;
      hRes: DWORD;
      lpBuffer : PWkstaTransportInfo0;
      lNBTransReq : PWkstaTransportInfo0;
      lServerName : WideString;
      i : DWORD;
      lMAC : string;
    begin
      Memo1.Lines.Add('');
      Memo1.Lines.Add('');
      Memo1.Lines.Add('==========================');
      Memo1.Lines.Add('Call to NetApi32.DLL >>   lStatus := JwaLM.NetWkstaTransportEnum(LPWSTR(lServerName), 0, Pointer(lpBuffer),' +
        sLineBreak + ' MAX_PREFERRED_LENGTH, lEntriesRead, lEntriesTotal, @hRes)');
      Result := '';
      lServerName := '';
      hRes := 0;
    
      lStatus := JwaLM.NetWkstaTransportEnum(LPWSTR(lServerName), 0, Pointer(lpBuffer),
        MAX_PREFERRED_LENGTH, lEntriesRead, lEntriesTotal, @hRes);
    
      Memo1.Lines.Add('hRes: ' + IntToStr(hres));
      Memo1.Lines.Add('lEntriesRead: ' + IntToStr(lEntriesRead));
      Memo1.Lines.Add('lEntriesTotal: ' + IntToStr(lEntriesTotal));
      Memo1.Lines.Add('lStatus: ' + IntToStr(lStatus));
    
      try
        if (lStatus = NERR_Success) and (lEntriesRead > 0)  then
        begin
          lNBTransReq := lpBuffer;
          Result := '';
          for i := 0 to lEntriesRead - 1 do
          begin
            lMAC := lNBTransReq.wkti0_transport_address;
            // exclude disabled nics...
            if lMAC <> '000000000000' then
            begin
              Insert('-', lMAC, 11);
              Insert('-', lMAC, 9);
              Insert('-', lMAC, 7);
              Insert('-', lMAC, 5);
              Insert('-', lMAC, 3);
              Result := Result + lMAC+'  ';
              Memo1.Lines.Add('MAC Addess: ' + Result);
            end;
            Inc(lNBTransReq);
          end;
        end;
      finally
        NetApiBufferFree(lpBuffer);
      end;
      Memo1.Lines.Add('');
      Memo1.Lines.Add('');
    end;

     


  3. @KodeZwerg - I'm not planning to change the VCL code. Thank you for your contribution. 

    @Remy Lebeau - I suspected GetVersionEx() as I was looking TOSVersion constructor. Thank you for your detailed reply. I do not have manifests for both Exe1 and Exe2. I will create and include (as a resource to the executable) the manifest file with "requireAdministrator" privilege in Exe2.

     

    Couple of questions - (1). Do I need to create manifest with "requireAdministrator" privilege for Exe1 as well? (2). Could you please take a look at following link that has <supportedOS> tag? As I understand, I will remove tags for Windows 7 and Windows Vista from it.

     

    https://stackoverflow.com/questions/50818954/manifest-and-getversionex

     


  4. I got an unusual problem about the Windows Notification.. I implemented a basic Windows Notification in Exe2.. Following is the sample code:

     

      //ShowMessage(aMessage);
      vNotifiCenter := TNotificationCenter.Create(nil);
      try
        if vNotifiCenter.Supported then
        begin
          vNotification := vNotifiCenter.CreateNotification;
          try
            vNotification.AlertBody:= aMessage;
            vNotification.Title:= aTitle;
            vNotification.FireDate := Now;
            vNotification.EnableSound:= False;
            vNotifiCenter.PresentNotification(vNotification);
          finally
            vNotification.Free;
          end;
        end
        else
          ShowMessage('Not Supported');
      finally
        vNotifiCenter.Free;
      end;

     

    When I double-click on Exe2 from File Explorer OR run in debug,  it works fine... But in reality, Exe2 is started by Exe1 using CreatePorcess(). This is when I get "Not Supported" msg.

     

    Following is the code from Exe1 that starts Exe2..

     

    function CreateProcessAndReturn(const AppName: string; ShowState: Integer): THandle;
    var
      ProcessInfo: TProcessInformation;
      StartupInfo: TStartupInfo;
    begin
      FillChar(StartupInfo, SizeOf(TStartupInfo), #0);
      with StartupInfo do
      begin
        cb := SizeOf(TStartupInfo);
        dwFlags := STARTF_USESHOWWINDOW;
        wShowWindow := ShowState;
      end;
      if CreateProcess(nil, PChar(AppName), nil, nil, False,
        CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo,
        ProcessInfo) then
        Result := ProcessInfo.hProcess
      else
        Result := 0;
    end;

    Any ideas on how to fix it?

     

    Thank you in advance.


  5. Thanks for the pointer on WMI classes. There are few COM and DCOM related classes like Win32_ClassicCOMClass, Win32_COMClass, Win32_DCOMApplication. etc, but couldn't find anything that gives COM statistics. Appreciate your help.


  6. Update: Thank you very much, @pyscripter for pointing missing inheritance. We modified the code accordingly and tested in both the versions... In Rio, we still see the  memory increase.. But in 10.4.1, it did not.

     

    Thank you Anders and Dave as well.

     

    In conclusion, we are planning to upgrade our code base to 10.4.1.


  7. My bad.. Even when you take these 2 functions out and comment out all OutputDebugString() from Initialize and Destroy, it still increases the memory in Rio.. We are currently testing in 10.4.1.. Will post the update about that later.

     

    COMBug.zip


  8. Ok, Guys.. took me a while to come up with reproducible code.. But its attached here. You will see that with each click on a button, the memory increases. We also ran the same code under D 10.4.1 and unfortunately, the behavior is the same -- it keeps on increasing the memory.

     

    COMBug.zip

     


  9. Ok.. I tried passing the OleVariant by ref. That didn't work. I also tried assigning a temp variable "UnAssigned". That didn't work either. 

     

    Now I found the following QC: https://quality.embarcadero.com/browse/RSP-14749.

     

    This mentions the almost similar situation I have. I tried to look around in System.Win.ComObj to find  DispatchInvoke(). In it, it has try..finally, which calls FinalizeDispatchInvokeArgs() which does the clean up. I think, FinalizeDispatchInvokeArgs() has the same code mention in comment in QC: https://quality.embarcadero.com/browse/RSP-9819

     

    Can a Delphi Guru enlighten me with simplicity here -- whether the leak mentioned above is caused by these delphi units? Coz code in System.Win.ComObj and System.Variants are too hot for me... 🔥


  10. Thank you, Anders for your reply. Yes, the OleVariant parameter is passed as Value. Below snippet is from _TLB.pas.

      Ixxx = interface(Iyyy)
        ['{C2D9C6D7-2282-4DF2-B881-D097CA4B46EF}']
        procedure Save(ABusinessObj: OleVariant;.....); safecall;
    ...

    We are planning to experiment with few options, including passing Olevariant parameter by reference. Also planning to experiment with the solution found here: https://stackoverflow.com/questions/3639113/how-do-i-stop-this-variant-memory-leak

    var
    	tmp: OleVariant;
    begin
    ...
    	tmp := a.asXML;
     	SaveCOMServer.Save(tmp...);
    	tmp := Unassigned;
    ...

    Your other suggestions are good, too. Thank you for that. To answer your (3), the version I gave out is a stripped out version. AsSaveXML(), the procedure called when you write to the property AsXML(), calls internally few other functions and procedures. The "lOutputStream" is the common stream upon further processing happens to set the properties on an object. Apart from varOleStr, it also checks the varArray (compressed and uncompressed) and build "lOutputStream", ex:

     

      if (VarType(AInputVariant) = varOleStr) then
      begin
    	...      
    	lOutputStream.CopyFrom(lStringStream, lStringStream.Size);
    	....
      end
      else
      begin
        if VarIsArray(AInputVariant) then
        begin
    		...
    		lOutputStream.WriteBuffer(Data^, Size);
    		...
    	end;
    
    	// lOutputStream is now ready to further parsing.

     


  11. We have an old windows service, recently upgraded in Delphi Rio, that spawns off few threads. Each thread is interacting with COM DLLs. Each thread is working on its own and doesn't need synchronization. Each thread works on a separate record, so no issues of locking. When I run following code, the memory rapidly increases in the Task Manager as threads process records. Its also reported as BSTR (Widestring) type leak in Deleaker. And finally, we end up having the "out of memory".

    Each thread does following:

        var
            a: IBusinessClass;
        begin
            a := ObjCOMServer.GetNewInstance('TBusinessClass') // Not the actual call, but the idea is to ask a COM dll to give a new instance of TBusinessClass
            a.RowId := GetNextId;
            a.AWideStringProperty := 'Test';
            a.AnIntProperty := 1001;
            ....etc...
            
            SaveCOMServer.Save(a.asXML, ...) // This is the prior to upgrade code. If I comment out this line, leak doesn't happen
            // SaveCOMServer.Save(String(a.asXML), ...) <--- If I execute this statement then it doesn't leak.
        end;

    We tried following but still memory increased in the same way above...
    (1). Declare a local variable of type OleVariant to hold a.asXML..and pass it to Save().
    (2). In the Save() on SaveCOMServer, the first statement we put is "exit" to rule out any SetAsXML() (code below) leaks.

     

    Following is not showing any significant increase of memory in Task Manager (so technically, I don't know if it stopped memory leaks)

     

    (1). If I don't call Save() et all, then memory doesn't increase. But calling Save() is essential.
    (2). Declare a local string variable and assign a.asXML to it and pass it to Save(), then the memory increase stops.
    (3). When I apply casting string(a.asAXML) while calling Save(), the memory increase stops. 

     

    Some Details:

    asXML - is a read and write property of type OleVariant. Its get method - GetAsXML(), uses lib2XML parser and RTTI (old style, TypInfo),  builds up a XML structure of the object and return an OleVariant.

     

        function xxx.GetAsXML: OleVariant;
        var
          lDocument : xmlDocPtr;
          lRootNode: xmlNodePtr;
          lBuffer : xmlCharPtr; //PAnsiChar in LibXML2
          lBufferSize : integer;
          lResult : string;
          lName : string;
        begin
          lDocument := xmlNewDoc(nil);
          try
    
            // ... Builds up the XMLDocuments here using RTTI
            
            xmlDocDumpMemory(lDocument, @lBuffer, @lBufferSize);
            try
              lResult := lBuffer; // Ansi converted in unicode string
              Result := lResult; // unicode string to olestr
            finally
              xmlFree(lBuffer);
            end;
          finally
            xmlFreeDoc(lDocument);
          end;
        end;
            
      procedure xxx.SetAsXML(const AInputVariant: OleVariant)
      var 
        ...
      begin  
          lOutputStream := TMemoryStream.Create;
          lOutputStream.Position := 0;
    
          if (VarType(AInputVariant) = varOleStr) then
          begin
            lStringStream := TStringStream.Create(String(AInputVariant)); // <--- Notice the casting
            try
              lStringStream.Position := 0;
              lOutputStream.CopyFrom(lStringStream, lStringStream.Size);
            finally
              lStringStream.Free;
            end;
            
            // Now process the lOutputStream using libXML2 and RTTI and assign back the properties.
          end
       end;    
    

    Save() -- Takes input OleVariant, fills a local blank object with the data from OleVariant as setting local object's asXML := ABusinessObj
    Here is how Save() is defined in type library:

          interface Ixxx: Iyyy
          {
            [id(0x00000001)]
            HRESULT _stdcall Save([in] VARIANT ABusinessObj, ......)

    Can someone help to figure out what's going on? Why its leaking the memory, and not increasing when String() is applied, and what could be the clean solution?
        
     


  12. Was connecting to SQL server 14, creating FDConnection at run-time in a thread. Thread was running 3 queries on the same FDConnection. The program was fine in dev, but was getting this error in Prod. Following steps fixed it:

     

    - Prod machine had only default sql server driver (10.xx, I guess) that came with a new PC. Downloaded the ODBC 17 driver (and redistribution files, if needed) mentioned elsewhere.

    - Added following code:

     

        cnMain.DriverName := 'MSSQL';
        cnMain.Params.Add('Server=' + server);
        cnMain.Params.Add('Database=' + db);
        cnMain.Params.Add('User_name=' + username);
        cnMain.Params.Add('Password=' + pwd);
        cnMain.Params.Add('MARS=Yes');
        cnMain.Params.Add('OSAuthent=No');

     

    - Even with the new driver(ODBC 17), if I remove MARS, I get the error. The FireDAC documentation says, MARS=Yes by default, but somehow it didn't work in my case.

     

    Hope someone find it useful.

    Thanks.

    • Like 1

  13. Yes, I can compile our COM DLLs without UWP. I really don't have to use UWP. The reason I was exploring other options is that sometimes our components, when installed in COM+ servers, fail to perform well.. sometimes, admin has to kill dllhost on server. So I was wondering if UWP APIs can help me converting those COM components so that I don't have to rely on installing them in COM+ servers.

    • Like 1

  14. Looking at "modernize" videos, it appears that Emb is gently advocating for adoption of more of Windows 10 related changes.  In this regards, I was looking at WinRT related programming in Delphi Rio. Apart from few handful components, there is not much information on how to go about utilizing WinRT APIs. As I understood WinRT is adopting positives from COM and .Net architecture.. In a layman programmer's term, I was wondering if one would to "modernize" his existing COM DLLs using WinRT APIs, where to start? Its hard to learn and adopt WinRT programming just from the bunch of header .pas files. 


  15. Hi,

     

    Can a TCP File Server written using TWSocketServer be effective for serving hundreds of clients? Typically, when a client connects to a TCP server, the connection lasts for bit long time and during that time, more than 100+ files, each of few MB, are served.  Since ICS TCP server component is asynchronous and non-blocking, I was wondering, if any time-consuming operation on server can be implemented without threads.

     

    Any example would be appreciated.

     


  16. Original code was written in Delphi 2006.. But I think, I found the solution (untested). Somewhere Remy B. wrote that IdObjs and IdSys(??!!) has been obsolete. So the changes I made to above code were like:

    - Removed IdObjs from "interface uses"

    - Derived TSyncContext from TIdSeverContext (instead of TIdContext)

    - And changed the constructor signature to use TIdContextThreadList

     

    interface
    
    uses IdContext, Classes, SysUtils, IdCustomTCPServer, StreamUtils,
      IdTCPConnection, IdYarn, IdComponent; //IdObjs,
    
    type
      TRequestType = (rtNone, rtGetFileList, rtGetFile, rtGetMasterHash, rtDisconnect);
      TResponseType = (rstNone, rstFile, rstString, rstStringList);
    
      TGetMasterHash = procedure (var AHash : string) of object;
      TGetFileList = procedure (var AFileList : TStrings) of object;
      TGetFile = procedure (const AFilename : string; var AStream : TStream) of object;
      TBytesSent = procedure (AByteCount : integer) of object;
    
      TSyncContext = class(TIdServerContext)
      private
        FOnGetMasterHash: TGetMasterHash;
        FOnGetFileList: TGetFileList;
        FOnGetFile: TGetFile;
        FOnBytesSent: TBytesSent;
        FBytesSentTotal : integer;
      protected
        procedure WriteResponseHeader(AResponseType : TResponseType; ASize : Int64);
        function ReadRequestHeader : TRequestType;
    
        procedure DoGetMasterHash; virtual;
        procedure DoGetFileList; virtual;
        procedure DoGetFile(const AFilename : string); virtual;
        procedure DoBytesSent(AByteCount : integer); virtual;
    
        procedure DoWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
        procedure DoWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
      public
        //constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
          //AList: TIdThreadList = nil); override;
        constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
          AList: TIdContextThreadList = nil); override;

     

×