News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Understanding C structures

Started by jj2007, August 24, 2016, 12:44:27 AM

Previous topic - Next topic

jj2007

Windows.inc:
COAUTHIDENTITY STRUCT
    User DWORD              ?
    UserLength DWORD        ?
    Domain DWORD            ?
    DomainLength DWORD      ?
    Password DWORD          ?
    PasswordLength DWORD    ?
    Flags DWORD             ?
COAUTHIDENTITY ENDS


VC help:
  typedef struct _COAUTHIDENTITY {
    USHORT *User;
    ULONG  UserLength;
    USHORT *Domain;
    ULONG  DomainLength;
    USHORT *Password;
    ULONG  PasswordLength;
    ULONG  Flags;
  } COAUTHIDENTITY;


User, Domain and Password are strings. What does USHORT *User mean here?

Obviously, it's not an unsigned 16-bit value here, but rather a pointer. To what? A string composed of USHORTs? If a Unicode string is meant, why is not WCHAR instead of USHORT?

I fail to see the logic here ::)

Btw I found many structures in Windows.inc that do not exist in the Visual C headers, e.g.
RAS_PORT_STATISTICS
EMRCREATECOLORSPACEA doesn't exist, but EMRCREATECOLORSPACE and EMRCREATECOLORSPACEW are OK
RECOVERY_AGENT_INFORMATION
BROWSER_EMULATED_DOMAIN
APPDETAIL

Yuri

What it means depends on the Flags member.

Flags
Indicates whether the strings are Unicode strings.

Value Meaning
SEC_WINNT_AUTH_IDENTITY_ANSI
0x1 The strings are ANSI strings.

SEC_WINNT_AUTH_IDENTITY_UNICODE
0x2 The strings are Unicode strings.

jj2007

So USHORT is a synonym for "CHAR or WCHAR"???
:dazzled:

TouEnMasm

SHORT = USHORT (Unicode SHORT) = WORD
Fa is a musical note to play with CL

Zen

JOCHEN,
If you were REALLY an uber-cool programmer,...you'd use the: the TCHAR type (there's not really any TCHAR stuff in the MASM package, but, HUTCH is on RED ALERT). I'm only semi-cool, so I use the single-byte ANSI character set, unless I'm in UBER-MODE (COM Stuff, like COAUTHIDENTITY), then, I use UNICODE characters. In all honesty, it confuses the hell out of me. :bgrin:

#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif


Here's a good article for you newbie programmers: What are TCHAR, WCHAR, LPSTR, LPWSTR, LPCTSTR (etc.)?, CodeProject
...But,...you know all that stuff,...

jj2007

Quote from: Zen on August 24, 2016, 03:36:33 AM
If you were REALLY an uber-cool programmer,...you'd use the: the TCHAR type.

It's not me, it's the Redmond guys who use USHORT as a synonym for TCHAR 8)

jj2007

Back to topic: How can one recognise a structure in a C header file?

It's easy: Just look for typedef struct tagMYSTRUCT:typedef struct tagPOINT
    {
    LONG x;
    LONG y;
    } POINT;


Sometimes it looks a bit complicated, but the "tag" is there:typedef struct tagMIXERCONTROLA {
    DWORD           cbStruct;           /* size in bytes of MIXERCONTROL */
    DWORD           dwControlID;        /* unique control id for mixer device */
    DWORD           dwControlType;      /* MIXERCONTROL_CONTROLTYPE_xxx */
    DWORD           fdwControl;         /* MIXERCONTROL_CONTROLF_xxx */
    DWORD           cMultipleItems;     /* if MIXERCONTROL_CONTROLF_MULTIPLE set */
    CHAR            szShortName[MIXER_SHORT_NAME_CHARS];
    CHAR            szName[MIXER_LONG_NAME_CHARS];
    union {
        struct {
            LONG    lMinimum;           /* signed minimum for this control */
            LONG    lMaximum;           /* signed maximum for this control */
        } DUMMYSTRUCTNAME;
        struct {
            DWORD   dwMinimum;          /* unsigned minimum for this control */
            DWORD   dwMaximum;          /* unsigned maximum for this control */
        } DUMMYSTRUCTNAME2;
        DWORD       dwReserved[6];
    } Bounds;
    union {
        DWORD       cSteps;             /* # of steps between min & max */
        DWORD       cbCustomData;       /* size in bytes of custom data */
        DWORD       dwReserved[6];      /* !!! needed? we have cbStruct.... */
    } Metrics;
} MIXERCONTROLA, *PMIXERCONTROLA, *LPMIXERCONTROLA;


