Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

InstallScript Advanced Pointers

4.13/5 (5 votes)
3 Jul 2011CPOL4 min read 29.1K  
This article shows how to overcome the InstallScript limitations and to add to it new capabilities inherent in the C/C++ code.

1. Introduction

InstallScript language is actually a limited subversion of C/C++ language. An InstallScript syntax and data types are similar to C/C++ ones with some limitations lead from InstallScript compiler specifics. All InstallScript data types are automatically converted to NUMBER and STRING types and occupy 4 bytes in a memory.
When these data types are defined inside structures (typedef), they take their original memory sizes, like in C/C++.

Type Nametypedef SizeRegular SizeComments
Char1b4b 
Short2b4b 
Int4b4bInternally equals to NUMBER type
Bool4b4bInternally equals to NUMBER type
HWND4b4bInternally equals to NUMBER type
Long4b4bInternally equals to NUMBER type
String1b/2b1b/2bPointer to dynamically allocated null-terminated array of ASCII characters
WString1b/2b1b/2bPointer to dynamically allocated null-terminated array of Unicode characters (it is not implemented yet in IS2010)
Number4b4b4 bytes signed integer
Pointer4b4bInternally equals to NUMBER type
Variant12berrorStructured type which holds a numeric value or an address of any other data type

2. Member and Pointer Operators in C/C++ and their alternatives in InstallScript

Let's see how different types of C/C++ pointers can be implemented in an InstallScript code.

2.1. a[b] Array Subscript

An array subscripting is permitted for a STRING data type only:

C++
function TestPointers()
   STRING str[10];       //string array with 1 byte characters
   WSTRING wstr[10];     //string array with 2 byte characters 
                         //(is not supported in IS2010)
   NUMBER num[10];       //compilation error
begin
   //...
end;

This method can also be used for accessing arrays of integers (but not for changing ones). This is managed by adding bytes to the pointer address:

C++
typedef STRUCT
begin
   CHAR chval;
end;
function TestPointers()
   STRUCT pointer stt;
   STRING str;
begin
   str="1234567890";
   stt = &str+3;
   SprintfBox(0,"","%c",stt->chval);	// The result is "4".
end;

The correct amount of an allocated memory is checked by the SizeOf function. The StrLength and StrLengthChar functions show a number of characters or bytes for dynamically allocated strings. The array subscription operator that is used at an initialization step has no influence on these functions.

C++
function TestPointers()
   STRING str[10];
begin
   SprintfBox(0,"","[%d,%d]",SizeOf(str),StrLegth(str)); 	//The result is [10,0]
end;

The next code sample is the standard way to allocate memory and initialize an integer array. It uses Windows Kernel function:

C++
typedef STRUCT
begin
   INT intval;
end;

prototype NUMBER KERNEL.LocalAlloc ( NUMBER, NUMBER );
#define LPTR		0x0040

function TestLocalAlloc()
	STRUCT pointer stt;
begin
	stt = LocalAlloc(LPTR, 12);
	stt->intval = 1; stt += 4;
	stt->intval = 2; stt += 4;
	stt->intval = 3;
	stt -=4;
	SprintfBox(0,"","%d",stt->intval);
end;

The same result can be reached if we use pointer to any variable type.
It looks like InstallScript doesn't validate memory access for pointers. Weird.

function TestLocalAlloc()
	number nvPtr;
begin
	stt = &nvPtr;
	stt->intval = 1; stt += 4;
	stt->intval = 2; stt += 4;
	stt->intval = 3;
	stt -=4;
	SprintfBox(0,"","%d",stt->intval);
end;

2.2. &a ("address of a")

The address references are supported in InstallScript the same way as in C/C++. A referenced memory address occupies 4 bytes and can be treated as a regular integer value.

Below is an example of the Windows Kernel function use for memory copy instead of an InstallScript structure pointer.

C++
prototype void kernel32.RtlMoveMemory(pointer,pointer,long);
function TestMoveMemory()
   INT intval,intval2;
   POINTER pint;
begin
   pint = &intval;
   intval = 10;
   RtlMoveMemory(&intval2,pint,4);
   SprintfBox(0,"","%d",intval2);
end;

2.3. *a Indirection ("variable pointed by a")

The indirection operator returns an integer value. InstallScript supports it only in the right side of the expression. For using it at the left side, you can use a pointer to a structure.

C++
function TestIndirection()
   INT intval, resval;
   pointer stt;
begin
   intval = 10;
   stt = &intval;
   resval = *stt;
   SprintfBox(0,"","%d",resval);		//returns a value;
end;
C++
typedef STRUCT
begin
   INT intval;
end;
function TestIndirection()
   STRUCT pointer stt;
   INT intval;
begin
   intval = 10;
   stt = &intval;
   SprintfBox(0,"","%d",Stt->intval);		//returns a value;
end;

2.4. &a Reference types (inside function arguments)

Referenced types in InstallScript can be defined inside function arguments only.

C++
Prototype BOOL MyFunc(HWND,byref int,byref string);

Two examples of redefinitions of windows function. Original declarations in C\C++:

