News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

VARIANT structure

Started by Siekmanski, February 12, 2015, 02:12:37 AM

Previous topic - Next topic

Siekmanski

I'm trying to get control over a HD Webcam using DirectShow.
For retrieving the webcam device name, I need the VARIANT structure to get it.

Tried to understand it by reading MSDN about this structure, but I'm not sure of what I wrote is acceptable. It works though......

VARIANT struct
vt dw ?
wReserved1 dw ?
wReserved2 dw ?
wReserved3 dw ?
pString dd ?
VARIANT ends

VT_BSTR   equ 8 ; Pointer to a null-terminated Unicode string.

Variant VARIANT <>

invoke MultiByteToWideChar,CP_ACP,0,TEXT_("FriendlyName"),-1,addr szLstring,255

mov     Variant.vt,VT_BSTR
mov     Variant.pString,NULL
coinvoke g_pPropertyBag,IPropertyBag,Read,addr szLstring,addr Variant,0

invoke  WideCharToMultiByte,CP_ACP,0,Variant.pString,-1,addr szFriendlyName,255,0,0

invoke  SysFreeString,Variant.pString

Creative coders use backward thinking techniques as a strategy.

jj2007

There are many variants of VARIANT, it seems. Here is one from \Masm32\MasmBasic\MasmBasic.inc:

  VARIANT struct      ; see oaidl.h
  vt      dd ?      ; VARTYPE ?
  wReserved1      dw ?
  wReserved2      dw ?
  wReserved3      dw ?
  union
  bstrVal      dd ?
  pintVal      dd ?
  pvRecord      dd ?      ; add whatever you need..,
  ends
  VARIANT      ends

IUnknown STRUCT
  QueryInterface      dd ?
  AddRef      dd ?
  Release      dd ?
IUnknown ENDS

Siekmanski

So I'm not violating the rules by using this structure as I did ?
(I should ofcourse change pString in to bstrVal)
I noticed your vt VARTYPE typedef, shouldn't it be a 16 bit word size ?
Creative coders use backward thinking techniques as a strategy.

dedndave

i think most VARIANT structs are 4 DWORD's in size
a while back, we were playing with zip through iShellDispatch
i disassembled some C code and found that the VARIANT struct was passed in it's entirety (not a pointer to struct)
so for assembler, rather that trying to pass a VARIANT, i broke it up and passed 4 DWORD arguments
;IShellDispatch::NameSpace Folder object interface vTable (structure)
;   vItem VARIANT struct = vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD
;vOptions VARIANT struct = vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD

