Discussion:
Anyway to use a picture object as an image source in HTA?
(too old to reply)
JJ
2018-10-17 19:40:13 UTC
Permalink
The picture object I meant is the one which was loaded using the
LoadPicture() function, which has an IPicture interface.

I want to put the picture onto the HTML document of an HTA file.

I know that IPicture has a method called SaveAsFile(), but it's not callable
from VBScript or JScript (at least directly). In fact, none of IPicture's
method is callable. The KeepOriginalFormat property which is read+write, is
also not readable. Other properties which are read-only, can be read
properly.

So, the only available (and relevant) picture's data are: HDC and GDI
handle.

Is there anything that can be used from VBScript to access those handles?

FYI, I actually don't use LoadPicture(). I got the picture object via OLE
Document Properties component (DSOFILE.DLL). From the Thumbnail field of the
Summary Properties collection. The source file is an MS-Word v6 document
(i.e. OLE format).
Mayayana
2018-10-17 21:37:50 UTC
Permalink
"JJ" <***@vfemail.net> wrote

| I want to put the picture onto the HTML document of an HTA file.
|
| I know that IPicture....

Wow. Big topic. First, there's not much you
can do with the Picture object from script.
With the following sample you can demonstrate
how to get an image's W and H. But even that
is slightly tricky. To do it right you first have to
check the PPI setting for the display.

The handle does no good because VBS can't
use a long integer datatype, which means
it also can't reference a DC for writing the image
to. Those are pointers to memory addresses, but
VBS converts them to variants.

Here's a webpage that loads an image dynamically
and writes the image W/H, getting them from
LoadPicture.

-------------------------------
<HTML><HEAD>
<STYLE> #Pic1 {position: absolute;}</STYLE>
<SCRIPT LANGUAGE="VBScript">
Sub window_onload()
Dim oPic, PPI
PPI = 96 ' may be 120 if display is set for large fonts.
'set appropriate image file path here:
Pic1.src = "C:\Windows\Desktop\button.jpg"
Set oPic = LoadPicture("C:\Windows\Desktop\button.jpg")
'watch out for wordwrap here:
Lab1.innerText = "Width of button.jpg: " & CStr(CInt((PPI * oPic.height) /
2540)) & " Height of button.jpg: " & CStr(CInt((PPI * oPic.width) / 2540))
Set oPic = Nothing
End Sub
</SCRIPT>
</HEAD>
<BODY>
<LABEL ID="Lab1"></LABEL><BR><BR>
<IMG ID="Pic1">
</BODY></HTML>
--------------------------------------------------

So, so much for LoadPicture. It's mostly just
a novelty in script.

WIA:

WIA, on the other hand, has lots of capability. The
functionality is *really* badly designed, but it
works. Here's a fullscale image editor and scanner
UI I wrote as an HTA using WIA:

https://www.jsware.net/jsware/scrfiles.php5#wiaed

But there are caveats. Only WIA v. 2 has the good stuff,
and that's not on XP. It needs to be installed on XP. And
WIA won't help script to draw an image. There are
ways to do that but you need to be able to reference a
DC.

---------------------------------------------------

I don't see why you can't just load an image dynamically,
like the sample above. Why do you need to paint it when
you're already in a webpage where you can show it? You
can add an IMG tag dynamically if necessary. You can also
set the whole thing into the
webpage as a data URI tag that holds the entire image file
as base-64 encoded text, but I can't think of any reason to
go to that trouble. That's mostly just for putting
small-ish files into webpages without having to have any
external resources.
JJ
2018-10-18 17:18:51 UTC
Permalink
Post by Mayayana
I don't see why you can't just load an image dynamically,
like the sample above. Why do you need to paint it when
you're already in a webpage where you can show it? You
can add an IMG tag dynamically if necessary. You can also
set the whole thing into the
webpage as a data URI tag that holds the entire image file
as base-64 encoded text, but I can't think of any reason to
go to that trouble. That's mostly just for putting
small-ish files into webpages without having to have any
external resources.
I did mention that I don't actually use LoadPicture(). I mentioned that as
an example of the picture object I'm having problem with.

