User Management using API calls: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
No edit summary
No edit summary
Line 625: Line 625:




[[User:CancerFace|CancerFace]] 04:18, 10 June 2006 (PDT)
 
 
 
[[User:CancerFace|CancerFace]] 12:08, 7 October 2006 (PDT)

Revision as of 19:08, 7 October 2006

Author: CancerFace (talk, contrib)


NSIS forum thread


Description

Here is a list of macros that can be used to manipulate user and group accounts on a windows machine. All macros use API calls to netapi32.dll and/or advapi32.dll. The code is provided without any error checking so you may want to test it in your installer scenario carefully. It is assumed that the installer will run using admin privileges in order to create/modify user/group accounts. You should add some code to check that these privileges are active in order to avoid problems.

Macros for user/group manipulation

Get Server Name

This macro will obtain the name of the computer that the installer is running. An attempt to determine the NetBios name will be carried out first using the GetComputerNameEx API call and if it is not successful then the GetComputerName API call will be established. If both fail then the computer name will be set to null. Note that this is not a problem since for all NetApi32 calls a null computer name implies the local computer.

Usage

Use the following code in your NSIS installer to get the computer name to some variable ${VAR}:

!include "LogicLib.nsh"
!insertmacro GetServerName $VAR
Macro
!macro GetServerName SERVER_NAME_OUT
  System::Call 'kernel32.dll::GetComputerNameExW(i 4,w .r0,*i ${NSIS_MAX_STRLEN} r1)i.r2'
  ${If} $2 = 1
   StrCpy ${SERVER_NAME_OUT} "\\$0"
  ${Else}
   System::Call "kernel32.dll::GetComputerNameW(t .r0,*i ${NSIS_MAX_STRLEN} r1)i.r2"
   ${If} $2 = 1
    StrCpy ${SERVER_NAME_OUT} "\\$0"
   ${Else}
    StrCpy ${SERVER_NAME_OUT} ""
   ${EndIf}
  ${EndIf}
  System::Free $0
!macroend

Create User

User creation can be achieved using the NetUserAdd API call

Usage

From within your NSIS code call the macro providing the appropriate parameters. For the GROUP_ID parameter use one of the following values (needed to construct the equivalent well-known group SID):

Administrators S-1-5-32-544 (use 544)
Users S-1-5-32-545 (use 545)
Guests S-1-5-32-546 (use 546)
Power Users S-1-5-32-547 (use 547)
!insertmacro CreateUser "\\ComputerName" "jdoe" "password" "Some User" "John" "Doe" 545
Macro
!macro CreateUser SERVER_NAME USERNAME PASSWORD DESCRIPTION NAME SURNAME GROUP_ID
  # Create the user
  System::Call '*(w "${USERNAME}",w "${PASSWORD}",i n,i 1,w n,w "${DESCRIPTION}",i 513,w n)i.R0'
  System::Call 'netapi32::NetUserAdd(w "${SERVER_NAME}",i 1,i R0,*i.r0) i.r1'
  System::Free $R0
  # Set the user's name and surname
  System::Call '*(w "${NAME} ${SURNAME}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1011, \
i R0,*i.r0)i.r1'
  System::Free $R0
  # Get the user's SID in $R8
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R8'
  System::Call 'advapi32::LookupAccountNameW(w "${SERVER_NAME}",w "${USERNAME}",i R8, \
*i ${NSIS_MAX_STRLEN}, w .R1, *i ${NSIS_MAX_STRLEN}, *i .r0)i .r1'
  System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0'
  # Get the group name in $R9 using its SID
  System::Call '*(&i1 0,&i4 0,&i1 5)i.R0'
  System::Call 'advapi32::AllocateAndInitializeSid(i R0,i 2,i 32,i ${GROUP_ID},i 0, \
i 0,i 0,i 0,i 0,i 0,*i .r2)'
  System::Free $R0
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R9'
  System::Call 'advapi32::LookupAccountSidW(i 0,i r2,i R9,*i ${NSIS_MAX_STRLEN},t .r3, \
*i ${NSIS_MAX_STRLEN},*i .r4)'
  System::Call 'advapi32::FreeSid(i r2)'
  # Add the user to the user's group
  System::Call 'netapi32::NetLocalGroupAddMembers(w "${SERVER_NAME}",i R9,i 0,*i R8,i 1)i .r0'
  System::Call 'advapi32::FreeSid(i R8)i .r0'
  System::Free $R9
