Trouble doing DLL call

Started by kdmoyers, June 26, 2019, 06:20:23 AM

Previous topic - Next topic

kdmoyers

I'm needing to convert binary buffers (BMP screenshot images) to base64 so I can encode them into html files.

I found a function Base64ToBinary (below) in the database, and it works great.  Not what I need, but it works.

What I need is BinaryToBase64 , So I tried making that and I'm having trouble. Dll error 234. I'm not great at DLL calls.

what am I doing wrong in the BinaryToBase64 function below?
probably something with the parameters to the dll call...

TIA, Kirby

Code (winbatch) Select

#DefineFunction Base64ToBinary(strBase64, phBinary)
; CryptStringToBinaryA function https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptstringtobinarya
; BOOL CryptStringToBinaryA(
;   LPCSTR pszString,
;   DWORD  cchString,
;   DWORD  dwFlags,
;   BYTE   *pbBinary,
;   DWORD  *pcbBinary,
;   DWORD  *pdwSkip,
;   DWORD  *pdwFlags
; )
   *phBinary = 0
   CRYPT_STRING_BASE64 = 1
   hCrypt  = DllLoad(DirWindows(1):"Crypt32.dll")
   hByteCount = BinaryAlloc(4)

   ; Get the binary buffer size
   bResult = DllCall(hCrypt, long:"CryptStringToBinaryA",lpstr:strBase64, long:0, long:CRYPT_STRING_BASE64, lpnull, lpbinary:hByteCount, lpnull, lpnull)
   If !bResult
      BinaryFree(hByteCount)
      DllFree(hCrypt)
      Return DllLastError()
   EndIf

   BinaryEodSet( hByteCount, 4 )
   nConvertedBytes = BinaryPeek4(hByteCount, 0)
   hConverted = BinaryAlloc(nConvertedBytes)

   ; Convert the string
   bResult = DllCall(hCrypt, long:"CryptStringToBinaryA",lpstr:strBase64, long:0, long:CRYPT_STRING_BASE64, lpbinary:hConverted, lpbinary:hByteCount, lpnull, lpnull)
   If !bResult Then nReturn = DllLastError()
   Else nReturn = 0

   BinaryFree(hByteCount)
   DllFree(hCrypt)
   BinaryEodSet(hConverted, nConvertedBytes)
   *phBinary = hConverted

   Return nReturn  ; 0 on success otherwise system error
#EndFunction


#DefineFunction BinaryToBase64(hBinary, pstrBase64)
; CryptBinaryToStringA function   https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptbinarytostringa
; BOOL CryptBinaryToStringA(
;   const BYTE *pbBinary,
;   DWORD      cbBinary,
;   DWORD      dwFlags,
;   LPSTR      pszString,
;   DWORD      *pcchString
; )
   CRYPT_STRING_BASE64 = 1
   hCrypt  = DllLoad(DirWindows(1):"Crypt32.dll")

   ByteCount = BinaryEodGet(hBinary)

   hStrCount = BinaryAlloc(4)

   strBase64 = ""

   ; Convert to string
   bResult = DllCall(hCrypt, long:"CryptBinaryToStringA", lpbinary:hBinary, long:ByteCount, long:CRYPT_STRING_BASE64, lpstr:StrBase64, lpbinary:hStrCount)
   If !bResult
      nReturn = DllLastError()
   Else
      nReturn = 0
   endif

   DllFree(hCrypt)

   StrCount = BinaryPeek4(hStrCount, 0)
   BinaryFree(hStrCount)

   *pstrBase64 = strBase64

   Return nReturn

#EndFunction


Betty = "12345"

If Base64ToBinary(Betty, &hResult) == 0

  if BinaryToBase64(hResult, &Sally) == 0

    message(betty, sally)

  endif

EndIf
The mind is everything; What you think, you become.

td

You need to call the "CryptBinaryToStringA" function twice.  On the first call set the pszString parameter to lpNull so that you get the size of the date after conversion in the pcchString parameter.  On the second call, you pass a binary buffer to the function and use BinaryEodSet and BinaryPokeStr to get your converted value.  The binary buffer should be allocated to the size indicated in the pcchString parameter after the first call to the function.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Quick and dirty example:
Code (winbatch) Select
#DefineFunction BinaryToBase64(hBinary, pstrBase64)
; CryptBinaryToStringA function   https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptbinarytostringa
; BOOL CryptBinaryToStringA(
;   const BYTE *pbBinary,
;   DWORD      cbBinary,
;   DWORD      dwFlags,
;   LPSTR      pszString,
;   DWORD      *pcchString
; )
   CRYPT_STRING_BASE64 = 1
   hCrypt  = DllLoad(DirWindows(1):"Crypt32.dll")

   ByteCount = BinaryEodGet(hBinary)

   hStrCount = BinaryAlloc(4)

   strBase64 = ""

   ; Get converted to string's size.
   bResult = DllCall(hCrypt, long:"CryptBinaryToStringA", lpbinary:hBinary, long:ByteCount, long:CRYPT_STRING_BASE64, lpNull, lpbinary:hStrCount)
   If !bResult
      nReturn = DllLastError()
   Else
      nReturn = 0
   endif

   if !nReturn
      StrCount = BinaryPeek4(hStrCount, 0)
      hConverted = BinaryAlloc(StrCount)
      ; Convert to string
      bResult = DllCall(hCrypt, long:"CryptBinaryToStringA", lpbinary:hBinary, long:ByteCount, long:CRYPT_STRING_BASE64, lpBinary:hConverted, lpbinary:hStrCount)
      If !bResult
         nReturn = DllLastError()
      Else
         BinaryEodSet(hConverted, StrCount)
         *pstrBase64 = BinaryPeekStr(hConverted, 0, StrCount)
         nReturn = 0
      endif
      BinaryFree(hConverted)
   endif
   DllFree(hCrypt)

   
   BinaryFree(hStrCount)

   ;;*pstrBase64 = strBase64

   Return nReturn

