Why does an ANY data type in API call StringFromGuid2() not give me a fixed GUID?

In the clw below, when the parameter IS_StringFromGuid2(ANY,long,long),long,Pascal,Name(‘StringFromGuid2’)
is an ANY, the GUID generated when clicking the template button stays the same on my machine. If I come out of the template back to the appgen tree sometimes it might generate a new guid, but repeated clicks of the template button doesnt change the guid like it should.

If I change the ANY datatype back to a Long datatype, the template button generates guid’s as expected everytime its clicked.
!IS_StringFromGuid2(long,long,long),long,Pascal,Name(‘StringFromGuid2’) !<— This as a long works fine.

The ANY works fine in an exe, but not in a dll used in a template.

Any ideas why?

TIA

1 Global extension template

#TEMPLATE(AAAAIntelligentSiliconLtd,'Template Example Get Guid'),FAMILY('ABC')
#EXTENSION(IS_GetGuid,'IS_GetGuid Global Extension'),Application
#Sheet,HScroll
#Tab('&01 Settings')
    #Display('')
    #Prompt('',@s40),%LIProcpGUID,Req,At(,,175,10)
    #Button('Generate GUID'),At(,,175,12),WhenAccepted(%grpLIGloExtGetGUIDstring(%LIProcpGUID))
    #EndButton
#EndTab
#EndSheet

#Group(%grpLIGloExtGetGUIDstring,*%pIO1)
#RunDll('C6GetGuidString.dll','C6GETGUIDSTRING@FRsc',%pIO1),Release,Win32

2 C6GetGuidString.prj file

-- C6GetGuidString
#noedit
#system win32
#model clarion dll
#pragma define(maincode=>off)
#pragma debug(vid=>full)
#compile "C6GetGuidString.clw"
#link "C6GetGuidString.dll"

3 C6GetGuidString.exp file

Library 'C6GetGuidString' GUI
Exports
    C6GETGUIDSTRING@FRsc  @1

4 C6GetGuidString.clw file

Program

ST:guid                         Group,Type
Data1                               ulong       !4
Data2                               ushort      !2
Data3                               ushort      !2
Data4                               string(8)   !8=
                            End

Map
    Module('WinAPI')
    IS_CoCreateGuid(long),long,Pascal,Name('CoCreateGuid')
    IS_StringFromGuid2(ANY,long,long),long,Pascal,Name('StringFromGuid2') !<--- This as an ANY fixes the GUID when used as a dll. Why?
    !IS_StringFromGuid2(long,long,long),long,Pascal,Name('StringFromGuid2')  !<--- This as a long works fine.
    IS_GetACP(),ulong,Pascal,Name('GetACP')
    IS_GetOEMCP(),ulong,Pascal,Name('GetOEMCP')
    IS_GetSystemDefaultLCID(),Long,Pascal,Raw,Name('GetSystemDefaultLCID')
    IS_GetSystemDefaultLangID(),short,Pascal,Raw,Name('GetSystemDefaultLangID')
    IS_WideCharToMultiByte(ulong,ulong,long,long,long,long,long,long),long,Pascal,Raw,Name('WideCharToMultiByte')
    IS_lstrlenA(long),long,Pascal,Raw,Name('lstrlenA')
    IS_lstrlenW(long),long,Pascal,Raw,Name('lstrlenW')
    IS_lstrcpyA(long,long),long,Pascal,Raw,Name('lstrcpyA')
End
C6GetGuidString (*Cstring pGetGuidString)
End

Code


C6GetGuidString     Procedure (*Cstring pGetGuidString)
Loc:Guid                Like(ST:GUID)
Loc:GuidAddress         long
Loc:RPCresult           long
Loc:RVlong              long

Loc:GuidStringW         cstring(90)
Loc:GuidStringWAddress  long
Loc:GuidStringWSize     long

Loc:GuidStringA         cstring(90)
Loc:GuidStringAAddress  long
Loc:GuidStringASize     long

