2012. november 22., csütörtök

Get Current User's SID

...Retrieve the current user's SID?

Author: Yorai Aminov 
Homepage: http://www.shorterpath.com 

Category: System

(******************************************************************************)
(* SPGetSid - Retrieve the current user's SID in text format                  *)
(*                                                                            *)
(* Copyright (c) 2004 Shorter Path Software                                   *)
(* http://www.shorterpath.com                                                 *)
(******************************************************************************)


{
  SID is a data structure of variable length that identifies user, group,
  and computer accounts.
  Every account on a network is issued a unique SID when the account is first created.
  Internal processes in Windows refer to an account's SID
  rather than the account's user or group name.
}


unit SPGetSid;

interface

uses
  Windows, SysUtils;

function GetCurrentUserSid: string;

implementation

const
  HEAP_ZERO_MEMORY = $00000008;
  SID_REVISION     = 1; // Current revision level

type
  PTokenUser = ^TTokenUser;
  TTokenUser = packed record
    User: TSidAndAttributes;
  end;

function ConvertSid(Sid: PSID; pszSidText: PChar; var dwBufferLen: DWORD): BOOL;
var
  psia: PSIDIdentifierAuthority;
  dwSubAuthorities: DWORD;
  dwSidRev: DWORD;
  dwCounter: DWORD;
  dwSidSize: DWORD;
begin
  Result := False;

  dwSidRev := SID_REVISION;

  if not IsValidSid(Sid) then Exit;

  psia := GetSidIdentifierAuthority(Sid);

  dwSubAuthorities := GetSidSubAuthorityCount(Sid)^;

  dwSidSize := (15 + 12 + (12 * dwSubAuthorities) + 1) * SizeOf(Char);

  if (dwBufferLen < dwSidSize) then
  begin
    dwBufferLen := dwSidSize;
    SetLastError(ERROR_INSUFFICIENT_BUFFER);
    Exit;
  end;

  StrFmt(pszSidText, 'S-%u-', [dwSidRev]);

  if (psia.Value[0] <> 0) or (psia.Value[1] <> 0) then
    StrFmt(pszSidText + StrLen(pszSidText),
      '0x%.2x%.2x%.2x%.2x%.2x%.2x',
      [psia.Value[0], psia.Value[1], psia.Value[2],
      psia.Value[3], psia.Value[4], psia.Value[5]])
  else
    StrFmt(pszSidText + StrLen(pszSidText),
      '%u',
      [DWORD(psia.Value[5]) +
      DWORD(psia.Value[4] shl 8) +
      DWORD(psia.Value[3] shl 16) +
      DWORD(psia.Value[2] shl 24)]);

  dwSidSize := StrLen(pszSidText);

  for dwCounter := 0 to dwSubAuthorities - 1 do
  begin
    StrFmt(pszSidText + dwSidSize, '-%u',
      [GetSidSubAuthority(Sid, dwCounter)^]);
    dwSidSize := StrLen(pszSidText);
  end;

  Result := True;
end;

function ObtainTextSid(hToken: THandle; pszSid: PChar;
  var dwBufferLen: DWORD): BOOL;
var
  dwReturnLength: DWORD;
  dwTokenUserLength: DWORD;
  tic: TTokenInformationClass;
  ptu: Pointer;
begin
  Result := False;
  dwReturnLength := 0;
  dwTokenUserLength := 0;
  tic := TokenUser;
  ptu := nil;

  if not GetTokenInformation(hToken, tic, ptu, dwTokenUserLength,
    dwReturnLength) then
  begin
    if GetLastError = ERROR_INSUFFICIENT_BUFFER then
    begin
      ptu := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwReturnLength);
      if ptu = nil then Exit;
      dwTokenUserLength := dwReturnLength;
      dwReturnLength    := 0;

      if not GetTokenInformation(hToken, tic, ptu, dwTokenUserLength,
        dwReturnLength) then Exit;
    end 
    else 
      Exit;
  end;

  if not ConvertSid((PTokenUser(ptu).User).Sid, pszSid, dwBufferLen) then Exit;

  if not HeapFree(GetProcessHeap, 0, ptu) then Exit;

  Result := True;
end;

function GetCurrentUserSid: string;
var
  hAccessToken: THandle;
  bSuccess: BOOL;
  dwBufferLen: DWORD;
  szSid: array[0..260] of Char;
begin
  Result := '';

  bSuccess := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True,
    hAccessToken);
  if not bSuccess then
  begin
    if GetLastError = ERROR_NO_TOKEN then
      bSuccess := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY,
        hAccessToken);
  end;
  if bSuccess then
  begin
    ZeroMemory(@szSid, SizeOf(szSid));
    dwBufferLen := SizeOf(szSid);

    if ObtainTextSid(hAccessToken, szSid, dwBufferLen) then
      Result := szSid;
    CloseHandle(hAccessToken);
  end;
