funkcja sprawdzająca w rejestrze systemu jakie mamy zainstalowane programy.

funkcja sprawdzająca w rejestrze systemu jakie mamy zainstalowane programy.
FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
7

Siemka, własnie koduje sobie w Delphi.
Robiłem funkcję która sprawdzi w rejestrze jakie mamy zainstalowane programy, a następnie zapisze do pliku (jak nazwiemy)

Być może komuś się przyda, a jak chce niech modyfikuje na swoje potrzeby.
Kodowane pod/w Embarcadero® Delphi 12 Version 29.0.51961.7529

Do uses należy dodać: System.Generics.Collections, Registry;

oto i ona :

Kopiuj
// CODE BY FPERSON in Embarcadero® Delphi 12

type
  TLogLanguage = (tlEnglish, tlPolish);

var
  EnglishDictionary: TDictionary<string, string>;
  PolishDictionary: TDictionary<string, string>;

// Procedure for initializing dictionaries with translations
procedure InitializeDictionaries;
begin
  if EnglishDictionary = nil then  begin
    EnglishDictionary := TDictionary<string, string>.Create;
    EnglishDictionary.Add('InstallAppList', 'Installed Programs List:');
    EnglishDictionary.Add('Name', 'Name: ');
    EnglishDictionary.Add('Version', 'Version: ');
    EnglishDictionary.Add('Publisher', 'Publishern: ');
    EnglishDictionary.Add('Location', 'Location: ');
    EnglishDictionary.Add('ErrAccessKey', 'Error accessing key: ');
  end;

  if PolishDictionary = nil then begin
    PolishDictionary := TDictionary<string, string>.Create;
    PolishDictionary.Add('InstallAppList', 'Lista Zainstalowanych Programów:');
    PolishDictionary.Add('Name', 'Nazwa: ');
    PolishDictionary.Add('Version', 'Wersja: ');
    PolishDictionary.Add('Publisher', 'Wydawca: ');
    PolishDictionary.Add('Location', 'Lokalizacja: ');
    PolishDictionary.Add('ErrAccessKey', 'Błąd przy dostępie do klucza: ');
  end;
end;

// free
procedure FinalizeDictionaries;
begin
  FreeAndNil(EnglishDictionary);
  FreeAndNil(PolishDictionary);
end;

// a function that retrieves the translated word based on the key and language
function GetLocalizedString(Key: string; Language: TLogLanguage): string;
begin
  case Language of
    tlEnglish:
      // English
      if EnglishDictionary.TryGetValue(Key, Result) then
        Exit;
    tlPolish:
      // Polish
      if PolishDictionary.TryGetValue(Key, Result) then
        Exit;
  end;
  // If not translation found, we return just the key as the default value
  Result := Key;
end;

