|
|
Hi,
I am starting a process using P/Invoke of
CreateProcessWithTokenW
Eyerthing works fine, e.g. when I run a simple command like "ipconfig > test.log" then the file contains the output of the file. Process Explorer also tells me that the process runs as the user I impersonated to...
When I start a GUI application, however, I run into a strange problem: The process starts without error and the icon of the process pops up in the taskbar (e.g. the notepad icon), but I cannot "look at the GUI". Its a bit hard to explain, but I can click on the taskbar icon and a frame of the Window appears, but is has no "contents". I can click on the close icon and the windows closes.
Please refer to this screenshot. It also shows that this is not a "move to screen" issue
ScreenShot
I'd be happy for all sorts of advice.
Cheers,
Guido
|
|
|
|
|
Without seeing the code you used to call this and setup the parameters you passed in, it's pretty much impossible to tell you what's wrong.
|
|
|
|
|
Hi,
mea culpa.
The code is as follows
public class MyRunAs
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool CreateProcessWithTokenW(
IntPtr dupeTokenHandle,
LogonFlags dwLogonFlags,
string applicationName,
string commandLine,
CreationFlags dwCreationFlags,
IntPtr environment,
string currentDirectory,
ref STARTUPINFO sui,
out PROCESS_INFORMATION processInfo);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool DuplicateTokenEx(IntPtr tokenHandle, int
dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpTokenAttributes, int
SECURITY_IMPERSONATION_LEVEL,
int TOKEN_TYPE, ref IntPtr dupeTokenHandle);
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateEnvironmentBlock(
ref IntPtr lpEnvironment,
IntPtr hToken,
bool bInherit);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CreateProcessAsUser(
IntPtr Token,
[MarshalAs(UnmanagedType.LPTStr)] string ApplicationName,
[MarshalAs(UnmanagedType.LPTStr)] string CommandLine,
ref SECURITY_ATTRIBUTES ProcessAttributes,
ref SECURITY_ATTRIBUTES ThreadAttributes,
bool InheritHandles,
CreationFlags dwCreationFlags,
IntPtr Environment,
[MarshalAs(UnmanagedType.LPTStr)] string CurrentDirectory,
ref STARTUPINFO StartupInfo,
out PROCESS_INFORMATION ProcessInformation);
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
internal int nLength;
internal int lpSecurityDescriptor;
internal bool bInheritHandle;
}
public enum CreationFlags
{
DefaultErrorMode = 0x04000000,
NewConsole = 0x00000010,
NewProcessGroup = 0x00000200,
SeparateWOWVDM = 0x00000800,
Suspended = 0x00000004,
UnicodeEnvironment = 0x00000400,
ExtendedStartupInfoPresent = 0x00080000
}
public enum LogonFlags
{
WithProfile = 1,
NetCredentialsOnly
}
[StructLayout(LayoutKind.Sequential)]
internal struct STARTUPINFO
{
internal int cb;
[MarshalAs(UnmanagedType.LPTStr)]
internal string lpReserved;
[MarshalAs(UnmanagedType.LPTStr)]
internal string lpDesktop;
[MarshalAs(UnmanagedType.LPTStr)]
internal string lpTitle;
internal int dwX;
internal int dwY;
internal int dwXSize;
internal int dwYSize;
internal int dwXCountChars;
internal int dwYCountChars;
internal int dwFillAttribute;
internal int dwFlags;
internal short wShowWindow;
internal short cbReserved2;
internal IntPtr lpReserved2;
internal IntPtr hStdInput;
internal IntPtr hStdOutput;
internal IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
internal IntPtr hProcess;
internal IntPtr hThread;
internal int dwProcessId;
internal int dwThreadId;
}
const int SecurityAnonymous = 0;
const int SecurityIdentification = 1;
const int SecurityImpersonation = 2;
const int SecurityDelegation = 3;
const int TokenPrimary = 1;
const int TokenImpersonation = 2;
const int LOGON_WITH_PROFILE = 1;
const int LOGON_NETCREDENTIALS_ONLY = 2;
private const int TOKEN_QUERY = 0x0008;
private const int TOKEN_DUPLICATE = 0x0002;
private const int TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int TOKEN_IMPERSONATE = 0x0004;
private const int TOKEN_QUERY_SOURCE = 0x0010;
private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
private const int TOKEN_ADJUST_GROUPS = 0x0040;
private const int TOKEN_ADJUST_DEFAULT = 0x0080;
private const int TOKEN_ADJUST_SESSIONID = 0x0100;
private const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private const int TOKEN_ALL_ACCESS =
STANDARD_RIGHTS_REQUIRED |
TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE |
TOKEN_IMPERSONATE |
TOKEN_QUERY |
TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT |
TOKEN_ADJUST_SESSIONID;
public static bool CreateTokenChild()
{
try {
STARTUPINFO startInfo = new STARTUPINFO();
startInfo.cb = Marshal.SizeOf(startInfo);
IntPtr dupeTokenHandle = IntPtr.Zero;
WindowsIdentity id = new WindowsIdentity("user@domain");
IntPtr tokenHandle = id.Token;
SECURITY_ATTRIBUTES lpTokenAttributes = new SECURITY_ATTRIBUTES();
lpTokenAttributes.nLength = Marshal.SizeOf(lpTokenAttributes);
bool retVal = DuplicateTokenEx(
tokenHandle,
TOKEN_ALL_ACCESS,
ref lpTokenAttributes,
SecurityImpersonation,
TokenPrimary,
ref dupeTokenHandle);
if (!retVal)
{
int winError = Marshal.GetLastWin32Error();
File.AppendAllText("C:\\tmp\\out.log", DateTime.Now.ToLongTimeString() + " " + winError + Environment.NewLine);
return false;
}
string app = @"c:\Windows\System32\notepad.exe";
string cmd = null;
string spath = @"C:\";
IntPtr env = GetEnvironmentBlock(dupeTokenHandle);
PROCESS_INFORMATION processInfo;
bool ret = CreateProcessWithTokenW(
dupeTokenHandle,
LogonFlags.WithProfile,
app,
cmd,
CreationFlags.UnicodeEnvironment,
env,
spath,
ref startInfo,
out processInfo);
if (!ret)
{
int winError = Marshal.GetLastWin32Error();
File.AppendAllText("C:\\tmp\\out.log", DateTime.Now.ToLongTimeString() + " error: " + winError + Environment.NewLine);
return false;
}
else
{
File.AppendAllText("C:\\tmp\\out.log", DateTime.Now.ToLongTimeString() + " success " + Environment.NewLine);
}
}
catch (Exception e)
{
return false ;
}
finally
{
}
return true;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
var envBlock = IntPtr.Zero;
if (!CreateEnvironmentBlock(ref envBlock, token, false))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateEnvironmentBlock failed");
}
return envBlock;
}
}
Sorry for that lack of information. I somehow thought, its not code related but rather some security issue. I compared two processes with process explorer and the only thing that I realized was that "normal" processes have security attribute "NT AUTHORITY\INTERACTIVE" which the process I am starting has not ...
I also tried CreateProcessAsUser and it shows exactly the same behavior ...
Cheers and thanks.
Guido
|
|
|
|
|
I can't do impersonation here at work. I have to wait until I get home to examine the code.
|
|
|
|
|
gobbo-dd wrote: I compared two processes with process explorer and the only thing that I realized was that "normal" processes have security attribute "NT AUTHORITY\INTERACTIVE" which the process I am starting has not ... There's a nice hint.
If you ever created a Windows Service, you'll remember that you cannot access the desktop or interact with it if that flag is not set.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Yeah, I know. But according to the documentation here CreateProcessWithTokenW it says:
Quote: If the lpDesktop member is NULL or an empty string, the new process inherits the desktop and window station of its parent process. The function adds permission for the specified user account to the inherited window station and desktop. Otherwise, if this member specifies a desktop, it is the responsibility of the application to add permission for the specified user account to the specified window station and desktop, even for WinSta0\Default.
So how should I set this interactive flag if the function is supposed to take care of it ...
|
|
|
|
|
If it is running as another user, it may need to run on its own desktop.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
I cant believe. First of all, there is this "runas" functionality which very well creates a GUI on the calling user's desktop. And secondly, the documentation says that it should work ...
Anyone has an idea why it doesn't? Or how I could assign the proper permissions to the process, I create ?
Thanks
Guido
|
|
|
|
|
gobbo-dd wrote: And secondly, the documentation says that it should work ... Where does it say that? On MSDN it states that there are two alternatives if the call fails with an ERROR_PRIVILEGE_NOT_HELD.
gobbo-dd wrote: Anyone has an idea why it doesn't? ..because (AFAIK) you can only interact with windows-objects that belong to your desktop-object; that is user-specific, as it is created after logon. You could create a new desktop-object and show it there.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Hi,
it does not fail with "privilege not held" (i had that error 1342 and fixed it ...)
it even shows an icon in the taskbar (as you can see on the screenshot) - which indicates imho that the process is allowed to create "something" on the desktop....
it simply doesn't show the "full window"...
and yes, the documentation says:
Quote: If the lpDesktop member is NULL or an empty string, the new process inherits the desktop and window station of its parent process. The function adds permission for the specified user account to the inherited window station and desktop.
Quote: ..because (AFAIK) you can only interact with windows-objects that belong to your desktop-object; that is user-specific, as it is created after logon. You could create a new desktop-object and show it there.
Again - that is not what the documentation says "... process inherits ... "
Cheers
Guido
|
|
|
|
|
gobbo-dd wrote: which indicates imho that the process is allowed to create "something" on the desktop.... Which is what a service may do also, but it will not be showing a window unless it is marked as 'interactive'.
Found this[^] on SO, where the last answer suggests a possible solution
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
AWESOME
IntPtr hWinSta = GetProcessWindowStation();
WindowStationSecurity ws = new WindowStationSecurity(hWinSta, AccessControlSections.Access);
ws.AddAccessRule(new WindowStationAccessRule("user@domain", WindowStationRights.AllAccess, AccessControlType.Allow));
ws.AcceptChanges();
IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId());
DesktopSecurity ds = new DesktopSecurity(hDesk, AccessControlSections.Access);
ds.AddAccessRule(new DesktopAccessRule("user@domain", DesktopRights.AllAccess, AccessControlType.Allow));
ds.AcceptChanges();
Did the trick. Thank you veeery much for pointing me to this.
Cheers
Guido
|
|
|
|
|
Thank you, for sharing your answer - bookmarked it for future reference
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Congratulations on a solution!
I was just looking at this again last night and didn't get anywhere.
|
|
|
|
|
I have a panel of size width=150 pixel,height=100 pixel, backcolor=green with autoscroll is true. I also have a picturebox within that panel which contains a picture of size width=500 pixel, height=200 pixel. I also have a button, on clicking that button picturebox image rotates 90 degree clockwise. and according to that scrollbar values should change. but it is not.
Here is my code
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim h, w
PictureBox1.Image.RotateFlip(RotateFlipType.Rotate90FlipNone)
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
h = PictureBox1.Image.Height
w = PictureBox1.Image.Width
Panel1.HorizontalScroll.Maximum = 0
Panel1.HorizontalScroll.Visible = False
Panel1.VerticalScroll.Maximum = 0
Panel1.VerticalScroll.Visible = False
Panel1.AutoScroll = False
Panel1.HorizontalScroll.Minimum = 0
Panel1.HorizontalScroll.Maximum = w - 1
Panel1.HorizontalScroll.Visible = True
Panel1.HorizontalScroll.Value = 0
Panel1.VerticalScroll.Minimum = 0
Panel1.VerticalScroll.Maximum = h - 1
Panel1.VerticalScroll.Visible = True
Panel1.VerticalScroll.Value = 0
Panel1.AutoScroll = True
PictureBox1.Left = 0
PictureBox1.Top = 0
Panel1.Invalidate()
End Sub
My problem is: On form load both the scrollbars working perfectly. But after clicking the button picturebox rotates to 90 degrees, so picturebox's images's width and height values interchanges, so the scrollbars maximum values. But its not. After clicking the button first time vscrollbar does not go down after a certain value. This thing happens on odd number of clicking the button. On even number of clicking it works perfectly.
This odd and even number problem also interchanges when the original picturebox image dimension interchanges. i.e. when picturebox image width=200, height=500, then hscrollbar creates the problem on odd number of button clicking.
|
|
|
|
|
The PictureBox is not perfect and if you turn on the borders you should be able to see that it does not resize itself correctly after the image has been rotated. In theory PictureBox.Refresh() or PictureBox.Invalidate() should sort out any problems but in my experience those methods do not help and the picturebox does not seem to know that the image has rotated. What I found helpful was to simply reassign the rotated image to the PictureBox.Image property.
The strange alternate click issue with the Panel's scrollbars tends to happen if you muck about with the scroll properties. I came to the conclusion that Microsoft never meant us to change them!
Alan.
|
|
|
|
|
Hello
Hope it's ok to post this here - it's a general programming query but maybe it should be in "Database"? Anyway...
I've never really understood database connection pooling...
If I want to make a lot of separate database updates I could take one of two approaches:
Do
run some code to prepare SQL command
Open DB connection
Run SQL command
Close DB connection
Loop Or
Open DB connection
Do
run some code to prepare SQL command
Run SQL command
Loop
Close DB connection
Which is the better approach?
Thanks...
|
|
|
|
|
Those aren't separate database-updates. Combine them, execute a single IDbCommand and wrap it in a transaction.
As for the loop; There's a good explanation on MSDN[^].
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Well, I'm not sure to combine a number of statements of the form
insert into table (field1, field2,...) values (?,?,...) on duplicate key update field2 = ?, ... with associated command parameters into one.... (MySQL)
I guess I need to look into using transactions more....
thanks
|
|
|
|
|
A_Griffin wrote: Well, I'm not sure to combine a number of statements of the form In case of doubt, verify.
INSERT statements that use VALUES syntax can insert multiple rows. To do this, include multiple lists of column values, each enclosed within parentheses and separated by commas. I don't work with MySQL a lot, but in SQLite there is a very noticeable slowdown if you insert each record separately.
It looks a lot like a bulk-insert. If you don't want to import it in a single go, I'd still recommend doing it at least in batches.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Yes, for simple inserts MySQL can use bulk inserts as you suggest, but when combined with the "on duplicate key update" option then, as far as I know, this cannot be done.
|
|
|
|
|
|
Yes, thanks - it says nothing there about how (or even if one can) combine multiple such statements into one.
And why, out of interest, would you never use it?
|
|
|
|
|
A_Griffin wrote: Yes, thanks - it says nothing there about how (or even if one can) combine multiple such statements into one. It does, even comes with an example - and a note it might be unsafe.
A_Griffin wrote: And why, out of interest, would you never use it? Because I could not longer guarantee the integrity of the database, which is rather important to me.
What is your key? Let's take a VARCHAR(10) as an example key. There's already an entry "test" in your DB, and the same key occurs in your import-set 15 times. Which of those would you reckon to be the correct one? Which one do you think will be inserted? That may lead to some inconsistencies in a replication-scenario.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|