Loc:CodePage            ulong
Loc:dwFlags             ulong
Loc:lpWideCharStr       long
Loc:cchWideChar         long
Loc:lpMultiByteStr      long
Loc:cbMultiByte         long
Loc:UsedDefaultChar     long
Loc:lpUsedDefaultChar   long
Loc:ResultLength        long
Loc:lpDefaultChar       long


IS_WC_COMPOSITECHECK           Equate(200h)    !// WC_COMPOSITECHECK, WC_DISCARDNS and WC_SEPCHARS are deprecated, not recommended,
                                        !// and provide out-of-date behavior.
                                        !// Windows typically uses Unicode Normalization Form C type sequences,
                                        !// If explicit normalization forms are required, please use NormalizeString.
IS_WC_DEFAULTCHAR              Equate(40h)
IS_WC_DISCARDNS                Equate(10h)
IS_WC_SEPCHARS                 Equate(20h)
IS_WC_ERR_INVALID_CHARS        Equate(80h)
IS_WC_NO_BEST_FIT_CHARS        Equate(400h)

Code
Loc:GuidAddress = Address(Loc:Guid)
Loc:RPCresult = IS_CoCreateGuid(Loc:GuidAddress)
IF Loc:RPCresult = 0 !S_OK !0
    Loc:GuidStringWSize      = 90
    Loc:GuidStringWAddress   = Address(Loc:GuidStringW)
    Loc:RVlong = IS_StringFromGuid2(Loc:GuidAddress,Loc:GuidStringWAddress,Loc:GuidStringWSize)
    IF Loc:RVlong
        Loc:CodePage            = IS_GetACP()
        Loc:dwFlags             = 0
        !Loc:dwFlags            += IS_WC_COMPOSITECHECK !Convert composite checks
        !Loc:dwFlags            += IS_WC_DEFAULTCHAR    !Replace exceptions with the default character during conversion.
        !Loc:dwFlags            += IS_WC_DISCARDNS      !Discard nonspacing characters
        !Loc:dwFlags            += IS_WC_SEPCHARS       !Seperate characters during conversion
        !Loc:dwFlags            += IS_WC_ERR_INVALID_CHARS      !Vista or later. Fail if an invalid input character
        !Loc:dwFlags            += IS_WC_NO_BEST_FIT_CHARS      !Translate unicode characters that do not translate to multibyte equivalents

        
        Loc:lpWideCharStr       = Loc:GuidStringWAddress
        Loc:cchWideChar         = IS_lstrlenW(Loc:GuidStringWAddress)  !Returns length of the W (unicode) string. Its a clarion cstring
        Loc:GuidStringAAddress  = Address(Loc:GuidStringA)
        Loc:lpMultiByteStr      = Loc:GuidStringAAddress
        Loc:cbMultiByte         = 90 !IS_lstrlenA(Loc:GuidStringAAddress) !Returns length of the A (ansi) string. Its a clarion cstring.

        Loc:lpDefaultChar       = 0 !Set to NULL to use system defaults
        Loc:UsedDefaultChar     = 0
        Loc:lpUsedDefaultChar   = Address(Loc:UsedDefaultChar)
        Loc:ResultLength        = IS_WideCharToMultiByte(Loc:CodePage,Loc:dwFlags,Loc:lpWideCharStr,Loc:cchWideChar,Loc:lpMultiByteStr,Loc:cbMultiByte,Loc:lpDefaultChar,Loc:lpUsedDefaultChar)
        IF Loc:ResultLength = 0
            pGetGuidString = 'Error: Unicode Guid String to Ansi string conversion failed.'
        Else
            pGetGuidString  = Sub(Loc:GuidStringA,1,Loc:ResultLength)
        End
    Else
        pGetGuidString = 'Error: Guid output to string failed'
    End
Else
    pGetGuidString = 'Error: Guid generation failed'
End

The RAW attribute is missed in the prototype of IS_StringFromGuid2.

test.clw (2.8 KB)

Of course, the guid is a clarion group which is treated as a string in Clarion, and I see in the docs, the RAW attribute comes into play. I was thinking it might have been the Real not being aligned.

Now I know. Thanks! :grinning:

