Is there a way to determine the Physical Sector Size for removable drives with file systems other than NTFS (like FAT16, exFAT...)? - delphi

I need a function to get the Physical Sector Size for all kind of system drives, in Win7 or higher.
This is the code that I've used until today, when I found out that it's not working with my external USB HDD (exFAT file system) and with my USB MP3 Player (FAT16). In these cases the function DeviceIoControl fails and I get the exception: "System Error. Code 50. The request is not suported". But it works very well with NTFS volumes.
function GetSectorSize(Drive:Char):DWORD;
var h:THandle;
junk:DWORD;
Query:STORAGE_PROPERTY_QUERY;
Alignment:STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
begin
result:=0;
h:=CreateFileW(PWideChar('\\.\'+UpperCase(Drive)+':'),0,FILE_SHARE_READ or FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
if h=INVALID_HANDLE_VALUE then RaiseLastOSError;
try
FillChar(Query,SizeOf(Query),0);
Query.PropertyId:=StorageAccessAlignmentProperty;
Query.QueryType:=PropertyStandardQuery;
if not DeviceIoControl(h,IOCTL_STORAGE_QUERY_PROPERTY,#Query,SizeOf(Query),#Alignment,SizeOf(Alignment),junk,nil) then RaiseLastOSError;
result:=Alignment.BytesPerPhysicalSector;
finally
CloseHandle(h);
end;
end;

According to MSDN:
File Buffering
Most current Windows APIs, such as IOCTL_DISK_GET_DRIVE_GEOMETRY and GetDiskFreeSpace, will return the logical sector size, but the physical sector size can be retrieved through the IOCTL_STORAGE_QUERY_PROPERTY control code, with the relevant information contained in the BytesPerPhysicalSector member in the STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR structure. For an example, see the sample code at STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR. Microsoft strongly recommends that developers align unbuffered I/O to the physical sector size as reported by the IOCTL_STORAGE_QUERY_PROPERTY control code to help ensure their applications are prepared for this sector size transition.
This same quote also appears in the following MSDN document:
Advanced format (4K) disk compatibility update
Which includes the following additional information:
The below list summarizes the new features delivered as part of Windows 8 and Windows Server 2012 to help improve customer and developer experience with large sector disks. More detailed description for each item follow.
...
•Provides a new API to query for physical sector size (FileFsSectorSizeInformation)
...
Here’s how you can query for the physical sector size:
Preferred method for Windows 8
With Windows 8, Microsoft has introduced a new API that enables developers to easily integrate 4K support within their apps. This new API supports even greater numbers of scenarios than the legacy method for Windows Vista and Windows 7 discussed below. This API enables these calling scenarios:
•Calling from an unprivileged app
•Calling to any valid file handle
•Calling to a file handle on a remote volume over SMB2
•Simplified programming model
The API is in the form of a new info class, FileFsSectorSizeInformation, with associated structure FILE_FS_SECTOR_SIZE_INFORMATION
FILE_FS_SECTOR_SIZE_INFORMATION structure
This information can be queried in either of the following ways:
•Call FltQueryVolumeInformation or ZwQueryVolumeInformationFile, passing FileFsSectorSizeInformation as the value of FileInformationClass and passing a caller-allocated, FILE_FS_SECTOR_SIZE_INFORMATION-structured buffer as the value of FileInformation.
•Create an IRP with major function code IRP_MJ_QUERY_VOLUME_INFORMATION.
•Call FsRtlGetSectorSizeInformation with a pointer to a FILE_FS_SECTOR_SIZE_INFORMATION-structured buffer. The FileSystemEffectivePhysicalBytesPerSectorForAtomicity member will not have a value initialized by the file system when this structure is returned from FsRtlGetSectorSizeInformation. A file system driver will typically call this function and then set its own value for FileSystemEffectivePhysicalBytesPerSectorForAtomicity.

Your principal error is that you try to get physical sector size from a volume handle rather than from that of an underlying physical device (\\.\PhysicalDriveX). Device's physical sector size doesn't depend on FS and shouldn't be confused with a logical sector size defined by FS properties.

Related

How to assign a special section to an independent sector on STM32 flash memory?

As you can see,STM32F4xx series memory map is like that.I am develop an IAP application, the boot firmware may be over 64KB size, so I use sector 0-4 to put boot firmware.Now I want to put version msg and verify msg so on to a sector which wouldn`t be erased, its a waste to put them to sector 5.I try to modify ld file using STM32CubeIDE in many ways iI know but failed, So is there anyone know how to put msg to sector 1-3?
Changing boot address is an option:STM32 boot address setting .
For example you can set boot address as start address of sector 1 and you can save your parameters on sector 0.
Another solution is using a dummy boot loader which only checks a button etc. to decide jump to boot loader or main application. For example you can put dummy boot loader on sector 0, parameters can be placed on sector 1, the main boot loader can be placed on sector 2-3-4 and main application can be placed on any where rest of the flash memory.

How do I get my UEFI EDK2 based BIOS to automatically load a driver located in its own firmware volume?

I am using the UEFI EDK2 to create a BIOS. I have modified the FDF to move a driver (both UEFI and legacy versions) from the main firmware volume into a separate firmware volume (FV) that I created strictly to hold the driver.
Before I moved the driver from the main FV, I would see the legacy OROM sign-on during POST. However, since I have moved the driver to the new FV, I no longer see the legacy OROM sign-on. It would seem the legacy OROM is no longer being loaded.
It seems that EDK2 "automatically" loads only certain FVs and then dispatches their drivers, but I can't figure out how these particular FVs are identified in EDK2.
I have searched the EDK2 code for several hours trying to find out where/how the FV HOB is created/initialized, but I cannot find this code. I'm guessing I need to add the new FV's GUID to some list or data structure, but I'm really guessing at this point.
Any pointers would be greatly appreciated.
I found the location in the BIOS where the firmware volume HOBs are created (in a proprietary file). I added code there to create a FV HOB for my new firmware volume.
After that, I had to install a PPI that could process the new firmware volume. Here is the PPI creation code:
static EFI_PEI_FIRMWARE_VOLUME_INFO_PPI mNewFvPpiInfo = {
EFI_FIRMWARE_FILESYSTEM2_GUID,
(VOID*) <Starting address of new FV in the ROM>,
<size of the new FV in the ROM>,
NULL,
NULL
};
static EFI_PEI_PPI_DESCTRIPTOR mNewFvPpi = {
(EFI_PEI_PPI_DESCTRIPTOR_PPI | EFI_PEI_PPI_DESCTRIPTOR_TERMINATE_LIST),
&gEfiPeiFirmwareVolumeInfoPpiGuid,
&mNewFvPpiInfo
};
Here is the code that installs the PPI (placed after the new FV HOB is added to the FV HOB list):
(*ppPeiServices)->InstallPpi(ppPeiServices, &mNewPvPpi);

Why is COMMON_APPDATA returned as a null string on Windows XP

One of my users at a large university (with, I imagine, the aggressive security settings that university IT departments general have on their computers) is getting an empty string returned by Windows XP for CSIDL_COMMON_APPDATA or CSIDL_PERSONAL. (I'm not sure which of these is returning the empty string, because I haven't yet examined his computer to see how he's installed the software, but I'm pretty sure it's the COMMON_APPDATA...)
Has anyone encountered this or have suggestions on how to deal with this?
Here's the Delphi code I'm using to retrieve the value:
Function GetSpecialFolder( FolderID: Integer):String;
var
PIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
begin
SHGetSpecialFolderLocation(Application.Handle, FolderID, PIDL);
SHGetPathFromIDList(PIDL, Path);
Result := Path;
end; { GetSpecialFolder }
ShowMessage(GetSpecialFolder(CSIDL_COMMON_APPDATA)); <--- This is an empty string
Edit:
Figuring out this API made me feel like I was chasing my tail - I went in circles trying to find the right call. This method and others similar to it are said to be deprecated by Microsoft (as well as by a earlier poster to this question (#TLama?) who subsequently deleted the post.) But, it seems like most of us, including me, regularly and safely ignore that status.
In my searches, I found a good answer here on SO from some time ago, including sample code for the non-deprecated way of doing this: what causes this error 'Unable to write to application file.ini'.
If you want to find out why an API call is failing you need to check the return values. That's what is missing in this code.
You need to treat each function on its own merits. Read the documentation on MSDN. In the case of SHGetSpecialFolderLocation, the return value is an HRESULT. For SHGetPathFromIDList you get back a BOOL. If that is FALSE then the call failed.
The likely culprit here is SHGetSpecialFolderLocation, the code that receives the CSIDL, but you must check for errors whenever you call Windows API functions.
Taking a look at the documentation for CSIDL we see this:
CSIDL_COMMON_APPDATA
Version 5.0. The file system directory that contains application data for all users. A typical path is C:\Documents and Settings\All
Users\Application Data. This folder is used for application data that
is not user specific. For example, an application can store a
spell-check dictionary, a database of clip art, or a log file in the
CSIDL_COMMON_APPDATA folder. This information will not roam and is
available to anyone using the computer.
If the machine has a shell version lower than 5.0, then this CSIDL value is not supported. That's the only documented failure mode for this CSIDL value. I don't think that applies to your situation, so you'll just have to see what the HRESULT status code has to say.

Get user picture

OS: Win7x64 (2008,2008r2).
Lang: Delphi Xe2.
How to receive a full path (and file name) to the image "user account picture"?
How to set new picture?
Example on delphi plz.
Need:
...function GetCurrentUserPicture:string;
...function GetUserPicture(UserName:String):string;
...function SetUserNewPicture(UserName, ImageFileName:String):bool;
There is an undocumented function in shell32.dll. On Windows XP its ordinal is 233, on Windows Vista and 7 its ordinal is 261.
Its function prototype (from Airesoft) is:
HRESULT WINAPI SHGetUserPicturePath (
LPCWSTR pwszPicOrUserName,
DWORD sguppFlags,
LPWSTR pwszPicPath,
UINT picPathLen
)
You can use this function to retrieve the path where the user picture is stored. Just pass the user name as pwszPicOrUserName, the buffer where you want to store the path to the picture as pwszPicPath and the size of the buffer in chars as picPathLen. You can set sguppFlags to 0 or to any of the other flags posssible.
There is also a undocumented function which you can use in order to set the user picture of a user. Its ordinal is 234 on Windows XP, 262 on Windows Vista and Windows 7.
Its function prototype (from Airesoft) is:
HRESULT WINAPI SHSetUserPicturePath (
LPWSTR pwszAcctName,
DWORD reserved,
LPCWSTR pwszPictureFile
)
Pass the name of the user whose picture should be changed as pwszAcctName and the path to the picture you want to set as pwszPictureFile. Set reserved to 0. You have to initialize COM prior to calling this function.
According to Microsoft you should not rely on undocumented function because they can be removed or changed with any patch which is installed on Windows.
According to MSDN:
In Windows 7 or later, each user profile has an associated image
presented as a user tile. These tiles appear to users on the User
Accounts Control Panel item and its Manage Accounts subpage.. The
image files for the default Guest and default User accounts also
appear here if you have Administrator access rights.
....
The user's tile image is stored as
C:\Users\<username>\Local\Temp folder as .bmp. Any slash
characters () are converted to plus sign characters (+). For example,
DOMAIN\user is converted to DOMAIN+user.
I could not find an API to obtain the image and since the official documentation is calling out this implementation detail I think that means that you are safe to rely on it. That is I think this is a supported way to obtain the tile image.

How to get Excel version and macro security level

Microsoft has recently broken our longtime (and officially recommended by them) code to read the version of Excel and its current omacro security level.
What used to work:
// Get the program associated with workbooks, e.g. "C:\Program Files\...\Excel.exe"
SHELLAPI.FindExecutable( 'OurWorkbook.xls', ...)
// Get the version of the .exe (from it's Properties...)
WINDOWS.GetFileVersionInfo()
// Use the version number to access the registry to determine the security level
// '...\software\microsoft\Office\' + VersionNumber + '.0\Excel\Security'
(I was always amused that the security level was for years in an insecure registry entry...)
In Office 2010, .xls files are now associated with "“Microsoft Application Virtualization DDE Launcher," or sftdde.exe. The version number of this exe is obviously not the version of Excel.
My question:
Other than actually launching Excel and querying it for version and security level (using OLE CreateOLEObject('Excel.Application')), is there a cleaner, faster, or more reliable way to do this that would work with all versions starting with Excel 2003?
Use
function GetExcelPath: string;
begin
result := '';
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
if OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\excel.exe', false) then
result := ReadString('Path') + 'excel.exe';
finally
Free;
end;
end;
to get the full file name of the excel.exe file. Then use GetFileVersionInfo as usual.
As far as I know, this approach will always work.
using OLE CreateOLEObject('Excel.Application'))
you can get installed Excel versions by using the same registry place, that this function uses.
Basically you have to clone a large part of that function registry code.
You can spy on that function call by tools like Microsoft Process Monitor too see exactly how does Windows look for installed Excel - and then to do it exactly the same way.
You have to open registry at HKEY_CLASSES_ROOT\ and enumerate all the branches, whose name starts with "Excel.Application."
For example at this my workstation I only have Excel 2013 installed, and that corresponds to HKEY_CLASSES_ROOT\Excel.Application.15
But on my another workstation I have Excel 2003 and Excel 2010 installed, testing different XLSX implementations in those two, so I have two registry keys.
HKEY_CLASSES_ROOT\Excel.Application.12
HKEY_CLASSES_ROOT\Excel.Application.14
So, you have to enumerate all those branches with that name, dot, and number.
Note: the key HKEY_CLASSES_ROOT\Excel.Application\CurVer would have name of "default" Excel, but what "default" means is ambiguous when several Excels are installed. You may take that default value, if you do not care, or you may decide upon your own idea what to choose, like if you want the maximum Excel version or minimum or something.
Then when for every specific excel branch you should read the default key of its CLSID sub-branch.
Like HKEY_CLASSES_ROOT\Excel.Application.15\CLSID has nil-named key equal to
{00024500-0000-0000-C000-000000000046} - fetch that index to string variable.
Then do a second search - go into a branch named like HKEY_CLASSES_ROOT\CLSID\{00024500-0000-0000-C000-000000000046}\LocalServer ( use the fetched index )
If that branch exists - fetch the nil-named "default key" value to get something like C:\PROGRA~1\MICROS~1\Office15\EXCEL.EXE /automation
The last result is the command line. It starts with a filename (non-quoted in this example, but may be in-quotes) and is followed by optional command line.
You do not need command line, so you have to extract initial commanlind, quoted or not.
Then you have to check if such an exe file exists. If it does - you may launch it, if not - check the registry for other Excel versions.

Resources