Get the origin data source with System.Windows.Clipboard? - c#

I am using System.Windows.Clipboard to copy some text, and I would like to know is there a chance to get the origin source,
e.g. a the file where I copyied it from, or the website, folder.... ?
Thanks

The Win32 GetClipboardOwner() function can be used to get the Handle of the Window that last placed data into the Clipboard.
You can then pass the returned handle to GetWindowThreadProcessId() to get the Process ID and Thread ID of that Window.
Back in .Net territory, you can use the Process ID as the parameter to pass to the System.Diagnostics.Process.GetProcessById() method, to retrieve the information needed.
Note that you have to build a 64bit application to fully inspect a 64bit
process. If your project has the Prefer 32-bit option set, some
information will not be available.
See also:
How to get the process ID or name of the application that has updated the clipboard?
Windows API declarations. The overloaded GetClipboardOwnerProcessID() wrapper method returns the ProcessID of the ClipBoard Owner and, optionally, its Thread ID.
public class WinApi
{
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardOwner();
//The return value is the identifier of the thread that created the window.
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
//Wrapper used to return the Window processId
public static uint GetClipboardOwnerProcessID()
{
uint processId = 0;
GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
//Overload that returns a reference to the Thread ID
public static uint GetClipboardOwnerProcessID(ref uint threadId)
{
uint processId = 0;
threadId = GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
}
The wrapper can be called like this, if you just need the Process Id:
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID();
Or this way, if you also need the Thread Id:
uint ClipBoadrOwnerThreadId = 0;
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID(ref ClipBoadrOwnerThreadId);
Pass the returned value to the Process.GetProcessById() method:
Process ClipBoardOwnerProcess = Process.GetProcessById((int)WinApi.GetClipboardOwnerProcessID());
string ProcessName = ClipBoardOwnerProcess.ProcessName;
string ProcessWindowTitle = ClipBoardOwnerProcess.MainWindowTitle;
string ProcessFileName = ClipBoardOwnerProcess.MainModule.FileName;
//(...)
If you copy some text from your browser, the ProcessName will be the name of your browser and the ProcessFileName the path to its executable.

Related

Calling unmanaged c++ code from managed C# code to generate offline domain join blob

I am writing a piece of program that generates Offline Domain Join blob and saves it for future use. This action can be done using command prompt. Below is a sample command that will generate the mentioned file and save it on D drive:
D:\djoin.exe /REUSE /PROVISION /DOMAIN MyDomain.MyCompany.com /MACHINE "user1-pc" /SAVEFILE blob.txt
More information: Offline Domain Join (Djoin.exe) Step-by-Step Guide
Now, I want to add a method to my program (written with C#) to does this functionality for me.
One of the problems here is, the API that Microsoft has provided is a C++ API. I have tried to use the API in managed code using PInvoke. Below is the code I have written.
using System;
using System.Runtime.InteropServices;
namespace TestBlob
{
public class Program
{
static void Main(string[] args)
{
String domain = "MyDomain.MyCompany.com";
String machineName = "user1-pc";
String machineAccoutOU = null;
String dcName = "MyDomain";
uint options = 1;
IntPtr provisionBinData = IntPtr.Zero;
IntPtr provisionBinDataSize = IntPtr.Zero;
string blob = string.Empty;
IntPtr pProvisionTextData = Marshal.StringToHGlobalUni(blob);
uint status = ODJNativeMethods.NetProvisionComputerAccount(domain, machineName, machineAccoutOU, dcName, options, ref provisionBinData, ref provisionBinDataSize, ref pProvisionTextData);
Console.WriteLine(status);
Console.WriteLine(Marshal.PtrToStringUni(pProvisionTextData));
Console.Read();
}
}
public static class NativeMethods
{
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint NetProvisionComputerAccount([In] String lpDomain,
[In] String lpMachineName,
[In] String lpMachineAccountOU,
[In] String lpDcName,
[In] uint dwOptions,
[In] [Out] ref IntPtr pProvisionBinData,
[In] [Out] ref IntPtr pdwProvisionBinDataSize,
[In] [Out] ref IntPtr pProvisionTextData);
}
}
When I run the application, it always returns 87 (shows on console), which after a quick search turns out to be an error message: The parameter is invalid.
What am I doing wrong here? Are my PInvoke types not the correct ones corresponding to native language API?
The 3 last parameters are declared out, which means you must not initialize them, but pass correct pointer so the function can allocate things for you.
Also, from what I understand reading the function doc, the binary one and the string one are mutually exclusive, so let's say you want to get back the binary one, then you can define the API like this ([in] are usually implicit):
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
public static extern int NetProvisionComputerAccount(
string lpDomain,
string lpMachineName,
string lpMachineAccountOU,
string lpDcName,
int dwOptions,
out IntPtr pProvisionBinData,
out int pdwProvisionBinDataSize,
IntPtr pProvisionTextData);
Note the function does not use SetLastError, so don't declare it in the declaration.
And here is how to call it:
string domain = "MyDomain.MyCompany.com";
string machineName = "user1-pc";
string machineAccoutOU = null;
string dcName = "MyDomain";
// I suggest you don't use hardcoded values to be nice with readers
const int NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 1;
int status = NetProvisionComputerAccount(
domain,
machineName,
machineAccoutOU,
dcName,
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT,
out IntPtr binData, // let the system allocate the binary thing
out int binDataSize, // this will be the size of the binary thing
IntPtr.Zero); // we don't use this one here, pass null
I can't test further (I get error 1354 which I suppose is normal in my context).
Note the doc doesn't say anything about deallocating what the function allocates (if it allocates something? there are some rare Windows API that use static buffers they own). I think you're supposed to call NetApiBufferFree on binData once all work is done, but it's just a guess.
It could be the encoding of your text file - if you have saved it as ANSI and consuming it as Unicode, it might not work.
Use these statements corresponding to your above code:
IntPtr pProvisionTextData = Marshal.StringToHGlobalAnsi(blob);
and
[DllImport("user32", CharSet=CharSet::Ansi)]
Hope it helps.

Setting all checkboxes in a listView in another application with winAPI

So I have this code to set all items (or even a single item i stated the code for item number 3) for a listview in another application using sendmessage, I already managed to successfully get the item window handle of the listview and got the item count right but when i use setitemstate the other application gives me error and closes (given that there is another application I have that do this job just fine but i don't have its source code)
this is my code :
if (windowName.Contains("Invite to Room")) {
IntPtr hwndChild0 = FindWindowEx(hWnd, IntPtr.Zero, "SysListView32", "");
int itemCount = SendMessage(hwndChild0, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
LV_ITEM lvItem = new LV_ITEM();
lvItem.Index = 3;
lvItem.SubIndex = 2;
lvItem.TextLength = 50;
lvItem.Mask = LVIF_STATE;
lvItem.State = LVIS_SELECTED;
lvItem.StateMask = LVIS_SELECTED;
SendMessage(hwndChild0, LVM_SETITEMSTATE, 3, IntPtr.Zero);
}
this is my LV_ITEM structure
public struct LV_ITEM
{
public uint Mask;
public int Index;
public int SubIndex;
public int State;
public IntPtr StateMask;
public string Text;
public int TextLength;
public int ImageIndex;
public IntPtr LParam;
}
and this is the declaration of the used (sendmessage and setitemstate)
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);
private const uint LVM_FIRST = 0x1000;
private const uint LVM_SETITEMSTATE = (LVM_FIRST + 43);
any help with this?
Edit :
the application error has a log file that said the following message :
"The thread tried to read from or write to a virtual address for which it doesn't have the appropriate access"
Some messages only use WPARAM, LPARAM and return LRESULT to pass data around, like LVM_GETITEMCOUNT that worked for you.
Other messages use pointers to some data structure to be used or filled, like LVM_SETITEMSTATE. It expects a pointer to pre-filled LV_ITEM structure in LPARAM, while you pass IntPtr.Zero - so you cause an access violation in that other application trying to dereference that zero pointer.
However, you can't just simply pass the pointer to your lvItem, as it wouldn't make any sense in another process. You need to allocate a memory for that structure in the second process, initialize it, send you message and read that memory back (if you expect any response).

Get the PID of a Windows service

Could anyone help me to know how to get the PID of a Windows service?
I need to get the PID in order to run the following command:
Process.Start(new ProcessStartInfo
{
Filename = "cmd.exe",
CreateNoWindow = true,
UseShellExecute = false,
Arguments = string.Format("/c taskkill /pid {0} /f", pidnumber)
});
What the other answers neglect is the fact that a single process can also host multiple, autonomous services. The multiple instances of the svchost.exe process, each hosting a couple of services, is the best example.
So in general, it is absolutely unsafe to try to kill an arbitrary service by killing it's hosting process (I assume that is what you attempt to do, since you refer to taskkill.exe). You might take down several unrelated services in the process.
If you do know that the service's process only hosts the service you care about, than you can choose the strategy as suggested by #M C in his/her answer.
Alternatively, you can also use the ServiceController class to open a handle to your service and then use it (via the ServiceHandle property) to P/Invoke the QueryServiceStatusEx function to find out the Process ID you want to know.
If you need more details, you should clarify what it is that you're actually trying to achieve. It is not clear from your question.
Update Here is some code I ripped out of an existing project that should do what you want, given you have a ServiceController instance. _As said above, use with care!__
[StructLayout(LayoutKind.Sequential)]
internal sealed class SERVICE_STATUS_PROCESS
{
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceType;
[MarshalAs(UnmanagedType.U4)]
public uint dwCurrentState;
[MarshalAs(UnmanagedType.U4)]
public uint dwControlsAccepted;
[MarshalAs(UnmanagedType.U4)]
public uint dwWin32ExitCode;
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceSpecificExitCode;
[MarshalAs(UnmanagedType.U4)]
public uint dwCheckPoint;
[MarshalAs(UnmanagedType.U4)]
public uint dwWaitHint;
[MarshalAs(UnmanagedType.U4)]
public uint dwProcessId;
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceFlags;
}
internal const int ERROR_INSUFFICIENT_BUFFER = 0x7a;
internal const int SC_STATUS_PROCESS_INFO = 0;
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool QueryServiceStatusEx(SafeHandle hService, int infoLevel, IntPtr lpBuffer, uint cbBufSize, out uint pcbBytesNeeded);
public static int GetServiceProcessId(this ServiceController sc)
{
if (sc == null)
throw new ArgumentNullException("sc");
IntPtr zero = IntPtr.Zero;
try
{
UInt32 dwBytesNeeded;
// Call once to figure the size of the output buffer.
QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, 0, out dwBytesNeeded);
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate required buffer and call again.
zero = Marshal.AllocHGlobal((int)dwBytesNeeded);
if (QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, dwBytesNeeded, out dwBytesNeeded))
{
var ssp = new SERVICE_STATUS_PROCESS();
Marshal.PtrToStructure(zero, ssp);
return (int)ssp.dwProcessId;
}
}
}
finally
{
if (zero != IntPtr.Zero)
{
Marshal.FreeHGlobal(zero);
}
}
return -1;
}
Assuming you know the name of the EXE the service uses and there is exactly one of them:
int procID = Process.GetProcessesByName("yourservice")[0].Id;
The method Process.GetProcessesByName("yourservice") returns an Array of Processes with your specified name, so in case you don't know how much of "yourservice.exe" runs simultaneously you might need a foreach loop.
See this answer on a similar question:
Finding out Windows service's running process name
Using a WMI query you can -
Find all services related to a single exe (a single exe can host multiple services):
select Name from Win32_Service where ProcessId = 588
Or, to answer this question, you can get the PID of the process that a service is running in:
select ProcessId from Win32_Service where Name = 'wuauserv'

Send multiple directories to the Recycle Bin with FileSystem.DeleteDirectory

Making a file browser with loads of functions, coming back to fine tune some of my methods to find this:
foreach (ListViewItem item in listView1.SelectedItems)
{
FileSystem.DeleteDirectory(item.SubItems[3].Text,
UIOption.AllDialogs,
RecycleOption.SendToRecycleBin,
UICancelOption.ThrowException);
}
which works great to send a SINGLE directory or file to the recycle bin, but it will prompt for every selected item. Not great for deleting a pile of files and folders.
Any way to achieve this without the excess prompts? Or do I have to delve into SHFILEOPSTRUCT?
Thanks for your help, so far 90% of my questions were already answered here, best website ever.
If you don't want the prompts, you could use Directory.Delete instead of the FileSystem method. This will delete the directory and files and subdirectories (provided you specify that you want it to do so).
This seems to be the only way to do what you have required
Moving the files and directory to the recycle bin without prompt
using System.Runtime.InteropServices;
class Win32ApiUtils
{
// Don't declare a value for the Pack size. If you omit it, the correct value is used when
// marshaling and a single SHFILEOPSTRUCT can be used for both 32-bit and 64-bit operation.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public int wFunc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPWStr)]
public string pTo;
public ushort fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);
const int FO_DELETE = 3;
const int FOF_ALLOWUNDO = 0x40;
const int FOF_NOCONFIRMATION = 0x10; //Don't prompt the user.;
public static int DeleteFilesToRecycleBin(string filename)
{
SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
shf.wFunc = FO_DELETE;
shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
shf.pFrom = filename + "\0"; // <--- this "\0" is critical !!!!!
int result = SHFileOperation(ref shf);
// Any value different from zero is an error to lookup
return result;
}
}
foreach (ListViewItem item in listView1.SelectedItems)
{
int result = Win32ApiUtils.DeleteFilesToRecycleBin(item.SubItems[3].Text);
if(result != 0) ...... // ??? throw ??? message to user and contine ???
}
-- Warning --
This code needs to be tested. I have found the layout of SHFILEOPSTRUCT on PInvoke site and on that link there are some notes about the declaration of the strings used.
Well. tested on my Win7 64bit with a single directory to delete. Works like a charm....