The image is in a MS-Word document. A DOC file. A thumbnail image - which
was retrieved from its OLE document properties. So, I can't use that
directly as a source of an image in a HTML document.
Mayayana
2018-10-17 21:56:15 UTC
Permalink
"JJ" <***@vfemail.net> wrote

| The picture object I meant is the one which was loaded using the
| LoadPicture() function, which has an IPicture interface.
|

A small addendum to that last post. I've never been
clear about the actual difference between IPicture,
StdPicture and Picture. StdPicture seems to be the same
as Picture and perhaps a subset of IPicture.
Usually I'm dealing with a DIB if it's VB, or PictureBox
properties.

Anyway, all that's to say that LoadPicture provides
a Picture object, which is only the subset of IPicture.
Oddly, WSH help lists LoadPicture but not the Picture
object!
MSDN describes it like so:

Properties:
Handle, Height, Width, HPal, Type

Method:
Render

But render requires a handle to a DC. As noted
above, that's a long pointer. That whole method
is very ugly from VBS point of view:

Sub Render(hdc As Long, x As Long, y As Long, cx As Long, cy As Long, xSrc
As OLE_XPOS_HIMETRIC, ySrc As OLE_YPOS_HIMETRIC, cxSrc As
OLE_XSIZE_HIMETRIC, cySrc As OLE_YSIZE_HIMETRIC, prcWBounds As Any)

As you can see from my sample code above, even
the width and height are returned as HIMETRIC.
That's why they need to be converted to pixels
and depend on PPI settings. Weird stuff.
JJ
2018-10-18 17:18:51 UTC
Permalink
Post by Mayayana
A small addendum to that last post. I've never been
clear about the actual difference between IPicture,
StdPicture and Picture. StdPicture seems to be the same
as Picture and perhaps a subset of IPicture.
`IPicture` is an interface to access a picture object.
`StdPicture` is a class name for picture objects.
`Picture` is the picture object itself.

You use `Picture` class to create a `Picture` object which can be accessed
via `IPicture` interface.
Post by Mayayana
Oddly, WSH help lists LoadPicture but not the Picture
object!
That's the problem I'm dealing with.
Post by Mayayana
As you can see from my sample code above, even
the width and height are returned as HIMETRIC.
That's why they need to be converted to pixels
and depend on PPI settings. Weird stuff.
I try not to bother with the image dimension unit until I can actually
access the picture object.
Mayayana
2018-10-18 18:21:50 UTC
Permalink
"JJ" <***@vfemail.net> wrote

| > A small addendum to that last post. I've never been
| > clear about the actual difference between IPicture,
| > StdPicture and Picture. StdPicture seems to be the same
| > as Picture and perhaps a subset of IPicture.
|
| `IPicture` is an interface to access a picture object.
| `StdPicture` is a class name for picture objects.
| `Picture` is the picture object itself.
|

If you look in stdole2.tlb you'll see that IPicture
exposes a "superset" of functionality, but doesn't
provide a Dispatch interface, so it's not visible to
script. In other words, StdPicture and IPicture are
both interfaces, but StdPicture is dual (typekind
coclass) while IPicture is not. IPicture provides only
a vtable interface for early binding. StdPicture
provides a dispatch interface but only exposes a
limited subset of IPicture.

Picture seems to be just another name for StdPicture,
depending on context. (Neither is really a picture object.
They're both just an object that abstract access to image
data.)
In VB a PictureBox Picture is a StdPicture, but in code
it's called just a "Picture", for convenience. If you look
them up you'll find they both point to the same thing
and have the same properties. I'm not sure, but I'm
guessing the "Picture" name is probably only valid in a
VB context.