Edit.

But if the Raw attribute was coming into play, then shouldnt the compiler have complained about the use of ANY instead of ? as the datatype?

They are synonyms in functions prototypes, though syntax *ANY as a replacement of *? is slightly confusing and it requires special processing in the compiler - inside the function body formal parameters declared as *T are treating just like locals having type &T but declarations with type &ANY are not allowed.

1 Like

Who IS that masked man??? :clap:

1 Like

US Timezone I would suggest, but not a native US English speaker. Not trying to be disparaging or rude or anything.

To code an Windows API call you add PASCAL. It’s safe to always add RAW, with PASCAL even if its not required. It will save problems if LONG lpCString changes to *CSTRING and you forget to add RAW.

You should always add DLL(1) since Windows API will always be External. If its an RTL DLL call, e.g. MkDir(), then DLL(Dll_Mode) is correct not DLL(1).

This is not so, The compiler and the linker can produce the correct thunking code to call functions from external DLLs regardless presence of the DLL attribute in the prototype. The difference between presence of the DLL(1) and the absence of the DLL attribute is how corresponding thunking code is organized.

If the function has the DLL attribute in the prototype, incorrect parameter of DLL (0 if function is in some DLL and 1 if library with function is to be linked locally) can cause GPF on attempt to call this function at run time.

You would certain know more than I would on what happens in the compiled code. I agree with what you said. An External Function works with or without the DLL(1). I’m asking is with DLL(1) better?

My understanding is DLL(1) is like Microsoft __declspec(dllimport). Is Clarion DLL(1) different?

A number of web sources say it is best to specify dllimport / DLL(1) on External Functions then the Compiler can write the best code. Without DLL(1) the Linker has to fix it by adding a thunk stub which means extra code each call. And the little extra thunk code in the DLL.


Import into an application using __declspec(dllimport)

Using __declspec(dllimport) is optional on function declarations, but the compiler produces more efficient code if you use this keyword.


Index to the series on DLL imports and exports

Calling an imported function, the naive way … without __declspec(dllimport) … The compiler would generate a normal call instruction, leaving the linker to resolve the external. The linker then sees that the external is really an imported function, and, uh-oh, the direct call needs to be converted to an indirect call. But the linker can’t rewrite the code generated by the compiler. What’s a linker to do? The solution is to insert another level of indirection.

How a less naive compiler calls an imported function If a function is declared with the dllimport declaration specifier (Clarion DLL(1)), this instructs the C/C++ compiler that the function in question is an imported function rather than a normal function with external linkage. With this additional information, the compiler generates slightly different code when it needs to reference an imported function, since the compiler is aware of the special way imported functions are implemented. First, there is no need for the stub function any more, because the compiler can generate the special call instruction inline.


Its good to know the Clarion Compiler will not fix a wrong DLL(1). I don’t think I’ve ever had the problem. Ray Chen in What happens when you get dllimport wrong? … I think … says the VS C/C++ Compiler will fix that problem by creating a Fake Import but it sounds like a big mess.


Note that DLL(1) is mandatory on Data declared that is in an External DLL. The same for Microsoft:

However, you must use __declspec(dllimport) for the importing executable to access the DLL’s public data symbols and objects.

I don’t know what the word “better” means here. The address of imported function is stored in the import table (one of structures of files having PE format) and it is resolving by the system loader. If prototype has the DLL(1) attribute, calls
to this function are translating to the far indirect call instruction to address from the import table. If prototype has no the DLL attribute, calls to this function are translating to near call instruction to the address of the generated thunk containing far indirect jmp instruction to address from the import table, The processor pipelining is breaking in both variants, The variant without DLL attribute takes several more cycles of the processor for execution. On the other hand, it allows, for example, hooking of the function for purpose of profiling, If the program assigns the imported function prototyped with the DLL(1) attribute to a reference variable of matching procedural type, the thunk code for this function is generating in any case,

Both are hints for the compiler that this function is in external DLL. Under carpet everything can be different, it depends from how the compiler processes such functions, how this attribute interferes with others, etc.

1 Like