I tried to write the advapi32.hhf, here is procedure GetUserNameA I transfered:
------------------------------------------
GetUserNameA: procedure
(
var lpBuffer: var;
nSize: dword
);
@stdcall;
@returns("eax");
@external("__imp__GetUserNameA@8");
--------------------------------------------
Is it correct to write it like that?

then I like to call it in my program:
-----------------------------------
static
dwSize: dword := w.MAX_PATH;
aUserName: Char;
// aUserName: String; //can I use string here?
//How to convert Char to string?

begin UserName;
GetUserNameA( aUserName, dwSize);
-----------------------------------

Thank you for your help.
Posted on 2003-05-17 02:48:32 by henryw

I tried to write the advapi32.hhf, here is procedure GetUserNameA I transfered:
------------------------------------------
GetUserNameA: procedure
(
var lpBuffer: var;
nSize: dword
);
@stdcall;
@returns("eax");
@external("__imp__GetUserNameA@8");
--------------------------------------------
Is it correct to write it like that?

then I like to call it in my program:
-----------------------------------
static
dwSize: dword := w.MAX_PATH;
aUserName: Char;
// aUserName: String; //can I use string here?
//How to convert Char to string?

begin UserName;
GetUserNameA( aUserName, dwSize);
-----------------------------------

Thank you for your help.


The easy way to convert zero-terminated strings to HLA is to use
str.cpyz or str.a_cpyz
(see http://webster.cs.ucr.edu/Page_hla/HLADoc/HTMLDoc/HLAStdlib.html#pgfId-1072115).

However, Win32 API functions that return string data typically return the string
length in EAX, knowing a little bit about the HLA string format can help you manipulate the
strings more efficiently. E.g.,

static
s:str.strvar(256);
.
.
.
mov( s, edi );
w.GetUserName( , (type str.strRec ).MaxStrLen) );
mov( eax, (type str.strRec ).length );

How this works:

HLA strings are a pointer to an array of bytes. At offset -4 from this pointer
HLA stores the current length of the string, at offset -8 from this pointer HLA
stores the maximum length of the string. The str.strvar macro statically allocates
storage for a string and initializes its maximum length field (it also initializes
it to the empty string by setting the current length to zero and zero-terminating it).

The code example above loads "s" into edi (remember, strings in HLA are pointers,
so s is a pointer to the data structure just described above). This code passes
the address of the character buffer (which s points at) and the maximum string
length to GetUserName. On return, EAX contains the current length, which this code
stores into the current length field of the HLA string variable.
Note that Win32 API functions preserve EDI, so there is no need to reload it across this call.

Another option, of course, is to use zero terminated strings in HLA, though they are
quite a bit less efficient than HLA strings.
Cheers,
Randy Hyde
Posted on 2003-05-20 12:31:53 by rhyde
Thank you for your reply, I'll try to read more documents with HLA.
Posted on 2003-05-20 19:07:46 by henryw
Just swich the key procedure & the procedure name like this:

procedure GetUserNameA
(
var lpBuffer: var;
nSize: dword
);
@stdcall;
@returns("eax");
@external("__imp__GetUserNameA@8");

this worked for me...
Posted on 2003-05-21 04:35:38 by mistronr1
Another way to do things is: "Everything is DWORD".
This is approach MASM adopted. As put Iczelion in one of his tutorials:

The size of each parameter is NOT essential. For your information, currently MASM always regards each parameter as a DWORD no matter which size specifier you use.


You can look at inc files in Hutch's MASM32 package and find out that all function prototypes look like:



GetTokenInformation PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
GetTrusteeTypeW PROTO :DWORD
GetUserNameA PROTO :DWORD,:DWORD


Not too much descriptive of course, but actually inc files are not help files and anyway you should consult
your Win32 Reference. I don't know all pros and cons of these two different approaches.
It's a good topic for discussion. Personally I prefer everything-is-dword. It looks more straightforward and clear
especially for beginners like myself.

To put long story a little bit shorter prototype:



static
GetUserName: procedure( p0: dword; p1: dword);
@stdcall;
@returns("eax");
@external("__imp__GetUserNameA@8");



also works.

Regards, GJ
Posted on 2003-05-21 13:03:04 by Green Joe

Another way to do things is: "Everything is DWORD".
This is approach MASM adopted. As put Iczelion in one of his tutorials:



Not too much descriptive of course, but actually inc files are not help files and anyway you should consult
your Win32 Reference. I don't know all pros and cons of these two different approaches.
It's a good topic for discussion. Personally I prefer everything-is-dword. It looks more straightforward and clear
especially for beginners like myself.


Regards, GJ