(the !!! needed? comment is not mine, that's the Redmondster speaking 8))

So it's the "tag"... except when they use "_" instead:typedef struct _POINTL
    {
    LONG x;
    LONG y;
    } POINTL;


followed by this cryptic statement:typedef struct _POINTL *PPOINTL; ::)

But wait, there are some that use neither "tag" nor "_":typedef struct {
        unsigned short usFlags;
short     cfFormat;
} DDEADVISE;


Now, looking carefully at the three options so far, the only thing they have in common is that the name of the structure appears at the end after a }:
    } POINT;
    } POINTL;
    } DDEADVISE;


Bingo! Except for this case:typedef struct tagEMRSETTEXTCOLOR
{
    EMR     emr;
    COLORREF crColor;
} EMRSETBKCOLOR,   *PEMRSETBKCOLOR,
  EMRSETTEXTCOLOR, *PEMRSETTEXTCOLOR;


No problem! The rule is apparently "find the name of the structure, then go back until you encounter a }" 8)

Except for this case:struct GdiplusStartupInput
{
    UINT32 GdiplusVersion;             // Must be 1  (or 2 for the Ex version)
    DebugEventProc DebugEventCallback; // Ignored on free builds
    BOOL SuppressBackgroundThread;     // FALSE unless you're prepared to call
                                       // the hook/unhook functions properly
    BOOL SuppressExternalCodecs;       // FALSE unless you want GDI+ only to use
                                       // its internal image codecs.
   
    GdiplusStartupInput(
        DebugEventProc debugEventCallback = NULL,
        BOOL suppressBackgroundThread = FALSE,
        BOOL suppressExternalCodecs = FALSE)
    {
        GdiplusVersion = 1;
        DebugEventCallback = debugEventCallback;
        SuppressBackgroundThread = suppressBackgroundThread;
        SuppressExternalCodecs = suppressExternalCodecs;
    }
};


Can anybody see a rule here...? Or explain why GdiplusStartupInput appears twice, as
struct GdiplusStartupInputand
GdiplusStartupInput(...)?  ::)

Yuri

From what I remember of C++, the second GdiplusStartupInput is a constructor function. That is, when you create such a structure, this function is called automatically, and you can pass arguments for it to initialize the members. Something like this:

GdiplusStartupInput *gpsi = new GdiplusStartupInput(pCallback, TRUE, TRUE);

Of course it could do something more sophisticated than plain assignments. There could also be a destructor, which would be called when the structure was deleted. It could be used to free some resources like memory allocated from the heap, for example. You can also define your own functions inside a structure. Actually a C++ class is such a sructure containing data members and functions that operate on them (methods). The difference is that the members of a structure are public by default, i.e. directly accessible from the outside world, and in a class they are private.

jj2007

Quote from: Yuri on August 24, 2016, 01:57:52 PMFrom what I remember of C++, the second GdiplusStartupInput is a constructor function.

Thanks for the explanation, Yuri :t

Here is another goodie:#define RASCONNA struct tagRASCONNA
RASCONNA
{
    DWORD    dwSize;
    HRASCONN hrasconn;
    CHAR     szEntryName[ RAS_MaxEntryName + 1 ];

#if (WINVER >= 0x400)
    CHAR     szDeviceType[ RAS_MaxDeviceType + 1 ];
    CHAR     szDeviceName[ RAS_MaxDeviceName + 1 ];
#endif
#if (WINVER >= 0x401)
    CHAR     szPhonebook [ MAX_PATH ];
    DWORD    dwSubEntry;
#endif
#if (WINVER >= 0x500)
    GUID     guidEntry;
#endif
#if (WINVER >= 0x501)
    DWORD    dwFlags;
    LUID     luid;
#endif
#if (WINVER >= 0x600)
    GUID     guidCorrelationId;
#endif
};


No STRUCTNAME after the closing }, but instead a tagSTRUCTNAME hidden in a define... sick ::)

adeyblue


typedef struct tagPOINT
    {
    LONG x;
    LONG y;
    } POINT;

This creates a type that has a name of tagPOINT and an alternative name of POINT. In original C, without the typedef you'd have to write "struct tagPOINT" each time you wanted to use one since struct types belong to their own namespace (i.e. just tagPOINT wouldn't work). The typedef is used to elide that so you can just write POINT everywhere.


typedef struct _POINTL *PPOINTL;

