I use Hard-Drive Serial-Number check to register my products.
But I see that sometimes it's being changed.
Just 2 weeks after the client has activated the product, the hard-drive Serial-Num changed.
Is this property changeable? and if so, how when and why is it changed?
Here is the code i use in VB:
Dim WMIService As Object, Items As Object, SubItems As Object, temp
Set WMIService = GetObject("winmgmts:\\" & "." & "\root\cimv2")
Set Items = WMIService.ExecQuery("Select * from Win32_PhysicalMedia", , 48)
For Each SubItems In Items
temp = SubItems.SerialNumber
If LenB(temp) Then Exit For
Next
and the same thing i use in C#
using System.Management;
public string GetHDDSerial()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
foreach (ManagementObject wmi_HD in searcher.Get())
{
// get the hardware serial no.
if (wmi_HD["SerialNumber"] != null)
return wmi_HD["SerialNumber"].ToString();
}
return string.Empty;
}
Thanks to the comment from FunThomas and after a little troubleshooting with my client, i found out that he is using sometimes an external drive for more space, and when the external drive is plugged in - i'm getting a different HDD Serial-number.
Now i have figured how to get the serial number from the current drive letter.
here is the code for VB:
Dim WMIService As Object, DiskDrives As Object, Drive As Object, DiskPartitions As Object, Partition As Object, SerialNumber, currentDrive
currentDrive = Environ("HomeDrive")
Set WMIService = GetObject("winmgmts:\\" & "." & "\root\cimv2")
Set DiskPartitions = WMIService.ExecQuery("ASSOCIATORS OF {Win32_LogicalDisk.DeviceID='" & currentDrive & "'} WHERE ResultClass=Win32_DiskPartition")
For Each Partition In DiskPartitions
Set DiskDrives = WMIService.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & Partition.DeviceID & "'} WHERE ResultClass=Win32_DiskDrive")
For Each Drive In DiskDrives
Debug.Print "Caption: " & Drive.Caption, "Description: " & Drive.Description, "Name: " & Drive.Name, "Model: " & Drive.Model, "SerialNumber: " & Drive.SerialNumber
SerialNumber = Drive.SerialNumber
Next
Next
Related
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
foreach (ManagementObject currentObject in theSearcher.Get())
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
MessageBox.Show(theSerialNumberObjectQuery["SerialNumber"].ToString());
}
How do I get the internal serial number of a USB-Stick or USB-HardDrive?
If i insert Hp or SanDisk Pen Drive it will display correct and whole serial number but if i insert some other pen drive means it will display only the first digit of the serial number. Why the problem occurs I don't know so please make me correct.
Try using ...
currentObject["PNPDeviceID"] instead of currentObject["DeviceID"]
Edit : Change your query to Select * From __InstanceOperationEvent Within 1 Where TargetInstance ISA 'Win32_DiskDrive' and TargetInstance.InterfaceType='USB' and check..
I have a program that gets values from a database through a stored procedure. What it does is gets the values, downloads an image and then uploads it to a server. and after upload the run another stored procedure just to update the table to the new url.
Now it uploads a few images correct then all of a sudden it makes my ID = "" and I cant see why?
Result as log file:
Property ID : 29140
pk_photo_id (TRY): 47334070
- File : propimage29140_9.jpg has been uploaded to AWS S3 bucket: ruans
Property ID : 30402
pk_photo_id (TRY): 47174447
Property ID : 29330
Property ID : `<-- why make it nothing`
pk_photo_id (TRY): 47174392
ERROR 6 : Cannot insert duplicate key row in object 'dbo.ET_EXPORT_PROPERTY_QUEUE' with unique index 'idx_pk'. The duplicate key value is (<NULL>).
The statement has been terminated.
My VB sub where this takes place:
Dim files As String() = Directory.GetFiles(txtFolderPath)
'for each image get file name
For Each images As String In files
Dim id As String = ""
Dim photoID As String = ""
Dim position As String = ""
For Each str As String In IDS
If Path.GetFileNameWithoutExtension(sKey).Contains(str) Then
id = str
End If
Log("Property ID" & id)
For Each value As String In PhotoIDS
photoID = value
Next
For Each URL As String In fullURL
fullURLIndex = URL
Next
Next
'AWS upload
Try
utility.Upload(txtFolderPath & sKey, bucket)
Catch
needsToExitSub = True
Dim cmdSetError As New SqlCommand("ETSP_UPDATE_PHOTO_URLS")
cmdSetError.CommandType = CommandType.StoredProcedure
cmdSetError.Parameters.AddWithValue("#stype", "L")
cmdSetError.Parameters.AddWithValue("#surl", fullURLIndex)
cmdSetError.Parameters.AddWithValue("#status", 3)
cmdSetError.Parameters.AddWithValue("#pk_photo_id", photoID)
Log("pk_photo_id (CATCH): " & photoID)
cmdSetError.Parameters.AddWithValue("#bactive", 1)
cmdSetError.Parameters.AddWithValue("#property_id", id)
cmdSetError.Parameters.AddWithValue("#client_id", clientIDs)
conn.Open()
cmdSetError.Connection = conn
cmdSetError.ExecuteNonQuery()
Log(" - ERROR 4 : File : " & sKey & " Image ID : " & photoID & " Has NOT been uploaded to " & bucket & " , URL: " & fullURLIndex)
conn.Close()
If needsToExitSub = True Then
Exit Sub
End If
End Try
'Make files public - check this
Dim cannedACL As S3CannedACL = S3CannedACL.PublicRead
Dim fileNameOnly As String = Path.GetFileNameWithoutExtension(sKey)
Dim token As String = fileNameOnly.Remove(fileNameOnly.LastIndexOf("_"c))
Dim number As New String(token.SkipWhile(AddressOf [Char].IsLetter).ToArray())
'Run stored procudure and update if uploaded
Dim cmdSet As New SqlCommand("ETSP_UPDATE_PHOTO_URLS")
cmdSet.CommandType = CommandType.StoredProcedure
cmdSet.Parameters.AddWithValue("#stype", "L")
cmdSet.Parameters.AddWithValue("#surl", Convert.ToString("www.url.com/" & bucket & "/" & sKey))
cmdSet.Parameters.AddWithValue("#status", 2)
cmdSet.Parameters.AddWithValue("#pk_photo_id", photoID)
Log("pk_photo_id (TRY): " & photoID)
cmdSet.Parameters.AddWithValue("#bactive", 1)
cmdSet.Parameters.AddWithValue("#property_id", id)
cmdSet.Parameters.AddWithValue("#client_id", clientIDs)
conn.Open()
cmdSet.Connection = conn
cmdSet.ExecuteNonQuery()
conn.Close()
Next
Log(" - File : " & sKey & " has been uploaded to AWS S3 bucket: " & bucket)
Catch ex As System.NullReferenceException
Log("ERROR 5 : " & ex.Message)
Catch ex As Exception
Log("ERROR 6 : " & ex.Message)
Exit Try
End Try
So the images downloads file to my computer in the correct folder, then it uploads it also to the correct folder but then when it comes to update the database to update new url and the other parameters it gives the error, now i think it is because the Property ID : is made null.
Why would it get the correct Property ID : for lets say 8 images then get a null for a while and then again get the correct Property ID :. and how can i fix this.
C# help welcome
We are designing a monitoring solution for our system, and we're looking into WMI as a possible option.
In short, we want to create a generic system where it shall be possible to subscribe to multiple changes in WMI data instances. We're looking into the __InstanceModificationEvent to do this:
The following prototype code monitors all changes on any instance of notepad:
void StartMonitor()
{
var query = "SELECT * "
+ "FROM __InstanceModificationEvent "
+ "WITHIN 1 "
+ "WHERE TargetInstance Isa \"Win32_PerfFormattedData_PerfProc_Process\" "
+ "AND TargetInstance.Name = \"notepad\"";
var scope = new ManagementScope(#"root\cimv2", null);
scope.Connect();
EventQuery qry = new EventQuery(query);
ManagementEventWatcher w = new ManagementEventWatcher(scope, qry);
w.EventArrived += EventArrived;
w.Start();
}
void EventArrived(object sender, EventArrivedEventArgs e)
{
var targetInstance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (var p in targetInstance.Properties)
{
Console.WriteLine(p.Name + ":\t" + (p.Value != null ? p.Value.ToString() : "{null}"));
}
}
So whenever any instance of notepad is changes, we will get an output like this (excerpt)
PageFileBytes: 1499136
PageFileBytesPeak: 1740800
PercentPrivilegedTime: 0
PercentProcessorTime: 0
PercentUserTime: 0
PoolNonpagedBytes: 7040
PoolPagedBytes: 172856
This is fine, however we need to improve this a bit. For example, let's pretend we are only interested in the changes to PercentProcessorTime. With the current code, the event will be fired whenever anything in the object changes. This is not good enough, because we might monitor hundreds of processes across multiple computers.
Thus, we need a way to specify that we only want the event to be triggered whenever this or that property changes, not the entire instance
Is this possible using WMI? What's the best practice to achieve what we want?
Edit: I know that it is possible to write a query such as the one below and cycically poll for the value, however we were hoping to avoid that approach.
SELECT PercentProcessorTime
FROM Win32_PerfFormattedData_PerfProc_Process
WHERE Name = "notepad"
The __InstanceModificationEvent has a reference to the previous instance so you might be able to compare values between the PreviousInstance and TargetInstance. For example, to filter for PercentProcessorTime changes:
var query = "SELECT * "
+ "FROM __InstanceModificationEvent "
+ "WITHIN 1 "
+ "WHERE TargetInstance Isa \"Win32_PerfFormattedData_PerfProc_Process\" "
+ "AND TargetInstance.Name = \"notepad\" "
+ "AND PreviousInstance.PercentProcessorTime != TargetInstance.PercentProcessorTime ";
I have a SQL table which stores the different rates charged to clients.
The table also includes a list of 'default' rates to be used if the client has none specific to them.
What I need, is once the user selects a client from a combo box, to create a table of rates for that client, consisting of the defaults, or the rates specific to that client, or a mix of both, as follows:
Client Type of Work Rate
ABC Ltd Type A 100
XYZ & Co Type B 150
Default Type A 125
Default Type B 175
So the rates returned would look as follows:
ABC Ltd Type A 100
Type B 175 (default)
XYZ & Co Type A 125 (default)
Type B 150
Anyone else Type A 125 (default)
Type B 175 (default)
The information in the database is structured as follows
Table for clients
Table for Types of Work
Table for Rates (foreign keys linking to clients and types of work)
Can anyone explain how best to obtain the above result using either a SQL statement, a C# routine in the winforms application or a combination of both.
PS. I apologise but I'm unsure how to set my data out in tables on this website.
WHAT I HAVE TRIED SO FAR
public static DataTable TableOfRates(string pIPName = "Default")
{
try
{
string sqlText1 = "SELECT RateID, IPName, RateName, Rate, DateFrom, DateTo, Active, " +
"(RateName + ': £' + CONVERT(VARCHAR, Rate) + CONVERT(VARCHAR, " +
"(CASE WHEN Active = '1' THEN ' (Active)' ELSE ' (Inactive)' END))) AS ListRate " +
"FROM tblRates WHERE IPName IS NULL AND Active = 'true';";
aSqlQuery cQ1 = new aSqlQuery(sqlText1, "table");
DataTable defaultRates = cQ1.TableResult;
string sqlText2 = "SELECT RateID, IPName, RateName, Rate, DateFrom, DateTo, Active, " +
"(RateName + ': £' + CONVERT(VARCHAR, Rate) + CONVERT(VARCHAR, " +
"(CASE WHEN Active = '1' THEN ' (Active)' ELSE ' (Inactive)' END))) AS ListRate " +
"FROM tblRates WHERE IPName = '" + pIPName + "' " +
"AND Active = 'true';";
aSqlQuery cQ2 = new aSqlQuery(sqlText2, "table");
DataTable clientRates = cQ2.TableResult;
foreach(DataRow defaultRow in defaultRates.Rows)
{
string defaultType = defaultRow["RateName"].ToString();
foreach (DataRow clientRow in clientRates.Rows)
{
string clientType = clientRow["RateName"].ToString();
if (defaultType == clientType)
{
clientRates.Rows.Add(defaultRow.ItemArray);
}
}
}
return clientRates;
}
catch (Exception eX)
{
throw new Exception("cRate: Error compiling Table Of Rates (mode = '" + pIPName + "')" + Environment.NewLine + eX.Message);
}
}
However, whether I use Add Row, Import Row, or Add Item Array I get errors. It doesnt seem to like using datarows from more than one table.
You must have a table that lists clients and jobs - with that in mind you might be doing something like the following
SELECT
CASE R1.RATE WHEN NULL THEN R2.RATE else r1.rate AS RATE END
FROM
CLIENTJOBS C LEFT JOIN RATES R1 ON
R1.CLIENT = C.CLIENT AND R1.WORKTYPE = C.WORKTYPE
JOIN RATES R2 ON
R2.WORKTYPE = C.WORKTYPE AND R2.CLIENT = 'DEFAULT'
bear in mind this is untested guess-code, but hopefully points you in the right direction
If you really need to load up a DataTable:
CREATE TABLE
#MyTempTable (myColumn1 DataType, myColumn2 DataType, myColumn3 DataType)
INSERT INTO
#MyTempTable(myColumn1, myColumn2, myColumn3)
SELECT
f.Bar1, o.Bar2, o.Bar3 FROM FooTable AS f
INNER JOIN
TableFoo o ON f.Bar1 = o.Bar1
With some tweaking this script would create a temporary table that persists as long as your connection is kept alive and contains the data from the other tables that you need.
Hi all I'm porting over my VBScript over to C#. And I ran into a problem that Active Directory properties retrieval is much slower in C#.
This is my incomplete C# code
foreach(string s in dictLast.Keys)
{
if(s.Contains("/"))
str = s.Insert(s.IndexOf('/'), "\\");
else
str = s;
dEntry = new DirectoryEntry("LDAP://" + str);
strUAC = dEntry.Properties["userAccountControl"].Value.ToString();
cmd.CommandText = "INSERT INTO [NOW](readTime) VALUES(\"" + test.Elapsed.Milliseconds.ToString() + "\")";
cmd.ExecuteNonQuery();
test.Reset();
test.Start();
}
If I comment out this line.
strUAC = dEntry.Properties["userAccountControl"].Value.ToString();
It runs at 11 secs. But if I don't, it runs at 2mins 35 secs. The number of records are 3700. On average each record runs at 50 secs. I'm using the Stopwatch Class.
My VBscript runs at only 39 secs (Using difference of Time). With each record either a 0 or 15 milliseconds. I'm using the difference of Timer().
Here's my VBscript
strAttributes = "displayName, pwdLastSet, whenCreated, whenChanged, userAccountControl"
For Each strUser In objList.Keys
prevTime = Timer()
strFilter = "(sAMAccountName=" & strUser & ")"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
Set adoRecordset = adoCommand.Execute
On Error Resume Next
If (adoRecordset.Fields("displayName") = null) Then
strCN = "-"
Else
strCN = adoRecordset.Fields("displayName")
End If
If (Err.Number <> 0) Then
MsgBox(strUser)
End If
strCr8 = DateAdd("h", 8, adoRecordset.Fields("whenCreated"))
strUAC = adoRecordset.Fields("userAccountControl")
If (strUAC AND ADS_UF_DONT_EXPIRE_PASSWD) Then
strPW = "Never expires"
Else
If (TypeName(adoRecordset.Fields("pwdLastSet").Value) = "Object") Then
Set objDate = adoRecordset.Fields("pwdLastSet").Value
dtmPwdLastSet = Integer8Date(objDate, lngBias)
Else
dtmPwdLastSet = #1/1/1601#
End If
If (dtmPwdLastSet = #1/1/1601#) Then
strPW = "Must Change at Next Logon"
Else
strPW = DateAdd("d", sngMaxPwdAge, dtmPwdLastSet)
End If
End If
retTime = Timer() - prevTime
If (objList.Item(strUser) = #1/1/1601#) Then
Wscript.Echo strCN & ";" & strUser & ";" & strPW & ";" & strCr8 & ";" & ObjChange.Item(strUser) & ";0;" & strUAC & ";" & retTime
Else
Wscript.Echo strCN & ";" & strUser & ";" & strPW & ";" & strCr8 & ";" & ObjChange.Item(strUser) & ";" & objList.Item(strUser) & ";" & strUAC & ";" & retTime
End If
Next
Any ideas what's the problem?
Please tell me if I'm not giving enough information. Thank you.
DirectorySearcher way. 1 min 8 secs.
dEntry = new DirectoryEntry("LDAP://" + strDNSDomain);
string[] strAttr = {"userAccountControl"};
foreach(string s in dictLast.Keys)
{
if(s.Contains("/"))
str = s.Insert(s.IndexOf('/'), "\\");
else
str = s;
ds = new DirectorySearcher(de, "(sAMAccountName=" + s + ")", strAttr, SearchScope.Subtree);
ds.PropertiesToLoad.Add("userAccountControl");
SearchResult rs = ds.FindOne();
strUAC = rs.Properties["userAccountControl"][0].ToString();
cmd.CommandText = "INSERT INTO [NOW](readTime) VALUES(\"" + test.Elapsed.Milliseconds.ToString() + "\")";
cmd.ExecuteNonQuery();
test.Reset();
test.Start();
}
where strDNSDomain is the defaultNamingContext. I've tried with domain name but it runs worse, at 3 mins 30 secs.
Looking at someone else's code. Would there be a difference if we omit the domain part?
using (var LDAPConnection = new DirectoryEntry("LDAP://domain/dc=domain,dc=com", "username", "password"))
And just use "LDAP://dc=domain,dc=com" instead.
Work around. Instead of binding each user and getting the properties. I stored all the properties in the first search for lastLogon instead. And output using StreamWriter.
Both DirectoryEntry and ADOConnection use ADSI underlying. There shouldn't be any performance difference.
The only reason for the performance difference is that you are trying to retrieve two different sets of data.
In your VBScript, you are setting ""displayName, pwdLastSet, whenCreated, whenChanged, userAccountControl" to strAttributes. ADSI is going to load these five attributes back from AD only.
In your C# code, you didn't call the RefreshCache method to specify what attributes that you like to load. So, when you access DirectoryEntry.Properties, it automatically calls a RefreshCache() for you without passing in any attributes for you. By default, ADSI will return all non-constructed attributes to you (pretty much all attributes) if you don't specify what attributes to load.
Another problem is that in your VBscript, you run only one LDAP query while in your C# code, you are running many LDAP queries. Each of the DirectoryEntry.RefreshCache() is going to translate to one single LDAP query. So, if you are trying to access 1000 objects, you are going to run 1000 different LDAP queries.
Take a relational database analogy, in VBscript, you are running
SELECT * FROM USER_TABLE
In C# code, you are running multiple times of the following queries
SELECT * FROM USER_TABLE WHERE id = #id
Of course, the C# code will be slower.
To do similar thing in C# code, you should use DirectorySearcher instead of DirectoryEntry.
Similarly, you need to remember to specify DirectorySearcher.PropertiesToLoad in order to specify what attributes to return from a LDAP query. If you don't specify, it will return all non-constructed attributes to you again.
Here are couple of things you can do
Enable Audit log in the LDAP server and see how your requests are going through. Audit logs will show you how much time it is taking for each request from your application and how many connections are opened etc.
Use System.DirectoryServices.Protocols which can make Asynchronous calls to LDAP. Check this sample post. Another advantage of using this name space is that you can specify attributes.
Close connection properly.
use DirectorySearcher.PropertiesToLoad to load only required properties instead of all properties.
what i see from vbscript following is a bit closer .. copying from some project, kindly test
DirectoryEntry de = new DirectoryEntry("ldap://domainname");
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(ObjectCategory=user)(sAMAccountName="+strUser+"))";
deSearch.PropertiesToLoad.Add("displayName");
deSearch.PropertiesToLoad.Add("pwdLastSet");
deSearch.PropertiesToLoad.Add("whenCreated");
deSearch.PropertiesToLoad.Add("whenChanged");
deSearch.PropertiesToLoad.Add("userAccountControl);
deSearch.SearchScope = SearchScope.Subtree;
SearchResult sr = deSearch.FindOne();
This is the correct way to read the property:
If searchResult.Properties.Contains(PropertyName) Then
Return searchResult.Properties(PropertyName)(0).ToString()
Else
Return String.Empty
End If