!macroend

Delete User

This macro uses the NetUserDel API call to delete a user from the local system

Usage

Call the macro from your NSIS script and pass the name of the user to be deleted:

!insertmacro DeleteUser "\\ComputerName" "jdoe"
Macro
!macro DeleteUser SERVER_NAME USERNAME
  # Delete a user
  System::Call 'netapi32::NetUserDel(w "${SERVER_NAME}",w "${USERNAME}")i.r0'
!macroend

Create Group

This macro uses the NetLocalGroupAdd API call to create a new group

Usage

From within your NSIS code call the macro providing the appropriate parameters:

!insertmacro CreateGroup "\\ComputerName" "NewGroup" "Some Description for the group"
Macro
!macro CreateGroup SERVER_NAME GROUP_NAME GROUP_DESCRIPTION
  # Create a local group
  System::Call '*(w "${GROUP_NAME}",w "${GROUP_DESCRIPTION}")i.R0'
  System::Call 'netapi32::NetLocalGroupAdd(w "${SERVER_NAME}",i 1,i R0,*i r0)i.r1'
  System::Free $R0
!macroend

Delete Group

This macro usee the NetLocalGroupDel API call to delete a group from the local machine

Usage

Call the macro from your NSIS installer:

!insertmacro "\\ComputerName" "SomeGroupName"
Macro
!macro DeleteGroup SERVER_NAME GROUP_NAME
  # Delete a local group
  System::Call 'netapi32::NetLocalGroupDel(w "${SERVER_NAME}",w "${GROUP_NAME}")i.r3'
!macroend

Add User to a group

Here are two macros to add a user to a group using the NetLocalGroupAddMembers API call. The first macro uses the SID of a group (constructed from the well-known SID) and the second macro uses the group name. The advantage of using a well-known SID is that the macro will work on any system regardless of the system's language. For example the english version of windows has a group called 'Users' while in the french version this group is called 'Utilisateurs'. In both versions the SID of this particular group is S-1-5-32-545.

Usage

Call the macros from your NSIS script providing sufficient information. If you use the first macro then use one of the following values for the group SID:

Administrators 544
Users 545
Guests 546
Power Users 547
!insertmacro AddUserToGroup1 "\\ComputerName" "jdoe" "547"

Call the second macro providing the name of the group:

!insertmacro AddUserToGroup2 "\\ComputerName" "jdoe" "Power Users"
Macros
!macro AddUserToGroup1 SERVER_NAME USERNAME GROUP_ID
  # Add a user to a group using the group's SID (from well-known SIDs)
  # Administrators S-1-5-32-544 (use 544)
  # Users S-1-5-32-545 (use 545)
  # Guests S-1-5-32-546 (use 546)
  # Power Users S-1-5-32-547 (use 547)
  # Get the user's SID
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R8'
  System::Call 'advapi32::LookupAccountNameW(w "${SERVER_NAME}",w "${USERNAME}",i R8, \
*i ${NSIS_MAX_STRLEN}, w .R1, *i ${NSIS_MAX_STRLEN}, *i .r0)i .r1'
  System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0'
  # Get the group's SID
  System::Call '*(&i1 0,&i4 0,&i1 5)i.R0'
  System::Call 'advapi32::AllocateAndInitializeSid(i R0,i 2,i 32,i ${GROUP_ID},i 0, \
i 0,i 0,i 0,i 0,i 0,*i .r2)'
  System::Free $R0
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R9'
  System::Call 'advapi32::LookupAccountSidW(i 0,i r2,i R9,*i ${NSIS_MAX_STRLEN},t .r3, \
*i ${NSIS_MAX_STRLEN},*i .r4)'
  System::Call 'advapi32::FreeSid(i r2)'
  # Add the user to the group
  System::Call 'netapi32::NetLocalGroupAddMembers(w "${SERVER_NAME}",i R9,i 0,*i R8,i 1)i .r0'
  System::Call 'advapi32::FreeSid(i R8)i .r0'
  System::Free $R9