#EndFunction
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

Cool.  Thanks so much Tony. I'll have to stare at this for while.  The arcane ways of the Dll call are inscrutable, but apparently perceptible with study. (smile)

But it works great, so I'm on my way.  This idea is to pack the image for my HTML report right inside the HTML itself using src="data:image...."
I'll have to flip the BMP produced by Snapshot() to JPG for the reduction in size, but I know how to use pixie for that.

I'll post if I get it working.
The mind is everything; What you think, you become.

kdmoyers

OK, FWIW, this puts together an HTML file with a screen shot encoded therein.
Not terribly useful unless you need exactly that.
Code (winbatch) Select
#DefineFunction MakeSnapShotBinary(ShotType)
; requires Pixie Extender WWIMG44I.DLL
;
; returns binarybuffer handle with snapshot encoded therein
;
; JPG snapshot is somewhat grainy, but only 3% the size of the bitmap.
; GIF makes sharp image, and is 25% the size of the bitmap.
;
    tf1 = StrCat(Environment("TEMP"), "\MakeSnapShot.bmp")
    tf2 = StrCat(Environment("TEMP"), "\MakeSnapShot.gif") ; or jpg
    ;Takes a bitmap snapshot of a window to the clipboard.
    Snapshot(ShotType) ; 0 for whole screen, 4 for active window
    ;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)
    BinaryFree(bb)

    FileDelete(tf1)
    BinaryWrite(bb2,tf1)

    AddExtender("WWIMG44I.DLL")
    ImgConvert(tf1,tf2) ; convert bmp to ???
    FileDelete(tf1)

    BinaryRead(bb2, tf2)
    FileDelete(tf2)

    return bb2 ; return the BinaryBuffer handle

#EndFunction


#DefineFunction BinaryToBase64(hBinary, pstrBase64)
; CryptBinaryToStringA function   https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptbinarytostringa
; BOOL CryptBinaryToStringA(
;   const BYTE *pbBinary,
;   DWORD      cbBinary,
;   DWORD      dwFlags,
;   LPSTR      pszString,
;   DWORD      *pcchString
; )
   CRYPT_STRING_BASE64 = 1
   hCrypt  = DllLoad(DirWindows(1):"Crypt32.dll")
   ByteCount = BinaryEodGet(hBinary)
   hStrCount = BinaryAlloc(4)

   ; Get converted to string's size.
   bResult = DllCall(hCrypt, long:"CryptBinaryToStringA", lpbinary:hBinary, long:ByteCount, long:CRYPT_STRING_BASE64, lpNull, lpbinary:hStrCount)
   If !bResult
      nReturn = DllLastError()
   Else
      nReturn = 0
   endif

   if !nReturn
      StrCount = BinaryPeek4(hStrCount, 0)
      hConverted = BinaryAlloc(StrCount)
      ; Convert to string
      bResult = DllCall(hCrypt, long:"CryptBinaryToStringA", lpbinary:hBinary, long:ByteCount, long:CRYPT_STRING_BASE64, lpBinary:hConverted, lpbinary:hStrCount)
      If !bResult
         nReturn = DllLastError()
      Else
         BinaryEodSet(hConverted, StrCount)
         *pstrBase64 = BinaryPeekStr(hConverted, 0, StrCount)
         nReturn = 0
      endif
      BinaryFree(hConverted)
   endif
   DllFree(hCrypt)
   BinaryFree(hStrCount)
   Return nReturn

#EndFunction

  hBuf = MakeSnapShotBinary(3) ; 3=client area of active window
  terminate(BinaryToBase64(hBuf, &Base64string), "BinaryToBase64", "failed to convert")

  intcontrol(53,0,0,0,0) ; no auto line terminate on filewrite
  of = shortcutdir("desktop"):"\DeleteMe.htm"
  oh = fileopen(of, "write")
  filewrite(oh, `Here's a bunch of complicated html.<br>`:@crlf)
  filewrite(oh, `Here's a snapshot of the <b>active window</b><br>`:@crlf)
  filewrite(oh, `encoded right here in this html file.<br>`:@crlf)
  filewrite(oh, `<img src="data:image/jpg;base64,`:@crlf) ; either jpg or gif
  filewrite(oh, Base64String)
  filewrite(oh, `";  alt="Base64 encoded image" />`:@crlf)

  fileclose(oh)

Exit
The mind is everything; What you think, you become.