InstallScript 高级指针






4.13/5 (5投票s)
本文介绍了如何克服 InstallScript 的限制,并为其添加 C/C++ 代码固有的新功能。
1. 引言
InstallScript 语言实际上是 C/C++ 语言的受限版本。InstallScript 的语法和数据类型与 C/C++ 类似,但由于 InstallScript 编译器的特性而存在一些限制。所有 InstallScript 数据类型都会自动转换为 NUMBER
和 STRING
类型,并在内存中占用 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
函数进行检查。StrLength
和 StrLengthChar
函数显示动态分配的 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 代码中。GetModuleHandle
和 GetProcAddress
函数在 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
和指针实现技术。