我遇到了SetLength
命令的问题。
这基本上就是这个问题:
我尽可能地与WindowsAPI
合作。我的目标是将一定大小设置为动态数组:
以下是一些需要理解的代码:
var
thefile : array of char;
// or thefile : array [0..9999] of char; // <---- not really a good way, works tho
FileHandle := CreateFileA(paramstr0, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
dwSize := GetFileSize(FileHandle,NIL);
SetFilePointer(FileHandle, 0, nil, FILE_BEGIN);
SetLength(TheFile, dwsize); // <--- I can't use this
ReadFile(FileHandle, thefile[1], dwSize , dwRead, nil);
CloseHandle (FileHandle);
CloseHandle(CreateFileA (sfile, 0, 0,NIL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,0));
FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
WriteFile (FileHandle, thefile, dwSize, testCardinal, NIL);
CloseHandle (FileHandle);
如何替换SetLength
?我还想了解Windows / Delphi如何在内存中分配数组等。感谢您的帮助
答案 0 :(得分:8)
你的代码中有几个缺陷
使用CreateFile函数时,必须根据INVALID_HANDLE_VALUE检查返回的句柄。
您必须始终使用try ..来释放资源。
您正在尝试使用ReadFile函数使用索引1读取thefile
缓冲区内的内容,这是错误的,您必须使用索引0,因为动态数组为零基于索引。
您正在使用此代码
CloseHandle(CreateFileA(sfile,0,0,NIL,CREATE_NEW, FILE_ATTRIBUTE_NORMAL,0));
要创建一个空文件,然后再使用CreateFile函数打开和写入文件,您可以一步完成相同的操作
FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, 0, 0);
尝试使用此代码的改进版本。
顺便说一句,使用SetLengh没有任何问题,问题在第3点有解释。
var
thefile : array of AnsiChar;
begin
FileHandle := CreateFileA(paramstr0, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if FileHandle <> INVALID_HANDLE_VALUE then
try
dwSize := GetFileSize(FileHandle, nil);
SetFilePointer(FileHandle, 0, nil, FILE_BEGIN);
SetLength(TheFile, dwsize);
ReadFile(FileHandle, thefile[0], dwSize , dwRead, nil);
finally
CloseHandle (FileHandle);
end;
FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, 0, 0);
if FileHandle <> INVALID_HANDLE_VALUE then
try
WriteFile (FileHandle, thefile[0], dwSize, testCardinal, nil);
finally
CloseHandle (FileHandle);
end;
SetLength(TheFile, 0);
end;
答案 1 :(得分:3)
SetLength()
不是问题所在。
如果您真的喜欢Windows API,只需使用CopyFile()
函数即可完成所有工作:
CopyFile(pointer(paramstr0),pointer(sFile));
使用几MB的临时固定缓冲区比一次读取所有文件要快。
您的代码将无法处理文件&gt; 2GB。您需要在代码中处理Int64大小和位置。
恕我直言,你最好在这里使用非Windows API来处理文件,但TFileStream
是跨平台的。
SetLength()
可能是此处唯一的好调用之一 - 但您应该编码theFile[0]
或pointer(theFile)^
而不是theFile[1]
。
答案 2 :(得分:1)
您可以将GetMem / FreeMem与通用缓冲区一起使用,而不是使用动态数组。它与你在C中的表现方式更为相似。
无论如何设置与文件一样大的缓冲区通常不是最佳解决方案。通常使用固定大小的缓冲区并以块的形式读/写文件。即使有足够的可用内存,您也可能无法分配整个数组。数组内存需要是连续的,因此您需要一个与您需要分配的数组一样大或更大的空闲内存块。随着时间的推移,内存可能会碎片化,因此无法获得足够大的块。
即使您能够分配它,您也可以强制操作系统将一些内存从RAM中交换出来以分配它 - 从而减慢其他应用程序的速度。因此,应该考虑可用的RAM,性能和磁盘速度来设置缓冲区的大小。
Delphi如何分配内存取决于您正在使用的内存管理器。大多数人会使用Windows内存分配功能请求更大的块,并根据需要对它们进行子分配(为了加快内存管理,Windows并不像大多数OO应用程序那样有效地分配小内存块)。只有在包含不再使用的数据时,才能将块返回给操作系统。
为什么 char 的数组?在Delphi中,char不是8位类型。例如,它是Delphi 2007的8位,但从2009年开始是16位类型,因为它变成了Unicode字符(不像C)。使用字节类型(或更新版本中的UInt8),无论您使用何种版本的Delphi和目标平台,它都将保持8位类型。
如果您需要复制文件,TStream.CopyFrom()将在一次调用中完成您所需的操作(在您创建两个流之后)。