end;

end.

2012. november 9., péntek

Use a TPanel as a host for child windows (MDI simulation)


Problem/Question/Abstract:

I was wondering if someone can offer assistance with this application. Basically the application is for configuring our system. At present it is a MDI where child windows are various functions (security, report options, etc.). The number of functions are growing, currently around 15, which means an increase in different child forms and, overall, a growing exe. I would like the child forms to be standalone programs or dlls which can appear in the control program as child windows and also execute by themselves. Only one child form is displayed at a time and always maximised within the parent window. I did see some code about that provided for a dll as a child form, but this would not help as a standalone execution.

Answer:

This is an interesting problem. As it happens it is possible in Win32 to make another processes window appear like a child window in ones own windows. It does not work quite as well as a true child in your own process but takes care about moving the pseudo-child with your menu app.

The general design is this: the main/menu app has a form with menu, perhaps tool and status bars, and a client-aligned panel that will serve as the host for the child windows. It reads the available child apps from INI file or registry key and builds a menu or selection list from this info. On user request it launches the appropriate child app and passes the panels window handle on the commandline. The child app checks the command line, if there are no parameters it rans as designed, if there is a parameter it reads it, removes its border and bordericon, parents itself to the passed window handle and sizes itself to its client area. It also sends a message with *its* window handle to the panels parent (the main app form) to register itself. The main app can close the child with this handle and also resize it when the user resizes the main app.

Main app: has a menu with two entries (OpenMenu, CloseMenu), a toolbar with two buttons attached to the same events as the two menus, a statusbar, a client-aliged panel.

unit MenuApp;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Menus, ExtCtrls, ComCtrls, ToolWin;

const
  UM_CHILDREGISTER = WM_USER + 111;
  UM_CHILDUNREGISTER = WM_USER + 112;