They all refer to a COM wrapper around an image.
I don't understand how that works, but I'm guessing
it really is a wrapper in the sense that it wraps
something like a DIB and makes the file format
transparent, so that one can do things like load
or save different image formats while treating them
as simply "images", with the wrapper managing
conversion.

Not that that does you any good. :)

I remember years ago IE used to come with custom controls.
I used to use one called iemenu.ocx. It provided a system-
style menu in a webpage. But they were phased out when
ActiveX went out of favor.
And there's webvw.dll, left over
from Active Desktop. That can display an image thumbnail,
but only loaded from a file. I guess that makes sense. Anyone
dealing with something like a StdPicture is not likely to be using
scripted controls.
Mayayana
2018-10-18 14:48:46 UTC
Permalink
This post might be inappropriate. Click to display it.
JJ
2018-10-18 17:18:50 UTC
Permalink
Post by Mayayana
I ended up downloading dsofile.dll because I didn't
understand that part of your post. I didn't know
about that. I don't use MS Office, so I guess
I wouldn't be likely to have a use for it. But the
download came with a sample VB6 project with
demo code for using dsofile.... and I managed to
find a Word DOC. :)
I'm glad I've mentioned LoadPicture() as an example. :)
Post by Mayayana
The thumbnail is a StdPicture, which seems to be
some kind of funky COM handle. You can access it
via COM or you can use API, but VBS can't handle
it alone.
Vbscript.dll, for whatever reason, includes the VB
LoadPicture method but not SavePicture, which is
what can write the StdPicture to disk. I don't think
there's any way to get at that in VBScript. I also
don't see anything in WIA. That has its own picture
handling functions.
I also don't know why `LoadPicture()` exist in VBScript's global context. It
seems like it was there as a workaround for a problem accessing IIS from
WSH.
Post by Mayayana
The best I can think of would be to write an ActiveX
control based on something like a VB PictureBox, put
the control in the HTA, and expose methods to load
the image in the PictureBox.
Yes. That would be the ultimate solution.
Post by Mayayana
You use VB6, don't you?
Um... not really. I only know enough VB to undestand its language so that I
can convert it to my main programming language, which is Delphi - or
secondly, to Assembly; or to other languages. I'd use Delphi to make
ActiveX/COM. I already have my own jack-in-the-box COM library for personal
use, so the solution for the IPicture problem would likely be added into it.
Post by Mayayana
An ActiveX control shouldn't need to be "safe" for an HTA.
I'm not an expert at ActiveX, so I'll keep that in mind.
Post by Mayayana
But there is a catch: An OCX is an in-process component,
so you'd need to be using 32-bit IE for the HTA.
Yes, of course.
Post by Mayayana
Or maybe .Net can make a 64-bit OCX? I have
no idea about that.
.NET (crap) can make ActiveX/COM for both 32-bit or 64-bit. But I just don't
like its large overhead. So, I won't use it unless its absolutely necessary.
For 64-bit ActiveX/COM, I use Delphi.
Post by Mayayana
On the other hand, you could load
an ActiveX EXE from 64-bit IE and that could easily
do the job of saving the image to disk. Then you could
load the image using HTML.
Yes. I was hoping that someone know a (Windows built in) ActiveX which can
solve the IPicture problem. Otherwise, I'd have to make my own ActiveX
solution.
Mayayana
2018-10-19 14:25:44 UTC
Permalink
"JJ" <***@vfemail.net> wrote

| I want to put the picture onto the HTML document of an HTA file.
|

I had too much time on my hands and started playing
with this. First I made the mistake of writing an ActiveX
EXE and then realized that sending in a pointer to a
StdPicture from a script would be sending a pointer from
a different memory context. So it had to be a DLL.

I couldn't test a Word DOC thumbnail as I have very
few DOCs and doubt that any of them have thumbnails.
What I have are pretty much just Microsoft docs and
my own Libre Office DOCs. So I loaded the image via
LoadPicture, which should be the same.