!macroend
!macro AddUserToGroup2 SERVER_NAME USERNAME GROUP_NAME
  # Add a user to a group using the group's name
  # Get the user's SID
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R8'
  System::Call 'advapi32::LookupAccountNameW(w "${SERVER_NAME}",w "${USERNAME}",i R8, \
*i ${NSIS_MAX_STRLEN}, w .R1, *i ${NSIS_MAX_STRLEN}, *i .r0)i .r1'
  System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0'
  # Add the user to the group
  System::Call 'netapi32::NetLocalGroupAddMembers(w "${SERVER_NAME}",w "${GROUP_NAME}", \
i 0,*i R8,i 1)i .r0'
  System::Free $R8
!macroend

Delete User from a group

Deleting a user from a group is achieved by using the NetLocalGroupDelMembers API call. The first macro uses the SID of a group (constructed from the well-known SID) and the second macro uses the group name.

Usage

Call the macros from your NSIS script providing sufficient information. If you use the first macro then use one of the following values for the group SID:

Administrators 544
Users 545
Guests 546
Power Users 547
!insertmacro DeleteUserFromGroup1 "\\ComputerName" "jdoe" "547"

Call the second macro providing the name of the group:

!insertmacro DeleteUserFromGroup2 "\\ComputerName" "jdoe" "Power Users"
Macros
!macro DeleteUserFromGroup1 SERVER_NAME USERNAME GROUP_ID
  # Delete a user from a group using the group's SID
  # Administrators S-1-5-32-544 (use 544)
  # Users S-1-5-32-545 (use 545)
  # Guests S-1-5-32-546 (use 546)
  # Power Users S-1-5-32-547 (use 547)
  # Get the user's SID
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R8'
  System::Call 'advapi32::LookupAccountNameW(w "${SERVER_NAME}",w "${USERNAME}",i R8, \
*i ${NSIS_MAX_STRLEN}, w .R1, *i ${NSIS_MAX_STRLEN}, *i .r0)i .r1'
  System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0'
  # Get the group's SID
  System::Call '*(&i1 0,&i4 0,&i1 5)i.R0'
  System::Call 'advapi32::AllocateAndInitializeSid(i R0,i 2,i 32,i ${GROUP_ID},i 0,i 0, \
i 0,i 0,i 0,i 0,*i .r2)'
  System::Free $R0
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R9'
  System::Call 'advapi32::LookupAccountSidW(i 0,i r2,i R9,*i ${NSIS_MAX_STRLEN},t .r3, \
*i ${NSIS_MAX_STRLEN},*i .r4)'
  System::Call 'advapi32::FreeSid(i r2)'
  # Delete the user
  System::Call 'netapi32::NetLocalGroupDelMembers(w "${SERVER_NAME}", i R9, i 0, *i R8, \
i 1) i.r6'
  System::Free $R8
  System::Free $R9
!macroend
!macro DeleteUserFromGroup2 SERVER_NAME USERNAME GROUP_NAME
  # Delete a user from a group using the group's name
  # Get the user's SID
  System::Call '*(&w${NSIS_MAX_STRLEN})i.R8'
  System::Call 'advapi32::LookupAccountNameW(w "${SERVER_NAME}",w "${USERNAME}",i R8, \
*i ${NSIS_MAX_STRLEN},w .R1,*i ${NSIS_MAX_STRLEN},*i .r0)i .r1'
  System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0'
  # Delete the user
  System::Call 'netapi32::NetLocalGroupDelMembers(w "${SERVER_NAME}", w "${GROUP_NAME}", \
i 0,*i R8,i 1)i.r6'
  System::Free $R8
