65.9K
CodeProject 正在变化。 阅读更多。
Home

InstallScript 高级指针

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.13/5 (5投票s)

2010年10月27日

CPOL

4分钟阅读

viewsIcon

29498

本文介绍了如何克服 InstallScript 的限制,并为其添加 C/C++ 代码固有的新功能。

1. 引言

InstallScript 语言实际上是 C/C++ 语言的受限版本。InstallScript 的语法和数据类型与 C/C++ 类似,但由于 InstallScript 编译器的特性而存在一些限制。所有 InstallScript 数据类型都会自动转换为 NUMBERSTRING 类型,并在内存中占用 4 个字节。
当这些数据类型在结构(typedef)中定义时,它们会像 C/C++ 一样占用原始内存大小。

类型名称 typedef 大小 常规大小 注释
Char 1b 4b  
Short 2b 4b  
Int 4b 4b 内部等同于 NUMBER 类型
Bool 4b 4b 内部等同于 NUMBER 类型
HWND 4b 4b 内部等同于 NUMBER 类型
长整型 4b 4b 内部等同于 NUMBER 类型
字符串 1b/2b 1b/2b 指向动态分配的以 null 结尾的 ASCII 字符数组的指针
WString 1b/2b 1b/2b 指向动态分配的以 null 结尾的 Unicode 字符数组的指针(IS2010 中尚未实现)
数字 4b 4b 4 字节有符号整数
指针 4b 4b 内部等同于 NUMBER 类型
Variant 12b error 结构类型,包含数值或任何其他数据类型的地址

2. C/C++ 中的成员和指针运算符及其在 InstallScript 中的替代方法

让我们看看如何在 InstallScript 代码中实现不同类型的 C/C++ 指针。

2.1. a[b] 数组下标

数组下标仅允许用于 STRING 数据类型。

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;

此方法也可用于访问整数数组(但不能用于修改)。这可以通过向指针地址添加字节来实现。

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;

已分配内存的正确大小通过 SizeOf 函数进行检查。StrLengthStrLengthChar 函数显示动态分配的 strings 的字符数或字节数。在初始化步骤中使用的数组下标运算符不会影响这些函数。

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

下一个代码示例是分配内存并初始化整数数组的标准方法。它使用了 Windows 内核函数。

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;

如果我们使用指向任何变量类型的指针,也可以达到相同的结果。
看起来 InstallScript 不会验证指针的内存访问。真奇怪。

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(“a 的地址”)

地址引用在 InstallScript 中与 C/C++ 中的方式相同。引用的内存地址占用 4 个字节,可以视为常规整数值。

下面是一个使用 Windows 内核函数进行内存复制而不是 InstallScript 结构指针的示例。

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 解引用(“a 指向的变量”)

解引用运算符返回一个整数值。InstallScript 仅在表达式的右侧支持它。要在左侧使用它,可以使用指向结构的指针。

function TestIndirection()
   INT intval, resval;
   pointer stt;
begin
   intval = 10;
   stt = &intval;
   resval = *stt;
   SprintfBox(0,"","%d",resval);		//returns a value;
end;
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 引用类型(在函数参数内)

引用类型只能在 InstallScript 的函数参数内定义。

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

重新定义 windows 函数的两个示例。C\C++ 中的原始声明。

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

函数参数为 byref string

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;

函数参数为指针

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 a 指向的对象的成员 b

此运算符在 InstallScript 中的支持方式与 C/C++ 相同。

2.6. a.b 对象 a 的成员 b

此运算符在 InstallScript 中的支持方式与 C/C++ 相同。

2.9. NULL 指针

NULL 指针等于整数值 0

2.10. C/C++ 中的函数指针及其在 InstallScript 中的替代方法

InstallScript 不支持函数指针,会导致编译错误。回调函数可以在外部 DLL 中定义,并且它们的地址可以导出到 InstallScript 代码中。GetModuleHandleGetProcAddress 函数在 InstallScript 中定义,但未被文档化(就像许多其他有趣的 Windows 函数一样)。

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 类型

正如我们之前所说,Variant 数据类型实际上是一个结构,它包含一个数值或任何其他数据类型的地址。

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

在增加其内存地址后,可以直接访问 Variant 变量的值。

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. 数组作为指针

InstallScript 数组实际上是一个 VARIANT 类型变量,它包含一个 SAFEARRAY 结构的地址。它可以用于内存分配,而不是使用 Windows 函数,如 LocalAlloc

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;

通过使用这种方法,我们可以创建一个非常优雅的 C/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;

它可以使用如下所示的方式。

POINTER ret; 
ret = new(100);

上面的代码分配了 100*4 字节的内存。问题在于,当内存分配函数返回一个值时,我们已经离开了用于内存分配的数组的作用域,因此 InstallScript 引擎可以正式将其用于任何自己的需求。为了解决这个问题,我们可以创建一个全局数组,并将其与内存管理和优化算法一起使用。

6. 示例

这些示例是用“C”和 InstallScript 语言编写的。它们展示了本文讨论的 string 和指针实现技术。

© . All rights reserved.