Anyway, for what it's worth, here are two methods.
I wrote the first, more complicated version before I
realized the second would work. But they both use
SavePicture, which may not be callable from Delphi.
My attempt to use OleSavePictureFile is causing wscript
to crash. I'm guessing that's because it's trying to use
the original picture from wscript. Oddly, it works with
SavePicture but not with OleSavePictureFile. So the
Delhpi solution might require something like GetDIBits
to recreate the bitmap internally and then saving
via API. That's somewhat convoluted.
Maybe Delphi could call SavePicture if you set a
reference to stdole2.tlb? I don't know anything about
whether Delphi can use typelibs.

Both functions here write the image
to disk as a BMP. IE will display that. If you wanted a
JPG you'd need to convert it. That can be done with
gdiplus, but it's a complicated mess, requiring that you
use the bitmap handle to get the bits and then jump
through the convoluted hoops of gdiplus.

Here's the script code:

Dim oPic, PS, Ret, picpath

Set oPic = LoadPicture("C:\Windows\Desktop\button.jpg")
Set PS = CreateObject("PicSaverDLL.Saver")

'-- SavePic1 method
'picpath = "C:\Windows\Desktop\Up\button.bmp"
'Ret = PS.LoadStdPic(oPic.Handle, oPic.hPal, picpath)

picpath = "C:\Windows\Desktop\Up\button.bmp"
PS.SavePic2 oPic, picpath

Set PS = Nothing
Set oPic = Nothing

'--------------------------------------

Here's the VB6 code from the DLL class. Note
how simple SavePic2 is. VB6 can't make
a 64-bit DLL, but you could perhaps transer
this to Delphi:
(wordwrap unavoidable)

Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type

Private Type PICTDESC
Size As Long
Type As Long
hBMP As Long
hPal As Long
Reserved As Long
End Type

Private Declare Sub OleSavePictureFile Lib "oleaut32.dll" (ByRef
lpdispPicture As IPicture, ByRef bstrFileName As String)
Private Declare Function OleCreatePictureIndirect Lib "olepro32" _
(PicDesc As PICTDESC, RefIID As GUID, _
ByVal fPictureOwnsHandle As Long, _
IPic As IPicture) As Long

Public Sub SavePic1(vpic As Variant, vPal As Variant, vPath As Variant)
Dim hBMP As Long, hPal As Long
Dim sPath As String
Dim Pic As PICTDESC
Dim IPic As IPicture
Dim IID_IDispatch As GUID
On Error Resume Next
'-- parameters come in as variants from script.
hBMP = CLng(vpic)
sPath = CStr(vPath)
hPal = CLng(vPal)
With IID_IDispatch
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With

With Pic
.Size = Len(Pic)
.Type = vbPicTypeBitmap
.hBMP = hBMP
.hPal = hPal
End With

Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
'-- this seems like it should work, but doesn't.
'-- OleSavePictureFile IPic, sPath

'-- this works.
SavePicture IPic, sPath

End Sub