Folder STRUCT
    IDispatch            <>
  METHOD(getTitle,     _this:LPVOID,pbs:LPVOID)
  METHOD(Application,  _this:LPVOID,ppid:LPVOID)
  METHOD(Parent,       _this:LPVOID,ppid:LPVOID)
  METHOD(ParentFolder, _this:LPVOID,ppsf:LPVOID)
  METHOD(Items,        _this:LPVOID,ppid:LPVOID)
  METHOD(ParseName,    _this:LPVOID,bstrName:LPVOID,ppid:LPVOID)
  METHOD(NewFolder,    _this:LPVOID,bstrName:LPVOID,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(MoveHere,     _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(CopyHere,     _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(GetDetailsOf, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,iColumn:DWORD,pbs:LPVOID)
Folder ENDS

jj2007

Quote from: Siekmanski on February 12, 2015, 03:10:47 AM
I noticed your vt VARTYPE typedef, shouldn't it be a 16 bit word size ?

Yep, you are right (wtypes.h): typedef unsigned short VARTYPE;
Thanks :t
I wonder why it always worked ::)

Siekmanski

#5
Quote from: jj2007 on February 12, 2015, 06:35:40 AM
I wonder why it always worked ::)

Translating cpp headers can be a pain in the **s.  :(
I had something similar last month with a structure from the bluetooth header files.
Used a dword instead of a word, and it worked. Then found it and changed it to a word and guess what ?
Yeah, it didn't work anymore. The structure needed to be 8 byte aligned and it worked again.

Traced the VARIANT STRUCTURE used by DirectShow back to this C++ Header file: OAIdl.h

/* VARIANT STRUCTURE
*
*  VARTYPE vt;
*  WORD wReserved1;
*  WORD wReserved2;
*  WORD wReserved3;
*  union {
*    LONGLONG       VT_I8
*    LONG           VT_I4
*    BYTE           VT_UI1
*    SHORT          VT_I2
*    FLOAT          VT_R4
*    DOUBLE         VT_R8
*    VARIANT_BOOL   VT_BOOL
*    SCODE          VT_ERROR
*    CY             VT_CY
*    DATE           VT_DATE
*    BSTR           VT_BSTR
*    IUnknown *     VT_UNKNOWN
*    IDispatch *    VT_DISPATCH
*    SAFEARRAY *    VT_ARRAY
*    BYTE *         VT_BYREF|VT_UI1
*    SHORT *        VT_BYREF|VT_I2
*    LONG *         VT_BYREF|VT_I4
*    LONGLONG *     VT_BYREF|VT_I8
*    FLOAT *        VT_BYREF|VT_R4
*    DOUBLE *       VT_BYREF|VT_R8
*    VARIANT_BOOL * VT_BYREF|VT_BOOL
*    SCODE *        VT_BYREF|VT_ERROR
*    CY *           VT_BYREF|VT_CY
*    DATE *         VT_BYREF|VT_DATE
*    BSTR *         VT_BYREF|VT_BSTR
*    IUnknown **    VT_BYREF|VT_UNKNOWN
*    IDispatch **   VT_BYREF|VT_DISPATCH
*    SAFEARRAY **   VT_BYREF|VT_ARRAY
*    VARIANT *      VT_BYREF|VT_VARIANT
*    PVOID          VT_BYREF (Generic ByRef)
*    CHAR           VT_I1
*    USHORT         VT_UI2
*    ULONG          VT_UI4
*    ULONGLONG      VT_UI8
*    INT            VT_INT
*    UINT           VT_UINT
*    DECIMAL *      VT_BYREF|VT_DECIMAL
*    CHAR *         VT_BYREF|VT_I1
*    USHORT *       VT_BYREF|VT_UI2
*    ULONG *        VT_BYREF|VT_UI4
*    ULONGLONG *    VT_BYREF|VT_UI8
*    INT *          VT_BYREF|VT_INT
*    UINT *         VT_BYREF|VT_UINT
*  }
*/
#if (__STDC__ && !defined(_FORCENAMELESSUNION)) || defined(NONAMELESSUNION) || (!defined(_MSC_EXTENSIONS) && !defined(_FORCENAMELESSUNION))
#define __VARIANT_NAME_1 n1
#define __VARIANT_NAME_2 n2
#define __VARIANT_NAME_3 n3
#define __VARIANT_NAME_4 brecVal
#else
#define __tagVARIANT
#define __VARIANT_NAME_1
#define __VARIANT_NAME_2
#define __VARIANT_NAME_3
#define __tagBRECORD
#define __VARIANT_NAME_4
#endif
typedef /* [wire_marshal] */ struct tagVARIANT VARIANT;

struct tagVARIANT
    {
    union
        {
        struct __tagVARIANT
            {
            VARTYPE vt;
            WORD wReserved1;
            WORD wReserved2;
            WORD wReserved3;
            union
                {
                LONGLONG llVal;
                LONG lVal;
                BYTE bVal;
                SHORT iVal;
                FLOAT fltVal;
                DOUBLE dblVal;
                VARIANT_BOOL boolVal;
                _VARIANT_BOOL bool;
                SCODE scode;
                CY cyVal;
                DATE date;
                BSTR bstrVal;
                IUnknown *punkVal;
                IDispatch *pdispVal;
                SAFEARRAY *parray;
                BYTE *pbVal;
                SHORT *piVal;
                LONG *plVal;
                LONGLONG *pllVal;
                FLOAT *pfltVal;
                DOUBLE *pdblVal;
                VARIANT_BOOL *pboolVal;
                _VARIANT_BOOL *pbool;
                SCODE *pscode;
                CY *pcyVal;
                DATE *pdate;
                BSTR *pbstrVal;
                IUnknown **ppunkVal;
                IDispatch **ppdispVal;
                SAFEARRAY **pparray;
                VARIANT *pvarVal;
                PVOID byref;
                CHAR cVal;
                USHORT uiVal;
                ULONG ulVal;
                ULONGLONG ullVal;
                INT intVal;
                UINT uintVal;
                DECIMAL *pdecVal;
                CHAR *pcVal;
                USHORT *puiVal;
                ULONG *pulVal;
                ULONGLONG *pullVal;
                INT *pintVal;
                UINT *puintVal;
                struct __tagBRECORD
                    {
                    PVOID pvRecord;
                    IRecordInfo *pRecInfo;
                    } __VARIANT_NAME_4;
                } __VARIANT_NAME_3;
            } __VARIANT_NAME_2;
        DECIMAL decVal;
        } __VARIANT_NAME_1;
    } ;
typedef VARIANT *LPVARIANT;

typedef VARIANT VARIANTARG;

typedef VARIANT *LPVARIANTARG;

#ifdef MIDL_PASS
typedef const VARIANT *REFVARIANT;

#else

#ifndef _REFVARIANT_DEFINED
#define _REFVARIANT_DEFINED
#ifdef __cplusplus
#define REFVARIANT const VARIANT &
#else
#define REFVARIANT const VARIANT * __MIDL_CONST
#endif
#endif


Initializing the VARTYPE vt with VT_BSTR gives me the string data pointer directly after wReserved3.
I was just lucky finding the string, didn't know ( still don't know ) how this internally works.

This is the VARIANT structure I use in my code,


VARIANT struct
vt dw ?
wReserved1 dw ?
wReserved2 dw ?
wReserved3 dw ?
union
bstrVal dd ?
ends
VARIANT ends
Creative coders use backward thinking techniques as a strategy.

Antariy

Marinus, every element listed in the "union" block (after union word and the "{" till the "}") are placed in the same place in memory, i.e. overall size of the unit in memory is equal to the size of the greatest element (aligned to a structure alignment) of the union, to to sum of every element of the union. The data type is set in the vt element of the variant struct, the data set to the union, the kind of the data that is contained in the union is checked with vt element, but the reffering to the data is to the same place. If vt type is BSTR - the data in the union is pointer to a binary string, if it is VT_I4 - it's dword etc. A bunch of elements in the union is just for C compiler, to allow reference to an appropriate datatype (which is checked by checking vt element) without explicit data-type conversion (casting).

You may for an instance modify your structure so:

ARIANT struct
   vt      dw ?
   wReserved1   dw ?
   wReserved2   dw ?
   wReserved3   dw ?
   dw1      dd ?
   dw2      dd ?
VARIANT ends

So when you set/get the variant structure with vt set to VT_I4, you know that the data type contained in the variant is a DWORD, and may refer to it as to dw1. If the vt set to VT_I8, the data in the variant is a qword, so you may refer to it as to dw1 - low part of qword, dw2 - high part of qword.
If vt is VT_BSTR, then dw1 is a pointer to a BSTR. When the VT_XXX marker is ORed with the VT_BYREF flag, then the data in variant is a pointer to the data marked with VT_XXX (which is beside VT_REF). I.e. if the vt is VT_I4 OR VT_REF, then to get the actual value you should get dw1 as a pointer to a dword, then from that pointer get the dword: mov eax,dw1; mov eax,[eax];
If vt is VT_BSTR OR VT_REF, then the variant contains the pointer to a BSTR pointer, so, to access the string content, you should take content of a dw1 as a pointer to a pointer, i.e. mov eax,dw1; mov eax,[eax] - now eax is a pointer to the string data. But the forms with a VT_BYREF are more rare used.

Siekmanski

Antariy, thank you very much for explaining this "mystery" to me.  :t
If I only want a vt type BSTR, do I need to put the extra DWORD in the structure ?
Creative coders use backward thinking techniques as a strategy.

jj2007

Vertograd played around successfully with another variant of VARIANT:

VARIANT STRUCT
  dw1   dd 0CCh
  dw2   dd 0CCh
  dw3   dd 0CCh
  dw4   dd 0CCh
VARIANT   ENDS

When googling for this stuff, I found an article that you will love:

QuoteMasm's Changing Face
By Mike Schmit, July 01, 1991
Compared to previous versions, Microsoft's recently released Macro Assembler 6.0 embodies some ambitious changes.

Unfortunately, they messed up their html, but I managed to produce a readable version, see attachment. Example:
QuoteFigure 2: MASM 6.0 automatically generates a jump fixup when there is a jump out of range. Notice that in this example the generated code is 5 bytes long instead of 2.

       cmp ax, error_code
       je exit_error
       db 128 dup(90h)  ; (128 bytes of code, NOP's here)
  exit_error:

  MASM 6.0 translates the above code to the following:

       cmp ax, error_code
       jne $+3          ; Note: $+3 is a relative
                        ; jump 3 bytes ahead
       jmp exit_error
       db 128 dup (90h)
  exit_error:

If you are attempting to craft very compact code and you don't want this automatic action to take place, you can use the OPTION: NOLJMP directive.

Antariy

Hi Marinus :t

Quote from: Siekmanski on February 12, 2015, 10:18:19 AM
If I only want a vt type BSTR, do I need to put the extra DWORD in the structure ?

Yes, actually you should make the structure size always at least 16 bytes long, because you may not be sure that the called code doesn't fills the entire structure, for an instance, with zeroes, before filling it with the resulting value. If you use the structure size smaller than that, then you may accidently get some data overwritten in the stack/heap/.data section (depends on where the struct is placed).
For an instance, as a simple example, check something like this:

.data?

variant VARIANT <?> ; the VARIANT structure defined with one dword, not two, after wReserved3
testdword dd ?

.code

mov testdword,1234

print str$(testdword)

invoke VariantClear, offset variant ; this API clears (fills with zeros) the VARIANT variable, this function is called by many APIs which get/return variants, besides of other possible ways of overwriting the entire structure

print str$(testdword)


After the call to API, second print will reveal the possible place for a hard to find bug (accidental overwriting of the other variable which may contain any data, or pushed data / registed value / return address on the stack). So you should always define the structure at least 16 bytes long, even if you don't need to used the full size of the data field.

Siekmanski

jj2007, interesting read.  :t

Antariy, thanks.  :t
I just wondered, because it didn't overwrite memory and I didn't use VariantInit and VariantClear.
I just clear the string pointer in the struct and after retrieving the device string, I clear the string memory with a call to SysFreeString.
To be safe I'll put an extra DWORD in the structure and keep it 16 bytes long.
Creative coders use backward thinking techniques as a strategy.

Antariy

Quote from: Siekmanski on February 15, 2015, 08:39:43 PM
jj2007, interesting read.  :t

Antariy, thanks.  :t
I just wondered, because it didn't overwrite memory and I didn't use VariantInit and VariantClear.
I just clear the string pointer in the struct and after retrieving the device string, I clear the string memory with a call to SysFreeString.
To be safe I'll put an extra DWORD in the structure and keep it 16 bytes long.

Yes, that's required just because the struct has the fixed "standard" size and you may not be sure that any internals of an API will not overwrite the data in entire 16 byte struct, even if you didn't do that in your code.