!macroend

Get information for a group

Obtaining information for a group, such as its name and comment, can be achieved by calling the NetLocalGroupGetInfo API call.

Usage

Call the macro from your NSIS installer passing two variables that will accept the name and comment respectively:

Var "Var1"
Var "Var2"
!insertmacro GetGroupInfo "\\ComputerName" "Power Users" $Var1 $Var2
Macro
!macro GetGroupInfo SERVER_NAME GROUP_NAME NAME_OUT COMMENT_OUT
  # Obtain the group name and comment
  System::Call 'netapi32::NetLocalGroupGetInfo(w "${SERVER_NAME}",w "${GROUP_NAME}", \
i 1,*i.R0)i.r1'
  System::Call '*$R0(w .R1,w .R2)'
  StrCpy ${NAME_OUT} '$R1'
  StrCpy ${COMMENT_OUT} '$R2'
  System::Free $R0
!macroend

Set group information

It is possible to change a group's name and description using the NetLocalGroupSetInfo API call

Usage

Call the macro from your NSIS script providing all the necessary information

!insertmacro SetGroupInfo "\\ComputerName" "MyGroup" "My Renamed Group" "New Group Description"
Macro
!macro SetGroupInfo SERVER_NAME GROUP_NAME NEW_GROUP_NAME NEW_GROUP_DESCRIPTION
  # Set a group's name
  System::Call '*(w "${NEW_GROUP_NAME}")i.R0'
  System::Call 'netapi32::NetLocalGroupSetInfo(w "${SERVER_NAME}",w "${GROUP_NAME}",i 0, \
i R0,*i r0)i.r1'
  System::Free $R0
  # Set a group's name and description
  System::Call '*(w "${NEW_GROUP_NAME}",w "${NEW_GROUP_DESCRIPTION}")i.R0'
  System::Call 'netapi32::NetLocalGroupSetInfo(w "${SERVER_NAME}",w "${NEW_GROUP_NAME}", \
i 1,i R0,*i r0)i.r1'
  System::Free $R0
!macroend

Set user information

Using the NetUserSetInfo API call it is possible to change a user's username, password, name and surname, privileges etc.

Change user's username

This is done using the User Info Structure 0:

Usage

Call the macro from your NSIS installer providing a new name for the user:

!insertmacro RenameUser "\\ComputerName" "jdoe" "jdoe1"
Macro
!macro RenameUser SERVER_NAME USERNAME NEW_USERNAME
  # Change the username of a user
  System::Call '*(w "${NEW_USERNAME}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 0,i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Change user's password

This is done using the User Info Structure 1003:

Usage

Call the macro from your NSIS installer providing a new password for the user:

!insertmacro SetUserPassword "\\ComputerName" "jdoe" "newpassword"
Macro
!macro SetUserPassword SERVER_NAME USERNAME PASSWORD
  # Change a user's password
  System::Call '*(w "${PASSWORD}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1003, \
i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Change user's home directory

This is done using the User Info Structure 1006:

Usage

Call the macro from your NSIS installer providing a new home directory for the user:

!insertmacro SetUserHomeDir "\\ComputerName" "jdoe" "F:\My Documents"
Macro
!macro SetUserHomeDir SERVER_NAME USERNAME HOME_PATH
  # Change a user's password
  System::Call '*(w "${HOME_PATH}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1006, \
i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Change user's comment

This is done using the User Info Structure 1007:

Usage

Call the macro from your NSIS installer providing a new home comment for the user:

!insertmacro SetUserComment "\\ComputerName" "jdoe" "jdoe's new comment"
Macro
!macro SetUserComment SERVER_NAME USERNAME COMMENT
  # Change a user's comment
  System::Call '*(w "${COMMENT}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1007, \
i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Change user's name and surname

This is done using the User Info Structure 1011:

Usage

Call the macro from your NSIS installer providing a new name and surname for the user:

