UUID/GUID generation

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.

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

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.

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…

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

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

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.