Oxygen Basic
Programming => Problems & Solutions => Topic started by: jcfuller on July 25, 2019, 07:17:09 AM
-
Charles,
A bit of a hack but it worked with the WAVE test.
James
-
Not there yet.
Does not work for WAVE with a numerical id instead of a string.
James
-
Hi James,
I may have the solution for LinkRes2Exe:
original:
'----------------------------------------------------------
' Returns 0 if sz$ is an ordinal (numeric id)
' Returns a DWORD aligned length if a unicode name
'----------------------------------------------------------
FUNCTION IsUnicode(sz$) AS DWORD
IF LOWORD(*(DWORD*)sz) = 0xFFFF THEN
FUNCTION = 0
END IF
FUNCTION = dwAlign(2 + (2 * wcslen((LPCWSTR)sz)))
END FUNCTION
take out the dwalign call in isUnicode:
'----------------------------------------------------------
' Returns 0 if sz$ is an ordinal (numeric id)
' Returns a DWORD aligned length if a unicode name
'----------------------------------------------------------
FUNCTION IsUnicode(sz$) AS DWORD
IF LOWORD(*(DWORD*)sz) = 0xFFFF THEN
FUNCTION = 0
END IF
FUNCTION = 2 + (2 * wcslen((LPCWSTR)sz))
END FUNCTION
The resource names are not dwaligned.
PS I now have a working LinkRes in 32bit and 64bit o2 :)
-
Charles,
Hooray!
Sorry to let my OCD distract you. Now back to O2 building for you. :)
Did you rewrite in O2??
James
-
PS I now have a working LinkRes in 32bit and 64bit o2 :)
;D 8) :o :D
-
For an integrated resource linker, you can detatch the console and parsing utilities. Then use LinkResources() directly.
LinkRes
'
'https://docs.microsoft.com/en-us/windows/win32/menurc/resource-file-formats
$ Filename "LinkRes.exe"
uses RTL64
uses kernel
%NoConsole
uses console
indexbase 1
type ResHead1
dword DataSize
dword HeaderSize
end type
type ResHead2
dword DataVersion
word MemFlags
word LangId
dword Version
dword Charistics
end type
function RetResType (int ResType) as string
===========================================
'
select ResType
case 0x0000 : return "NULL"
case 0x0001 : return "Cursor"
case 0x0002 : return "Bitmap"
case 0x0003 : return "Icon"
case 0x0004 : return "Menu"
case 0x0005 : return "Dialog"
case 0x0006 : return "String Table"
case 0x0007 : return "Font Directory"
case 0x0008 : return "Font"
case 0x0009 : return "Accelerators Table"
case 0x000A : return "RC Data (custom binary data)"
case 0x000B : return "Message table"
case 0x000C : return "Group Cursor"
case 0x000E : return "Group Icon"
case 0x0010 : return "Version Information"
case 0x0011 : return "Dialog Include"
case 0x0013 : return "Plug'n'Play"
case 0x0014 : return "VXD"
case 0x0015 : return "Animated Cursor"
case 0x0018 : return "Manifest"
case 0x2002 : return "Bitmap (new version)"
case 0x2004 : return "Menu (new version)"
case 0x2005 : return "Dialog (new version)"
end select
'
return "Unknown"
end function
function RetMemFlags (int flags) as string
==========================================
if flags = 0 then return "None"
string MemFlags
if flags and 0x10 then MemFlags="MOVEABLE"
if flags and 0x20 then MemFlags+=" PURE"
if flags and 0x40 then MemFlags+=" PRELOAD"
if flags and 0x1000 then MemFlags+=" DISCARDABLE"
return MemFlags
end function
function AddResources(string ResFile, TgtFile, int verbose=0) as int
====================================================================
sys hRes
sys ResBase
sys ResData
sys ResLen
sys ResPos
sys ResName
sys ResType
wchar* ResNameStr
wchar* ResTypeStr
wchar* unihead
int HeadOffset
int UniSize
int DataSpace
int Result
ResHead1 *rh1
ResHead2 *rh2
string ResBuf
ResBuf=getfile(ResFile)
if not ResBuf
print "File not found: " ResFile cr
return 1
endif
'
hRes=BeginUpdateResource(TgtFile, 1) ' File to add the resource to
if not hRes
print "BeginUpdateResource failed..."
return 1
endif
'
ResBase=strptr(ResBuf)
ResLen = len(ResBuf)
ResPos = 0
int rhe1 = sizeof(rh1)
int rhe2 = sizeof(rh2)
while ResPos < ResLen
@rh1=ResBase + ResPos
UniSize = rh1.HeaderSize - rhe1
Unisize=(Unisize + 3) and 0x7ffffffc 'dword align
@Unihead=ResBase + ResPos + rhe1 'unicode/ordinal data then rh2
@rh2 = @UniHead + UniSize - rhe2
'
' Get the resource type
word *w
wchar st[64],sn[64]
int i
'
@w=@unihead 'word overlay
if w=0xffff
ResType=w[2]
st=RetResType(ResType)
@ResTypeStr = @st
HeadOffset=4 'dword
else
@ResTypeStr=@Unihead 'unicode
ResType=@ResTypestr
'
'locate unicode null terminator
i=0
while w<>0
i+=2 : @w+=2
wend
HeadOffset=i+2 'for start of ResName
endif
'
' Get the resource name
word w at @unihead+HeadOffset
if w=0xffff
ResName = w[2]
sn=str(ResName)
@ResNameStr = @sn
else
@ResNameStr = @unihead+HeadOffset
ResName = @ResNameStr
endif
if Verbose
string rt,rn
rt=ResTypeStr
rn=ResNameStr
print "DataSize = " rh1.DataSize cr
print "HeaderSize = " rh1.HeaderSize cr
print "ResType = " rt cr
print "ResName = " rn cr
print "DataVersion = " rh2.DataVersion cr
print "MemFlags = " RetMemFlags(rh2.MemFlags) cr
print "LangId = " rh2.LangId cr
print "Version = " rh2.Version cr
print "Charistics = " rh2.Charistics cr
print string(60, "-") cr
endif
'
ResPos += rhe1 + UniSize 'ready for ResData
ResData = ResBase+ResPos
DataSpace = (rh1.DataSize+3) and 0x7ffffffc
'
ResPos+=DataSpace
'
if rh1.DataSize
if not UpdateResourceW( hRes, ResType, ResName, rh2.LangId, ResData, rh1.DataSize)
print "UpdateResource failed..."
Result = 1
exit while
endif
endif
wend
if not EndUpdateResource(hRes, Result)
print "EndUpdateResource failed..." cr
Result = 1
endif
return Result
end function
indexbase 1
int swd,lenw,ascw,ascn
sub SkipSpace(string*s, int*i)
==============================
byte bb at strptr(s)
do
ascn=bb[i]
select ascn
case 0 : exit do
'case 65 to 90 : ascn+=32 : exit do 'force lowercase
case 33 to 255 : exit do
end select
i++
loop
end sub
function GetWord(string*s, int*i) as string
===========================================
byte bb at strptr(s)
skipspace(s,i)
swd=i
do
ascn=bb[i]
select ascn
case 0 to 32 : exit do
case 34 : i=instr(i+1,s,chr(ascn))
if i=0
i=len(s)
endif
end select
i++
loop
lenw=i-swd
skipspace(s,i)
if lenw>0
return mid(s,swd,lenw)
endif
end function
function CommandLineArgs() as string
====================================
string s,command
int i=1
Command = (char*) GetCommandLine
s=GetWord command,i 'step over command filepath\file
SkipSpace command,i
return mid Command, i
end function
function main() as int
======================
int vb=0
int i=1
string s=lcase(CommandLineArgs())
string resfile=getword(s,i)
string tgtfile=getword(s,i)
string opts=getword(s,i)
'
AttachConsole(-1) 'attach to console of parent process
sys ho=GetStdHandle( STD_OUTPUT_HANDLE )
if ho<>consout then consout=ho 'Windows10
'
if tgtfile
if opts="-verbose"
vb=1
endif
function = AddResources(resfile, tgtfile, vb)
else
print " LinkRes 2019" cr " LinkRes file.res file.exe [-verbose]" cr
endif
FreeConsole
end function
return main()
-
Hi Charles,
I was distracted and have not followed this thread so far. You ported LinkRes2Exe.bas to Oxygenbasic? That is really brilliant. Can LinkRes be created as a 32-bit exe also? Is there a different behaviour of LinkRes2Exe and LinkRes? And what does LinkResources() mean? I have not yet tried LinkRes.o2bas.
Roland
-
Hi Roland,
32 or 64 bit. The only reason for compiling is to use it as a command-line tool.
It behaves the same as LinkRes2exe, linking a res file to an exe or dll, though the source code is rather different.
-
Charles, will you provide both exe versions of LinkRes or is one version sufficient? GoRC also makes a distinction between /machine X86 (default) and /machine X64, but I am not sure if this really matters. As a precaution, I used two different versions of LinkRes and two different batch files in WinDynDialogs\ExeWithRes and \Multilingual. In any case, everything works fine with LinkRes and these two examples, 32-bit and 64-bit.
-
I will provide LinkRes as a 64bit exe, but there's no difference apart from using RTL32/RTL64, and the functionality is the same.
Here is a minimal version which can be included in compilers or other tools. It simply passes the required data from the res file to AddResource().
AddResources.inc
'
'https://docs.microsoft.com/en-us/windows/win32/menurc/resource-file-formats
type ResHead1
dword DataSize
dword HeaderSize
end type
type ResHead2
dword DataVersion
word MemFlags
word LangId
dword Version
dword Characteristics
end type
extern lib "kernel32.dll"
! BeginUpdateResourceA
! UpdateResourceW
! EndUpdateResourceA
end extern
function AddResources(string ResFile, TgtFile) as int
=====================================================
indexbase 1
sys hRes
sys ResBase
sys ResData
sys ResLen
sys ResPos
sys ResName
sys ResType
wchar* unihead
int HeadOffset
int UniSize
int DataSpace
int Result
ResHead1 *rh1
ResHead2 *rh2
string ResBuf
ResBuf=getfile(ResFile)
if not ResBuf
'"File not found: " ResFile
return 4
endif
'
hRes=BeginUpdateResourceA(TgtFile, 1) ' File to add the resource to
if not hRes
'"BeginUpdateResource failed..."
return 2
endif
'
ResBase=strptr(ResBuf)
ResLen = len(ResBuf)
ResPos = 0
int rhe1 = sizeof(rh1)
int rhe2 = sizeof(rh2)
while ResPos < ResLen
@rh1=ResBase + ResPos
UniSize = rh1.HeaderSize - rhe1
Unisize=(Unisize + 3) and 0x7ffffffc 'dword align
@Unihead=ResBase + ResPos + rhe1 'unicode/ordinal data then rh2
@rh2 = @UniHead + UniSize - rhe2
'
' Get the resource type
word *w
int i
'
@w=@unihead 'word overlay
if w=0xffff
ResType=w[2]
HeadOffset=4 'dword
else
ResType=@Unihead
'
'locate unicode null terminator
i=0
while w<>0
i+=2 : @w+=2
wend
HeadOffset=i+2 'for start of ResName
endif
'
' Get the resource name
word w at @unihead+HeadOffset
if w=0xffff
ResName = w[2]
else
ResName = @unihead+HeadOffset
endif
'
ResPos += rhe1 + UniSize 'ready for ResData
ResData = ResBase+ResPos
DataSpace = (rh1.DataSize+3) and 0x7ffffffc
'
ResPos+=DataSpace
'
if rh1.DataSize
if not UpdateResourceW( hRes, ResType, ResName, rh2.LangId, ResData, rh1.DataSize)
'"UpdateResource failed..."
Result = 1
exit while
endif
endif
wend
if not EndUpdateResourceA(hRes, Result)
'"EndUpdateResource failed..." cr
Result = 3
endif
return Result
end function
-
Here is a minimal version which can be included in compilers or other tools. It simply passes the required data from the res file to AddResource().
That would be a fine addition to co2 HINT HINT
James
-
Thanks Charles, it works very nice! :)
-
Charles, i noticed in your code that you use:
if w=0xffff
This is normal and i use it often but i recall PowerBASIC interprets this as a signed integer: -1
while it requires &h0ffff for 65535.
What is your opinion regarding this?
-
That seems a bit fragile. It is only a bit pattern. o2 depends only on the type of variable being used. (In this case w is a word overlay for reading unicode character codes.)
-
Charles,
While waiting for you to add linkres to co2 :) I thought I would play around with it myself to become more familiar with O2.
I created a clone of co2.o2bas called brco2.o2bas and compiled it with co2 -c brco2.o2bas.
I then ran it from the command line and up pops the here's what I want window as it should.
But if I add a resource(version info and manifest) it no longer pops up the window.
James
-
Hi James,
It is gratifying to see Charles getting contributions at the compiler source code level. I'm sure self compiling has made that easier. It would great if Mike and Jose would conjure up some O2 magic as well.
-
Charles,
If you remove #compact from co2.o2bas source Defender gets all in a tizzy
James
-
Defender has been quite disruptive recently, but they fix it within a few hours, once the false positive has been reported.
I'm including AddResources inside oxygen.dll, and indirectly in co2
All you will need to do is include myresources.res in the source code:
uses myresources.res
or using command line co2:
co2 -32 myprog myresources.res
Nearly ready to post :)
-
Version 0.2.5 now released including the built-in resource linker, as well as LinkRes and an updated Co2 compiler
Also, true=-1 instead of 1
-
Charles,
BIG thumbs UP on new co2 and uses *.res. Very simple integration with RadAsm3.
Looks like it's time to put together a new O2RadAsm package.
James
-
Hi Charles,
that is just amazing, and I can only say "Wow!". Oxide can now compile and execute the source code immediately integrating the resource file. This gives a total new feeling. You have taken another (very) big step in the development of Oxygenbasic.
Roland
% SND_SYNC = 0
% SND_RESOURCE = 262148
! PlaySound lib "winmm.dll" alias "PlaySoundA"
! Sleep lib "kernel32.dll"
uses wow.res
Sleep 500
PlaySound("MySound", 0, SND_RESOURCE or SND_SYNC)
Sleep 300
PlaySound("MySound", 0, SND_RESOURCE or SND_SYNC)
print "Wow!"
-
Here is a FYI in case you did not know it.
I always use ID's for my resources in all languages I use which means MAKEINTRESOURE(100) or (char*)100 for O2.
You can also use "#100".
James
-
This is the way to go!!!
Many programmers think that the more complex a source code is, the more advanced it is...
Charles is proving that simplicity can be very powerful wih features like this. :)
Thmubs up Charles! and Thanks!