type
  TUmChildRegister = packed record
    msg: Cardinal;
    childwnd: HWND;
    unused: Integer;
    result: Integer;
  end;
  TUmChildUnregister = TUmChildregister;

  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    OpenMenu: TMenuItem;
    StatusBar1: TStatusBar;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    CloseMenu: TMenuItem;
    ToolButton2: TToolButton;
    Panel1: TPanel;
    procedure OpenMenuClick(Sender: TObject);
    procedure CloseMenuClick(Sender: TObject);
    procedure Panel1Resize(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    FChildAppHandle: HWND;
    procedure UMChildRegister(var msg: TUmChildRegister);
      message UM_CHILDREGISTER;
    procedure UMChildUnRegister(var msg: TUmChildUnRegister);
      message UM_CHILDUNREGISTER;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  shellapi;

{$R *.DFM}

procedure TForm1.OpenMenuClick(Sender: TObject);
var
  path, param: string;
begin
  if FChildAppHandle = 0 then
  begin
    path := ExtractFilePath(Application.Exename) + 'childAppProj.exe';
    param := '$' + IntTohex(panel1.handle, 8);
    ShellExecute(handle, 'open', pchar(path), pchar(param), nil, SW_SHOWNORMAL);
  end
  else
    ShowMessage('Child already loaded');
end;

procedure TForm1.CloseMenuClick(Sender: TObject);
begin
  if FChildAppHandle <> 0 then
    SendMessage(FchildApphandle, WM_CLOSE, 0, 0);
end;

procedure TForm1.Panel1Resize(Sender: TObject);
begin
  if FChildAppHandle <> 0 then
    MoveWindow(FchildAppHandle, 0, 0, Panel1.ClientWidth, Panel1.ClientHeight, true);
end;

procedure TForm1.UMChildRegister(var msg: TUmChildRegister);
begin
  FChildAppHandle := msg.childwnd;
end;

procedure TForm1.UMChildUnRegister(var msg: TUmChildUnRegister);
begin
  if FChildAppHandle = msg.childwnd then
    FChildAppHandle := 0;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if FChildAppHandle <> 0 then
    SendMessage(FchildApphandle, WM_CLOSE, 0, 0);
end;

end.

Child app has a couple of edits, two buttons, a memo.

unit ChildApp;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, AppEvnts;

type
  TForm2 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    ApplicationEvents1: TApplicationEvents;
    procedure Button1Click(Sender: TObject);
    procedure ApplicationEvents1Activate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
    FMenuAppWnd: HWND;
    FParentPanelWnd: HWND;
  public
    { Public declarations }
    constructor Create(aOwner: TComponent); override;
    procedure CreateWnd; override;
    procedure DestroyWnd; override;
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

const
  UM_CHILDREGISTER = WM_USER + 111;
  UM_CHILDUNREGISTER = WM_USER + 112;

procedure TForm2.Button1Click(Sender: TObject);
begin
  close;
end;

procedure TForm2.ApplicationEvents1Activate(Sender: TObject);
begin
  if FMenuAppWnd <> 0 then
    SendMessage(FMenuAppWnd, WM_NCACTIVATE, 1, 0);
  memo1.lines.add('Activated');
end;

constructor TForm2.Create(aOwner: TComponent);
begin
  if ParamCount > 0 then
  begin
    FParentPanelWnd := StrToInt(ParamStr(1));
    FMenuAppWnd := Windows.GetParent(FParentPanelWnd);
  end;
  inherited;
  if FParentPanelWnd <> 0 then
  begin
    Borderstyle := bsNone;
    BorderIcons := [];
    {remove taskbar button for the child app}
    SetWindowLong(Application.Handle, GWL_EXSTYLE,
      GetWindowLong(Application.Handle, GWL_EXSTYLE)
      and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
  end;
end;

procedure TForm2.CreateWnd;
var
  r: Trect;
begin
  inherited;
  if FMenuAppWnd <> 0 then
  begin
    SendMessage(FMenuAppWnd, UM_CHILDREGISTER, handle, 0);
    Windows.SetPArent(handle, FParentPanelWnd);
    Windows.GetClientRect(FParentPanelWnd, r);
    SetBounds(r.left, r.top, r.right - r.left, r.bottom - r.top);
  end;
end;

procedure TForm2.DestroyWnd;
begin
  if FMenuAppWnd <> 0 then
    SendMessage(FMenuAppWnd, UM_CHILDUNREGISTER, handle, 0);
  inherited;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  {Closing the main form does not fire DestroyWnd for some reason}
  if FMenuAppWnd <> 0 then
    SendMessage(FMenuAppWnd, UM_CHILDUNREGISTER, handle, 0);
end;

procedure TForm2.FormResize(Sender: TObject);
begin
  memo1.width := clientwidth - memo1.Left - 10;
  memo1.height := clientheight - memo1.Top - 10;
end;

end.

One problem I noted is that sometimes the main applications caption will loose the active look when switching between main and child despite the action taken in the childs Application.OnActivate handler.