Public Sub SavePic2(oPic As Variant, vPath As Variant)
Dim sPath As String
sPath = CStr(vPath)
SavePicture oPic, sPath
End Sub
JJ
2018-10-20 00:19:38 UTC
Permalink
Post by Mayayana
I had too much time on my hands and started playing
with this. First I made the mistake of writing an ActiveX
EXE and then realized that sending in a pointer to a
StdPicture from a script would be sending a pointer from
a different memory context. So it had to be a DLL.
Hmm... I thought COM would handle the IPC when any data is passed to a COM
server such as when passing a string to a method which is provided by a COM
server.
Post by Mayayana
I couldn't test a Word DOC thumbnail as I have very
few DOCs and doubt that any of them have thumbnails.
What I have are pretty much just Microsoft docs and
my own Libre Office DOCs. So I loaded the image via
LoadPicture, which should be the same.
Yes, the returned value of LoadPicture() is exactly the same type as the
Thumbnail property of an MS-Word document.
Post by Mayayana
Anyway, for what it's worth, here are two methods.
I wrote the first, more complicated version before I
realized the second would work. But they both use
SavePicture, which may not be callable from Delphi.
Oddly, SavePicture() is not declared anywhere. It's not mentioned in any
MSDN documentation except for FoxPro's SAVEPICTURE().
Post by Mayayana
My attempt to use OleSavePictureFile is causing wscript
to crash. I'm guessing that's because it's trying to use
the original picture from wscript.
I believe it's because OleSavePictureFile() expects an IPictureDisp. What we
have is an IPicture.
Post by Mayayana
So the
Delhpi solution might require something like GetDIBits
to recreate the bitmap internally and then saving
via API. That's somewhat convoluted.
True.
Post by Mayayana
Maybe Delphi could call SavePicture if you set a
reference to stdole2.tlb? I don't know anything about
whether Delphi can use typelibs.
In stdole2.tlb, SavePicture is declared as a module function which is mapped
to OLEAUT32.DLL's OleSavePictureFile(). I know that Delphi supports modules
in Type Libraries because Delphi can create them, but I don't have any
experience about it yet. So I think I'll just use OleSavePictureFile()
instead.
Post by Mayayana
Both functions here write the image
to disk as a BMP. IE will display that. If you wanted a
JPG you'd need to convert it.
Well, I'm working with HTA. I'm pretty sure it can handle image formats
which are supported by the GDI. :)

I'd have to consider between OleCreatePictureIndirect() &
OleSavePictureFile() pair, and IPicture.SaveAsFile() & IStream. To see which
one is more applicable for me.

Thanks for the suggestions.
Mayayana
2018-10-20 02:44:29 UTC
Permalink
OK. Here's one that works and only needs basic API.
OleSavePictureFile just doesn't seem to like VB.
This is ridiculously complex, but I had this code already
written for an image editor.

This has to be a DLL. An AxEXE won't work.

------------------------------------------------
VBScript loads image:

Dim oPic, PS, picpath

Set oPic = LoadPicture("C:\Windows\Desktop\deer.jpg")
Set PS = CreateObject("PicSaverDLL.Saver")
picpath = "C:\Windows\Desktop\Up\deer.bmp"
PS.PicToFile oPic.Handle, picpath

Set PS = Nothing
Set oPic = Nothing

-------------------------------------------------
Total code for class module:

Public Sub PicToFile(oPicHandle As Variant, vPath As Variant)
Dim sPath As String
Dim LRet As Long
Dim Boo As Boolean
Dim PicHandle As Long

PicHandle = CLng(oPicHandle)
sPath = CStr(vPath)
LRet = CopyStdPicture(PicHandle)
If LRet = 0 Then
Boo = WriteBMP(sPath)
End If
End Sub
---------------------------------------
Total code for bas module that goes with cls
to make DLL:


Option Explicit

Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private Type BITMAPINFOHEADER '40
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type BITMAPFILEHEADER '14
bfType As Integer
bfSize As Long
bfReserved1 As Integer
bfReserved2 As Integer
bfOffBits As Long
End Type
Private Type Bitmap
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private Type BITMAPINFO '24 bit
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal
hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X
As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal
hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long)
As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long)
As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal
hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
Long
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal
hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits
As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hdc As Long,
pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal Handle
As Long, ByVal dw As Long) As Long 'lplpVoid changed to ByRef
Private Declare Function GetLastError Lib "kernel32.dll" () As Long

Private CurPic As Long ' Handle to the current DIBSection
BITMAP
Private hBMPOld As Long ' Handle to the old bitmap in the DC, for
clear up
Private CurPicDC As Long ' Handle to the Device context
holding the DIBSection
Private CurPicBytes As Long ' Address of memory pointing to
the DIBSection's bits
Private BMPInfo As BITMAPINFO ' Type containing the Bitmap
information