While you can combine the typedef with the definition of the struct, you don't have to. Since these headers are used in C, "struct _POINTL" has to be used to refer to the struct, rather than just _POINTL becaus of the namespace thing. This line defines an alias to a pointer to such a struct and gives it a name PPOINTL, like LPWSTR is an alias for WCHAR*.


typedef struct tagEMRSETTEXTCOLOR
{
    EMR     emr;
    COLORREF crColor;
} EMRSETBKCOLOR,   *PEMRSETBKCOLOR,
  EMRSETTEXTCOLOR, *PEMRSETTEXTCOLOR;

This combines the previous two things, so that the struct has 3 ways to refer to it, and two pointer aliases.


typedef struct {
        unsigned short usFlags;
short     cfFormat;
} DDEADVISE;

This is an anonymous struct. It can only be referred using DDEADVISE.


struct GdiplusStartupInput
{ ...
};

GDI+ are C++ headers. In C++, they threw away the struct namespace stuff so you can just use the type name without having to put struct before it. Since these headers aren't usable from C, there's no need to create an alias to avoid the "struct" since nobody has to write it anyway.

There's also another thing, but I don't think there are any examples in the MS headers. Anyway, you can also have

struct GdiplusStartupInput
{ ...
} MyStartupInput;

Unlike the typedef one which creates an alias, this creates an actual object called MyStartupInput which you'd be able to pass to GdiplusStartup.

To parse these out you'd probably want to use a regex something like "typedef? struct (\w+)?\s*\{[^\}]*\}\s?(\*?\w+)*[,;]"
The first capture group/match would be the typename (unless its an anonymous struct), and the second would be the first word after it.
It doesn't work fully as is, since it wouldn't match the MIXERCONTROL struct properly due to the embedded structs/unions, and it'd cry on the RASCONNA example due to the extra RASCONNA between the "struct" and the bracket.

jj2007

Thanks, adeyblue, very clear and very helpful :t

In case you are wondering why I am digging into VC headers:
Quote from: hutch-- on July 14, 2016, 12:07:43 PM
The solution for what habran is after is in fact to use only the Microsoft C/C++ header files and make a front end for the assembler that can read them. That would solve all of the problems in creating include files.

jj2007

#11
SYSTEM_INFO structure
  ...
  DWORD     dwPageSize;
  LPVOID    lpMinimumApplicationAddress;
  LPVOID    lpMaximumApplicationAddress;
  DWORD_PTR dwActiveProcessorMask;
  DWORD     dwNumberOfProcessors;


QuotedwActiveProcessorMask
    A mask representing the set of processors configured into the system. Bit 0 is processor 0; bit 31 is processor 31.

Looks like a DWORD, somehow. So why is the type DWORD_PTR?  ::)

There is a particularly smart comment on SOF:
QuoteThe function will not fail if you pass a DWORD, because it fits into a DWORD_PTR. A pointer, however, is guaranteed to fit into a DWORD_PTR but not into a DWORD on 64-bit platforms.

And MSDN:
QuoteA DWORD_PTR is an unsigned long type used for pointer precision. It is used when casting a pointer to an unsigned long type to perform pointer arithmetic.
- a LONG has 32 bits in both 32-bit and 64-bit code, right?

Well, it turns out that a DWORD_PTR is an __int3264. And there it becomes even clearer, as clear as the London fog in the sixties (MSDN):

Quote2.2.1 __int3264

An alias that is resolved to either:

    An __int32 in a 32-bit translation and execution environment, or

    An __int64 in a 64-bit translation and execution environment. For backward compatibility, it is 32-bit on the wire. The higher 4 bytes MUST be truncated on the sender side during marshaling and MUST be extended appropriately (signed or unsigned), as specified in [C706] section 14.2.5, on the receiving side during unmarshaling.

I like that part where it says "truncate when sending, sign-extend when receiving". Sounds logical :P

P.S.: Did you know that a file handle can be a HFILE (32 bits) or a HANDLE (64 bits)?

