There's an article in the tech database for "Snapshot Server" that sometimes gets looked at.
I've just realized that it has an error, because I think a parameter got added to sRecvBinary.
It is NOT good code, but it does illustrate a cool trick for making your own webserver.
Dunno if this is worth fixing, but in case anyone cares, here's a fixed version below. Maybe
replace the code in the tech database?
; SnapShotServer.wbt
;
; This serves up (via HTTP) a gif image of whats on the screen
;
portnumber = 17072
;;;based on the LISTENER script from the hlp file
AddExtender("wwwsk34i.dll")
AddExtender("wwctl44i.dll")
AddExtender("WWIMG44I.DLL")
IntControl(12,1+4,0,0,0) ; exit without complaint
title = "SnapShotServer"
GoSub localdefs
BoxesUp("650 800 999 950",@NORMAL)
BoxTitle(title)
BoxButtonDraw(1,1,"Exit","000 500 999 999")
listensocket=sOpen()
sListen(listensocket,portnumber)
Served=0
While 1
datasocket=sAccept(listensocket,@FALSE) ; NO Block for a connection
If datasocket
GoSub ProcessRequest
Else
err = wxGetLastErr()
If err != @SERRNOCONN
sClose(listenSocket)
Message("Socket Error %err%",wxGetErrDesc(err))
Exit
EndIf
EndIf
TimeDelay(1.0)
If BoxButtonStat(1,1) Then Goto CANCEL
EndWhile
Exit
:CANCEL
sClose(listenSocket)
BoxText("Normal exit.")
TimeDelay(0.5)
Exit
:ProcessRequest
Served=Served+1
binbuf = BinaryAlloc( 1000 )
BinaryEodSet( binbuf, 1000 )
binaddr = IntControl( 42, binbuf, 0, 0, 0 )
rqst=sRecvBinary(datasocket,binaddr,1000)
; rqst is the requested URL, but we dont
; actually use it here.
GoSub dosnap
tf2s = FileSize(tf2)
bb = BinaryAlloc(tf2s)
BinaryRead(bb,tf2)
bbh = IntControl(42,bb,0,0,0)
guy = wxGetInfo(2,datasocket)
now = udfYmdHmsToHTTPStamp(TimeYmdHms(),1)
sSendLine(datasocket,"HTTP/1.1 200 OK")
sSendLine(datasocket,StrCat("Date: ",now))
sSendLine(datasocket,StrCat("Expires: ",now))
sSendLine(datasocket,StrCat("Content-Type: ","image/gif"))
sSendLine(datasocket,StrCat("Content-Length: ",tf2s))
sSendLine(datasocket,"")
sSendBinary(datasocket, bbh, tf2s)
sClose(datasocket)
BoxText(StrCat("served: ", served, " last: ", guy," at: ",now))
BoxTitle(StrCat(title," ", served))
FileDelete(tf2)
Return
:dosnap
tf1 = StrCat(Environment("TEMP"), "\SnapShotServer.bmp")
tf2 = StrCat(Environment("TEMP"), "\SnapShotServer.gif")
;Takes a bitmap snapshot of the screen and pastes it to the clipboard.
Snapshot(0)
;returns the size of buffer needed for a subsequent BinaryAlloc,
;but doesn't attempt to place the contents of clipboard into a buffer
size=BinaryClipGet(0,8)
;allocates a data buffer
bb=BinaryAlloc(size)
;read file format type CF_DIB
BinaryClipGet(bb,8)
; need to add first 14 bytes to make it
; a BMP file format
bmpdatasize=14
bb2=BinaryAlloc(size + bmpdatasize)
;The characters identifying the bitmap.'BM'
BinaryPokeStr(bb2, 0, "BM")
;Complete file size in bytes.
BinaryPoke4(bb2,2,size + bmpdatasize)
;Reserved
BinaryPoke4(bb2,6,0)
;Data offset
headersize=BinaryPeek4(bb,0)
dataoffset = headersize + bmpdatasize
BinaryPoke4(bb2,10,dataoffset)
BinaryCopy(bb2,bmpdatasize,bb,0,size)
BinaryWrite(bb2,tf1)
BinaryFree(bb)
BinaryFree(bb2)
ImgConvert(tf1,tf2) ; convert bmp to gif
FileDelete(tf1)
; note that imgconvert makes a nonstandard GIF, but
; IE and FF and Chrome seem to display it OK
Return
:localdefs
#DefineFunction udfYmdHmsToHTTPStamp (sYmdHms, iMode)
iMode = Min(5,Max(1,iMode)) ; Force iMode=1..5.
sYear = ItemExtract(1,sYmdHms,":")
sMonth = ItemExtract(2,sYmdHms,":")
sDay = ItemExtract(3,sYmdHms,":")
sHms = StrSub(sYmdHms,12,8)
sMName = ItemExtract(sMonth,"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",",")
sDName = ItemExtract(1+((TimeJulianDay(sYmdHms)+5) mod 7),"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",",")
Select iMode
Case 1
sHTTPStamp = StrCat(StrSub(sDName,1,3),', ',sDay,' ',sMName,' ',sYear,' ',sHms,' GMT')
Break
Case 2
sHTTPStamp = StrCat(sDName,', ',sDay,'-',sMName,'-',StrSub(sYear,3,2),' ',sHms,' GMT')
Break
Case 3
sHTTPStamp = StrCat(StrSub(sDName,1,3),' ',sMName,' ',StrFixLeft(0+sDay,' ',2),' ',sHms,' ',sYear)
Break
Case 4
sHTTPStamp = StrCat(sYear,'-',sMonth,'-',sDay,'T',sHms)
Break
Case 5
sHTTPStamp = StrCat(sYear,sMonth,sDay,'T',StrReplace(sHms,":",""))
Break
EndSelect
Return (sHTTPStamp)
;...
; This function udfYmdHmsToHTTP (sYmdHms, iMode) returns a string representation of date/time stamps for HTTP applications.
;
; iMode=1 : "Sun, 06 Nov 1994 08:49:37 GMT" ; RFC 822, updated by RFC 1123
; iMode=2 : "Sunday, 06-Nov-94 08:49:37 GMT" ; RFC 850, obsoleted by RFC 1036
; iMode=3 : "Sun Nov 6 08:49:37 1994" ; ANSI C's asctime() format
; iMode=4 : "1994-11-06T08:49:37" ; ISO 8601 (European Standard EN 28601) (see altered German standard DIN 5008)
; iMode=5 : "19941106T084937" ; ISO 8601 compacted
;
; The first format is preferred as an Internet standard and
; represents a fixed-length subset of that defined by RFC 1123 (an update to RFC 822).
; The second format is in common use, but is based on the obsolete RFC 850 date format and lacks a four-digit year.
;
; HTTP/1.1 clients and servers that parse the date value MUST accept
; all three formats (iMode=1..3) (for compatibility with HTTP/1.0), though they MUST
; only generate the RFC 1123 (iMode=1) format for representing HTTP-date values in header fields.
;
; All HTTP date/time stamps MUST be represented in Greenwich Mean Time (GMT) without exception.
; For the purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal Time).
; This is indicated in the first two formats by the inclusion of "GMT"
; as the three-letter abbreviation for time zone, and MUST be assumed when reading the asctime format.
; HTTP-date is case sensitive and MUST NOT include additional linear white spaces.
;...
#EndFunction
Return
Thanks for reporting the problem. The exmaple has several other minor problems. The Pixie extender has been retired for one and the the time conversion business can be replaced with the TimeFormat function.
<< time conversion business can be replaced >>
Yep, I should do that. That's some ugly code in there.
<<The Pixie extender has been retired>>
Ooch. that one's worse. I may look at it, no promises.
Will get the Tech article updated with your posted code in the near future.
It's been updated. Let me know what we screwed up.
I found the C# below that will supposedly convert BMP to GIF, but my C# is like my German.
Eins zwei drei... and things get foggy after that. (grin)
I may puzzle on how to turn this into a #definefunction
static void Main(string[] args)
{
Bitmap myBitmap=new Bitmap(@"test.bmp");
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/gif"); ;
EncoderParameter encCompressionrParameter
= new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionLZW); ;
EncoderParameter encQualityParameter
= new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 0L);
EncoderParameters myEncoderParameters
= new EncoderParameters(2);
myEncoderParameters.Param[0] = encCompressionrParameter;
myEncoderParameters.Param[1] = encQualityParameter;
myBitmap.Save("Output.gif", myImageCodecInfo, myEncoderParameters);
}
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
OK, I'm stuck on this bit of C#: ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
I've 100 variations on myarray = objectclrnew('System.Drawing.GetImageEncoders') but nothing seems to work -- I always get "CLR type name not found"
What is this newbie doing wrong?
(doc seems to be https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imagecodecinfo.getimageencoders?view=dotnet-plat-ext-3.1#System_Drawing_Imaging_ImageCodecInfo_GetImageEncoders (https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imagecodecinfo.getimageencoders?view=dotnet-plat-ext-3.1#System_Drawing_Imaging_ImageCodecInfo_GetImageEncoders))
-Kirby
GetImageEncoders() is a static class method, not a type. It returns an array of ImageCodecInfo objects.
https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imagecodecinfo.getimageencoders?view=dotnet-plat-ext-3.1
When looking to port C# / .NET code, always refer to Microsoft's documentation for the declaration of things like class methods and enum values so that you understand how to make use of them. Once you have that understanding, porting code to WinBatch is much easier.
Yeah, I've been staring at exactly that page,
but I just can't figure out what lines of winbatch that would translate to.
I've got this
myarray = objectclrnew('System.Drawing.Imaging.ImageCodecInfo','GetImageEncoders()')
but it fails with "CLR type creation failed", and
myarray = objectclrnew('System.Drawing.ImageCodecInfo','GetImageEncoders()')
which failed with "CLR type name not found"
I can get the codec to work but the 'System.Drawing.Imaging.EncoderParameters' business is a bit bulky. Here is a much simpler approach that lacks the fine-tuning of the compression algorithm:
; Load assemblies.
ObjectClrOption('useany', 'System')
ObjectClrOption('useany', 'System.Drawing')
; Bitmap to load.
strIn = 'c:\temp\Hawaii.bmp' ; Big waves on the shores of Kauai.
; Gif file as output.
strOut = 'c:\temp\Output.Gif'
; Load the input bitmap.
objBitmap = ObjectClrNew('System.Drawing.Bitmap', strIn)
Guid = ObjectClrNew('System.Guid', 'B96B3CB0-0728-11D3-9D7B-0000F81EF32E') ; GIF guid.
objFormat = ObjectClrNew('System.Drawing.Imaging.ImageFormat',Guid)
; Convert and write to disk.
objBitmap.Save(strOut, objFormat.Gif)
exit
Bless you Tony.
I was thinking that there might be some steps that could be skipped, at the cost of being less-than-official. I bet that GUID isn't going to change much.
I really am going to learn more C# I promise. Its tough on us old dogs. (grin)
Thanks man!
-Kirby
I had to use the GUID because the class's only constructor requires one. The GIF GUID value returned by the "Gif" property is the same value as the GUID value dropped into the constructor but it is actually defined by the class internally. You could likely use any valid image mime type GUID in the constructor as still get the correct result. Confusing I know but it all works out in the end.
OK, Here's snapshotserver all cleaned up and working.
; SnapShotServer.wbt
;
; This serves up (via HTTP) a gif image of whats on the screen
;
portnumber = 17072
;;;based on the LISTENER script from the hlp file
AddExtender("wwwsk34i.dll")
ObjectClrOption('useany', 'System')
ObjectClrOption('useany', 'System.Drawing')
IntControl(12,1+4,0,0,0) ; exit without complaint
; temp files used for snapshot
tf1 = StrCat(Environment("TEMP"), "\SnapShotServer.bmp")
tf2 = StrCat(Environment("TEMP"), "\SnapShotServer.gif")
title = "SnapShotServer"
BoxesUp("650 800 999 950",@NORMAL)
BoxTitle(title)
BoxButtonDraw(1,1,"Exit","000 500 999 999")
BoxDataTag(1,"AAA")
listensocket=sOpen()
sListen(listensocket,portnumber)
Served=0
While 1
datasocket=sAccept(listensocket,@FALSE) ; NO Block for a connection
If datasocket
GoSub ProcessRequest
Else
err = wxGetLastErr()
If err != @SERRNOCONN
sClose(listenSocket)
Message("Socket Error %err%",wxGetErrDesc(err))
Exit
EndIf
EndIf
TimeDelay(1.0)
If BoxButtonStat(1,1) Then Goto CANCEL
EndWhile
Exit
:CANCEL
sClose(listenSocket)
BoxText("Normal exit.")
TimeDelay(0.5)
Exit
:ProcessRequest
rqst1=sRecvLine(datasocket,250)
if strlen(rqst1) == 0 || strindexnc(rqst1,"favicon",1,0)
sClose(datasocket)
return
endif
BoxText("working...":rqst1)
timedelay(0.4)
Served=Served+1
GoSub dosnap
tf2s = FileSize(tf2)
bb = BinaryAlloc(tf2s)
BinaryRead(bb,tf2)
bbh = IntControl(42,bb,0,0,0)
guy = wxGetInfo(2,datasocket)
now = timeformat(timeymdhms(),"ddd, dd MMM yyyy HH:mm:ss"):" GMT"
sSendLine(datasocket,"HTTP/1.1 200 OK")
sSendLine(datasocket,StrCat("Date: ",now))
sSendLine(datasocket,StrCat("Expires: ",now))
sSendLine(datasocket,StrCat("Content-Type: ","image/gif"))
sSendLine(datasocket,StrCat("Content-Length: ",tf2s))
sSendLine(datasocket,"")
sSendBinary(datasocket, bbh, tf2s)
sClose(datasocket)
BoxDataClear(1,"AAA")
BoxText(StrCat("served: ", served, " last: ", guy," at: ",now))
BoxTitle(StrCat(title," ", served))
FileDelete(tf2)
Return
:dosnap
if fileexist(tf1)
FileDelete(tf1)
endif
;Takes a bitmap snapshot of the screen and pastes it to the clipboard.
Snapshot(0)
;returns the size of buffer needed for a subsequent BinaryAlloc,
;but doesn't attempt to place the contents of clipboard into a buffer
size=BinaryClipGet(0,8)
;allocates a data buffer
bb=BinaryAlloc(size)
;read file format type CF_DIB
BinaryClipGet(bb,8)
; need to add first 14 bytes to make it
; a BMP file format
bmpdatasize=14
bb2=BinaryAlloc(size + bmpdatasize)
;The characters identifying the bitmap.'BM'
BinaryPokeStr(bb2, 0, "BM")
;Complete file size in bytes.
BinaryPoke4(bb2,2,size + bmpdatasize)
;Reserved
BinaryPoke4(bb2,6,0)
;Data offset
headersize=BinaryPeek4(bb,0)
dataoffset = headersize + bmpdatasize
BinaryPoke4(bb2,10,dataoffset)
BinaryCopy(bb2,bmpdatasize,bb,0,size)
BinaryWrite(bb2,tf1)
BinaryFree(bb)
BinaryFree(bb2)
;;Replace this old code
;;ImgConvert(tf1,tf2) ; convert bmp to gif
;; with this:
oBitmap = ObjectClrNew('System.Drawing.Bitmap',tf1)
GifGuid = 'B96B3CB0-0728-11D3-9D7B-0000F81EF32E' ; Magic Number for GIF
oGifGuid = ObjectClrNew('System.Guid', GifGuid) ; turn into guid object
oGifFormat = ObjectClrNew('System.Drawing.Imaging.ImageFormat',oGifGuid)
oBitmap.Save(tf2, oGifFormat.Gif)
oBitmap.Dispose()
Return
Per Microsoft's documentation, GetImageEncoders() [note the parenthesis] is the name of a static method, not a data type. You can't use WinBatch's objectclrnew() function with it.
IIRC, there was a discussion on here in a different thread earlier this year regarding a limitation in the WIL support for .NET static class methods. The work around, IIRC, was to instantiate an object of the class type that implements the static method and then use that object instance to invoke the static method via the usual dot "." notation.
Quote from: kdmoyers on September 17, 2020, 09:10:12 AM
Yeah, I've been staring at exactly that page,
but I just can't figure out what lines of winbatch that would translate to.
I've got this
myarray = objectclrnew('System.Drawing.Imaging.ImageCodecInfo','GetImageEncoders()')
but it fails with "CLR type creation failed", and
myarray = objectclrnew('System.Drawing.ImageCodecInfo','GetImageEncoders()')
which failed with "CLR type name not found"
The ImageCodecInfo part of the C# code migth look something like this in a WinBatch script:
; Find a codec.
strMimeType = "image/gif"
objEncoderInfo = ObjectClrNew('System.Drawing.Imaging.ImageCodecInfo')
aInfos = objEncoderInfo.GetImageDecoders()
nMax = ArrInfo(aInfos, 1) - 1
for i = 0 to nMax
if aInfos[i].MimeType == strMimeType then objCodec = aInfos[i]
then break
next
Ah. I tried many arrangements close to that, but not quite that.
Now that I see it, it makes sense.
Thanks for closing the loop!
-Kirby