Public Function CopyStdPicture(ByRef StdPicHandle As Long) As Long
Dim DC1 As Long
Dim LBMPOld As Long
Dim tBMP As Bitmap
Dim LRet As Long
On Error Resume Next
GetObjectAPI StdPicHandle, Len(tBMP), tBMP

LRet = CreateDIB(tBMP.bmWidth, tBMP.bmHeight)
If (LRet <> 0) Then
CopyStdPicture = LRet '-- returns 87 if win98 memory problem.
Exit Function
End If
DC1 = CreateCompatibleDC(0&)
If (DC1 = 0) Then
CopyStdPicture = 2
Exit Function
End If
LBMPOld = SelectObject(DC1, StdPicHandle)
BitBlt CurPicDC, 0, 0, BMPInfo.bmiHeader.biWidth,
BMPInfo.bmiHeader.biHeight, DC1, 0, 0, vbSrcCopy
SelectObject DC1, LBMPOld
DeleteObject DC1
CopyStdPicture = 0
End Function

Public Function WriteBMP(sPath As String) As Boolean
Dim hdc As Long, LRet As Long, LRet2 As Long, BytesPerScanLine As Long
Dim BBits() As Byte
Dim BFH As BITMAPFILEHEADER
Dim FF2 As Integer
On Error Resume Next

BytesPerScanLine = (BMPInfo.bmiHeader.biWidth * 3 + 3) And &HFFFFFFFC
ReDim BBits(BMPInfo.bmiHeader.biHeight * BytesPerScanLine) As Byte
hdc = CreateCompatibleDC(0&)
LRet = GetDIBits(hdc, CurPic, 0, Abs(BMPInfo.bmiHeader.biHeight),
BBits(0), BMPInfo, 0)
LRet2 = DeleteDC(hdc)
If (LRet = 0) Then
WriteBMP = False
Exit Function
End If

With BFH '14
.bfType = &H4D42
.bfSize = BMPInfo.bmiHeader.biSizeImage
.bfOffBits = 54
End With

FF2 = FreeFile()
Open sPath For Binary As #FF2
Put #FF2, , BFH ' 14
Put #FF2, , BMPInfo.bmiHeader ' 40
Put #FF2, , BBits()
Close
WriteBMP = True
End Function

Public Function CreateDIB(LWidth As Long, LHeight As Long) As Long
Dim BytesPerScanLine As Long
On Error Resume Next
Clear 'Set Dimensions in this cImage
CurPicDC = CreateCompatibleDC(0&)
If CurPicDC = 0 Then
CreateDIB = 1 '--failed.
Exit Function
End If
BytesPerScanLine = (LWidth * 3 + 3) And &HFFFFFFFC
With BMPInfo.bmiHeader
.biSize = Len(BMPInfo.bmiHeader)
.biWidth = LWidth
.biHeight = LHeight
.biPlanes = 1
.biBitCount = 24
.biCompression = 0
.biSizeImage = BytesPerScanLine * .biHeight
End With

CurPic = CreateDIBSection(CurPicDC, BMPInfo, 0, CurPicBytes, 0, 0)
If CurPic = 0 Then
CurPic = GetLastError() '-- returns 87 with win98 memory limit.
If (CurPic = 0) Then CurPic = 1
CreateDIB = CurPic
DeleteObject CurPicDC '-- quit
Else
hBMPOld = SelectObject(CurPicDC, CurPic)
CreateDIB = 0
End If
End Function

Private Sub Clear()
On Error Resume Next
If (CurPicDC <> 0) Then
If (CurPic <> 0) Then
SelectObject CurPicDC, hBMPOld
DeleteObject CurPic
End If
DeleteObject CurPicDC
End If
CurPicDC = 0
CurPic = 0
hBMPOld = 0
CurPicBytes = 0
End Sub

Loading...