diff --git a/Packages/D2007/X2Utils.dpk b/Packages/D2007/X2Utils.dpk
index f4b7e6f..0e12d8b 100644
--- a/Packages/D2007/X2Utils.dpk
+++ b/Packages/D2007/X2Utils.dpk
@@ -52,6 +52,7 @@ contains
X2UtPersist in '..\..\X2UtPersist.pas',
X2UtPersistForm in '..\..\X2UtPersistForm.pas',
X2UtPersistIntf in '..\..\X2UtPersistIntf.pas',
- X2UtPersistRegistry in '..\..\X2UtPersistRegistry.pas';
+ X2UtPersistRegistry in '..\..\X2UtPersistRegistry.pas',
+ X2UtElevation in '..\..\X2UtElevation.pas';
end.
diff --git a/Packages/D2007/X2Utils.dproj b/Packages/D2007/X2Utils.dproj
index b87d6d7..da6ddd4 100644
--- a/Packages/D2007/X2Utils.dproj
+++ b/Packages/D2007/X2Utils.dproj
@@ -33,16 +33,6 @@
FalseTrueFalseX2UtilsTrueFalseTrue2007TrueFalse1000FalseFalseFalseFalseFalse104312521.0.0.01.0.0.0
-
-
-
-
-
-
-
-
-
-
Microsoft Office 2000 Sample Automation Server Wrapper Components
Microsoft Office XP Sample Automation Server Wrapper Components
@@ -52,14 +42,12 @@
MainSource
-
-
-
+
@@ -78,5 +66,8 @@
+
+
+
\ No newline at end of file
diff --git a/X2UtElevation.pas b/X2UtElevation.pas
new file mode 100644
index 0000000..821d00c
--- /dev/null
+++ b/X2UtElevation.pas
@@ -0,0 +1,347 @@
+{
+ Helper functions and classes for writing UAC-compatible elevated COM objects.
+ Backwards-compatible with previous Windows versions.
+}
+unit X2UtElevation;
+
+interface
+uses
+ ActiveX,
+ ComObj;
+
+
+ { Checks if the current process has the elevation token. }
+ function IsElevated: Boolean;
+
+ { Creates an elevated instance of COM object. Returns False if the
+ user cancelled the elevation prompt. }
+ function CoCreateElevatedInstance(AParentWnd: THandle; AClassID: TCLSID;
+ AIID: TIID; var AIntf): Boolean;
+
+
+type
+ (*
+ Registers a COM object for elevation.
+
+ The ResourceID must point to a String Table resource in the current
+ module and contains the program name to display in the elevation prompt.
+
+ An example .rc file:
+
+ STRINGTABLE
+ {
+ 42, "Elevated COM Object"
+ }
+ *)
+ TElevatedClassFactory = class(TTypedComObjectFactory)
+ private
+ FResourceID: string;
+ public
+ constructor Create(const AResourceID: string; AComServer: TComServerObject;
+ ATypedComClass: TTypedComClass; const AClassID: TGUID;
+ AInstancing: TClassInstancing;
+ AThreadingModel: TThreadingModel = tmSingle);
+
+ procedure UpdateRegistry(Register: Boolean); override;
+
+ property ResourceID: string read FResourceID;
+ end;
+
+
+implementation
+uses
+ ComConst,
+ SysUtils,
+ Windows,
+
+ X2UtOS;
+
+
+type
+ BIND_OPTS3 = packed record
+ cbStruct: DWORD;
+ grfFlags: DWORD;
+ grfMode: DWORD;
+ dwTickCountDeadline: DWORD;
+ dwTrackFlags: DWORD;
+ dwClassContext: DWORD;
+ locale: LCID;
+ pServerInfo: PCOSERVERINFO;
+ hwnd: HWND;
+ end;
+ PBIND_OPTS3 = ^BIND_OPTS3;
+
+ TOKEN_INFORMATION_CLASS = (
+ TokenICPad,
+ TokenUser,
+ TokenGroups,
+ TokenPrivileges,
+ TokenOwner,
+ TokenPrimaryGroup,
+ TokenDefaultDacl,
+ TokenSource,
+ TokenType,
+ TokenImpersonationLevel,
+ TokenStatistics,
+ TokenRestrictedSids,
+ TokenSessionId,
+ TokenGroupsAndPrivileges,
+ TokenSessionReference,
+ TokenSandBoxInert,
+ TokenAuditPolicy,
+ TokenOrigin,
+ TokenElevationType,
+ TokenLinkedToken,
+ TokenElevation,
+ TokenHasRestrictions,
+ TokenAccessInformation,
+ TokenVirtualizationAllowed,
+ TokenVirtualizationEnabled,
+ TokenIntegrityLevel,
+ TokenUIAccess,
+ TokenMandatoryPolicy,
+ TokenLogonSid,
+ MaxTokenInfoClass
+ );
+
+ TOKEN_ELEVATION = packed record
+ TokenIsElevated: DWORD;
+ end;
+ PTOKEN_ELEVATION = ^TOKEN_ELEVATION;
+
+
+ { Who in the advapi32 team came up with this name?! Descriptive for sure. }
+ TConvertStringSecurityDescriptorToSecurityDescriptorA = function(StringSecurityDescriptor: PAnsiChar;
+ StringSDRevision: Cardinal;
+ var SecurityDescriptor: PSecurityDescriptor;
+ var SecurityDescriptorSize: Cardinal): LongBool; stdcall;
+
+ TCoGetObject = function(pszName: PWideChar; pBindOptions: PBIND_OPTS3;
+ const iid: TIID; out ppv): HResult; stdcall; //external 'ole32.dll' name 'CoGetObject';
+
+ TOpenProcessToken = function(ProcessHandle: THandle; DesiredAccess: DWORD;
+ var TokenHandle: THandle): BOOL; stdcall;
+
+ TGetTokenInformation = function(TokenHandle: THandle;
+ TokenInformationClass: TOKEN_INFORMATION_CLASS;
+ TokenInformation: Pointer;
+ TokenInformationLength: DWORD;
+ var ReturnLength: DWORD): BOOL; stdcall;
+
+
+var
+ ConvertStringSecurityDescriptorToSecurityDescriptorA: TConvertStringSecurityDescriptorToSecurityDescriptorA;
+ CoGetObject: TCoGetObject;
+ OpenProcessToken: TOpenProcessToken;
+ GetTokenInformation: TGetTokenInformation;
+
+
+
+{ Helper functions }
+function IsElevated: Boolean;
+var
+ tokenHandle: THandle;
+ tokenInfo: TOKEN_ELEVATION;
+ dummy: Cardinal;
+
+begin
+ Result := False;
+ if (not Assigned(OpenProcessToken)) or
+ (not Assigned(GetTokenInformation)) then
+ Exit;
+
+
+ if OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, tokenHandle) then
+ begin
+ dummy := 0;
+
+ if GetTokenInformation(tokenHandle, TokenElevation, @tokenInfo, SizeOf(TOKEN_ELEVATION), dummy) then
+ Result := (tokenInfo.TokenIsElevated <> 0);
+
+ CloseHandle(tokenHandle);
+ end;
+end;
+
+
+function CoCreateElevatedInstance(AParentWnd: THandle; AClassID: TCLSID;
+ AIID: TIID; var AIntf): Boolean;
+var
+ bindOptions: BIND_OPTS3;
+ monikerName: WideString;
+ status: HRESULT;
+
+begin
+ Result := True;
+
+ if OS.SupportsUAC and (not IsElevated) then
+ begin
+ { Use elevation moniker }
+ monikerName := 'Elevation:Administrator!new:' + GUIDToString(AClassID);
+
+ FillChar(bindOptions, SizeOf(bindOptions), 0);
+ bindOptions.cbStruct := SizeOf(bindOptions);
+ bindOptions.dwClassContext := CLSCTX_LOCAL_SERVER;
+ bindOptions.hwnd := AParentWnd;
+
+ status := CoGetObject(PWideChar(monikerName), @bindOptions, aIID, AIntf);
+ if HResultCode(status) = ERROR_CANCELLED then
+ Result := False
+ else
+ OleCheck(status);
+ end else
+ { Use good ole' CoCreateInstance }
+ OleCheck(CoCreateInstance(AClassID, nil, CLSCTX_ALL, AIID, AIntf));
+end;
+
+
+{ Internal helper functions }
+procedure LoadAPIFunctions;
+var
+ dllHandle: THandle;
+
+begin
+ dllHandle := GetModuleHandle(advapi32);
+ if dllHandle <> 0 then
+ begin
+ @ConvertStringSecurityDescriptorToSecurityDescriptorA := GetProcAddress(dllHandle, 'ConvertStringSecurityDescriptorToSecurityDescriptorA');
+ @OpenProcessToken := GetProcAddress(dllHandle, 'OpenProcessToken');
+ @GetTokenInformation := GetProcAddress(dllHandle, 'GetTokenInformation');
+ end;
+
+ dllHandle := GetModuleHandle('ole32.dll');
+ if dllHandle <> 0 then
+ @CoGetObject := GetProcAddress(dllHandle, 'CoGetObject');
+end;
+
+
+procedure CreateRegKeyDWORD(const AKey, AValue: string; AData: DWORD; ARootKey: HKEY = HKEY_CLASSES_ROOT);
+var
+ keyHandle: HKEY;
+ status: Integer;
+ disposition: Integer;
+
+begin
+ status := RegCreateKeyEx(ARootKey, PChar(AKey), 0, '',
+ REG_OPTION_NON_VOLATILE, KEY_READ or KEY_WRITE, nil,
+ keyHandle, @disposition);
+
+ if status = ERROR_SUCCESS then
+ begin
+ status := RegSetValueEx(keyHandle, PChar(AValue), 0, REG_DWORD,
+ @AData, SizeOf(DWORD));
+ RegCloseKey(keyHandle);
+ end;
+
+ if status <> ERROR_SUCCESS then
+ raise EOleRegistrationError.CreateRes(@SCreateRegKeyError);
+end;
+
+
+procedure SetAccessPermission(const AKey: string);
+const
+ LocalCallSecDesc = 'O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)';
+
+const
+ SDDL_REVISION_1 = 1;
+
+var
+ descriptor: PSecurityDescriptor;
+ size: Cardinal;
+ keyHandle: HKEY;
+
+begin
+ if not Assigned(ConvertStringSecurityDescriptorToSecurityDescriptorA) then
+ Exit;
+
+ if not ConvertStringSecurityDescriptorToSecurityDescriptorA(LocalCallSecDesc,
+ SDDL_REVISION_1,
+ descriptor, size) then
+ RaiseLastOSError;
+
+ try
+ if RegOpenKeyExA(HKEY_CLASSES_ROOT, PChar(AKey), 0, KEY_READ or KEY_WRITE, keyHandle) = ERROR_SUCCESS then
+ try
+ if RegSetValueExA(keyHandle, PChar('AccessPermission'), 0, REG_BINARY, descriptor, size) <> ERROR_SUCCESS then
+ RaiseLastOSError;
+ finally
+ RegCloseKey(keyHandle);
+ end else
+ RaiseLastOSError;
+ finally
+ LocalFree(Cardinal(descriptor));
+ end;
+end;
+
+
+{ TElevatedClassFactory }
+constructor TElevatedClassFactory.Create(const AResourceID: string;
+ AComServer: TComServerObject;
+ ATypedComClass: TTypedComClass;
+ const AClassID: TGUID;
+ AInstancing: TClassInstancing;
+ AThreadingModel: TThreadingModel);
+begin
+ inherited Create(AComServer, ATypedComClass, AClassID, AInstancing,
+ AThreadingModel);
+
+ FResourceID := AResourceID;
+end;
+
+
+procedure TElevatedClassFactory.UpdateRegistry(Register: Boolean);
+var
+ classIDAsString: string;
+ filePath: string;
+ fileName: string;
+ appRegKey: string;
+ classRegKey: string;
+
+begin
+ if not OS.SupportsUAC then
+ begin
+ inherited;
+ Exit;
+ end;
+
+ try
+ classIDAsString := GUIDToString(Self.ClassID);
+ filePath := ComServer.ServerFileName;
+ fileName := ExtractFileName(filePath);
+
+ appRegKey := 'AppID\' + classIDAsString;
+ classRegKey := 'CLSID\' + classIDAsString;
+
+ if Register then
+ begin
+ inherited;
+
+ { Out-of-process }
+ CreateRegKey(appRegKey, '', Description);
+ CreateRegKey(appRegKey, 'DllSurrogate', '');
+ CreateRegKey('AppID\' + fileName, 'AppID', classIDAsString);
+
+ { Over-The-Shoulder elevation }
+ SetAccessPermission(appRegKey);
+
+ { COM object elevation }
+ CreateRegKey(classRegKey, 'AppID', classIDAsString);
+ CreateRegKey(classRegKey, 'LocalizedString', '@' + filePath + ',-' + fResourceId);
+ CreateRegKeyDWORD(classRegKey + '\Elevation', 'Enabled', 1);
+ end else begin
+ DeleteRegKey(classRegKey + '\Elevation');
+ DeleteRegKey(appRegKey);
+ DeleteRegKey('AppID\' + fileName);
+
+ inherited;
+ end;
+ except
+ on E: Exception do
+ raise EOleRegistrationError.Create(E.Message);
+ end;
+end;
+
+
+initialization
+ LoadAPIFunctions;
+
+end.
diff --git a/X2UtOS.pas b/X2UtOS.pas
index ef4d8b2..ecc8def 100644
--- a/X2UtOS.pas
+++ b/X2UtOS.pas
@@ -14,9 +14,25 @@ uses
Windows;
type
+ TOSVersionInfoEx = packed record
+ dwOSVersionInfoSize: DWORD;
+ dwMajorVersion: DWORD;
+ dwMinorVersion: DWORD;
+ dwBuildNumber: DWORD;
+ dwPlatformId: DWORD;
+ szCSDVersion: array[0..127] of AnsiChar;
+ wServicePackMajor: WORD;
+ wServicePackMinor: WORD;
+ wSuiteMask: WORD;
+ wProductType: Byte;
+ wReserved: Byte;
+ end;
+
+
//:$ Enumeration of the recognized Operating System versions
TX2OSVersion = (osWin95, osWin98, osWinME, osWinNT3, osWinNT4,
- osWin2K, osWinXP, osWin2003, osWinVista, osUnknown);
+ osWin2K, osWinXP, osWin2003, osWinVista, osWinServer2008,
+ osWin7, osUnknown);
//:$ Record to hold the Common Controls version
TX2CCVersion = record
@@ -33,19 +49,19 @@ type
FName: String;
FVersionString: String;
FBuild: Cardinal;
- FRawInfo: TOSVersionInfo;
+ FRawInfo: TOSVersionInfoEx;
public
//:$ Contains the name of the OS
- property Name: String read FName write FName;
+ property Name: String read FName write FName;
//:$ Contains a string representation of the OS' version
- property VersionString: String read FVersionString write FVersionString;
+ property VersionString: String read FVersionString write FVersionString;
//:$ Contains the build number of the OS
- property Build: Cardinal read FBuild write FBuild;
+ property Build: Cardinal read FBuild write FBuild;
//:$ Contains the raw version information as provided by the OS
- property RawInfo: TOSVersionInfo read FRawInfo write FRawInfo;
+ property RawInfo: TOSVersionInfoEx read FRawInfo write FRawInfo;
end;
{
@@ -57,22 +73,27 @@ type
FVersion: TX2OSVersion;
FVersionEx: TX2OSVersionEx;
- function GetXPManifest(): Boolean;
+ function GetSupportsUAC: Boolean;
+ function GetXPManifest: Boolean;
protected
- procedure GetVersion(); virtual;
- procedure GetCCVersion(); virtual;
+ procedure GetVersion; virtual;
+ procedure GetCCVersion; virtual;
public
- constructor Create();
- destructor Destroy(); override;
+ constructor Create;
+ destructor Destroy; override;
//:$ Returns the formatted version information
//:: If Build is False, the return value will not include the
//:: OS' Build number.
function FormatVersion(Build: Boolean = True): String;
+
//:$ Contains the Common Controls version
property ComCtlVersion: TX2CCVersion read FCCVersion;
+ //:$ Checks if the OS supports User Account Control
+ property SupportsUAC: Boolean read GetSupportsUAC;
+
//:$ Checks if the application uses an XP manifest
//:: If present, Common Controls version 6 or higher is available.
property XPManifest: Boolean read GetXPManifest;
@@ -84,8 +105,30 @@ type
property VersionEx: TX2OSVersionEx read FVersionEx;
end;
- function OS(): TX2OS;
+ function OS: TX2OS;
+
+const
+ { NT Product types: used by dwProductType field }
+ VER_NT_WORKSTATION = $0000001;
+ VER_NT_DOMAIN_CONTROLLER = $0000002;
+ VER_NT_SERVER = $0000003;
+
+ { NT product suite mask values: used by wSuiteMask field }
+ VER_SUITE_SMALLBUSINESS = $00000001;
+ VER_SUITE_ENTERPRISE = $00000002;
+ VER_SUITE_BACKOFFICE = $00000004;
+ VER_SUITE_COMMUNICATIONS = $00000008;
+ VER_SUITE_TERMINAL = $00000010;
+ VER_SUITE_SMALLBUSINESS_RESTRICTED = $00000020;
+ VER_SUITE_EMBEDDEDNT = $00000040;
+ VER_SUITE_DATACENTER = $00000080;
+ VER_SUITE_SINGLEUSERTS = $00000100;
+ VER_SUITE_PERSONAL = $00000200;
+ VER_SUITE_SERVERAPPLIANCE = $00000400;
+ VER_SUITE_BLADE = VER_SUITE_SERVERAPPLIANCE;
+
+
implementation
uses
SysUtils;
@@ -115,7 +158,7 @@ var
function OS;
begin
if not Assigned(GOS) then
- GOS := TX2OS.Create();
+ GOS := TX2OS.Create;
Result := GOS;
end;
@@ -128,9 +171,9 @@ constructor TX2OS.Create;
begin
inherited;
- FVersionEx := TX2OSVersionEx.Create();
- GetVersion();
- GetCCVersion();
+ FVersionEx := TX2OSVersionEx.Create;
+ GetVersion;
+ GetCCVersion;
end;
destructor TX2OS.Destroy;
@@ -146,73 +189,102 @@ end;
========================================}
procedure TX2OS.GetVersion;
var
- pVersion: TOSVersionInfo;
+ versionInfo: TOSVersionInfoEx;
+ versionInfoPtr: POSVersionInfo;
begin
FVersion := osUnknown;
{ Get version information }
- pVersion.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
- GetVersionEx(pVersion);
+ FillChar(versionInfo, SizeOf(versionInfo), 0);
+ versionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfoEx);
+
+ versionInfoPtr := @versionInfo;
+
+ if not GetVersionEx(versionInfoPtr^) then
+ begin
+ { Maybe this is an older Windows version, not supporting the Ex fields }
+ versionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
+ if not GetVersionEx(versionInfoPtr^) then
+ RaiseLastOSError;
+ end;
with FVersionEx do
begin
{ No Kylix support yet, sorry! }
- RawInfo := pVersion;
+ RawInfo := versionInfo;
Name := 'Windows';
- case pVersion.dwMajorVersion of
+ case versionInfo.dwMajorVersion of
3: { Windows NT 3.51 }
- FVersion := osWinNT3;
+ FVersion := osWinNT3;
4: { Windows 95/98/ME/NT 4 }
- case pVersion.dwMinorVersion of
+ case versionInfo.dwMinorVersion of
0: { Windows 95/NT 4 }
- case pVersion.dwPlatformId of
+ case versionInfo.dwPlatformId of
VER_PLATFORM_WIN32_NT: { Windows NT 4 }
- FVersion := osWinNT4;
+ FVersion := osWinNT4;
VER_PLATFORM_WIN32_WINDOWS: { Windows 95 }
- FVersion := osWin95;
+ FVersion := osWin95;
end;
10: { Windows 98 }
- FVersion := osWin98;
+ FVersion := osWin98;
90: { Windows ME }
- FVersion := osWinME;
+ FVersion := osWinME;
end;
5: { Windows 2000/XP/2003 }
- case pVersion.dwMinorVersion of
+ case versionInfo.dwMinorVersion of
0: { Windows 2000 }
- FVersion := osWin2K;
+ FVersion := osWin2K;
1: { Windows XP }
- FVersion := osWinXP;
+ FVersion := osWinXP;
2: { Windows Server 2003 }
- FVersion := osWin2003;
+ FVersion := osWin2003;
+ end;
+ 6: { Windows Vista/Server 2008/7 }
+ if versionInfo.wProductType = VER_NT_WORKSTATION then
+ begin
+ case versionInfo.dwMinorVersion of
+ 0: { Windows Vista }
+ FVersion := osWinVista;
+ 1: { Windows 7 }
+ FVersion := osWin7;
+ end;
+ end else
+ begin
+ case versionInfo.dwMinorVersion of
+ 0, { Windows Server 2008 }
+ 1: { Windows Server 2008 R2 }
+ FVersion := osWinServer2008;
+ end;
end;
- 6: { Windows Vista/Server 2008 }
- FVersion := osWinVista;
end;
case Version of
- osWin95: VersionString := '95';
- osWin98: VersionString := '98';
- osWinME: VersionString := 'ME';
- osWinNT3: VersionString := 'NT 3.51';
- osWinNT4: VersionString := 'NT 4';
- osWin2K: VersionString := '2000';
- osWinXP: VersionString := 'XP';
- osWin2003: VersionString := 'Server 2003';
- osWinVista: VersionString := 'Vista';
- osUnknown: VersionString := Format('%d.%d', [pVersion.dwMajorVersion,
- pVersion.dwMinorVersion]);
+ osWin95: VersionString := '95';
+ osWin98: VersionString := '98';
+ osWinME: VersionString := 'ME';
+ osWinNT3: VersionString := 'NT 3.51';
+ osWinNT4: VersionString := 'NT 4';
+ osWin2K: VersionString := '2000';
+ osWinXP: VersionString := 'XP';
+ osWin2003: VersionString := 'Server 2003';
+ osWinVista: VersionString := 'Vista';
+ osWin7: VersionString := '7';
+ osWinServer2008: VersionString := 'Server 2008';
+ else
+ VersionString := Format('%d.%d', [versionInfo.dwMajorVersion,
+ versionInfo.dwMinorVersion]);
end;
- if StrLen(pVersion.szCSDVersion) > 0 then
- VersionString := VersionString + ' ' + pVersion.szCSDVersion;
+ if StrLen(versionInfo.szCSDVersion) > 0 then
+ VersionString := VersionString + ' ' + versionInfo.szCSDVersion;
- case pVersion.dwPlatformId of
+ case versionInfo.dwPlatformId of
VER_PLATFORM_WIN32_NT:
- Build := pVersion.dwBuildNumber;
+ Build := versionInfo.dwBuildNumber;
VER_PLATFORM_WIN32_WINDOWS:
- Build := LoWord(pVersion.dwBuildNumber);
+ Build := LoWord(versionInfo.dwBuildNumber);
end;
end;
end;
@@ -234,7 +306,7 @@ begin
begin
FillChar(viVersion, SizeOf(viVersion), #0);
viVersion.cbSize := SizeOf(viVersion);
-
+
DllGetVersion(@viVersion);
with FCCVersion do
@@ -249,27 +321,34 @@ begin
end;
end;
-function TX2OS.GetXPManifest;
-begin
- Result := (FCCVersion.Major >= 6);
-end;
-
function TX2OS.FormatVersion;
var
sBuild: String;
begin
- sBuild := '';
+ sBuild := '';
if Build then
- sBuild := Format(' build %d', [FVersionEx.Build]);
+ sBuild := Format(' build %d', [FVersionEx.Build]);
with FVersionEx do
Result := Format('%s %s%s', [Name, VersionString, sBuild]);
end;
+function TX2OS.GetXPManifest;
+begin
+ Result := (FCCVersion.Major >= 6);
+end;
+
+
+function TX2OS.GetSupportsUAC: Boolean;
+begin
+ Result := (FVersionEx.RawInfo.dwMajorVersion >= 6);
+end;
+
+
initialization
finalization
FreeAndNil(GOS);