UINT MsiGetProperty(
  __in          MSIHANDLE hInstall,
  __in          LPCTSTR szName,
  __out         LPTSTR szValueBuf,
  __in_out      DWORD* pchValueBuf
);

Function arguments as byref strings:

prototype number MSI.MsiGetPropertyA(HWND, byval string, byref string, byref int); 

function TestByrefArguments()
	number nBuff;
	string szProp;
begin
	//MsiGetPropertyA uses buffer of MAX_PATH length
	nBuff = MAX_PATH;
	MSI.MsiGetPropertyA(ISMSI_HANDLE,"ARPPRODUCTICON",szProp,nBuff);
	MessageBox(szProp,0);    
end;

Function arguments as pointers:

prototype int MSI.MsiGetPropertyA(HWND, pointer, pointer, pointer); 

function TestPointerArguments()
	string szProp[MAX_PATH],szPropName;
    number nBuff;
    pointer ptProp;
begin
	nBuff = MAX_PATH;
	szPropName = "ARPPRODUCTICON"; 
	ptProp = &szProp;//LocalAlloc(LPTR,nBuff);

	MSI.MsiGetPropertyA(ISMSI_HANDLE,&szPropName,ptProp,&nBuff);
	
	//please see appendix the explanation and source code of this function
	PointerToStr(szProp,ptProp);
		
	MessageBox(szProp,0);
end;

2.5. a->b Member b of object pointed to by a

This operator is supported in InstallScript the same way as in C/C++.

2.6. a.b Member b of object a

This operator is supported in InstallScript the same way as in C/C++.

2.9. NULL Pointers

NULL pointer equals to an integer value of 0.

2.10. Function Pointers in C/C++ and their alternative in InstallScript

The function pointers are not supported in InstallScript and throw compilation error. The callback functions can be defined in external DLLs and their addresses can be exported to an InstallScript code. The GetModuleHandle and GetProcAddress functions are defined in InstallScript, but are not documented (as many other interesting windows functions).

C++
prototype int User32.SetTimer(HWND,int,int,pointer);
function TestFunctionPointers()
   HWND hDLL;
   POINTER pFunc;
   NUMBER nTimerID;
begin
   UseDLL("timerproc.dll");
   hDLL = GetModuleHandle("timerproc.dll");
   pFunc = GetProcAddress(hDLL,"TimerProc");
   nTimerID = SetTimer(NULL,1,1000, pFunc);
end;

4. Variant Type

As we said before, the Variant data type is actually a structure which holds a numeric value or an address of any other data type:

C++
typedef __VARIANT
begin
   SHORT  vt;
   SHORT  wReserver1;
   SHORT  wReserved2;
   SHORT  wReserved3;
   NUMBER nData;
end;

A Variant variable’s value can be accessed directly after incrementing its address in memory:

C++
typedef STRUCT
begin
   NUMBER nval;
end;

function TestVariant()
   STRUCT pointer ret;	
   __VARIANT vt;
begin
   vt = 10;
   ret = &vt + 8;
   SprintfBox(0,"","%d",ret->nval);
   SprintfBox(0,"","%d",*ret);
end;

5. Arrays as Pointers

An InstallScript array is actually a VARIANT type variable which holds an address of a SAFEARRAY structure. It can be used for a memory allocation instead of Windows functions, like LocalAlloc.

C++
typedef __VARIANT
begin
   SHORT  vt;
   SHORT  wReserver1;
   SHORT  wReserved2;
   SHORT  wReserved3;
   NUMBER nData;
end;

typedef __SAFEARRAY
begin
   SHORT   cDims;
   SHORT   fFeatures;
   LONG    cbElements;
   LONG    cLocks;
   POINTER pvData;
end;

typedef STRUCT
begin
   NUMBER nval;
end;

function GetArrayMember()
   NUMBER nArr(10);
   __SAFEARRAY pointer psArray; 
   __VARIANT   pointer psVariant; 
   STRUCT pointer ret; 
begin
   nArr(5) = 10;
   psVariant = &nArr;
   psArray = psVariant->nData;
   ret = psArray->pvData + 5*4;
   SprintfBox(0,"","%d",ret->nval);
endif;

By using this approach, we can create a very elegant C/C++ style memory allocation function:

C++
typedef STRUCT
begin
   NUMBER nval;
end;

prototype pointer new(number);
function pointer new(nSize)
   NUMBER nArr();
   STRUCT pointer ret; 
begin
   Resize(nArr, nSize);
   ret = &nArr + 8;
   ret = ret->nval + 12;
   return ret->nval;
end;

It can be used like is shown below:

C++
POINTER ret; 
ret = new(100);

The above code allocates 100*4 bytes of memory. The problem here is that at the moment when the memory allocator function returns a value, we have already left the scope of the array used for the memory allocation so, an InstallScript engine can formally take that memory for any of its own needs. In order to solve this issue, we can create a global array and use it together with memory management and optimization algorithms.

6. Examples

These examples are written in «C» and InstallScript languages. They show string and pointer implementation techniques discussed throughout this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)