How can I detect if a thread has windows handles?

How can I programmatically detect if a thread has windows handles on it for a given process?
spy++ gives me this information but I need to do it programmatically.
I need to do this in C#, however the .net diagnostics libs don't give me this information. I imagine spy++ is using some windows api call that I don't know about.
I have access to the code of the system I'm trying to debug. I want to embed some code called by a timer periodically that will detect how many thread contain windows handles and log this info.
thanks
I believe you can use win api functions: EnumWindowsProc to iterate through window handles and GetWindowThreadProcessId to get the thread id and process id associated with given window handle
Please check if an example below would work for you:
this code iterates through processes and threads using System.Diagnostics; for each thread ID I'm calling GetWindowHandlesForThread function (see code below)
foreach (Process procesInfo in Process.GetProcesses())
{
Console.WriteLine("process {0} {1:x}", procesInfo.ProcessName, procesInfo.Id);
foreach (ProcessThread threadInfo in procesInfo.Threads)
{
Console.WriteLine("\tthread {0:x}", threadInfo.Id);
IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id);
if (windows != null && windows.Length > 0)
foreach (IntPtr hWnd in windows)
Console.WriteLine("\t\twindow {0:x}", hWnd.ToInt32());
}
}
GetWindowHandlesForThread implementation:
private IntPtr[] GetWindowHandlesForThread(int threadHandle)
{
_results.Clear();
EnumWindows(WindowEnum, threadHandle);
return _results.ToArray();
}
private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
[DllImport("user32.Dll")]
private static extern int EnumWindows(EnumWindowsProc x, int y);
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
private List<IntPtr> _results = new List<IntPtr>();
private int WindowEnum(IntPtr hWnd, int lParam)
{
int processID = 0;
int threadID = GetWindowThreadProcessId(hWnd, out processID);
if (threadID == lParam) _results.Add(hWnd);
return 1;
}
result of the code above should dump into console smth like this:
...
process chrome b70
thread b78
window 2d04c8
window 10354
...
thread bf8
thread c04
...

Categories