|
|
|||||||
I've created a function that will change the Volume License Key on a local Computer. Any input on this function would be apreciated. Code:
[edit] Edited per Shawn's & NTDOC's suggestions. [/edit] |
||||||||
|
|
|||||||
Looks really good to me - well written. Just one comment in regards to syntax though ... this last line: Return $ChangeVLKey You might was well just remove the $ChangeVLKey altogether - the Return statement doesn't take arguments. The only reason that $ChangeVLKey didn't dump its contents to the console is because the script returned before it had a chance to. |
||||||||
|
|
|||||||
Ah ok, I thought you'd have to actually put "return" in there for it to return the value (Just mIRC scripting habit I guess ). Thanks |
||||||||
|
|
|||||||
well hold the phone now - as it stands, your returning the error code here: $ChangeVLKey = @error What is it you figure you want to return ? If you want to return the error code, I would make a slight addition to what you have. As well, it is possible to return a string AND an error code (in @ERROR), all depends on what you want to do. |
||||||||
|
|
|||||||
Exit @Error to return it. |
||||||||
|
|
|||||||
It works good, the way you said it should be. I wanted the function to return an error code when the function completes it's task, 0 for success should be handled ok now by "$changeVLKey = @error" as well as an error code when it fails it's task. for instance: Code:
|
||||||||
|
|
|||||||
Ok thats fair - then I would make the following changes ... Code:
Basically adding this line after the GetObject: $ChangeVLKey = @error This statement kills two birds with one stone 1) It initializes the return code to "something", which it wasn't doing before 2) It will catch a failure on the GetObject and return the code. |
||||||||
|
|
|||||||
I could do: Code:
|
||||||||
|
|
|||||||
Aha, yes I agree. That would be the best option. Edited the first post to reflect your modification. |
||||||||
|
|
|||||||
gave your script a test run and i got back a -2147352567 what does that mean? |
||||||||
|
|
|||||||
Thats the ubiquitous COM message that means "an error has occurred". |
||||||||
|
|
|||||||
if its ubiquitous what is the point of checking for an error? |
||||||||
|
|
|||||||
Well either it didn't get the object or it couln't be set. Please keep in mind that it changes Volume License Keys. Not Retail or OEM Keys. |
||||||||
|
|
|||||||
Well, i would seem that checking for errors did indeed serve its purpose - you got an error (for some reason) and I assume apronk isn't getting one. Problem is - whats the cause of your error? |
||||||||
|
|
|||||||
apronk got it right i think because i tried it with a retail lic. if that is the case then can the script be modified to work with retail? |
||||||||
|
|
|||||||
That is a good question, but to rule out the first case could you place an "? $ChangeVLKey" after the first "$ChangeVLKey = @error" ? If that one is 0 then we know for sure that it is because of the Retail version that this function isn't working. |
||||||||
|
|
|||||||
I've edited the function in the first post to get better error msg's. Try using it as this: Code:
If the function exit's with an error it is because of the GetObject. If the function complete's you get the msg that staes wether it was placed or not. |
||||||||
|
|
|||||||
As the final thing you should Code: Exit @ERROR or Code: Exit $ChangeVLKey So that any error is also preserved in @ERROR when the function returns. Code: Function ChangeVLKey($VOL_PROD_KEY) |
||||||||
|
|
|||||||
ok, the error is not because it can't get the object. |
||||||||
|
|
|||||||
ok, this is what i am running: Code:
|
||||||||
|
|
|||||||
Richard: I think I've caught everything safely in my last modification in the first post. Benny: Then it is because of the Retail version I'm afraid |
||||||||
|
|
|||||||
Well I think if it's going to be a UDF it should be modified a little bit. As for free tools out there to do this, take a look at RockXP http://www.rockxp.org/ Download here: http://korben.othersystem.net/web/softs/RockXP3.exe Recommended UDF changes |
||||||||
|
|
|||||||
I'll have a look trough this, the use of DecToHex might prove usefull. Also if you want a good tool that changes any xp license key use this: KeyFinder 1.5b3 Or goto the site for more info: http://www.magicaljellybean.com |
||||||||
|
|
|||||||
Yep, I've tried both those before. Just like the RockXP a bit better myself. Thanks for the links though. |
||||||||
|
|
|||||||
Soory to jump in so late. IMHO, the function should return the error code only in EXIT @ERROR and the $ChangeVLKey whatever the SET/GETOBJCT generates (0 in case of success otherwise WMI error code, thus Code:
|
||||||||
|
|
|||||||
I don't agree. If there is ANY error your code will be have invalid / unpredictable results and should quit the UDF then and there not set the UDF value to it. |
||||||||
|
|
|||||||
I can agree that the last Exit @ERROR is probably better However I don't think setting the Function to the Error is good. As an example let's suppose that you had a function that read a remote value from the registry and you expected it to be between 1 and 5 Well as you would rewrite it unless one specifically tested for the Error condition first (I agree that is probably best coding but we all know that we don't all always do that first) Example: Dim $T $T = ReadRegValue('some computer') 'The value was: ' + $T ? In this example if say 5 came back the admin would think, cool that's the right value and proceed from there, but in reality it could easily have been 5 - Access Denied Setting the Function to 0 after ANY error prohibits any misunderstanding like that. With the way it is now it would come back blank If one did better coding one would know, but I still don't see the value in setting it to the error. Dim $T $T = ReadRegValue('some computer') If @ERROR 'ERROR: There was an error accessing the information: ' + @ERROR + ' - ' + @SERROR ? Else 'The value was: ' + $T ? EndIf |
||||||||
|
|
|||||||
After thinking about it some more, I think setting the value to blank if an error was detected is better than setting it to 0 $ChangeVLKey = 0 Would be better set to $ChangeVLKey = "" I'm open to further discussion on this subject as well as WHY you think it should be one way or another. |
||||||||
|
|
|||||||
A function should always explicitly return a value, even if it is a null value. When the purpose of a function is to return data then it is reasonable to return either the data on success or a null value on an error - I agree that in this case that returning an error value is unwise. However, when the function is not expected to return data then it makes sense use the error status as the return value. Apart from anything else this makes it consistent with the KiXtart built-in functions (e.g. Open() EnumValue()) which use the error state as their exit value. The code that I posted earlier (repeated here for clarity) does just that: Code: Function ChangeVLKey($VOL_PROD_KEY) |
||||||||
|
|
|||||||
The problem is that the WMI action will return '0' to indicate success, thus if someone checks for the UDF return but not the @ERROR, it'll evaluate to 'success' even though the action failed. |
||||||||
|
|
|||||||
Quote: Eh? What do you mean? Both the exit value and the function value are set to @ERROR wether the error is set by the initial creation of the object or the enumeration. |
||||||||
|
|
|||||||
Richard, I think that those examples are special cases that return EXTENDED information, and return 0 on success, the opposite of what this and many other functions do. As for the rest of the comments... If you expect a function to return non-zero data, it should return zero or null in the case of an error, and exit with the error value. This allows Code:
- or - Code:
Conversely, if a function normally does not return specific data, it is helpful to return status info (which may or may not be the ERROR code). Look at the ExistKey/KeyExist functions. These return Status, one inverted, the other "fixed" to return true. The original one returned "success", which wasn't too helpful when doing simple "If ExistKey()" tests. The newer function returns status, not error info. Think about your Open example.. the sample in the book looks something like Code:
Which is generally what I see in UDFs that use it. However, consider Code:
This allows code to be more robust in an environment where many copies of a script may need to access a file. This is a simplified example, but should illustrate the point of checking the status returned. In this UDF example, and the one Doc is referring to, the UDF returns a specific value - valid data. Even worse, the UDFs return strings of numbers (Strings, because they may have dashes). Returning a pure number as an error is counter-intuitive. It should return a null (empty string) if it usully returns strings, and a zero if it usually returns numbers. If returning zero as a numeric value is valid, then the calling code should ALWAYS check the ERROR macro. Mode A - Either I get data (non-zero, non-null) or I don't, and if I don't, I check the ERROR macro for more info. Mode B - I don't expect data, so if I get some, it's a problem and should check the value further, and possibly the @ERROR macro. One last example.. Consider a UDF that updates some data repository (file, database, registry). It normally returns nothing, indicating success. It performs a "complex" validation process before writing the data, and if it fails validation, it exits with error 87. Hmm, bad data. What kind of bad data? The value returned might have the following meanings: 0 = Success -1 = Out of Bounds - Low 1 = Out of Bounds - High 2 = Incorrect data type (not number) 3 = Null value supplied 4 = System Error Of course, a return of 4 would indicate that some other error occurred, and would be identified by the exit value in the ERROR macro. Bottom line, I'm definitly against arbitrarily returning ERROR values as data unless the conditions warrant as outlined above. Glenn |
||||||||
|
|
|||||||
Since this UDF can theoretically change multiple instances of a class (its looping through a collection) - if I was coding this UDF - I would probably create it so that it returned the number of instances changed (which may be usefull information) and if an error occurred it would return 0 (zero nothing changed) and then @ERROR would indicate the error. |
||||||||
|
|
|||||||
eh, strike that idea - this is one of those "WMI returns a collection but it will always be only one item" thingies. |
||||||||
|
|
|||||||
Quote: The example script I gave above returns 0 on success or the error which caused the failure. This is consistent with... AddKey(), AddPrinterConnection(), AddProgramGroup(), AddProgramItem(), BackupEventLog(), DelKey(), LoadHive(), LogEvent(), SendKeys(), SendMessage(), SetConsole(), ShutDown(), WriteProfileString() ... and others. Maybe I misunderstand. Wouldn't be the first time |
||||||||
|
|
|||||||
Ahh I think I see where the confusion lies: Quote: This is not correct. Well, not what you mean anyway The value of SetProductKey is not a datum as such - it is the same error code that @ERROR is set to: Quote: |
||||||||
|
|
|||||||
The issue is not whether the UDF or Function returns error info as much as being consistent. Those functions you reference do not return Data OR the result (status) codes, they ONLY return the result codes. It makes sense for them to return 0 for success and some status on failure. Imagine the difficulty if ReadProfileString() returned an error code instead of the data it read! How could you tell, unless you ALWAYS checked the ERROR macro. I was trying to point out that if a function is expected to return data.. maybe "Variable Data" describes it better, that No Data is a better indication of an error (and a reason to check the ERROR macro) than Error Data, which makes you figure out if what you got was Valid Data or Error Data. Glenn |
||||||||
|
|
|||||||
Quote: Yes, that is exactly what I said in my earlier post. I know it's bad form to quote yourself but: Quote: However, this function never returns any data. It only ever returns an error state. |
||||||||
|
|
|||||||
I agree with Richard. So what were agreeing to is that the UDF should remain as is (returning the errorlevel). |
||||||||
|
|
|||||||
Quote: OK - this situation it makes sense.. I guess I didn't understand the function itself, but had recently had issues with other UDFs that returned data when it worked and returned errors when it didn't, and it made it more complex to code around that. Glenn |
||||||||
|
|
|||||||
My whole idea is that it returns 0 when succesfull and an @error code when it isn'succesfull and exits with an @error is it fails somewhere before the function finishes. |
||||||||
|
|
|||||||
Quote: Well then I would assume in most cases that it would be 0 If that's the case then why do we really need to set it to anything? If it's not expected to return data then as long as one checks for @ERROR and does not find an error then it should have worked, otherwise regardless of what caused the error it would be captured in the EXIT @ERROR code. Also in your example by using the NOT operator forces one to use KiXtart 4.50 or newer (which is okay ) But as I view it if no data is expected why would you check for data? That's like expecting to really see the Tooth Fairy when you know it's not there. Code: Function ChangeVLKey($VOL_PROD_KEY) Here we simply exit with the ERROR if anything caused one during that processing which we didn't expect data. I assume from your point of view that well we would see the error and know that something went wrong, but I'm thinking the opposite in that for a function that does not return data, I won't go looking, I'll only look for an ERROR Regardless of outcome here I think it has been a good discussion on the subject. |
||||||||
|
|
|||||||
I think the point is that returning the errorlevel is most "kix-like". For example - WriteValue from the manual is: Code:
WriteValue returns the errorlevel and sets the errorlevel. This keeps two types of coders happy - those that like to check @ERROR (you i think) and those that like to check the return value (me for example). The only pain-in-the-ass thing is when you forget to capture the return value - but we all deal with that day-in day-out. In fact, when I get rc's dumped to the console its a good reminder (to me anyways) about something that should be checked and handled if issues. |
||||||||
|
|
|||||||
Well after a bit more discussion with Glenn on this subject via MSN (Shawn is never on anymore to group chat on this) I think it boils down a bit more to what is easier to support / use / test and not so much as always mimicking the KiXtart functions which are not 100% consistent either. As an example in the manual by Ruud If Exist() That just doesn't work with 0 for success so he used positive logic for testing. Returns: 0 File not found 1 File found So basically in a nutshell (regardless of how KiX internally handles it) If you can with reasonable certainty know what error you want returned when the function did not do as you wanted then one would force set the value and not use @ERROR This is on a case by case basis When the error is fully unexpected then using the @ERROR makes sense. Setting a positive logic to a RETURN (when data is not excpected) makes more sense than 0 or @ERROR (imho) ie.. I would set it to 1 to mean it was successful and set the exit to 0 If I felt the @ERROR might give better feedback than I would use that. Sometimes though setting a specific error gives a better indication of what the problem is. I apologize for not seemingly being able to clearly define this subject, but I think I have better grasp of how I want to mabye write future UDFs. |
||||||||
|
|
|||||||
I think this should cover it all I removed the Error parsing NTDOC suggested earlier because I want to keep the function as simple as can be. It returns an error code, ppl who use this function should be able to recover the error themselves. Code:
|
||||||||
|
|
|||||||
That's okay, but you're missing an EXIT code when it's finished. All UDF code should have an Exit code and not just Exit either. This has been discussed in the past and shown that on some occassions it can lead to other problems if an Exit with value is not supplied. |
||||||||
|
|
|||||||
Quote: That is a very bad example to choose You've assumed that because the function is returning a 1 or a 0 that this is the same as returning an error state. But is not is it? The 1 or 0 returned by Exist() is data and has nothing to do with the error condition. This still fits into the simple rule set:
Now the example you have chosen (Exist) is designed to explicitly return a value, so it matches rule 1. If an error occurs then 0 will be returned, which fits it's purpose and the way it is used in scripts. AScan is a good example of a function which fits rule 1 but cannot return 0 on an error which is a bit of a design error IMO. |
||||||||
|
|
|||||||
No I did not assume or think it was ERROR it is data and I chose it on purpose to illustrate my point that I think you're still missing the overall larger picture here. Coding, using a UDF with a positive logic regardless of how KiXtart internals may handle them. A return and an Error are different and I don't believe I showed otherwise, but then again I seem to have trouble presenting my point here. I suppose what I'm trying to say is that a UDF does not and can not have a SOLID one rule fits all, but rather can and will change depending on what the UDF code consists of. I think I agree with your RULE 1 but I don't fully agree with your RULE 2 |
||||||||
|
|
|||||||
I think I understand what you are saying. If I have it right then:
I see the argument but it would be pretty counter-intuitive as it is the reverse of every other scripting or programming language I've ever used. I guess that's why it took me a while to see what you were getting at. |
||||||||
|
|
|||||||
Quote: I don't think that is the case in this scenario, it returns an error code at the end of the function. Thus the function is ended. I do not believe the function can "hang" in any case scenario however I'd like to hear it if you believe otherwise. |
||||||||
|
|
|||||||
Well I'm not saying it would/could/or might hang or error out, just saying that setting the Function to the error is not the same as exiting the function. A bit of this is theoritical and personal preference. You may run this function a thousand times and have no issues, but you might and having a set exit code (imho) would be a bit better. |
||||||||
|
|
|||||||
Ok sounds fair enough, could you give you last suggestion as to how the function should look like in your opinion then ? without converting error codes. |
||||||||
|
|
|||||||
Code: For Each $obj in $objects As I said though I'm sure the script as you have would probably run good on 99.9% of every run. Personal opinion is that having the Exit 0 or @ERROR is just a bit better is all. |
||||||||
|
|
|||||||
I agree with that, I've changed the first post and the UDF post to reflect your suggestion. Thanks |