The HLA Standard Library header files for windows takes the "structured" approach, of course
(that's more "high level" after all). The cool thing about HLA is that it will support the "everything-is-
dword" approach as well as the structured approach. Most of the time I find the structured approach to be better, but sometimes the "everything-is-a-dword" approach is more convenient.

One interesting aspect of the difference between these two approaches is the fact that MASM requires that you specify the "addr" operator when you want to pass the address of an object as a reference parameter, e.g.,

invoke HasRefParm, addr parameter

In HLA, with the appropriate declaration, all you've got to do is this:

HasRefParm( parameter );

OTOH, if you want to pass the value of a dword variable as the parameter, rather than the address of that dword variable, you do the following in MASM:

invoke HasRefParm, dwordValue

In HLA, you have to do the following:

HasRefParm( val dwordValue ); //Tells HLA to pass the value rather than the address of dwordValue

Fortunately, this latter case isn't all that frequent, but it is one more thing you've got to be aware of.

HLA's parameter passing mechanisms are quite sophisticated; this means that they can be quite overwhelming for beginners just approaching this stuff. I can easily see why someone would prefer the "everything-is-a-dword" approach. OTOH, once you figure all this stuff out, it begins to make a lot of sense to use typed parameters.

One other nice thing about the HLA header files for Windows is that they generally specify HLA string types whenever an API function expects a zero-terminated string address. This is convenient because you can use HLA's string functions to do a lot of manipulation on those strings before passing them on to Windows. This trick is a bit less convenient when using the "everything-is-a-dword" approach.

In any case, the whole issue of passing parameters by value and reference to Windows functions is complex. So much so, that I'm probably going to devote an early chapter to this one subject in my "Windows Programming in Assembly" book.

Cheers,
Randy Hyde
Posted on 2003-05-21 15:35:42 by rhyde
Thank you for all your help.

I encountered a new problem, how to write a procedure like UuidToStringA in .hhf?

The second parameter is char**, so I do not know how to translate it.
Posted on 2003-05-22 20:03:53 by henryw

Thank you for all your help.

I encountered a new problem, how to write a procedure like UuidToStringA in .hhf?

The second parameter is char**, so I do not know how to translate it.


char** objects are simply pointers to pointers.
So you just pass a dword value that is a pointer (probably to an array of strings).

e.g.,

type
strarray_t:string[256];
static
strarray:strarray_t;

procedure procWithCharStarStar( s:strarray_t );
.
.
.

procWithCharStarStar( &strarray);


The "pure" way to do it is like this:

type
pchar: pointer to char;
ppchar : pointer to pchar;

procedure hasPPchar( p:ppchar );
.
.
.

The real trick is how to dereferece the pointer inside the procedure.
Usually, char** objects are really just pointers to string arrays.
This means you use the pointer passed to the procedure as a pointer
to an array of four-byte string pointers (easy enough to deal with).
If it's really a pointer to a pointer to a character and you want to access
the individual character, you use double indirection, e.g.,

mov( p, ebx ); // get pointer to pointer
mov( , ebx ); // get pointer to char
mov( , ch ); // Get the character

Cheers,
Randy Hyde
Posted on 2003-05-23 10:54:39 by rhyde
Thank you Randy.

But does it mean a HLA style string can not work as a receiver to the result of the

procedure with pointer to pointers object? The function like UuidToString always return a

null terminated string.

The way following can work, but I do not know if it is correct enough:
--------------------------------------------
UuidToStringA: procedure
(
UUID: dword;
GUIDStr: dword
);
@stdcall;
@returns("eax");
@external("__imp__UuidToStringA@8");
--------------------------------------------
static
MyGuid:w.GUID;
aMacAddr: str.strvar(256);
aGUIDStr: str.strvar(256);
pGUID: pointer to char; //pointer to whatever, char, byte...

begin MacAdd;
UuidCreateSequential(&MyGuid);

UuidToStringA(&MyGuid, &pGUID);

mov(pGUID, eax);
str.cpyz(, aGUIDStr);
str.upper(aGUIDStr);
str.substr(aGUIDStr, aMacAddr, 24, 12);

w.MessageBox
(
0,
aMacAddr,
"UserName",
w.MB_OK
);
Posted on 2003-05-23 19:06:44 by henryw

Thank you Randy.

But does it mean a HLA style string can not work as a receiver to the result of the

procedure with pointer to pointers object? The function like UuidToString always return a

null terminated string.

The way following can work, but I do not know if it is correct enough:
--------------------------------------------
UuidToStringA: procedure
(
UUID: dword;
GUIDStr: dword
);
@stdcall;
@returns("eax");
@external("__imp__UuidToStringA@8");
--------------------------------------------
static
MyGuid:w.GUID;
aMacAddr: str.strvar(256);
aGUIDStr: str.strvar(256);
pGUID: pointer to char; //pointer to whatever, char, byte...

begin MacAdd;
UuidCreateSequential(&MyGuid);

UuidToStringA(&MyGuid, &pGUID);

mov(pGUID, eax);
str.cpyz(, aGUIDStr);
str.upper(aGUIDStr);
str.substr(aGUIDStr, aMacAddr, 24, 12);

w.MessageBox
(
0,
aMacAddr,
"UserName",
w.MB_OK
);



Okay, what UuidToStringA is really asking for is the address of a zero-terminated
string pointer. UuidToString will allocate sufficient storage for the zero terminated
string and store the address of that string in the pointer who address you pass
to UuidToStr. Here's what the calling sequence should look like:

static
pGUID: pointer to char; // Note that you do not initialize this pointer...
.
.
.
UuidToStringA(&MyGuid, &pGUID);

// Now pGUID points at a zero terminated array of characters somewhere.
// You can pass pGUID to a string conversion function like str.cpyz if you
// want to convert it to an HLA string, or you can operate on it directly using
// HLA's zero-terminated string functions.

According to the Win32 documentation, it is your responsibility to call
rpcStringFree to free the storage associated with pGUID when you are
done using it (in particular, do *not* call the HLA Stdlib's free function to
do this).
Cheers,
Randy Hyde
Posted on 2003-05-25 13:43:16 by rhyde