procedure ListInstalledPrograms(const LogFileName: string; Language: TLogLanguage = tlEnglish);
var
  Registry: TRegistry;
  KeyNames: TStringList;
  LogFile: TextFile;
  ProgramName, InstallLocation, DisplayVersion, Publisher: string;

  procedure ProcessKeys(const RootKey: HKEY; const Path: string);
  begin
    Registry.RootKey := RootKey;
    if Registry.OpenKeyReadOnly(Path) then
    begin
      Registry.GetKeyNames(KeyNames);
      Registry.CloseKey; // Close after fetching key names

      for var i := 0 to KeyNames.Count - 1 do
      begin
        if Registry.OpenKeyReadOnly(Path + '\' + KeyNames[i]) then
        begin
          try
            ProgramName := Registry.ReadString('DisplayName');
            InstallLocation := Registry.ReadString('InstallLocation');
            DisplayVersion := Registry.ReadString('DisplayVersion');
            Publisher := Registry.ReadString('Publisher');

            if ProgramName <> '' then
            begin
              WriteLn(LogFile, GetLocalizedString('Name', Language), ProgramName);
              if DisplayVersion <> '' then
                WriteLn(LogFile, GetLocalizedString('Version', Language), DisplayVersion);
              if Publisher <> '' then
                WriteLn(LogFile, GetLocalizedString('Publisher', Language), Publisher);
              if InstallLocation <> '' then
                WriteLn(LogFile, GetLocalizedString('Location', Language), InstallLocation);
              WriteLn(LogFile, 'GUID: ', KeyNames[i]);
              WriteLn(LogFile, '--------------------------------');
            end;
          except
            on E: Exception do
            begin
              WriteLn(LogFile, GetLocalizedString('ErrAccessKey', Language), KeyNames[i], ' - ', E.Message);
              WriteLn(LogFile, '--------------------------------');
            end;
          end;
          Registry.CloseKey; // Close after processing the key
        end;
      end;
    end;
  end;

begin
  Registry := TRegistry.Create(KEY_READ);
  KeyNames := TStringList.Create;

  InitializeDictionaries();
  try
    AssignFile(LogFile, LogFileName);
    Rewrite(LogFile);

    WriteLn(LogFile, GetLocalizedString('InstallAppList', Language));
    WriteLn(LogFile, '--------------------------------');

    // Process keys from HKEY_LOCAL_MACHINE
    ProcessKeys(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall');

    // Process keys from HKEY_CURRENT_USER
    ProcessKeys(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall');

    CloseFile(LogFile);
  finally
    KeyNames.Free;
    Registry.Free;
    FinalizeDictionaries;
  end;
end;

Funkcje wywołujemy:

Kopiuj
// plik InstalledPrograms.log bedzie zapisany do katalogu gdzie mamy skompilowany EXE
ListInstalledPrograms('InstalledPrograms.log');           // ENGLISH (domyslny)
ListInstalledPrograms('InstalledPrograms.log', tlPolish); // POLSKI

// plik InstalledPrograms.log bedzie zapisany do C:\InstalledPrograms.log 
ListInstalledPrograms('C:\InstalledPrograms.log');           // ENGLISH (domyslny)
ListInstalledPrograms('C:\InstalledPrograms.log', tlPolish); // POLSKI

Jeśli to się przyda, super, cieszę się, że mogłem pomóc! Jeśli nie, no cóż, przynajmniej nikt nie stracił złotówki! 😄

Remember, any fool can write code that a computer can understand. Good programmers write code that humans can understand.

woolfik
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1611
2

Jakbyś zrobił typ:

Kopiuj
type
  TRegistryDict = TDictionary<string, string>;

następnie stworzył listę TList<TRegistryDict> to nie potrzebowałbyś potem w GetLocalizedString if ować 😀 ale poza tym bardzo spoko masz like 😀

FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
2

Cześć @woolfik miało być czytelne, proste, bez fajerwerków.
Różne techniki można użyć, bardziej chodzi o odczyt rejestrów.

co do TDictionary, można chociażby i tak :)

Kopiuj
type
  TLogLanguage = (tlEnglish, tlPolish);

  // Define a record type for translations
  TTranslationRecord = record
    Key: string;
    Value: string;
  end;

  // Define a translations type as an array of records
  TTranslations = array[TLogLanguage] of array[0..5] of TTranslationRecord;

const
  Translations: TTranslations = (
    // English
    (
      (Key: 'InstallAppList'; Value: 'Installed Programs List:'),
      (Key: 'Name'; Value: 'Name: '),
      (Key: 'Version'; Value: 'Version: '),
      (Key: 'Publisher'; Value: 'Publisher: '),
      (Key: 'Location'; Value: 'Location: '),
      (Key: 'ErrAccessKey'; Value: 'Error accessing key: ')
    ),
    // Polish
    (
      (Key: 'InstallAppList'; Value: 'Lista Zainstalowanych Programów:'),
      (Key: 'Name'; Value: 'Nazwa: '),
      (Key: 'Version'; Value: 'Wersja: '),
      (Key: 'Publisher'; Value: 'Wydawca: '),
      (Key: 'Location'; Value: 'Lokalizacja: '),
      (Key: 'ErrAccessKey'; Value: 'Błąd przy dostępie do klucza: ')
    )
  );

function GetLocalizedString(const Key: string; Language: TLogLanguage): string;
var
  i: Integer;
begin
  for i := Low(Translations[Language]) to High(Translations[Language]) do
    if Translations[Language][i].Key = Key then
      Exit(Translations[Language][i].Value);
  Result := Key;
end;

Pozdrawiam.

EDIT: uproszczona wersja, z minimalną redundancją

Kopiuj

type
  TLogLanguage = (tlEnglish, tlPolish);
  TTranslations = array[TLogLanguage] of array[0..5] of record
    Key, Value: string;
  end;

// const i funkcja GetLocalizedString to samo :)
Manna5
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 667
0

I generalnie fajnie by było jakby ta procedura nie zapisywała uzyskanych danych od razu w pliku tylko zwracała jakąś tablicę z tymi informacjami, które można byłoby wykorzystać jak się chce. Chociaż pewnie rozwiązaniem typowym dla Delphi będzie użycie procedury w obecnej postaci a potem czytanie i parsowanie wygenerowanego pliku.

FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
1

Cześć @Manna5 pisząc funkcje, rozważałem różne warianty np taki. Zamiast zapisywać dane bezpośrednio do pliku, zwraca tablicę z informacjami o zainstalowanych programach.

Kopiuj
type
  TProgramInfo = record
    Name: string;
    Version: string;
    Publisher: string;
    Location: string;
    GUID: string;
  end;

function ListInstalledPrograms(Language: TLogLanguage = tlEnglish): TArray<TProgramInfo>;
var
  Registry: TRegistry;
  KeyNames: TStringList;
  ProgramInfo: TProgramInfo;
  ResultList: TList<TProgramInfo>;

  procedure ProcessKeys(const RootKey: HKEY; const Path: string);
  begin
    Registry.RootKey := RootKey;
    if Registry.OpenKeyReadOnly(Path) then
    begin
      Registry.GetKeyNames(KeyNames);
      Registry.CloseKey;
      for var i := 0 to KeyNames.Count - 1 do
      begin
        if Registry.OpenKeyReadOnly(Path + '\' + KeyNames[i]) then
        begin
          try
            ProgramInfo.Name := Registry.ReadString('DisplayName');
            ProgramInfo.Location := Registry.ReadString('InstallLocation');
            ProgramInfo.Version := Registry.ReadString('DisplayVersion');
            ProgramInfo.Publisher := Registry.ReadString('Publisher');
            ProgramInfo.GUID := KeyNames[i];
            if ProgramInfo.Name <> '' then
            begin
              ResultList.Add(ProgramInfo);
            end;
          except
            // Obsługa błędów - można dodać logowanie błędów jeśli potrzebne
          end;
          Registry.CloseKey;
        end;
      end;
    end;
  end;

begin
  Registry := TRegistry.Create(KEY_READ);
  KeyNames := TStringList.Create;
  ResultList := TList<TProgramInfo>.Create;
  InitializeDictionaries();
  try
    ProcessKeys(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall');
    ProcessKeys(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall');
    Result := ResultList.ToArray;
  finally
    KeyNames.Free;
    Registry.Free;
    ResultList.Free;
    FinalizeDictionaries;
  end;
end;

Przykład użycia

Kopiuj
var
  InstalledPrograms: TArray<TProgramInfo>;
  Program: TProgramInfo;
begin
  InstalledPrograms := ListInstalledPrograms(tlPolish);
  for Program in InstalledPrograms do
  begin
    // Tutaj możesz użyć informacji o programie jak chcesz
    // Na przykład:
    Writeln(GetLocalizedString('Name', tlPolish), Program.Name);
    Writeln(GetLocalizedString('Version', tlPolish), Program.Version);
    // itd.
  end;
end;

FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

Koduje dalej, wysmażyłem taką funkcję, może się komuś przyda 🙂

UnescapeStr która zwraca string. Niżej kod z opisem.

Kopiuj
{
  CODE FPERSON


Funkcja ta przekształca ciąg tekstowy, w którym niektóre znaki są poprzedzone znakiem ucieczki (backslash \), na tekst,
  w którym te sekwencje są zastępowane ich rzeczywistymi reprezentacjami.

  Opis działania funkcji
  Inicjalizacja:

  1. Funkcja UnescapeStr przyjmuje jeden parametr txt typu string.
  - Tworzy zmienną i do iteracji po znakach tekstu, oraz zmienną Result, która będzie zawierać wynikowy tekst.


  2. Pętla główna:
  - Pętla while przechodzi przez każdy znak w ciągu txt.

  3. Sprawdzanie znaku ucieczki:

  - Sprawdza, czy bieżący znak to backslash (\) i czy nie jest to ostatni znak w ciągu, aby uniknąć błędów.
  - Jeśli tak, sprawdza następny znak, aby zdecydować, jaką akcję podjąć:
  - 'n': Dodaje znak nowej linii (sLineBreak) do wyniku.
  - 't': Dodaje tabulator (#9) do wyniku.
  - '- \': Dodaje dosłowny backslash (\) do wyniku.
  - '"': Dodaje dosłowny cudzysłów (") do wyniku.
  - Inne znaki: Dodaje bieżący znak (backslash) do wyniku.

  4. Dodawanie pozostałych znaków:
  - Jeśli bieżący znak nie jest backslashem, jest bezpośrednio dodawany do wyniku.

  5. Przesuwanie wskaźnika:
  - Zwiększa wskaźnik i, aby przejść do następnego znaku.

  Przyklad:
  Jeśli funkcja UnescapeStr jest wywoływana z ciągiem Hello\nWorld\t!,
  wynikowy tekst będzie wyglądał następująco:

  Hello
  World    !
}

function UnescapeStr(const txt: string): string;
var
  i: Integer;
begin
  Result := '';
  i := 1;
  while i <= Length(txt) do
  begin
    if (txt[i] = '\') and (i < Length(txt)) then
    begin
      case txt[i + 1] of
        'n': begin Result := Result + sLineBreak; Inc(i); end;
        't': begin Result := Result + #9; Inc(i); end;
        '\': begin Result := Result + '\'; Inc(i); end;
        '"': begin Result := Result + '"'; Inc(i); end;
        else Result := Result + txt[i];
      end;
    end
    else
      Result := Result + txt[i];
    Inc(i);
  end;
end;

Śmiało można rozszerzyć funkcję
np o obsługę dodatkowych sekwencji escape, takich jak:

\r: Znak powrotu karetki (Carriage Return).
\b: Backspace.
\f: Form Feed.

Dodanie tych możliwości jest proste i polega na rozszerzeniu konstrukcji case o dodatkowe przypadki.

Kopiuj
case txt[i + 1] of
  'n': begin Result := Result + sLineBreak; Inc(i); end;
  't': begin Result := Result + #9; Inc(i); end;
  '\': begin Result := Result + '\'; Inc(i); end;
  '"': begin Result := Result + '"'; Inc(i); end;
  'r': begin Result := Result + #13; Inc(i); end; // powrót karetki
  'b': begin Result := Result + #8; Inc(i); end;  // backspace
  'f': begin Result := Result + #12; Inc(i); end; // form feed
  else Result := Result + txt[i];
end;

Tylko przykład, używajcie jak chcecie.

Manna5
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 667
0

Ciekawsza byłaby funkcja formatująca z opcją wstawiania do komunikatu w określonych miejsach danych, jak printf/sprintf w C, a nie tylko znaków specjalnych. Plus jedna uwaga, jak masz w linii 48 while i <= Length(txt) do to jest to dość popularny "błąd" popełniany w różnych językach, a mianowicie wyznaczasz na nowo długość łańcucha wejściowego za każdym przebiegiem pętli w ramach sprawdzenia warunku (co spowalnia program). Lepiej zapisz raz tę długość na początku do zmiennej, no chyba że długość tego łańcucha by się zmieniała podczas pętli, to wtedy Length w warunku by miało sens, ale u Ciebie tak nie jest.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
0
Manna5 napisał(a):

Plus jedna uwaga, jak masz w linii 48 while i <= Length(txt) do to jest to dość popularny "błąd" popełniany w różnych językach, a mianowicie wyznaczasz na nowo długość łańcucha wejściowego za każdym przebiegiem pętli w ramach sprawdzenia warunku (co spowalnia program).

Ale nie w Pascalach i nie w przypadku standardowych stringów. Wszystko dlatego, że typ danych dla długich ciągów znaków posiada metadane przed właściwymi danymi ciągu i w tych metadanych m.in. znajduje się jego długość, zwykle jako 32-bitowy int. Natomiast funkcja Length jest intrinsicsem (nie ma swojej definicji w RTL, jej kod jest generowany kontekstowo, w trakcie kompilacji), a więc jej używanie nie sprowadza się do wywołania funkcji. Jedyne co ona robi to odczytuje liczbę całkowitą ze z góry określonego adresu, względem adresu pierwszego znaku ciągu.

To o czym piszesz byłoby błędem, gdyby korzystać z C-stringów (czyli operować na typie PChar) i długość ciągu pobierać np. za pomocą funkcji StrLen, która iteruje od aktualnego znaku (wskazywanego przez wskaźnik ciągu, niekoniecznie od pierwszego znaku) aż do null-terminatora, zliczając znaki w pętli. Wtedy rozsądnym rozwiązaniem jest jednokrotne pobranie długości ciągu do zmiennej pomocniczej.

FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

Panowie, @Manna5 @furious programming
Jezeli chodzi o formatowanie podobne do printf/sprintf w C, mam propozycje.
Możemy zaimplementować funkcje używając varargs.

przykład

Kopiuj
 
function UnescapeFormatStr(const Format: string; const Args: array of const): string;
var
  i, j, k: Integer;
  S: string;
  ArgIndex: Integer;
begin
  Result := '';
  i := 1;
  ArgIndex := 0;
  while i <= Length(Format) do
  begin
    if (Format[i] = '%') and (i < Length(Format)) then
    begin
      case Format[i + 1] of
        'd': begin
          if ArgIndex < Length(Args) then
            Result := Result + IntToStr(Args[ArgIndex].VInteger);
          Inc(ArgIndex);
          Inc(i);
        end;
        's': begin
          if ArgIndex < Length(Args) then
            Result := Result + string(Args[ArgIndex].VString^);
          Inc(ArgIndex);
          Inc(i);
        end;
        '%': Result := Result + '%';
        else Result := Result + Format[i];
      end;
    end
    else
      Result := Result + Format[i];
    Inc(i);
  end;
end;

Funkcja obsługuje %d dla liczb całkowitych i %s dla ciągów znaków. Przykład użycia

Kopiuj
var s: string := UnescapeFormatStr('Witaj %s!\nMasz %d lat.', ['Jan', 30]);

Co do optymalizacji oryginalnej funkcji UnescapeStr, możemy ją zmodyfikować tak:

Kopiuj
function UnescapeStr(const txt: string): string;
var
  i, len: Integer;
begin
  Result := '';
  len := Length(txt);
  i := 1;
  while i <= len do
  begin
    if (txt[i] = '\') and (i < len) then
    begin
      case txt[i + 1] of
        'n': begin Result := Result + sLineBreak; Inc(i); end;
        't': begin Result := Result + #9; Inc(i); end;
        '\': begin Result := Result + '\'; Inc(i); end;
        '"': begin Result := Result + '"'; Inc(i); end;
        else Result := Result + txt[i];
      end;
    end
    else
      Result := Result + txt[i];
    Inc(i);
  end;
end;

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1
first_person napisał(a):

Możemy zaimplementować funkcje używając varargs.

To co podałeś to zwykła funkcja, przyjmująca w parametrach tablicę dynamiczną. Z varargs korzysta się do importu funkcji z bibliotek napisanych w C, w których faktycznie liczba parametrów jest zmienna — np. import funkcji printf. 😉

FP
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0
furious programming napisał(a):
first_person napisał(a):

Możemy zaimplementować funkcje używając varargs.

To co podałeś to zwykła funkcja, przyjmująca w parametrach tablicę dynamiczną. Z varargs korzysta się do importu funkcji z bibliotek napisanych w C, w których faktycznie liczba parametrów jest zmienna — np. import funkcji printf. 😉
Tylko propozycja

Cześć @furious programming
Wiem wiem, dałem tylko pomysła, jak osiągnąć podobny efekt, mając nadzieje ze zainspiruje innych 😀

Podsumowując, moje podejście z użyciem array of const jest "poprawne" i często stosowane w Delphi. Natomiast wiem, że Varargs w stylu C nie są bezpośrednio dostępne w Delphi, ale można osiągnąć podobną funkcjonalność używając tablic dynamicznych lub innych technik .

NP

  • Użycie przeciążonych funkcji:
Kopiuj
function UnescapeFormatStr(const Format: string): string; overload;
function UnescapeFormatStr(const Format: string; Arg1: Integer): string; overload;
function UnescapeFormatStr(const Format: string; Arg1: string): string; overload;
function UnescapeFormatStr(const Format: string; Arg1, Arg2: Integer): string; overload;
// itd.

Wiadomo, że takie pdoejscie jest mniej elastyczne, ale może być bardziej typobezpieczne

Móglbym temat rozwinąć, ale mam dużo pracy 😀

Tak czy inaczej można pójść w nowoczesne podejście, dostępne w nowszych wersjach Delphi.
Chociażby użycie generycznej metody z TArray, które oferuje elastyczność i typobezpieczeństwo.

Kopiuj
function UnescapeFormatStr<T>(const Format: string; const Args: TArray<T>): string;

Jedziemy, implementacja:

Kopiuj

// DODAJ w uses !!!
uses
  System.SysUtils, System.Rtti, System.TypInfo;

{

- Funkcja jest generyczna, co pozwala na użycie z różnymi typami argumentów.
- Używamy TRttiContext i TRttiType do sprawdzenia typu argumentów w czasie wykonania.
- TValue jest używane do konwersji argumentów na string.
- Obsługujemy podstawowe typy formatowania: %d dla liczb całkowitych, %s dla stringów, %f dla liczb zmiennoprzecinkowych i %% dla znaku procenta.
- Sprawdzamy, czy typ argumentu odpowiada oczekiwanemu formatowi.
- Pozwala na łatwe rozszerzenie o nowe typy formatowania.

Jednak nie jest za kolorowo, ponieważ ma pewne ograniczenia:

- Wszystkie argumenty muszą być tego samego typu.
- Obsługa błędów jest ograniczona (ale nad tym można popracować!)
* w przypadku niedopasowania typu po prostu zostawiamy oryginalny znacznik formatowania.

}

function UnescapeFormatStr<T>(const Format: string; const Args: TArray<T>): string;
var
  i, argIndex: Integer;
  ctx: TRttiContext;
  typ: TRttiType;
  value: TValue;
begin
  Result := '';
  argIndex := 0;
  i := 1;
  ctx := TRttiContext.Create;
  try
    while i <= Length(Format) do
    begin
      if (Format[i] = '%') and (i < Length(Format)) then
      begin
        Inc(i);
        if argIndex < Length(Args) then
        begin
          typ := ctx.GetType(TypeInfo(T));
          value := TValue.From<T>(Args[argIndex]);
          
          case Format[i] of
            'd':
              if typ.TypeKind in [tkInteger, tkInt64] then
                Result := Result + value.ToString
              else
                Result := Result + '%d';
            
            's':
              if typ.TypeKind in [tkUString, tkString, tkChar, tkWChar] then
                Result := Result + value.ToString
              else
                Result := Result + '%s';
            
            'f':
              if typ.TypeKind in [tkFloat] then
                Result := Result + FormatFloat('0.##', value.AsExtended)
              else
                Result := Result + '%f';
            
            '%': Result := Result + '%';
            
            else
              Result := Result + '%' + Format[i];
          end;
          
          Inc(argIndex);
        end
        else
          Result := Result + '%' + Format[i];
      end
      else
        Result := Result + Format[i];
      
      Inc(i);
    end;
  finally
    ctx.Free;
  end;
end;

przykład użycia:

Kopiuj
var
  result: string;
begin
  result := UnescapeFormatStr<Integer>('Liczba: %d', [42]);
  WriteLn(result);  // Wypisze: Liczba: 42

  result := UnescapeFormatStr<string>('Tekst: %s', ['Hello']);
  WriteLn(result);  // Wypisze: Tekst: Hello

  result := UnescapeFormatStr<Double>('Liczba zmiennoprzecinkowa: %f', [3.14]);
  WriteLn(result);  // Wypisze: Liczba zmiennoprzecinkowa: 3.14

  result := UnescapeFormatStr<string>('Procent: %%', ['']); 
  WriteLn(result);  // Wypisze: Procent: %
end;

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.