MSDN data types (small excerpt)
BOOL A Boolean variable (should be TRUE or FALSE). typedef int BOOL
BOOLEAN A Boolean variable (should be TRUE or FALSE). typedef BYTE BOOLEAN
COLORREF The red, green, blue (RGB) color value (32 bits)
DWORDLONG A 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
DWORD32 A 32-bit unsigned integer
DWORD64 A 64-bit unsigned integer
DWORD_PTR An unsigned long type ... commonly used for general 32-bit parameters that have been extended to 64 bits in 64-bit Windows
HANDLE typedef PVOID HANDLE;
HFILE A handle to a file opened by OpenFile, not CreateFile. typedef int HFILE; To close the file, call the CloseHandle function using this handle ->HANDLE WINAPI CreateFile
HRESULT typedef LONG HRESULT (32);
LRESULT typedef LONG_PTR LRESULT (64);
INT64 A 64-bit signed integer. typedef signed __int64 INT64;
LONG A 32-bit signed integer
LONGLONG A 64-bit signed integer
LPARAM typedef LONG_PTR LPARAM;
WPARAM typedef UINT_PTR WPARAM;
LPBOOL A pointer to a BOOL
POINTER_32 A 32-bit pointer. On a 32-bit system, this is a native pointer. On a 64-bit system, this is a truncated 64-bit pointer.
POINTER_64 A 64-bit pointer. On a 64-bit system, this is a native pointer. On a 32-bit system, this is a sign-extended 32-bit pointer.
PULONG32 A pointer to a ULONG32; typedef ULONG32 *PULONG32
QWORD A 64-bit unsigned integer.
SC_HANDLE typedef HANDLE SC_HANDLE
SC_LOCK typedef LPVOID SC_LOCK
SIZE_T typedef ULONG_PTR SIZE_T
SSIZE_T typedef LONG_PTR SSIZE_T;
UINT An unsigned INT. The range is 0 through 4294967295 decimal
UINT64 An unsigned INT64. The range is 0 through 18446744073709551615 decimal.
ULONG An unsigned LONG. The range is 0 through 4294967295 decimal
ULONGLONG A 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
ULONG32 An unsigned LONG32. The range is 0 through 4294967295 decimal.
VOID Any type. This type is declared in WinNT.h as follows: #define VOID void  << esoteric nonsense?

hutch--

There is a simple bypass to such weighty considerations, in Win 64, if its a pointer, its QWORD in size. The data MAY fit into a DWORD but a 64 bit pointer is a 64 bit value.

jj2007

Quote from: hutch-- on August 27, 2016, 08:08:27 PM
There is a simple bypass to such weighty considerations, in Win 64, if its a pointer, its QWORD in size. The data MAY fit into a DWORD but a 64 bit pointer is a 64 bit value.

You found the solution, Hutch :t So dwActiveProcessorMask is a pointer, right?

jj2007

Hooray, I finally found stdio.h :t

Actually, there are two of them:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\stdio.h
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\stdio.h

But they look very similar. Here is a simple example, the 13 entries for sprintf from which one could possibly build the PROTOs:
_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl sprintf_s(_Out_z_bytecap_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_z_ _P
__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1_ARGLIST(int, sprintf_s, vsprintf_s, _Deref_post_z_ char, _Dest, _In_z_ _Printf_format_string_ const char
_CRTIMP_ALTERNATIVE int __cdecl vsprintf_s(_Out_z_cap_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_z_ _Print
__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vsprintf_s, _Deref_post_z_ char, _Dest, _In_z_ _Printf_format_string_ const char *, _Format,
__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_ARGLIST(int, __RETURN_POLICY_SAME, _CRTIMP, sprintf, vsprintf, _Pre_notnull_ _Post_z_, char, _Dest, _In_z_ _Printf_format_string_ const
_Check_return_opt_ _CRTIMP int __cdecl _sprintf_p(_Out_z_cap_(_MaxCount) char * _Dst, _In_ size_t _MaxCount, _In_z_ _Printf_format_
_Check_return_opt_ _CRTIMP int __cdecl _vsprintf_p(_Out_z_cap_(_MaxCount) char * _Dst, _In_ size_t _MaxCount, _In_z_ _Printf_format_
_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_sprintf_s_l) _CRTIMP int __cdecl _sprintf_l(_Pre_notnull_ _Post_z_ char * _DstBuf, _In_z_ _
_Check_return_opt_ _CRTIMP int __cdecl _sprintf_p_l(_Out_z_cap_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_fo
_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _sprintf_s_l(_Out_z_bytecap_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_
_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_vsprintf_s_l) _CRTIMP int __cdecl _vsprintf_l(_Pre_notnull_ _Post_z_ char * _DstBuf, _In_z_
_Check_return_opt_ _CRTIMP int __cdecl _vsprintf_p_l(_Out_z_cap_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_fo
_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsprintf_s_l(_Out_z_cap_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_form