!insertmacro SetUserNameAndSurname "\\ComputerName" "jdoe" "John" "Doe"
Macro
!macro SetUserNameAndSurname SERVER_NAME USERNAME NAME SURNAME
  # Change a user's Name and Surname
  System::Call '*(w "${NAME} ${SURNAME}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1011, \
i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Change user's attributes

This is done using the User Info Structure 1008:

Usage

Call the macro from your NSIS installer providing the new user attributes. You need to define the possible attributes and then add the ones that you want the user to have:

!define UF_SCRIPT                               0x000001
!define UF_ACCOUNTDISABLE                       0x000002
!define UF_HOMEDIR_REQUIRED                     0x000008
!define UF_LOCKOUT                              0x000010
!define UF_PASSWD_NOTREQD                       0x000020
!define UF_PASSWD_CANT_CHANGE                   0x000040
!define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED      0x000080
!define UF_TEMP_DUPLICATE_ACCOUNT               0x000100
!define UF_NORMAL_ACCOUNT                       0x000200
!define UF_INTERDOMAIN_TRUST_ACCOUNT            0x000800
!define UF_WORKSTATION_TRUST_ACCOUNT            0x001000
!define UF_SERVER_TRUST_ACCOUNT                 0x002000
!define UF_DONT_EXPIRE_PASSWD                   0x010000
!define UF_MNS_LOGON_ACCOUNT                    0x020000
!define UF_SMARTCARD_REQUIRED                   0x040000
!define UF_TRUSTED_FOR_DELEGATION               0x080000
!define UF_NOT_DELEGATED                        0x100000
!define UF_USE_DES_KEY_ONLY                     0x200000
!define UF_DONT_REQUIRE_PREAUTH                 0x400000
!define UF_PASSWORD_EXPIRED                     0x800000
 
# The first two should always be there
StrCpy $0 0
IntOp $0 $0 + ${UF_NORMAL_ACCOUNT}
IntOp $0 $0 + ${UF_SCRIPT}
# Use IntOp to add more from the above list of definitions
!insertmacro SetUserAttributes "\\ComputerName" "jdoe" "$0"
Macro
!macro SetUserAttributes SERVER_NAME USERNAME ATTRIBUTES
  # Change user account attributes
  System::Call '*(i "${ATTRIBUTES}")i.R0'
  System::Call 'netapi32::NetUserSetInfo(w "${SERVER_NAME}",w "${USERNAME}",i 1008, \
i R0,*i.r0)i.r1'
  System::Free $R0
!macroend

Enumerate all users

This is achieved by calling the NetUserEnum API function We can create an array with all the users using Afrow UK's NSISArray Plugin

Usage

Call the macro from your NSIS installer providing a name for the user array that will be generated by the macro

!insertmacro EnumerateUsers "\\ComputerName" "LocalUserArray"
Macro
!macro EnumerateUsers SERVER_NAME USER_ARRAY_NAME
  # Enumerate the local users
  !define Index "Line${__LINE__}"
  # $R1 holds the number of entries processed
  # $R2 holds the total number of entries
  System::Call 'netapi32::NetUserEnum(w "${SERVER_NAME}",i 0,i 2,*i .R0,i ${NSIS_MAX_STRLEN}, \
*i .R1,*i .R2,*i .r1)i .r2'
  StrCpy $R8 $R0
  # dump them into an array object
  StrCpy $9 0
  NSISArray::New /NOUNLOAD ${USER_ARRAY_NAME}
  ${Index}-loop:
    StrCmp $9 $R2 ${Index}-stop +1
    System::Call "*$R0(w.R9)"
    NSISArray::Write /NOUNLOAD ${USER_ARRAY_NAME} $9 "$R9"
    IntOp $R0 $R0 + 4
    IntOp $9 $9 + 1
    Goto ${Index}-loop
  ${Index}-stop:
  NSISArray::SizeOf /NOUNLOAD ${USER_ARRAY_NAME}
  Pop $0
  StrCmp $0 $R2 +2 +1
    MessageBox MB_OK|MB_ICONEXCLAMATION 'Could not place all the user accounts into an array!'
  System::Call 'netapi32.dll::NetApiBufferFree(i R8)i .R1'
  !undef Index
