Is er een functie waarmee ik (@runtime) alle resourcestrings (en hun nummer) uit mijn programma kan halen?
Is er een functie waarmee ik (@runtime) alle resourcestrings (en hun nummer) uit mijn programma kan halen?
Heb je hier wat aan?
http://stackoverflow.com/a/13708412/1037511
Dus EnumResourceNames.
...en met een TResourceStream kun je ze vervolgens gebruiken.
TMemoryLeak.Create(Nil);
EnumResourceNames(0,RT_STRING,@Callback,0);
geeft een gastlasterror 998. Ook het inlezen van een extern bestand geeft dezelfde fout.
(de callback wordt 1x aangeroepen met geen enkele paramater gevuld)
Mogelijk antwoord hier:
http://stackoverflow.com/a/23024842/1037511
Het kan dus zijn dat je een ID terug krijgt en geen string. Je moet dus testen met IS_INTRESOURCE wat je terug krijgt.
ik gooi de resources even achter elkaar in een file, maar de volgorde is zeer anarchistisch...
Er lijken per Callback (er komen er maar een stuk of 20 a 25) een aantal strings achter elkaar geplakt te zijn.
Uit verschillende units door elkaar heen. En volgens mij niet compleet.
Ik wil "gewoon" een rijtje ID's en een rijtje strings...
callbackCode:fs := TFileStream.create('rt_string.bin', fmCreate); EnumResourceNames(0,RT_STRING,@Callback, long(fs)); fs.Free;
Code:function Callback(handle:THandle;ResType:PChar;ResName:Pchar;long:Lparam):bool;stdcall; var i: Integer; R: TResourceStream; begin if Is_IntResource(restype) then begin i := integer(resname); log(['int', i]); R := TResourceStream.CreateFromID(0, i, restype); TFileStream(Long).CopyFrom(R, 0); R.free; end result := true; end;
Je hebt hier ook niet te maken met een enkele string per ID maar met een STRINGTABLE.
Je zult het resultaat per ID dus een beetje uit elkaar moeten trekken om alle strings goed te kunnen krijgen.
Zoals dit:
Delphi Code:
function EnumResNameProc(hModule: THandle; lpszType, lpszName: PChar; lParam: longint): boolean; stdcall; var ID: longint; Min: Integer; Max: Integer; Index: Integer; Buffer: PWChar; stream: TResourceStream; Len: Word; S: String; begin if longint(lpszName) < 65535 then begin ID := longint(lpszName); Min := (ID - 1) * 16; Max := (ID * 16) - 1; stream := TResourceStream.CreateFromID(hModule, ID, RT_STRING); try TStringList(lParam).Add(Format('%d STRINGTABLE', [ID])); TStringList(lParam).Add('{'); Buffer := stream.Memory; for Index := Min to Max do begin // determine the length of the string Len := Word(Buffer^); if Len > 0 then begin Inc(Buffer, 1); S := Copy(WideCharToString(Buffer), 1, Len - 1); TStringList(lParam).Add(Format(' %d, "%s"', [Index, S])); Inc(Buffer, Len); end else Inc(Buffer); end; TStringList(lParam).Add('}'); finally stream.Free; end; end else TStringList(lParam).Add(string(lpszName)); Result := true; end; procedure TForm1.Button1Click(Sender: TObject); var sl: TStringList; begin sl := TStringList.Create; EnumResourceNames(0, RT_STRING, @EnumResNameProc, longint(sl)); sl.SaveToFile('c:\temp\rt_string.txt'); sl.Free; end;
Resultaat:
(Zie ook http://stackoverflow.com/a/6500134/1037511)Code:4007 STRINGTABLE { 64096, "WideString index out of bound" 64097, "No URL was specified for 'GET" 64098, "Invalid url '%s' - only supports 'http' and 'https' scheme" 64099, "Method has no RTT" 64100, "Unsuppported variant type %" 64101, "varDispatch type not supporte" 64102, "varError type not supporte" 64103, "TAggregateStream Error: no internal stream" 64104, "method not supporte" 64105, "Method not permitted in TSoapDataLis" 64106, "Insufficient data for Content-Lengt" 64107, "Error reading from Mime Request Strea" 64108, "No access to temporary fil" 64109, "Unable to load WSDL File/Location: %s. Error [%s" 64110, "Unable to generate temporary pat" 64111, "Rename to %s faile" } 4008 STRINGTABLE { 64112, "Parameter %s on Method %s of Interface %s has no RTT" 64113, "Invalid date string: %" 64114, "Invalid time string: %" .....
oooo wat lekker recht allemaal! Thanks.
Raadselachtig:
Code:Min := (ID - 1) * 16; Max := (ID * 16) - 1;
Dat stond ook hier.
Ook hier uitgelegd: The format of string resourcesUPDATE I updated the answer to reflect the strings id inside of the string table, the strings are grouped together in bundles of 16. So the first bundle contains strings 0 through 15, the second bundle contains strings 16 through 31, and so on. so the formula to calculate the strings id can be determined in this way
Bij mij lopen de string nummers door tot 65535 (in ID 4096) terug naar 64096 (in ID 4007). Ze zijn dus gegroepeerd per 16. (4007-1) * 16 = 64096.
Overigens staat er een klein foutje in mijn code (ik heb de string-code er n.l. zelf ff bij gemaakt want die stond niet in de oorspronkelijke routine):
Maar er stond een extra $0D achter de regels maar volgens mij komt dat door de TStringList (omdat de string tussen de "" wel goed is).Code:S := Copy(WideCharToString(Buffer), 1, Len);
Okidoki. Ja ik had hier en daar volgens mij te snel er doorheen gescand. Too eager for a solution.
Dat de string 1 te kort was had ik dan weer wel gezien
Ik zal morgen eens kijken welke resourcestrings er uit komen rollen. Nogmaal bedankt.
Ok werkend. Da's een goed begin.
Nu... Slechts wetende de Index van de resourcestring, kan ik deze dan terug vinden?
Ik wil met de volgende functie resourcestrings in mijn app aanpassen. Maar ik heb de pointer van PResStringRec nodig.
Code:procedure HookResourceString(Res: PResStringRec; const NewStr: string{PChar}); var oldprotect: DWORD; begin VirtualProtect(Res, SizeOf(Res^), PAGE_EXECUTE_READWRITE, @oldProtect); Res^.Identifier := Integer(PChar(NewStr)); VirtualProtect(Res, SizeOf(Res^), oldProtect, @oldProtect); end;
Je bedoelt zoiets als hier te zien is:
http://www.delphipraxis.net/1207541-post4.html
https://gist.github.com/artzub/9064231
Dit werkt bij mij wel gewoon:
De SMsgDlgNo is gedefinieerd in Consts.pas (die ik overigens gewoon heb vervangen door een Nederlandstalige versie).Code:HookResourceString(@SMsgDlgNo, 'Nope');
In mijn Consts.pas staat dit:
Dus die resourcestring zorgt al voor je ResStringRec structuur.Code:resourcestring //... SMsgDlgNo = '&Nee'; // '&No'; //...
Jij wilt deze natuurlijk ook voor onbekende strings aanmaken (zonder dat je de Consts.pas gebruikt) ?
Hoe ga je de vertaling doen (ik vond het makkelijker gewoon Consts.pas te vervangen door een kopie in mijn eigen source-directory te zetten)?
Edit: Misschien heb je hier wat aan als je de resoucestrings wilt wijzigen:
http://stackoverflow.com/a/26089036/1037511
Maar het is misschien ook even handig als je precies vertelt wat nu de bedoeling is.
Als ik bijvoorbeeld zeg ID 65100... in wat wil jij die dan wijzigen?
Of ga je die eerst uitlezen... vertalen... en dan weer terugschrijven?
Hoe doet je programma dan die vertaling?
Last edited by rvk; 26-Oct-14 at 17:08.
Thanks. Ik zal de links bekijken...
Resourcestrings zitten her en der verstopt in diverse units. soms ook onder de implementation.
Vandaar dat ik ze wil verzamelen in mijn "eigen" lijst tijdens de ontwikkeling. En dan bewaren in mijn externe bestandje. En daarna een vertalingsbestand maken. Want het gaat hier natuurlijk om taal
Dus wil ik ze terug kunnen vinden op de index <= 65535.
--- Edit: HookResourceString(@SMsgDlgNo, 'Nope') werkt inderdaad bij mij ook ---
Last edited by Anoniem; 26-Oct-14 at 17:29.
Ik denk dat controleren op ID niet zo handig is. Één wijziging in je source zou de hele volgorde in de war kunnen gooien. (Staat "Nee" altijd op hetzelfde ID-nummer, kan ik me niet voorstellen). Dus je zult sowieso moeten controleren op oude string en nieuwe string (en niet op ID-nummer).
Klopt... Misschien moet ik ergens in de source een harde array of pointers bewaren of zo. (maar dan nog heb ik die pointers nodig)
De volgorde is niet echt voorspelbaar, behalve dat wanneer ik een nieuwe toevoeg deze een lager nummer krijgt dan de laagste van de versie voordat ik hem toevoegde.
Vertaling is altijd een lastig ding... Maar ik ben vastbesloten mijn eigen oplossing erdoor te duwen
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks