Delphi apps - Windows tablet mode keyboard covering app - windows

We are looking to move our service technicians from laptops to Windows tablets. One issue while testing is that when the onscreen keyboard pops up, it obscures the bottom part of the application. How can we catch when the keyboard pops up and resize the application so this doesn't happen? App currently in Delphi XE5, but moving to Delphi 11.1.

You need to use IFrameworkInputPaneHandler
Here is sample unit implementing it.
unit UFrameworkInputPaneHandler;
interface
uses
Winapi.Windows;
const
CLSID_FrameworkInputPane: TGUID = '{D5120AA3-46BA-44C5-822D-CA8092C1FC72}';
SID_IFrameworkInputPane = '{5752238B-24F0-495A-82F1-2FD593056796}';
SID_IFrameworkInputPaneHandler = '{226C537B-1E76-4D9E-A760-33DB29922F18}';
type
IFrameworkInputPaneHandler = interface(IInterface)
[SID_IFrameworkInputPaneHandler]
function Showing(var AInputPaneScreenLocation: TRect;
AEnsureFocusedElementInView: BOOL): HResult; stdcall;
function Hiding(AEnsureFocusedElementInView: BOOL): HResult; stdcall;
end;
IFrameworkInputPane = interface(IInterface)
[SID_IFrameworkInputPane]
function Advise(AWindow: IUnknown; AHandler: IFrameworkInputPaneHandler;
var Cookie: DWORD): HRESULT; stdcall;
function AdviseWithHWND(AWnd: HWND; const AHandler: IFrameworkInputPaneHandler;
var Cookie: DWORD): HRESULT; stdcall;
function Unadvise(dwCookie: DWORD): HRESULT; stdcall;
function Location(var rcInputPaneScreenLocation: TRect): HRESULT; stdcall;
end;
TTouchKeyboardChangeEvent = procedure(Sender: TObject; const IsShowing: Boolean; var Rect: TRect;
const EnsureFocusedElementInView: Boolean) of object;
TFrameworkInputHandler = class(TInterfacedObject, IFrameworkInputPaneHandler)
strict private
FAdviseCookie: DWORD;
FInputPane: IFrameworkInputPane;
FOnTouchKeyboardVisibilityChanged: TTouchKeyboardChangeEvent;
public
{ IFrameworkInputPaneHandler }
function Showing(var AInputPaneScreenLocation: TRect; AEnsureFocusedElementInView: BOOL): HRESULT; stdcall;
function Hiding(AEnsureFocusedElementInView: BOOL): HRESULT; stdcall;
constructor Create(const AWnd: HWND);
destructor Destroy; override;
function GetLocation(var ARect: TRect): Boolean;
property OnTouchKeyboardChanged: TTouchKeyboardChangeEvent
read FOnTouchKeyboardVisibilityChanged write FOnTouchKeyboardVisibilityChanged;
property InputPane: IFrameworkInputPane read FInputPane;
end;
implementation
uses
Winapi.ActiveX, System.Win.ComObj, System.Types;
constructor TFrameworkInputHandler.Create(const AWnd: HWND);
var
HR: HRESULT;
begin
inherited Create();
FAdviseCookie := 0;
FInputPane := nil;
FOnTouchKeyboardVisibilityChanged := nil;
HR := CoCreateInstance(CLSID_FrameworkInputPane, nil, CLSCTX_ALL,
StringToGUID(SID_IFrameworkInputPane), FInputPane);
if (not FAILED(HR)) and Assigned(FInputPane) then
begin
FInputPane.AdviseWithHWND(AWnd, Self, FAdviseCookie);
end;
end;
destructor TFrameworkInputHandler.Destroy();
begin
if Assigned(FInputPane) then
begin
FInputPane.Unadvise(FAdviseCookie);
FInputPane := nil;
end;
inherited Destroy();
end;
function TFrameworkInputHandler.GetLocation(var ARect: TRect): Boolean;
begin
Result := False;
ARect := TRect.Empty;
if Assigned(FInputPane) then
begin
Result := not FAILED(FInputPane.Location(ARect));
end;
end;
function TFrameworkInputHandler.Hiding(AEnsureFocusedElementInView: BOOL): HRESULT;
begin
if Assigned(FOnTouchKeyboardVisibilityChanged) then
begin
var Rect := TRect.Empty;
FOnTouchKeyboardVisibilityChanged(Self, False, Rect, AEnsureFocusedElementInView);
end;
Result := S_OK;
end;
function TFrameworkInputHandler.Showing(var AInputPaneScreenLocation: TRect;
AEnsureFocusedElementInView: BOOL): HRESULT;
begin
if Assigned(FOnTouchKeyboardVisibilityChanged) then
begin
FOnTouchKeyboardVisibilityChanged(Self, True, AInputPaneScreenLocation,
AEnsureFocusedElementInView);
end;
Result := S_OK;
end;
end.
In your form declare it as
FInputHandler: IFrameworkInputPaneHandler;
and create it as
if not Assigned(FInputHandler) then
begin
FInputHandler := TFrameworkInputHandler.Create(Handle);
(FInputHandler as TFrameworkInputHandler).OnTouchKeyboardChanged := OnTouchKeyboardChanged;
end;
In OnTouchKeyboardChanged it will give you position of onscreen keyboard

Related

How to prevent mouse from turning on monitor?

The following code is able to lock mouse events successfully. This was my attempt to prevent the mouse from turning on the monitor when it is off, but the monitor always turns on when executing any action with the mouse, ex: left button click.
Is there some solution to this?
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
MouseHook: HHOOK;
implementation
{$R *.dfm}
function LowLevelMouseProc(nCode: Integer; WParam: WParam; LParam: LParam): LRESULT; stdcall;
begin
Result := 1{CallNextHookEx(MouseHook, nCode, WParam, LParam)};
case WParam of
WM_LBUTTONDOWN:
Form1.Memo1.Lines.Add('Mouse Left Button Down');
WM_LBUTTONUP:
Form1.Memo1.Lines.Add('Mouse Left Button Up');
WM_LBUTTONDBLCLK:
Form1.Memo1.Lines.Add('Mouse Left Button Double Click');
WM_RBUTTONDOWN:
Form1.Memo1.Lines.Add('Mouse Right Button Down');
WM_RBUTTONUP:
Form1.Memo1.Lines.Add('Mouse Right Button Up');
WM_RBUTTONDBLCLK:
Form1.Memo1.Lines.Add('Mouse Right Button Double Click');
WM_MBUTTONDOWN:
Form1.Memo1.Lines.Add('Mouse Middle Button Down');
WM_MBUTTONUP:
Form1.Memo1.Lines.Add('Mouse Middle Button Up');
WM_MBUTTONDBLCLK:
Form1.Memo1.Lines.Add('Mouse Middle Button Double Click');
WM_MOUSEMOVE:
Form1.Memo1.Lines.Add('Mouse Move');
WM_MOUSEWHEEL:
Form1.Memo1.Lines.Add('Mouse Wheel');
else
Form1.Memo1.Lines.Add('Unknown Event');
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
MONITOR_ON = -1;
MONITOR_OFF = 2;
MONITOR_STANDBY = 1;
begin
MouseHook := SetWindowsHookEx(WH_MOUSE_LL, #LowLevelMouseProc, HInstance, 0);
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF);
// SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
UnhookWindowsHookEx(MouseHook);
end;
Edit:
I need only turn off monitor without turn off system (hibernate or suspend). And prevent mouse actions revert it.
After analyze and test #AmigoJack's suggestion, seems that disable the device (like is made using Windows Device Manager) is a possible solution to me, even if some device cannot be disabled. I'm leaving the code to help future readers with this same doubt.
DeviceCtrl
unit DeviceCtrl;
interface
uses
Classes, SysUtils, Windows;
const
GUID_DEVCLASS_1394: TGUID = '{6BDD1FC1-810F-11D0-BEC7-08002BE2092F}';
GUID_DEVCLASS_ADAPTER: TGUID = '{4D36E964-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_APMSUPPORT: TGUID = '{D45B1C18-C8FA-11D1-9F77-0000F805F530}';
GUID_DEVCLASS_BATTERY: TGUID = '{72631E54-78A4-11D0-BCF7-00AA00B7B32A}';
GUID_DEVCLASS_CDROM: TGUID = '{4D36E965-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_COMPUTER: TGUID = '{4D36E966-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_DECODER: TGUID = '{6BDD1FC2-810F-11D0-BEC7-08002BE2092F}';
GUID_DEVCLASS_DISKDRIVE: TGUID = '{4D36E967-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_DISPLAY: TGUID = '{4D36E968-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_FDC: TGUID = '{4D36E969-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_FLOPPYDISK: TGUID = '{4D36E980-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_GPS: TGUID = '{6BDD1FC3-810F-11D0-BEC7-08002BE2092F}';
GUID_DEVCLASS_HDC: TGUID = '{4D36E96A-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_HIDCLASS: TGUID = '{745A17A0-74D3-11D0-B6FE-00A0C90F57DA}';
GUID_DEVCLASS_IMAGE: TGUID = '{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}';
GUID_DEVCLASS_INFRARED: TGUID = '{6BDD1FC5-810F-11D0-BEC7-08002BE2092F}';
GUID_DEVCLASS_KEYBOARD: TGUID = '{4D36E96B-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_LEGACYDRIVER: TGUID = '{8ECC055D-047F-11D1-A537-0000F8753ED1}';
GUID_DEVCLASS_MEDIA: TGUID = '{4D36E96C-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MEDIUM_CHANGER: TGUID = '{CE5939AE-EBDE-11D0-B181-0000F8753EC4}';
GUID_DEVCLASS_MODEM: TGUID = '{4D36E96D-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MONITOR: TGUID = '{4D36E96E-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MOUSE: TGUID = '{4D36E96F-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MTD: TGUID = '{4D36E970-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MULTIFUNCTION: TGUID = '{4D36E971-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_MULTIPORTSERIAL: TGUID = '{50906CB8-BA12-11D1-BF5D-0000F805F530}';
GUID_DEVCLASS_NET: TGUID = '{4D36E972-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_NETCLIENT: TGUID = '{4D36E973-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_NETSERVICE: TGUID = '{4D36E974-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_NETTRANS: TGUID = '{4D36E975-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_NODRIVER: TGUID = '{4D36E976-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_PCMCIA: TGUID = '{4D36E977-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_PORTS: TGUID = '{4D36E978-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_PRINTER: TGUID = '{4D36E979-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_PRINTERUPGRADE: TGUID = '{4D36E97A-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_SCSIADAPTER: TGUID = '{4D36E97B-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_SMARTCARDREADER: TGUID = '{50DD5230-BA8A-11D1-BF5D-0000F805F530}';
GUID_DEVCLASS_SOUND: TGUID = '{4D36E97C-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_SYSTEM: TGUID = '{4D36E97D-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_TAPEDRIVE: TGUID = '{6D807884-7D21-11CF-801C-08002BE10318}';
GUID_DEVCLASS_UNKNOWN: TGUID = '{4D36E97E-E325-11CE-BFC1-08002BE10318}';
GUID_DEVCLASS_USB: TGUID = '{36FC9E60-C465-11CF-8056-444553540000}';
GUID_DEVCLASS_VOLUME: TGUID = '{71A27CDD-812A-11D0-BEC7-08002BE2092F}';
type
TDeviceControlResult = (DCROK, DCRErrEnumDevIceInfo, DCRErrSetClassInstallParams, DCRErrDIF_PROPERTYCHANGE);
function LoadDevices(GUID_DevClass: TGUID): TStringList;
function EnableDevice(SelectedItem: DWORD): TDeviceControlResult;
function DisableDevice(SelectedItem: DWORD): TDeviceControlResult;
implementation
const
DIF_PROPERTYCHANGE = $00000012;
DICS_ENABLE = $00000001;
DICS_DISABLE = $00000002;
DICS_FLAG_GLOBAL = $00000001;
DIGCF_PRESENT = $00000002;
SPDRP_DEVICEDESC = $00000000;
SPDRP_CLASS = $00000007;
SPDRP_CLASSGUID = $00000008;
SPDRP_FRIENDLYNAME = $0000000C;
type
HDEVINFO = Pointer;
DI_FUNCTION = LongWord;
PSPClassInstallHeader = ^TSPClassInstallHeader;
SP_CLASSINSTALL_HEADER = packed record
cbSize: DWORD;
InstallFunction: DI_FUNCTION;
end;
TSPClassInstallHeader = SP_CLASSINSTALL_HEADER;
PSPPropChangeParams = ^TSPPropChangeParams;
SP_PROPCHANGE_PARAMS = packed record
ClassInstallHeader: TSPClassInstallHeader;
StateChange: DWORD;
Scope: DWORD;
HwProfile: DWORD;
end;
TSPPropChangeParams = SP_PROPCHANGE_PARAMS;
PSPDevInfoData = ^TSPDevInfoData;
SP_DEVINFO_DATA = packed record
cbSize: DWORD;
ClassGuid: TGUID;
DevInst: DWORD;
Reserved: ULONG_PTR;
end;
TSPDevInfoData = SP_DEVINFO_DATA;
TSetupDiEnumDeviceInfo = function(DeviceInfoSet: HDEVINFO; MemberIndex: DWORD; var DeviceInfoData: TSPDevInfoData): LongBool; stdcall;
TSetupDiSetClassInstallParamsA = function(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader; ClassInstallParamsSize: DWORD): LongBool; stdcall;
TSetupDiSetClassInstallParamsW = function(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader; ClassInstallParamsSize: DWORD): LongBool; stdcall;
TSetupDiSetClassInstallParams = TSetupDiSetClassInstallParamsA;
TSetupDiCallClassInstaller = function(InstallFunction: DI_FUNCTION; DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData): LongBool; stdcall;
TSetupDiGetClassDevs = function(ClassGuid: PGUID; const Enumerator: PAnsiChar; hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall;
TSetupDiGetDeviceRegistryPropertyA = function(DeviceInfoSet: HDEVINFO; const DeviceInfoData: TSPDevInfoData; Property_: DWORD; var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSIze: DWORD; var RequiredSize: DWORD): BOOL; stdcall;
TSetupDiGetDeviceRegistryPropertyW = function(DeviceInfoSet: HDEVINFO; const DeviceInfoData: TSPDevInfoData; Property_: DWORD; var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSIze: DWORD; var RequiredSize: DWORD): BOOL; stdcall;
TSetupDiGetDeviceRegistryProperty = TSetupDiGetDeviceRegistryPropertyA;
var
DevInfo: hDevInfo;
SetupDiEnumDeviceInfo: TSetupDiEnumDeviceInfo;
SetupDiSetClassInstallParams: TSetupDiSetClassInstallParams;
SetupDiCallClassInstaller: TSetupDiCallClassInstaller;
SetupDiGetClassDevs: TSetupDiGetClassDevs;
SetupDiGetDeviceRegistryProperty: TSetupDiGetDeviceRegistryProperty;
var
SetupApiLoadCount: Integer = 0;
function LoadSetupApi: Boolean;
var
SetupApiLib: System.THandle;
begin
Result := True;
Inc(SetupApiLoadCount);
if SetupApiLoadCount > 1 then
Exit;
SetupApiLib := LoadLibrary('SetupApi.dll');
Result := SetupApiLib <> 0;
if Result then
begin
SetupDiEnumDeviceInfo := Windows.GetProcAddress(SetupApiLib, 'SetupDiEnumDeviceInfo');
SetupDiSetClassInstallParams := Windows.GetProcAddress(SetupApiLib, 'SetupDiSetClassInstallParamsA');
SetupDiCallClassInstaller := Windows.GetProcAddress(SetupApiLib, 'SetupDiCallClassInstaller');
SetupDiGetClassDevs := Windows.GetProcAddress(SetupApiLib, 'SetupDiGetClassDevsA');
SetupDiGetDeviceRegistryProperty := Windows.GetProcAddress(SetupApiLib, 'SetupDiGetDeviceRegistryPropertyA');
end;
end;
function StateChange(NewState, SelectedItem: DWORD; hDevInfo: hDevInfo): TDeviceControlResult;
var
PropChangeParams: TSPPropChangeParams;
DeviceInfoData: TSPDevInfoData;
begin
PropChangeParams.ClassInstallHeader.cbSize := SizeOf(TSPClassInstallHeader);
DeviceInfoData.cbSize := SizeOf(TSPDevInfoData);
if (not SetupDiEnumDeviceInfo(hDevInfo, SelectedItem, DeviceInfoData)) then
begin
Result := DCRErrEnumDevIceInfo;
Exit;
end;
PropChangeParams.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE;
PropChangeParams.Scope := DICS_FLAG_GLOBAL;
PropChangeParams.StateChange := NewState;
if (not SetupDiSetClassInstallParams(hDevInfo, #DeviceInfoData, PSPClassInstallHeader(#PropChangeParams), SizeOf(PropChangeParams))) then
begin
Result := DCRErrSetClassInstallParams;
Exit;
end;
if (not SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, #DeviceInfoData)) then
begin
Result := DCRErrDIF_PROPERTYCHANGE;
Exit;
end;
Result := DCROK;
end;
function GetRegistryProperty(PnPHandle: hDevInfo; DevData: TSPDevInfoData; Prop: DWORD; Buffer: PChar; dwLength: DWORD): Boolean;
var
aBuffer: array[0..256] of Char;
begin
dwLength := 0;
aBuffer[0] := #0;
SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, Prop, Prop, PBYTE(#aBuffer[0]), SizeOf(aBuffer), dwLength);
StrCopy(Buffer, aBuffer);
Result := Buffer^ <> #0;
end;
function ConstructDeviceName(DeviceInfoSet: hDevInfo; DeviceInfoData: TSPDevInfoData; Buffer: PChar; dwLength: DWORD): Boolean;
const
UnknownDevice = '<Unknown DevIce>';
begin
if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_FRIENDLYNAME, Buffer, dwLength)) then
begin
if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_DEVICEDESC, Buffer, dwLength)) then
begin
if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_CLASS, Buffer, dwLength)) then
begin
if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_CLASSGUID, Buffer, dwLength)) then
begin
dwLength := DWORD(SizeOf(UnknownDevice));
Buffer := Pointer(LocalAlloc(LPTR, Cardinal(dwLength)));
StrCopy(Buffer, UnknownDevice);
end;
end;
end;
end;
Result := True;
end;
function LoadDevices(GUID_DevClass: TGUID): TStringList;
var
DeviceInfoData: TSPDevInfoData;
I: DWORD;
pszText: PChar;
begin
if (not LoadSetupApi) then
begin
Result := nil;
Exit;
end;
DevInfo := nil;
DevInfo := SetupDiGetClassDevs(#GUID_DevClass, nil, 0, DIGCF_PRESENT);
if (DevInfo = Pointer(INVALID_HANDLE_VALUE)) then
begin
Result := nil;
Exit;
end;
Result := TStringList.Create;
DeviceInfoData.cbSize := SizeOf(TSPDevInfoData);
I := 0;
while SetupDiEnumDeviceInfo(DevInfo, I, DeviceInfoData) do
begin
GetMem(pszText, 256);
try
ConstructDeviceName(DevInfo, DeviceInfoData, pszText, DWORD(nil));
Result.AddObject(string(PAnsiChar(pszText)), Tobject(I));
finally
FreeMem(pszText);
end;
Inc(I);
end;
end;
function EnableDevice(SelectedItem: DWORD): TDeviceControlResult;
begin
Result := StateChange(DICS_ENABLE, SelectedItem, DevInfo);
end;
function DisableDevice(SelectedItem: DWORD): TDeviceControlResult;
begin
Result := StateChange(DICS_DISABLE, SelectedItem, DevInfo);
end;
end.
DeviceTest
program DeviceTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Classes,
DeviceCtrl;
var
sl: TStringList;
I: Integer;
begin
sl := LoadDevices(GUID_DEVCLASS_MOUSE);
try
for I := 0 to sl.count - 1 do
begin
Writeln(I, ' : ', sl[I]);
if DisableDevice(I) = DCROK then
Writeln(sl[I], ' disabled');
if EnableDevice(I) = DCROK then
Writeln(sl[I], ' enabled');
end;
finally
sl.Free;
end;
Readln;
end.
To work, is need execute this program as Administrator, and compile as 32 or 64bit according with your Windows system.
References: Enabling and disabling devices and Possibly you are hitting the problem of 32bit apps trying to setup 64 bit drivers

Transforming file sizes to text representation

I am building on online file manager. One of the columns it displays is the file size, but this is always a high number of bytes. I would like to display the file size as does Windows Explorer, with a smaller number and the appropriate unit, e.g. 5 MB instead of 5000000.
It isn't at all hard for me to do this, but I was wondering of Windows rather had a built in function to do this. Is there something already, or must I roll my own?
I see 3 variants:
function FormatFileSize(const ASize: UInt64; AKbMode: Boolean): UnicodeString;
var
PS: IPropertySystem;
PD: IPropertyDescription;
PV: TPropVariant;
Flags: DWORD;
Display: PWideChar;
PUI: IPropertyUI;
begin
Result := '';
// Variant 1
if Succeeded(CoCreateInstance(CLSID_IPropertySystem, nil, CLSCTX_INPROC_SERVER, IPropertySystem, PS)) then
begin
if Succeeded(PS.GetPropertyDescription(PKEY_Size, IPropertyDescription, PD)) then
begin
PV.vt := VT_UI8;
PV.uhVal.QuadPart := ASize;
if AKbMode then Flags := PDFF_ALWAYSKB
else Flags := PDFF_DEFAULT;
if Succeeded(PD.FormatForDisplay(PV, Flags, Display)) then
begin
Result := Display;
CoTaskMemFree(Display);
end;
PD := nil;
end;
PS := nil;
end;
if Result <> '' then Exit;
// Variant 2 - Windows XP mode, can be replaced with Variant 3
if Succeeded(CoCreateInstance(CLSID_PropertiesUI, nil, CLSCTX_INPROC_SERVER, IPropertyUI, PUI)) then
begin
PV.vt := VT_UI8;
PV.uhVal.QuadPart := ASize;
SetLength(Result, 100);
if Succeeded(PUI.FormatForDisplay(PKEY_Size.fmtid, PKEY_Size.pid, PV, PUIFFDF_DEFAULT, PWideChar(Result), Length(Result) + 1)) then
Result := PWideChar(Result)
else
Result := '';
PUI := nil;
end;
if Result <> '' then Exit;
// Variant 3
SetLength(Result, 100);
if AKbMode then
Result := StrFormatKBSizeW(ASize, PWideChar(Result), Length(Result))
else
Result := StrFormatByteSizeW(ASize, PWideChar(Result), Length(Result));
end;
Here are two variants (they need Windows Vista) in C#:
...
Console.WriteLine(FormatByteSize(1031023120)); // 983 MB
Console.WriteLine(FormatByteSize2(1031023120, true)); // 1 006 859 KB
...
Note the benefit (or an inconvenient depending on how you see it) of using Windows is you will get localized version (if any), using the Shell/OS culture.
public static string FormatByteSize2(long size, bool alwaysKb = false)
{
// Here, we use Windows Shell's size column definition and formatting
// note although System.Size is defined as a UInt64, formatting doesn't support more than long.MaxValue...
PSGetPropertyKeyFromName("System.Size", out var pk);
var pv = new PROPVARIANT(size);
var sb = new StringBuilder(128);
const int PDFF_ALWAYSKB = 4;
PSFormatForDisplay(ref pk, pv, alwaysKb ? PDFF_ALWAYSKB : 0, sb, sb.Capacity);
return sb.ToString();
}
public static string FormatByteSize(long size)
{
// Here, we use use a Windows Shell API (probably the sames algorithm underneath)
// It's much simpler, we only need to declare one StrFormatByteSizeW API
var sb = new StringBuilder(128);
StrFormatByteSizeW(size, sb, sb.Capacity);
return sb.ToString();
}
[DllImport("shlwapi", CharSet = CharSet.Unicode)]
private static extern IntPtr StrFormatByteSizeW(long qdw, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf, int cchBuf);
[DllImport("propsys", CharSet = CharSet.Unicode)]
private static extern int PSFormatForDisplay(
ref PROPERTYKEY propkey,
PROPVARIANT pv,
int pdfFlags,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf, int cchBuf);
[DllImport("propsys", CharSet = CharSet.Unicode)]
private static extern int PSGetPropertyKeyFromName([MarshalAs(UnmanagedType.LPWStr)] string pszName, out PROPERTYKEY ppropkey);
[StructLayout(LayoutKind.Sequential)]
private struct PROPERTYKEY
{
public Guid fmtid;
public int pid;
}
[StructLayout(LayoutKind.Sequential)]
private class PROPVARIANT
{
// note this version of PROPVARIANT is far from being suited for all purposes...
public short vt;
short wReserved1;
short wReserved2;
short wReserved3;
public long val;
const short VT_UI8 = 21;
public PROPVARIANT(long ul)
{
wReserved3 = wReserved2 = wReserved1 = 0;
val = ul;
vt = VT_UI8;
}
}

Windows Explorer extension Delphi XE5 and Windows 8.1

My code nothing happens. What is missing to implement or there is any restriction on Windows 8.1 for x64 Shell Extension ?
Use
Delphi XE5
Windows 8.1 x64
unit uModuloIpm;
interface
uses
Windows, ComObj, ActiveX, Modulo_TLB, StdVcl, ShlObj, Registry;
type
TCoModulo = class(TAutoObject, ICoModulo, IExtractIcon)
protected
function GetIconLocation(uFlags: UINT; szIconFile: LPWSTR; cchMax: UINT;
out piIndex: Integer; out pwFlags: UINT): HResult; stdcall;
function Extract(pszFile: LPCWSTR; nIconIndex: UINT;
out phiconLarge, phiconSmall: HICON; nIconSize: UINT): HResult; stdcall;
end;
TCoModuloObjectFactory = class(TAutoObjectFactory)
protected
procedure ApproveShellExtension(&Register: Boolean; const ClsID: string);
function GetProgID: string; override;
public
procedure UpdateRegistry(Register: Boolean); override;
end;
implementation
uses ComServ, SysUtils;
procedure TCoModuloObjectFactory.ApproveShellExtension(Register: Boolean;
const ClsID: string);
Const
WinNTRegKey =
'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved';
var
Reg: TRegistry;
begin
Reg:= TRegistry.Create;
try
Reg.RootKey:= HKEY_LOCAL_MACHINE;
if not Reg.OpenKey(WinNTRegKey, True) then
Exit;
if &Register then
Reg.WriteString(ClsID, Description)
else
Reg.DeleteValue(ClsID);
finally
Reg.Free;
end;
end;
function TCoModuloObjectFactory.GetProgID: string;
begin
Result:= GUIDToString(IID_ICoModulo);
end;
procedure TCoModuloObjectFactory.UpdateRegistry(Register: Boolean);
Const
ColumKey = 'Folder\shellex\ColumnHandlers\%s';
begin
inherited UpdateRegistry(Register);
ApproveShellExtension(Register, GUIDToString(CLASS_CoModulo));
if Register then
CreateRegKey(Format(ColumKey, [GUIDToString(CLASS_CoModulo)]), '', '', HKEY_CLASSES_ROOT)
else
DeleteRegKey(Format(ColumKey, [GUIDToString(CLASS_CoModulo)]), HKEY_CLASSES_ROOT);
end;
function TCoModulo.Extract(pszFile: LPCWSTR; nIconIndex: UINT; out phiconLarge,
phiconSmall: HICON; nIconSize: UINT): HResult;
begin
MessageBox(0,'Extract','',0); // Only to trigger the event
Result := S_OK;
end;
function TCoModulo.GetIconLocation(uFlags: UINT; szIconFile: LPWSTR;
cchMax: UINT; out piIndex: Integer; out pwFlags: UINT): HResult;
begin
MessageBox(0,'GetIconLocation','',0); // Only to trigger the event
Result := S_OK;
end;
initialization
TCoModuloObjectFactory.Create(ComServer, TCoModulo, Class_CoModulo,
ciMultiInstance, tmApartment);
end.
Everything works fine in that scenario, when implement IContextMenu based on this example http://www.andreanolanusse.com/en/shell-extension-for-windows-32-bit-and-64-bit-with-delphi-xe2/.

c conversion in pascal, change parameters in a filter directshow

I have to convert this to free pascal for Lazarus to change parameters in a filter directshow,
i have a guide written in c.
Filters are : http://sourceforge.net/projects/videoprocessing/
I use only ScaleFilter.dll for resize my video. In SettingsInterface.h to describe how to configure the filter in application.
DEFINE_GUID( IID_ISettingsInterface, /* 388EEF20-40CC-4752-A0FF-66AA5C4AF8FA */
0x388eef20, 0x40cc, 0x4752, 0xa0, 0xff, 0x66, 0xaa, 0x5c, 0x4a,
0xf8, 0xfa);
#undef INTERFACE
#define INTERFACE ISettingsInterface
DECLARE_INTERFACE_( ISettingsInterface, IUnknown )
{
/// Method to set parameter named type to value
STDMETHOD(SetParameter)( const char* type, const char* value) = 0;
};
I have translated so:
type
ISettingsInterface = interface(IUnknown)
['{388EEF20-40CC-4752-A0FF-66AA5C4AF8FA}']
function SetParameter(const tipo , valore: bstr): HResult; stdcall;
//function SetParameter(const tipo , valore: PAnsiChar): HResult; stdcall;
end; // bstr -> thanks to Marco Van de Voort
...
const
targetwidth = 'targetwidth';
targetheight = 'targetheight';
tipowidth = '720';
tipoheight = '576';
IID_ISettingsInterface : TGUID = '{388EEF20-40CC-4752-A0FF-66AA5C4AF8FA}';
var
pSettingsInterface :ISettingsInterface;
...
ScaleFilter.QueryInterface(IID_ISettingsInterface, pSettingsInterface);
hr := pSettingsInterface.SetParameter(targetwidth, tipowidth);
hr := pSettingsInterface.SetParameter(targetheight, tipoheight);
fgRender.Play;
//*******
In run application :
hr := pSettingsInterface.SetParameter(targetwidth, tipowidth);
// ( External error SIGSEGV );
the parameters in the filter are not changed.
can anyone help me? thank you.

How to add(enable) standard "Send To" context menu option in a namespace extension

I have a namespace extension, which provides a virtual view of files/folders in a server.
In the IContextMenu::QueryContextMenu() I have added some of the custom menu items.
I have also set couple of SGAOF flags in the IShellFolder::GetAttributesOf() to get the rename, delete, and properties, in the context menu.
Is there any way I can get the "Send To" option in the context menu for items in my namespace extension? and How do I handle these commands once these are enabled?. Please advise.
This is the code I tried as Denis Anisimov suggested
const CLSID SendToCLSID = { 0x7BA4C740, 0x9E81, 0x11CF, { 0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37 } };
HRESULT CMyNSEContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder , IDataObject *pDataObj, HKEY hkeyProgID )
{
OutputDebugString(L"CMyNSEContextMenu::Initialize\n");
//Other initialization code
...
...
if (_pdtobj)
{
_pdtobj->Release();
_pdtobj = NULL;
}
_mpidlFolder = pidlFolder;
_pdtobj = pDataObj;
if (pDataObj)
{
_pdtobj->AddRef();
CoCreateInstance(SendToCLSID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu, (LPVOID*)&_pSendToMenu);
}
return S_OK;
}
HRESULT CMyNSEContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast , UINT uFlags )
{
OutputDebugString(L"CMyNSEContextMenu::QueryContextMenu\n");
UNREFERENCED_PARAMETER(indexMenu);
UNREFERENCED_PARAMETER(idCmdFirst);
//Get File Name
IShellItemArray *psia=NULL;
HRESULT hr;
USHORT items = 0;
//Adding other menu items
AddMenuItem(hmenu,
indexMenu++,
idCmdFirst + MENUVERB_XXX,
IDS_COMMAND_XXX,
IDB_XXX);
items++;
IShellExtInit *pShellExtInitSendTo = NULL;
_pSendToMenu->QueryInterface(IID_IShellExtInit, (LPVOID*)&pShellExtInitSendTo);
pShellExtInitSendTo->Initialize(NULL, _pdtobj, 0); // your IDataObject with CFSTR_SHELLIDLIST format)
hr = _pSendToMenu->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
if (SUCCEEDED(hr))
{
items += HRESULT_CODE(hr);
}
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(items));
}
HRESULT CMyNSEContextMenu::HandleMenuMsg(
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
IContextMenu2 *pSendToMenu = NULL;
_pSendToMenu->QueryInterface(IID_IContextMenu2, (LPVOID*)&pSendToMenu);
return pSendToMenu->HandleMenuMsg(uMsg,wParam,lParam);
}
HRESULT CMyNSEContextMenu::HandleMenuMsg2(
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
LRESULT *plResult
)
{
IContextMenu3 *pSendToMenu = NULL;
_pSendToMenu->QueryInterface(IID_IContextMenu3, (LPVOID*)&pSendToMenu);
return pSendToMenu->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
}
HRESULT CMyNSEContextMenu::GetCommandString(UINT_PTR idCmd , UINT uType , UINT * pRes , LPSTR pszName , UINT cchMax )
{
OutputDebugString(L"CMyNSEContextMenu::GetCommandString\n");
return _pSendToMenu->GetCommandString(idCmd, uType, pRes, pszName, cchMax);
}
The default context menu is created as part of GetUIObjectOf. and the instance of MyNSEContextMenu class is through the Classfactory.
HRESULT CMyNSEShellFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
REFIID riid, UINT * /* prgfInOut */, void **ppv)
{
OutputDebugString(L"CMyNSEShellFolder::GetUIObjectOf\n");
*ppv = NULL;
HRESULT hr = E_NOINTERFACE;
if (riid == IID_IContextMenu)
{
// The default context menu will call back for IQueryAssociations to determine the
// file associations with which to populate the menu.
DEFCONTEXTMENU const dcm = { hwnd, NULL, m_pidl, static_cast<IShellFolder2 *>(this),
cidl, apidl, NULL, 0, NULL };
hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);
}
//Others
....
....
else if (riid == IID_IQueryAssociations)
{
else
{
ASSOCIATIONELEMENT const rgAssocItem[] =
{
{ ASSOCCLASS_PROGID_STR, NULL, L"MyNSE_Type"},
};
hr = AssocCreateForClasses(rgAssocItem, ARRAYSIZE(rgAssocItem), riid, ppv);
}
}
...
...
return hr;
}
//Called from the class factory
HRESULT CMyNSEContextMenu_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CMyNSEContextMenu* pContextMenu = new (std::nothrow) CMyNSEContextMenu();
HRESULT hr = pContextMenu ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pContextMenu->QueryInterface(riid, ppv);
pContextMenu->Release();
}
return hr;
}
Related registries written are as follows
HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\%s", szContextMenuClassID, NULL, (LPBYTE)g_szExtTitle, REG_SZ,
HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\%s\\InprocServer32", szContextMenuClassID, NULL, (LPBYTE)L"%s", REG_SZ,
HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\%s\\InprocServer32", szContextMenuClassID, L"ThreadingModel", (LPBYTE)L"Apartment", REG_SZ,
HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\%s\\ProgID", szFolderViewImplClassID, NULL, (LPBYTE)L"MyNSE_Type", REG_SZ,
// For performance, only context menu verbs that register this are considered when the user double-clicks.
HKEY_CLASSES_ROOT, L"CLSID\\%s\\ShellEx\\MayChangeDefaultMenu", szContextMenuClassID, NULL, (LPBYTE)L"", REG_SZ,
// register the context menu handler under the MyNSE_Type type.
HKEY_CLASSES_ROOT, L"MyNSE_Type\\shellex\\ContextMenuHandlers\\%s", szContextMenuClassID, NULL, (LPBYTE)szContextMenuClassID, REG_SZ,
SendTo is just simple shell extension which implements IContextMenu(2,3). CLSID of extension is {7BA4C740-9E81-11CF-99D3-00AA004AE837} in Windows 7 (dont forget to check correct CLSID in other Windows versions you want to support). So just use something like this:
function TMenuWithSentTo.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
const
SendToCLSID: TGUID = '{7BA4C740-9E81-11CF-99D3-00AA004AE837}';
var
ShellExtInit: IShellExtInit;
begin
Result := 0;
// Add you menu items here
CoCreateInstance(SendToCLSID, nil, CLSCTX_INPROC_SERVER, IContextMenu, FSendToMenu);
FSendToMenu.QueryInterface(IShellExtInit, ShellExtInit);
ShellExtInit.Initialize(nil, FDataObject, 0); // your IDataObject with CFSTR_SHELLIDLIST format
Result := Result + FSendToMenu.QueryContextMenu(Menu, indexMenu, idCmdFirst, idCmdLast, uFlags);
// Add you menu items here
end;
function TMenuWithSentTo.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
begin
if IsMyCommand(lpici) then
begin
// Process your command here
Result := S_OK;
end
else
Result := FSendToMenu.InvokeCommand(lpici);
end;
function TMenuWithSentTo.GetCommandString(idCmd: UINT_PTR; uFlags: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
if IsMyCommandID(idCmd) then
begin
// Process your command here
Result := S_OK;
end
else
FSendToMenu.GetCommandString(idCmd);
end;
function TMenuWithSentTo.HandleMenuMsg(uMsg: UINT; WParam: WPARAM; LParam: LPARAM): HResult;
var
SendToMenu2: IContextMenu2;
begin
if IsMyMessage(uMsg, WParam, LParam) then
begin
// Process your command here
Result := S_OK;
end
else
begin
FSendToMenu.QueryInterface(IContextMenu2, SendToMenu2);
Result := SendToMenu2.HandleMenuMsg(uMsg, WParam, LParam);
end;
end;
function TMenuWithSentTo.HandleMenuMsg2(uMsg: UINT; wParam: WPARAM; lParam: LPARAM; var lpResult: LRESULT): HResult;
var
SendToMenu3: IContextMenu3;
begin
if IsMyMessage(uMsg, WParam, LParam) then
begin
// Process your command here
Result := S_OK;
end
else
begin
FSendToMenu.QueryInterface(IContextMenu3, SendToMenu3);
Result := SendToMenu3.HandleMenuMsg(uMsg, WParam, LParam);
end;
end;
But your should be ready that some command of SendTo will be hidden and some will not work correctly because some of them requests real files but you have virtual only.
Normal Send to menu:
Send to menu in NSE:
The simple way is to add a shortcut to the SendTo folder. To find that, simply paste %APPDATA%\Microsoft\Windows\SendTo into an Explorer window.
This only works if you have a command line program that takes a file name as an argument. If this is not what you need please edit your question with further details of how your extension code is accessed. Also, if this is C# please tag it so.
The registry key for SendTo can be found at HKEY_CLASSES_ROOT\AllFilesystemObjects\shellex\ContextMenuHandlers. The value since at least Vista and up to Windows 8 is {7BA4C740-9E81-11CF-99D3-00AA004AE837}. You can write a shell extension for this key. I have done this in the past, but don't have any source code that would help. The documentation is here: http://msdn.microsoft.com/en-us/library/windows/desktop/cc144067%28v=vs.85%29.aspx.

Resources