UUID/GUID generation


#1

Hi all.

Trying to generate a UUID/GUID using some of the Windows API functionality following MSDN pages.

So far I can fill the UUID group, but am having an issue converting it to a string, it keeps returning rubbish. Perhaps I have prototyped incorrectly or have completely misunderstood what it is doing?

Below is the code, can someone point me in the right direction or point out where I went wrong?

   GenerateUUID         FUNCTION                     ! Declare Procedure
    !map
    ! moved to Inside the Global Map, doesn't work here
    !    map
    !        ! used by the UUIDModule
    !        module('Rpcrt4.dll') ! Rpcrt4.dll
    !            UuidCreate(*UUIDGT),ULONG,PASCAL,RAW,NAME('UuidCreate')
    !            UuidToString(*UUIDGT,*CSTRING),ULONG,PASCAL,RAW,NAME('UuidToStringA')
    !            !RpcStringFree(*LONG),ULONG,PASCAL,RAW,NAME('RpcStringFreeA')
    !        end
    !    end

    !local variables

    ! UUID Structure
    !   GUID: 6B29FC40-CA47-1067-B31D-00DD010662DA
    !
    ! https://msdn.microsoft.com/en-us/library/windows/desktop/aa379358(v=vs.85).aspx
    !
    UUIDGT      GROUP,TYPE
    Data1         ULONG         ! the first 8 hexadecimal digits
    Data2         USHORT        ! the first group of 4 hexadecimal digits
    Data3         USHORT        ! the second group of 4 hexadecimal digits
    Data4         BYTE,DIM(8)   ! Array of eight elements. The first two elements contain the third group of 4 hexadecimal digits
                END             !                          the remaining six elements contain the final 12 hexadecimal digits.

    ! NOTE: Data4 is defined as an "unsigned char" of 8 elements.
    !       An unsigned char is a (unsigned) byte value (0 to 255).
    !       You may be thinking of "char" in terms of being a "character"
    !       but it is really a numerical value.
    !       The regular "char" is signed, so you have 128 values,
    !       and these values map to characters using ASCII encoding.
    ! https://stackoverflow.com/questions/75191/what-is-an-unsigned-char


    ! equates for return value of RPC_STATUS
    RPC_S_OK    EQUATE(0)   !pretty sure it's 0 but can't confirm
    !RPC_S_OUT_OF_MEMORY
    !RPC_S_UUID_LOCAL_ONLY
    !RPC_S_UUID_NO_ADDRESS

    UUID        LIKE(UUIDGT),AUTO
    UUIDCString CSTRING(50)  !it should be 37 (numbers and separators) + null terminator
    UUIDString  STRING(50)
    RPC_STATUS  LONG


      CODE                                            ! Begin processed code
    !generate UUID/GUID

    ! UuidCreate
    ! https://msdn.microsoft.com/en-us/library/windows/desktop/aa379205(v=vs.85).aspx
    ! UuidToString
    ! https://msdn.microsoft.com/en-us/library/windows/desktop/aa379352(v=vs.85).aspx
    ! RpcStringFree
    ! https://msdn.microsoft.com/en-us/library/windows/desktop/aa378483(v=vs.85).aspx

        ! unresolved external for UuidCreate and UuidToString
        ! requires Rpcrt4.lib to be in folder and added to project.

        RPC_STATUS = UuidCreate(UUID)

        if RPC_STATUS = RPC_S_OK

            DO DisplayDecimalUUID !to see if the GROUP has been populated

            RPC_STATUS = UuidToString(UUID,UUIDCstring) !<<<< this fails

            if RPC_STATUS = RPC_S_OK
                UUIDString = UUIDCstring
                message('uuid is: ' & clip(left(uuidstring)))
                !RPC_STATUS = RpcStringFree(UUIDCstring)     !
                return clip(left(uuidstring))
            else
                message('uuidtostring failed')
                return ''
            end

        else
            message('uuid generation failed')
            return ''
        end

    DisplayDecimalUUID ROUTINE
        ! I think this shows the UUID correctly in decimal format
        message('UUID.data1: ' & uuid.data1 & |
               '|UUID.data2: ' & uuid.data2 & |
               '|UUID.data3: ' & uuid.data3 & |
               '|UUID.data4: ' & uuid.data4[1] & '/' & uuid.data4[2] & '/' & uuid.data4[3] & '/' & uuid.data4[4] & '/' & |
                                 uuid.data4[5] & '/' & uuid.data4[6] & '/' & uuid.data4[7] & '/' & uuid.data4[8] )

The MSDN doco states the application is responsible to free the memory for the returned string, not sure how to go about creating a pointer to a pointer for the RpcStringFree function.

FYI, still on C5.5PE

Cheers,

Andrew.


#2

Hi Andrew,

Main issue is that what you get back from UuidToString is a pointer to a string dynamically created by the API (that’s why you later have to free it with RpcStringFree) - take a look at this (with acknowledgement to Randy Rogers)

					PROGRAM

					map
						module('Rpcrt4.dll')
							UuidCreate(*UUIDGT),ULONG,PASCAL,RAW,NAME('UuidCreate')
							UuidToString(*UUIDGT,*LONG),ULONG,PASCAL,RAW,NAME('UuidToStringA')
							RpcStringFree(*LONG),ULONG,PASCAL,RAW,NAME('RpcStringFreeA')
						end
						GUIDToString(*uuidgt),string
					end

