How to change the name of a NetworkAdapter in c#? - c#

People claim the following VB script works for changing network adapter names. However I am having a decidedly difficult time trying to convert this to a c# appliaction that can do the same thing. The problem I seem to be facing is that calls to the NetworkInterface.Name is readonly.
Option Explicit
Const NETWORK_CONNECTIONS = &H31&
Dim sOldName= WScript.Arguments(0)
Dim sNewName= WScript.Arguments(1)
Dim objShell, objFolder, colItems, objItem
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(NETWORK_CONNECTIONS)
Set colItems = objFolder.Items
For Each objItem in colItems
If objItem.Name = sOldName Then
objItem.Name =sNewName
End If
Next
I found this which explains it a bit more: http://blogs.technet.com/b/heyscriptingguy/archive/2005/05/11/how-can-i-rename-a-local-area-connection.aspx.
Ok, so there are special folders where the NIC names are stored and you access those folders by binding to the them via the SHELL. How then do you do something like this in c#?

You can change the name of a NIC easily through the registry if you know how the registry structure works.
You will need the NetworkAdapters GUID in order to locate which path to open. To get the network adapter GUID I recommend first querying the WMI "Win32_NetworkAdapter" class. There is a GUID property along with all the other properties needed to identify specific adapters.
You will notice this GUID in the registry path: {4D36E972-E325-11CE-BFC1-08002BE10318}Visit link for information on it:
http://technet.microsoft.com/en-us/library/cc780532(v=ws.10).aspx
string fRegistryKey = string.Format(#"SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{0}\Connection", NIC_GUID);
RegistryKey RegistryKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, #"\\" + Server.Name);
RegistryKey = RegistryKey.OpenSubKey(fRegistryKey, true); //true is for WriteAble.
RegistryKey.SetValue("Name", "<DesiredAdapterName>");
By design the windows UI will not allow for duplicate NIC names. However, you can force duplicate NIC names via the registry. We have done tests, there seem to be nothing critically effected by having duplicate names. Windows seems to still function fine. You just want to be wary about scripting against NIC names if you don’t incorporate anti-duplicate name logic.
To create uniqueness you can use the adapter index property associated with the WMI query.

You can use the System.Management assembly and use this class.
Follow the sample here - http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/727c8766-8189-4ad6-956d-958e52b97c05/

You can also create a VB.NET dll with the functionality you need and reference and call it from your C# code.
Here is a console app demonstrating the code (I tested and it works :)
Option Explicit On
Module Module1
Sub Main()
Const NETWORK_CONNECTIONS = &H31&
Dim sOldName = "Local Area Connection"
Dim sNewName = "Network"
Dim objShell, objFolder, colItems, objItem
objShell = CreateObject("Shell.Application")
objFolder = objShell.Namespace(NETWORK_CONNECTIONS)
colItems = objFolder.Items
For Each objItem In colItems
Console.WriteLine(objItem.Name)
If objItem.Name = sOldName Then
objItem.Name = sNewName
End If
Console.WriteLine(objItem.Name)
Next
End Sub
End Module
It prints out:
Local Area Connection
Network

Related

UWF_Volumes has no entries with CurrentSession=False

since some time now I try to figure out how to correctly setup this new UWF (Unified Write Filter). Unfortunately it seems there is only documentation for Win 8.1 industry (here), not for Win 10. I hope there were no relevant changes since.
I also asked this on the WindowsDevCenter but got no response so far.
Here is my problem:
With the WMI providers I got UWF enabled by now (UWF_Filter.Enable()), but I cannot protect any volume.
Also the volume list looks very strange: There are 4 entrys, everyone is with CurrentSession=True.
The first is for an volume with no drive letter, only a volume id.
The second is for C:
and then there are 2 identical for D: .
Should'nt there normally be 2 entrys per volume, one where CurrentSession is true and one where its false, meaning its the setting applied after reboot?
If I try to execute Protect on the ManagementObject with DriveLetter=C: I get an Access denied exception, I assume because its the object for the current session.
Also if I try uwfmgr.exe Volume Protect C: on the console it simply hangs: no reaction, no error, only a forever blinking cursor. EDIT: it turned out this was a problem caused by another installed software. See also below.
Do I have to enable or disable or do anything else before I can protect volumes?
Thanks in advance,
Sebastian
My system:
Windows 10 IOT Enterprise 2016 LTSB x64
1 SSD 250GB with Boot, C: and D:
Edit:
Here I asked a follow up question with some other details and a workaround. If I use uwfmgr.exe volume protect c: for example, it works and UWF_Volume now suddenly has (the correct) 2 entries for C:, one for the current and one for the next session.
However I want to avoid this, because IMHO it should be solveable by WMI only.
Edit 2: #sommmen
The partition layout is as following: One disk with 4 partitions.
Boot, 500MB
C:/ , 45GB
unknown, 500MB (Boot-Backup I think)
D:/ , ~200GB
PS:
Please could anyone create the tags uwf and uwfmgr? Would be nice :-)
Missing UWF_Volume instances often appeared after reboot in my tests. But if not, you can create them directly using ManagementClass.CreateInstance().
The problem here is that the official docs are not exactly correct. The description of the UWF_Volume.VolumeName property is:
The unique identifier of the volume on the current system. The
VolumeName is the same as the DeviceID property of the Win32_Volume
class for the volume.
from: https://learn.microsoft.com/en-us/windows-hardware/customize/enterprise/uwf-volume#properties
In fact, the DeviceID needs a slight modification, before using it as value for UWF_Volume.VolumeName:
DeviceID.Substring(4).TrimEnd('\\')
So, after removing prefix \\?\ and removing any trailing slashes you can create instances with CurrentSession=false for the specified device.
This also works in Windows 10 Pro without any uwfmgr.exe. Though, officially not recommended/supported.
Also, I was not able to delete instances, yet. So be sure to add only correct values.
Full Example:
// example value
var DeviceId_From_Win32_Volume = #"\\?\Volume{c2eac053-27e3-4f94-b28c-c2c53d5f4fe1}\";
// example value
var myDriveLetter = "C:";
var myDeviceId = DeviceId_From_Win32_Volume.Substring(4).TrimEnd('\\');
var wmiNamespace = "root\\standardcimv2\\embedded";
var className = "UWF_Volume";
var mgmtScope = new ManagementScope {Path = {NamespacePath = wmiNamespace}};
var mgmtPath = new ManagementPath(className);
var mgmtClass = new ManagementClass(mgmtScope, mgmtPath, null);
// prepare the new object
var newObj = mgmtClass.CreateInstance();
newObj.SetPropertyValue("DriveLetter", myDriveLetter);
newObj.SetPropertyValue("VolumeName", myDeviceId);
newObj.SetPropertyValue("CurrentSession", false);
newObj.SetPropertyValue("CommitPending", false);
newObj.SetPropertyValue("BindByDriveLetter", false);
// create the WMI instance
newObj.Put(new PutOptions {Type = PutType.CreateOnly});
I experience the similar issue in that I could not query the UWF_Volume with CurrentSession=False. However, there's one thing I did that seems to "generate" the UWF_Volume management object with CurrentSession=False. I ran "uwfmgr volume protect c:". Unfortunately, in your case running this causes it to hang.
Could you try running uwfmgr in cmd in admin? Also, if you run "uwfmgr get-config", would you be able to get the current setting of the write filter?
Another thing from your description: you said there are two identical volumes for D:, but if you looks closely at the properties, one would be CurrentSession=True, and the other one is CurrentSession=False. According to the documentation, if you want to make change, you must select the management object (UWF_Volume) with CurrentSession=False.
https://learn.microsoft.com/en-us/windows-hardware/customize/enterprise/uwf-volume
(scroll down to powershell script code sample section)
First of all a volume may have several partitions. They will show up as having the same drive label.
e.g.
C:/ //?/{some guid here}
C:/ //?/{some other guid here}
Now this is common for the %systemDrive% because this has the boot partition.
You can use the commands
mountvol
and
Diskpart
List volume
To figure out the right guid for your need (or you can protect both the boot partition and the system partition). Also using wmi you can look at Win32_volume under namespace cimv2 to get some more insight.
The command line util UWFmgr seems to create an UWF_VOLUME wmi instance once you run the protect command. The docs also hint that you need to create an object yourself.
function Set-ProtectVolume($driveLetter, [bool] $enabled) {
# Each volume has two entries in UWF_Volume, one for the current session and one for the next session after a restart
# You can only change the protection status of a drive for the next session
$nextConfig = Get-WMIObject -class UWF_Volume #CommonParams |
where {
$_.DriveLetter -eq "$driveLetter" -and $_.CurrentSession -eq $false
};
# If a volume entry is found for the drive letter, enable or disable protection based on the $enabled parameter
if ($nextConfig) {
Write-Host "Setting drive protection on $driveLetter to $enabled"
if ($Enabled -eq $true) {
$nextConfig.Protect() | Out-Null;
} else {
$nextConfig.Unprotect() | Out-Null;
}
}
=======> (!) im talking about this comment
# If the drive letter does not match a volume, create a new UWF_volume instance
else {
Write-Host "Error: Could not find $driveLetter. Protection is not enabled."
}
}
The docs however do not provide a method of doing this. For now it seems we need to use the command line util till someone has an example using the WMI provider.
To answer my own question: So far I have only a workaround but no real solution.
It is to check if there is an entry with CurrentSession=False and if not invoke the command directly:
ManagementObjectSearcher ms = new ManagementObjectSearcher(_Scope, new ObjectQuery("select * from UWF_Volume where VolumeName = \"" + volId + "\" AND CurrentSession=\"False\""));
ManagementObjectCollection c = ms.Get();
UInt32 res = 1;
foreach (ManagementObject mo in c)
{
// entry found: do it with WMI
res = (UInt32)mo.InvokeMethod(newState ? "Protect" : "Unprotect", new object[] { });
}
if (c.Count == 1 && res == 0)
// message: success
if (c.Count == 0)
{
// no entry found: invoke cmd
ProcessStartInfo info = new ProcessStartInfo("uwfmgr.exe", "volume " + (newState ? "Protect" : "Unprotect") + #" \\?\" + volId);
Process process = new Process();
info.Verb = "runas"; //needs admin
process.StartInfo = info;
process.Start();
process.WaitForExit();
}
This has the side effect that for a split second a command line window will pop up, but nevertheless it works well.

No return value of the PS command "net file" while accessing the powershell remotely with .NET

I've built a Sub to interact with PowerShells on other servers within my domain. I never needed any return values of the PS console so far. As I want to control the Computer Management on my FileShare I need the list of all open file-sessions (net file) as you need the session ID to close it with net file "ID" /close.
This is the modified Sub:
Public Sub remoteMSPowershell(ByVal Script As String)
Dim shellUri As String = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell"
Dim newCred As PSCredential = DirectCast(Nothing, PSCredential)
Dim connectionInfo As WSManConnectionInfo = New WSManConnectionInfo(False, "SVR2012r2-file", 5985, "/wsman", shellUri, newCred)
Using Runspace As Runspace = RunspaceFactory.CreateRunspace(connectionInfo)
Runspace.Open()
Dim pipeline As Pipeline = Runspace.CreatePipeline(Script)
Dim results = pipeline.Invoke()
For Each outputItem As PSObject In results
Console.WriteLine(outputItem.Members("Path").Value)
Console.WriteLine(outputItem.Members("Id").Value)
Next
Console.ReadLine()
End Using
End Sub
While outputItem.BaseObject.ToString() gives you 1:1 the same output as you see on the PS console, I only need the Attributes "Path" and "Id".
If I run this Sub on commands like Get-Command, Get-Service and so on it'll form a column with the values of the specified attribute. However if I run it on net file the script fails with a NullReferenceException. The only thing that I can imagine is that .NET is only capable of filtering to members (attributes) of Cmdlets, but I didn't find any reference on that.
EDIT (24.05.16): It doesn't work for all the NET.exe - commands.. Is there another way to read the return values out or even another method to get the return values using PS or .NET?
EDIT (26.05.16): I've been working on two work-arounds.. Both seem to be a dead end though:
Unify the results from outputItem.BaseObject.ToString() and form Arrays of the "Id" and "Path" column. This won't work as the only indicator to split the rows into attributes are (blancs). You can imagine what happens if the File-Path contains a blanc..
A more clean work-around would be to query the desired attributes already in PowerShell like the answers to this SO question. However none of the given answers works for me as you can't query NET commands. See example:

Active Directory userAccountControl null after otherwise valid search

and thanks for reading.
I have tried many variations of the following code, and all have returned a null value for the userAccountControl property:
DirectoryEntry de = new DirectoryEntry("LDAP://{my server/domain}");
DirectorySearcher ds= new DirectorySearcher(de);
ds.Filter = "(&(objectClass=user)(objectGUID=" + queryGUID + "))";
ds.PropertiesToLoad.Add("userAccountControl");
foreach (SearchResult sr in ds.FindAll())
{
var userFlags = sr.GetDirectoryEntry().Properties["userAccountControl"].Value;
int flags = (int)userFlags;
bool enabled = (flags & 0x2) == 0x2;
Console.WriteLine("Enabled: {0}", enabled ? "true" : "false");
}
Currently it's filtering using an objectGuid I retrieve from a valid user, converted into the proper form. (Being a test program I don't care about the string concatenation...I'll fix that in production code later.) I could (and have) use(d) other search filter values, including bitwise transitive filters. I've used direct binding versus a directory search. I've written a dozen or more variations of this, and all with the same result: the query succeeds but the userFlags property itself comes back null (is not present).
Since I'm specifically asking for a user class here, I know I'm not inadvertently getting a contact class (which wouldn't have the userAccountControl property). The bitwise operations shown in the code aren't important (I know I can convert to an enum and compare that way). It crashes with a null reference exception before the bitwise operations anyway.
This is running on Windows Server 2008 R2, using .NET 4 (I know of the issue with .NET 4.5 and AD account management). The account running this has both Administrator and Enterprise Administrator privileges. Also, as an aside, I downloaded Softerra's LDAP administrator console, and it as well doesn't show this property as present.
My question is simply why is this value null? It should not be, to my limited knowledge. Did I not set AD up properly in the beginning, perhaps? The search is improperly constructed?
Is your code find user? If it s true can u try that?
//...
var results = ds.FindAll();
foreach (SearchResult sResult in results)
{
var directoryEntry = sResult.GetDirectoryEntry();
using (directoryEntry)
{
bool enabled;
const string attrib = "userAccountControl";
const int ufAccountDisable = 0x0002;
de.RefreshCache(new string[] { attrib });
var flags =(int)de.Properties[attrib].Value;
if (((flags & ufAccountDisable) == ufAccountDisable))
{
enabled = false;
}
else
{
enabled true;
}
}
}
I use this code block. I tried it, after set disable a user in active directory. It must works correctly.
Found it. It turns out that new user attributes are protected and can be accessed only by code running as administrator ("Run As"). I wasn't originally running this code using elevated privilege. Once I did, the attributes appeared. This seems to be similar behavior to querying for tombstoned objects. All I can say is "Doh!" :)

C# - Load existing system environment variables when the current process don't have them loaded

On Windows, I have a C# assembly that is COM visible. It references other assemblies to control an application in the machine. It works fine.
However, under Apache Web Server and using CGI, it doesn't work. After doing some debuging, I found out that the problem is that, while running under Apache's CGI, the environment variables SYSTEMROOT and SYSTEMDRIVE, which aparently are needed by the referenced assemblies, are not loaded.
I can configure Apache to pass those environemtn variables too, but before doing so, I'd really like to know if there's some command I can put on my C# COM visible assembly to make it load environment variables as if it was, let's say, the SYSTEM user or something like that, so it doesn't have to relay on the environment passed by the starting application.
How do you force loading an existent system environment variable in C#, when IT IS NOT SET in the current process (or it was process-deleted by the launching process)?
Thanks in advance for any suggestions!
EDIT 1 - ADDED INFO: Just to make it more clear (as I see in the current answers it's not so clear): Apache intendedly deletes a lot of environment variables for CGI processes. It's not that Apache cannot see them, it can, but it won't pass them to CGI processes.
This should do the trick:
Environment.GetEnvironmentVariable("variable", EnvironmentVariableTarget.Machine);
I did a small test and it is working:
//has the value
string a = Environment.GetEnvironmentVariable("TMP");
Environment.SetEnvironmentVariable("TMP", null);
//does not have has the value
a = Environment.GetEnvironmentVariable("TMP");
//has the value
a = Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine);
SOLUTION: Marco's answer was great and technically answered my question - except that I found out that the environment variables SYSTEMROOT and SYSTEMDRIVE are not really set in the registry where all environment variables are set, so, the chosen answer works for all variables except those two, which I specified in the OP.
SYSTEMROOT is defined on the registry in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot, and apparently (after more research), SYSTEMDRIVE is generated as a substring of SYSTEMDRIVE.
So, to get SYSTEMDRIVE and SYSTEMROOT from registry and load them into the environment:
using Microsoft.Win32;
namespace MySpace
{
public class Setup
{
public Setup()
{
SetUpEnvironment();
}
private void SetUpEnvironment()
{
string test_a = Environment.GetEnvironmentVariable("SYSTEMDRIVE", EnvironmentVariableTarget.Process);
string test_b = Environment.GetEnvironmentVariable("SYSTEMROOT", EnvironmentVariableTarget.Process);
if (test_a == null || test_a.Length == 0 || test_b == null || test_b.Length == 0)
{
string RegistryPath = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
string SYSTEMROOT = (string) Registry.GetValue(RegistryPath, "SystemRoot", null);
if (SYSTEMROOT == null)
{
throw new System.ApplicationException("Cannot access registry key " + RegistryPath);
}
string SYSTEMDRIVE = SYSTEMROOT.Substring(0, SYSTEMROOT.IndexOf(':') + 1);
Environment.SetEnvironmentVariable("SYSTEMROOT", SYSTEMROOT, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("SYSTEMDRIVE", SYSTEMDRIVE, EnvironmentVariableTarget.Process);
}
}
}
}
Then you can just call Setup setup = new Setup(); from other classes. And that's it. :-)
Environment.GetEnvironmentVariable
see reference here.
e.g.
Environment.CurrentDirectory = Environment.GetEnvironmentVariable("windir");
DirectoryInfo info = new DirectoryInfo(".");
lock(info)
{
Console.WriteLine("Directory Info: "+info.FullName);
}
Are the variables set as system wide?
If they are not, that is what you need to do, otherwise create user variables for the user the COM is running under.
Thank you. I cannot state with any certainty that this has once and for all driven a stake through the heart of the vampire, but amazingly enough, the error has disappeared (for now). The odd thing is that access to the statement
Environment.GetEnvironmentVariable("variable", EnvironmentVariableTarget.Machine);
is a real oddity in the debugger. It does not show up in Intellisense and does not even appear to fire, which leads me to suspect, which you all knew already, that this is some sort of magic runtime object Environment that has no instantiation in the debugger but also can be benignly jumped over. Oh well.
Oh and I should mention that after you see that error, you will note oddities in your Windows OS, which is worrisome. In particular, you will see, if you try to use the Control Panel /System/Advanced Properties (whatever) that it cannot load the dialog for the environment variables any more, indicating that %windir% has been seriously hosed (compromised) across all applications. Bad bad bad....

Cannot access SystemListView contents using Managed Win32 API's

I am using the ManagedWindows API in a C# environment:
http://mwinapi.sourceforge.net/
In the past I have successfully scraped the contents of listbox-like parts of other running programs using the code below, where I iterate through key/value pairs to find the list items. For this particular list of items, however, I can get an accurate number of items, but the value is always null!
Using this:
TargetMidWindow.Content.ComponentType
I have discovered that the list I am having issues with is a 'listview' whereas the other windows I have had success with are 'detailslistview' in case it matters. Below is the code I have for finding the data I want, which is almost identical to my other successful code with the exception of altering the search terms I used. Also, in case its relevant, the program I'm trying to pull data out of is MetaTrader4, and I've been able to scrape data off other parts of the program successfully.
// Find the main window
SystemWindow[] TopLevel = SystemWindow.AllToplevelWindows;
SystemWindow TargetTopWindow = SystemWindow.ForegroundWindow;
foreach (SystemWindow SearchWindow in TopLevel)
{
string Title = SearchWindow.Title;
if (Title.Contains("MetaTrader"))
{
TargetTopWindow = SearchWindow;
break;
}
}
// Find the section where positions are contained
SystemWindow[] MidLevel = TargetTopWindow.AllDescendantWindows;
SystemWindow TargetMidWindow = SystemWindow.ForegroundWindow;
foreach (SystemWindow SearchWindow in MidLevel)
{
string ClassName = SearchWindow.ClassName;
if (ClassName.Contains("SysListView32"))
{
SystemWindow ParentWindow = SearchWindow.Parent;
if ((ParentWindow.Title.Contains("Terminal")))
{
TargetMidWindow = SearchWindow;
}
}
}
// Get the positions
Dictionary<string, string> RawValues = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> KVP in TargetMidWindow.Content.PropertyList)
{
string key = KVP.Key;
string value = KVP.Value;
}
Is there something special I need to do so that I do not get 'null' values for each list item?
Thanks!
Bill
Hmya, wrapping Windows messages with a friendly API isn't that difficult. Windows Forms would be a good example. But that has a knack for running into a very solid wall once you start doing this with another process.
The specific message you need in order to read ListView items is LVM_GETITEM. That's one of those solid wall messages. The LPARAM argument you pass to SendMessage() needs to be a pointer to an LVITEM structure. The control fills in the fields in that structure. Problem is, the pointer you pass is only valid in your process, not the process who owns that window.
Fixing this takes a great deal of hackery. You have to allocate memory that's valid inside that process. That takes VirtualAllocEx() and ReadProcessMemory(). Plus all the glue calls you need to make these work. I assume that this library you are using is not taking care of this. Easy to find out, grep the source code files for these API function names.
If you want to find the correct handle to a particular SysListView32 window, you need to start with the right window hierarchy. From the code snippet, it doesn't appear that you're actually finding the correct handle to retrieve a quote from the SysListView32 window. This is why you're receiving null values back. You would do well to run spy++ and determine the correct windows structure of the Metatrader terminal for your specific broker and build. I've found that the classes are different between builds for some of the windows, and also between some brokers, though to a lesser extent.
You're looking for the specific quote window hierarchy like this:
Metatrader -> Market Watch -> Market Watch -> SysListView32
By contrast, currently you're looking here in your code:
Metatrader -> Terminal -> (many sub-windows with SysListView32 class)
Where each level to the right is a child window of the window to the left.
Find the parent "Metatrader" window then chain down looking for the child window until you get to SysListView32. If you use spy++ you can read the class for the SysListView32 parent window (market watch), and use that to enumerate the windows to find the correct SysListView32 window. FYI the correct Market Watch class name for build 419 is:
Afx:00400000:b:00010003:00000000:00000000
Once you find the correct window, you may be able to extract its contents using your current component. I haven't tried that and am looking to port some code from VB6 from a ListView module that does in fact involve epic hackery. ;) I may take a look at the .NET Managed Windows API to see if this can help make the process simpler.
But in the mean time, if you do have to go low-level, the following VB6 source should help you get an idea of what is involved. This is fairly advanced material so good luck!
Public Function GetListviewItem(ByVal hWindow As Long, ByVal pColumn As Long, ByVal pRow As Long) As String
Dim result As Long
Dim myItem As LV_ITEMA
Dim pHandle As Long
Dim pStrBufferMemory As Long
Dim pMyItemMemory As Long
Dim strBuffer() As Byte
Dim index As Long
Dim tmpString As String
Dim strLength As Long
Dim ProcessID As Long, ThreadID As Long
ThreadID = GetWindowThreadProcessId(hWindow, ProcessID)
'**********************
'init the string buffer
'**********************
ReDim strBuffer(MAX_LVMSTRING)
'***********************************************************
'open a handle to the process and allocate the string buffer
'***********************************************************
pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, ProcessID)
pStrBufferMemory = VirtualAllocEx(pHandle, 0, MAX_LVMSTRING, MEM_COMMIT, PAGE_READWRITE)
'************************************************************************************
'initialize the local LV_ITEM structure
'The myItem.iSubItem member is set to the index of the column that is being retrieved
'************************************************************************************
myItem.mask = LVIF_TEXT
myItem.iSubItem = pColumn
myItem.pszText = pStrBufferMemory
myItem.cchTextMax = MAX_LVMSTRING
'**********************************************************
'write the structure into the remote process's memory space
'**********************************************************
pMyItemMemory = VirtualAllocEx(pHandle, 0, Len(myItem), MEM_COMMIT, PAGE_READWRITE)
result = WriteProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'*************************************************************
'send the get the item message and write back the memory space
'*************************************************************
result = SendMessage(hWindow, LVM_GETITEMTEXT, pRow, ByVal pMyItemMemory)
result = ReadProcessMemory(pHandle, pStrBufferMemory, strBuffer(0), MAX_LVMSTRING, 0)
result = ReadProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'**************************************************
'turn the byte array into a string and send it back
'**************************************************
For index = LBound(strBuffer) To UBound(strBuffer)
If Chr(strBuffer(index)) = vbNullChar Then Exit For
tmpString = tmpString & Chr(strBuffer(index))
Next index
tmpString = Trim(tmpString)
'**************************************************
'deallocate the memory and close the process handle
'**************************************************
result = VirtualFreeEx(pHandle, pStrBufferMemory, 0, MEM_RELEASE)
result = VirtualFreeEx(pHandle, pMyItemMemory, 0, MEM_RELEASE)
result = CloseHandle(pHandle)
If Len(tmpString) > 0 Then GetListviewItem = tmpString
End Function

Categories

Resources