!macroend

Enumerate all groups

This is achieved by calling the NetLocalGroupEnum API function We can create an array with all the groups using Afrow UK's NSISArray Plugin

Usage

Call the macro from your NSIS installer providing a name for the group array that will be generated by the macro

!insertmacro EnumerateGroups "\\ComputerName" "LocalGroupArray"
Macro
!macro EnumerateGroups SERVER_NAME GROUP_ARRAY_NAME
  # Enumerate the loacl groups
  !define Index "Line${__LINE__}"
  # $R1 holds the number of entries processed
  # $R2 holds the total number of entries
  System::Call 'netapi32::NetLocalGroupEnum(w "${SERVER_NAME}",i 0,*i .R0,i ${NSIS_MAX_STRLEN}, \
*i .R1,*i .R2,*i .R3)i .R4'
  StrCpy $R8 $R0
  # dump them into an array object
  NSISArray::New /NOUNLOAD ${GROUP_ARRAY_NAME}
  ${Index}-loop:
    StrCmp $9 $R2 ${Index}-stop +1
    System::Call "*$R0(w.R9)"
    NSISArray::Write /NOUNLOAD ${GROUP_ARRAY_NAME} $9 "$R9"
    IntOp $R0 $R0 + 4
    IntOp $9 $9 + 1
    Goto ${Index}-loop
  ${Index}-stop:
    NSISArray::SizeOf /NOUNLOAD ${GROUP_ARRAY_NAME}
  Pop $0
  StrCmp $0 $R2 +2 +1
    MessageBox MB_OK|MB_ICONEXCLAMATION 'Could not place all the groups into an array!'
  System::Call 'netapi32::NetApiBufferFree(i R8)i.r2'
  !undef Index
!macroend

Get User’s Group(s)

This is achieved by calling the NetUserGetLocalGroups API function We can create an array with all the groups that a user belongs to using Afrow UK's NSISArray Plugin

Usage

Call the macro from your NSIS installer providing a name for the user array that will be generated by the macro

!insertmacro EnumerateUsers "\\ComputerName" "UserName" "LocalUserGroupArray"
Macro
!macro GetUserGroups SERVER_NAME USERNAME GROUP_ARRAY_NAME
  # Get user's group(s)
  !define Index "Line${__LINE__}"
  # $R0 buffer with an array of LOCALGROUP_USERS_INFO_0 structures
  # $R1 holds the number of entries processed
  # $R2 holds the total number of entries
  System::Call 'netapi32::NetUserGetLocalGroups(w "${SERVER_NAME}",w "${USERNAME}",i 0, \
i 0,*i.R0,i ${NSIS_MAX_STRLEN},*i.R1,*i.R2)i.r2'
  StrCpy $R8 $R0
  StrCpy $9 0
  NSISArray::New /NOUNLOAD ${GROUP_ARRAY_NAME}
  ${Index}-loop:
    StrCmp $9 $R2 ${Index}-stop +1
    System::Call "*$R0(w.R9)"
    NSISArray::Write /NOUNLOAD ${GROUP_ARRAY_NAME} $9 "$R9"
    IntOp $R0 $R0 + 4
    IntOp $9 $9 + 1
    Goto ${Index}-loop
  ${Index}-stop:
  NSISArray::SizeOf /NOUNLOAD ${GROUP_ARRAY_NAME}
  Pop $0
  StrCmp $0 $R2 +2 +1
    MessageBox MB_OK|MB_ICONEXCLAMATION "Could not place all the user's groups into an array!"
  System::Call 'netapi32.dll::NetApiBufferFree(i R8)i .r2'
  !undef Index
!macroend

Resources and Links

API Functions used:



CancerFace 12:08, 7 October 2006 (PDT)