SetWindowLong & SetLayeredWindowAttributes doesnt work on different user C#

1.2k views Asked by At

The goal is

I want to change the opacity of some application (target) running on win xp.

The situation

  • I logged in win xp as AD (active dir) user account (some-domain\username)
  • most of the target applications are run as local user or another ad user

The problem and my Question

SetWindowLong & SetLayeredWindowAttributes doesn't work on target application that run as other user account. But it works on target application that runs under the same user account (logged user account)

How to change other app's window opacity that run as different user account?

Illustration app

It's a win form app (let's call it OpaciToggler.exe). I have 2 buttons (btnRunSomething and btnHideThatThing) and a textbox (txtPid). As simple as that.

When I click the btnRunSomething, it run an .exe as different user. All the details is in the app.config. In this case I run this application (OpaciToggler.exe, from the debug/bin) as different user (localComputer\user1)

The txtPid is for manual pid input. Usually I open the task manager (win > run > taskmgr) and find the pid (under the process tab) of any application (target) I want to test with, then type it here.

When I click the btnHideThatThing, it'll toggle the opacity of the target application (whose pid in the txtPid)

Code C#

This is how I call the target app.

    private void btnRunSomething_Click(object sender, EventArgs e)
    {
        System.Diagnostics.Process p = new System.Diagnostics.Process();

        p.StartInfo.Domain = ConfigurationManager.AppSettings["StartInfoDomain"]; 
        p.StartInfo.UserName = ConfigurationManager.AppSettings["StartInfoUserName"]; 

        p.StartInfo.FileName = ConfigurationManager.AppSettings["StartInfoFileName"];
        p.StartInfo.Arguments = ConfigurationManager.AppSettings["StartInfoArguments"];

        System.String rawPassword = ConfigurationManager.AppSettings["StartInfoPassword"];
        System.Security.SecureString encPassword = new System.Security.SecureString();

        foreach (System.Char c in rawPassword)
        {
            encPassword.AppendChar(c);
        }

        p.StartInfo.Password = encPassword;
        p.StartInfo.UseShellExecute = false;

        p.Start();
    }

This is how I try to call the opacity thing

    private void btnHideThatThing_Click(object sender, EventArgs e)
    {
        // manual input pid 
        int pid = int.Parse(txtPid.Text);
        IntPtr mwh = Process.GetProcessById(pid).MainWindowHandle;
        doHideThing(mwh);  

        // doHideThing(Process.GetProcessById(int.Parse(txtPid.Text)).MainWindowHandle);
    }

The hide method

    private void doHideThing(IntPtr hndl)
    {
        SetWindowLong(hndl, GWL_EXSTYLE, GetWindowLong(hndl, GWL_EXSTYLE) ^ WS_EX_LAYERED).ToString();
        // _whatNow = Marshal.GetLastWin32Error();

        SetLayeredWindowAttributes(hndl, 0, (255 * 20) / 100, LWA_ALPHA).ToString();
        // _whatNow = Marshal.GetLastWin32Error();

        RedrawWindow(hndl, IntPtr.Zero, IntPtr.Zero,
            RedrawWindowFlags.Erase | RedrawWindowFlags.Invalidate |
            RedrawWindowFlags.Frame | RedrawWindowFlags.AllChildren);            
    }

other codes

    private const int GWL_EXSTYLE = -20;
    private const int GWL_STYLE = -16;
    private const int WS_EX_LAYERED = 0x80000;
    private const int LWA_ALPHA = 0x2;
    private const int LWA_COLORKEY = 0x1;

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);

RedrawWindowFlags enum (I don't remember where I get this)

[Flags()]
enum RedrawWindowFlags : uint
{
    /// <summary>
    /// Invalidates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
    /// You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_INVALIDATE invalidates the entire window.
    /// </summary>
    Invalidate = 0x1,

    /// <summary>Causes the OS to post a WM_PAINT message to the window regardless of whether a portion of the window is invalid.</summary>
    InternalPaint = 0x2,

    /// <summary>
    /// Causes the window to receive a WM_ERASEBKGND message when the window is repainted.
    /// Specify this value in combination with the RDW_INVALIDATE value; otherwise, RDW_ERASE has no effect.
    /// </summary>
    Erase = 0x4,

    /// <summary>
    /// Validates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
    /// You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_VALIDATE validates the entire window.
    /// This value does not affect internal WM_PAINT messages.
    /// </summary>
    Validate = 0x8,

    NoInternalPaint = 0x10,

    /// <summary>Suppresses any pending WM_ERASEBKGND messages.</summary>
    NoErase = 0x20,

    /// <summary>Excludes child windows, if any, from the repainting operation.</summary>
    NoChildren = 0x40,

    /// <summary>Includes child windows, if any, in the repainting operation.</summary>
    AllChildren = 0x80,

    /// <summary>Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND and WM_PAINT messages before the RedrawWindow returns, if necessary.</summary>
    UpdateNow = 0x100,

    /// <summary>
    /// Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND messages before RedrawWindow returns, if necessary.
    /// The affected windows receive WM_PAINT messages at the ordinary time.
    /// </summary>
    EraseNow = 0x200,

    Frame = 0x400,

    NoFrame = 0x800
}

Example, Test and Test Result

  • logged in win xp as domainA\ad_user1
  • run the OpaciToggler.exe application (Instance1, run as domainA\ad_user1)
  • Then I click the run something. It opens another OpaciToggler.exe (Instance2), runs as another user account (run as localcomputer\l_user1) OR right click the .exe then click run as..

Instance1

  • pid: 1234
  • run as: domainA\ad_user1
  • txtPid: 5678 (Instance2, doesn't work)
  • txtPid: 1234 (self, works)
  • txtPid: any pid run as the same account (ex: notepad.exe, calc.exe, etc, works)

Instance2

  • pid: 5678
  • run as: localcomputer\l_user1
  • txtPid: 1234 (Instance 1, doesnt work)
  • txtPid: 5678 (self, works)
  • txtPid: any pid run as the same account (localcomputer\l_user1) (ex: notepad.exe, calc.exe, etc, doesn't work!!)

Again, my Question

How to change other app's window opacity that run as different user account?

Thanks and sorry for my bad English :(.

0

There are 0 answers