UUIDGT              GROUP,TYPE
Data1                   ULONG 
Data2                   USHORT
Data3                   USHORT
Data4                   BYTE,DIM(8)
					END

RPC_S_OK            EQUATE(0)

UUID                LIKE(UUIDGT),AUTO
RPC_STATUS          LONG

	code
		clear(UUID,-1)
		RPC_STATUS = UuidCreate(UUID)
		if RPC_STATUS = RPC_S_OK
			DO DisplayDecimalUUID
			if RPC_STATUS = RPC_S_OK
				message('uuid is: ' & GUIDToString(UUID))
			else
				message('uuidtostring failed')
			end
		else
			message('uuid generation failed')
		end

DisplayDecimalUUID  ROUTINE
	message('UUID.data1: ' & uuid.data1 & |
		'|UUID.data2: ' & uuid.data2 & |
		'|UUID.data3: ' & uuid.data3 & |
		'|UUID.data4: ' & uuid.data4[1] & '/' & uuid.data4[2] & '/' & uuid.data4[3] & '/' & uuid.data4[4] & '/' & |	
		uuid.data4[5] & '/' & uuid.data4[6] & '/' & uuid.data4[7] & '/' & uuid.data4[8] )		
		
GUIDToString        PROCEDURE  (*UUIDGT pUuid)
lResult                 LONG
lpszUuid                LONG
szUuid                  &CSTRING
sUuid                   STRING(36)
	CODE
		lResult = UuidToString(pUuid,lpszUuid) !address(lpszUuid))
		IF lResult = RPC_S_OK
			szUuid &= (lpszUuid)
			sUuid = szUuid
			lResult = RpcStringFree(lpszUuid)
		END
		RETURN upper(sUuid)

Graham


#3

Many thanks Graham - I should have picked that, a pointer not a CSTRING - doh!

I ended up doing this instead, a PEEK (and changing the UUIDCString to a LONG (probably a bad idea to name a LONG var with a CString name hey?) and the prototype etc):

    RPC_STATUS = UuidToString(UUID,UUIDCstring)

IF RPC_STATUS = RPC_S_OK
    PEEK(UUIDCString,UUIDString)
    RPC_STATUS = RpcStringFree(UUIDCstring)
    RETURN UPPER(CLIP(LEFT(UUIDString)))
ELSE
    RETURN ''
END

I think this gets the same result, including freeing the RPCString.

Still getting my head around references etc.

Again, thanks for your help and example.


#4

Graham, I’m still a little confused about the UuidToString prototype. As per MSDN:

RPC_STATUS RPC_ENTRY UuidToString(
  _In_  const UUID __RPC_FAR      *Uuid ,
  _Out_       RPC_CSTR  __RPC_FAR *StringUuid
);

Uuid [in]
Pointer to a binary UUID.

StringUuid [out]
Pointer to the null-terminated string into which the UUID specified in the *Uuid* parameter will be placed.

In the original map for the prototype I had:

UuidToString(*UUIDGT,*CSTRING),ULONG,PASCAL,RAW,NAME('UuidToStringA')

the first parameter is not a long, it is a pointer to a GROUP, yet the second as you suggest is a pointer to a LONG not a CSTRING as the MSDN suggests.

Just confused as to why one is a pointer to a type, the other is a pointer to a long (address?). Is it because one is a read, the other a write?

It’s woking with a long, I just can’t join the dots…


#5

Hi Andrew,

Well the key is in the RPC_CSTR bit of the prototype - I think that means it’s returning a pointer to a pointer (only a C++ programmer would know for sure :grinning: )

The prototype used to be this…

RPC_STATUS RPC_ENTRY UuidToString( > UUID *Uuid, > unsigned char **StringUuid)

which explicitly shows the pointer to a pointer bit - see this from Jim Kane on Icetips…

https://www.icetips.com/printarticle.php?articleid=22&productID=0

If you use the Clarion debugger and prototype the parameter as a LONG you can use ‘Examine Memory’ and then ‘Locate Offset’ and you’ll be able to see the returned memory address and follow it to the allocated memory string.

Graham


#6

Yep, the header files have this…

typedef Null_terminated unsigned char __RPC_FAR * RPC_CSTR;

Install one of the free Express versions of Visual Studio - that will get you all the C++ headers files etc in the Windows SDK.

For completeness these are the missing equates…

RPC_S_OK EQUATE(0)
RPC_S_OUT_OF_MEMORY EQUATE(14)
RPC_S_UUID_LOCAL_ONLY EQUATE(1824)
RPC_S_UUID_NO_ADDRESS EQUATE(1739)

Graham


#7

Thanks again Graham. I have been caught out before with prototypes, but didn’t pick this one.

I found two of the return values reviewing winerror.h, but not the out_of_memory one, I ended up with the following

    ! https://msdn.microsoft.com/en-us/library/windows/desktop/ms681386(v=vs.85).aspx
    RPC_S_OK                EQUATE(0)       !pretty sure it's 0 but can't confirm
    !RPC_S_OUT_OF_MEMORY     EQUATE(1721)    ! aka RPC_S_OUT_OF_RESOURCES?
    RPC_S_UUID_LOCAL_ONLY   EQUATE(1824)
    RPC_S_UUID_NO_ADDRESS   EQUATE(1739)
    ! https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
    RPC_S_OUT_OF_MEMORY     EQUATE(14)       ! ERROR_OUTOFMEMORY

Again, thanks for the reply and solution.