mshtml and CLS-compliance - mshtml

I am witnessing some odd behaviour with mshtml and CLS-compliance.
I have an assembly marked CLSCompliant(true). This assembly does not expose any types from mshtml publicly.
I can make the code conform to CLS fairly easily, but several things seem to break it:
Introducing a "using mshtml;" statement instead of referencing the namespace manually each time I use a type. For some reason, this breaks CLS compliance.
If I convert the following:
var doc = webBrowser.Document as mshtml.HTMLDocument;
var scriptNode = doc.createElement("SCRIPT") as mshtml.IHTMLScriptElement;
var nodes = doc.getElementsByTagName("head");
foreach (var head in nodes)
{
var htmlHead = (mshtml.HTMLHeadElement)head;
if (htmlHead != null)
htmlHead.appendChild((mshtml.IHTMLDOMNode)scriptNode);
}
to this:
foreach (var head in nodes.OfType<mshtml.IHTMLDOMNode>())
head.appendChild((mshtml.IHTMLDOMNode)scriptNode);
This also seems to break CLS compliance.
Can anyone offer any insight as to what might be going on here? Because for me, none of this breaks CLS compliance.
PS When I say breaks CLS compliance, 167 warnings are raised about each type in the mshtml namespace that begins with an underscore, or similar CLS violations.
Thanks.

Related

Powershell circular dependencies

I have a scenario where I THINK a circular dependency is the right answer. In my actual code I have a class that contains some data that is used for token replacements. That class contains other classes for the data, and contains a method that does the lookup of token values and returns the value. However, those dependent classes need to be validated, and some of those values are dependent on lookups. So, I have mocked up this code to validate the approach.
class Tokens {
[Collections.Specialized.OrderedDictionary]$primaryData
[Collections.Specialized.OrderedDictionary]$secondaryData
Tokens ([Secondary]$seconday) {
$this.primaryData = [Ordered]#{
'1' = 'One'
'2' = 'Two'
'3' = 'Three'
'4' = 'Four'
'5' = 'Five'
}
$this.secondaryData = $seconday.secondaryData
}
[String] GetToken ([String]$library, [String]$item) {
return $this.$library.$item
}
[Void] SetToken ([String]$library, [String]$item, [String]$value) {
$this.$library.$item = $value
}
[String] ToString () {
[Collections.Generic.List[String]]$toString = [Collections.Generic.List[String]]::new()
foreach ($key in $this.primaryData.Keys) {
$toString.Add("$key : $($this.primaryData.$key)")
}
foreach ($key in $this.secondaryData.Keys) {
$toString.Add("$key : $($this.secondaryData.$key)")
}
return [string]::Join("`n", $toString)
}
}
class Secondary {
[Collections.Specialized.OrderedDictionary]$secondaryData
Secondary () {
$this.secondaryData = [Ordered]#{
'A' = 'a'
'B' = 'b'
'C' = 'c'
'D' = 'd'
'E' = 'e'
}
}
[Void] Update ([Tokens]$tokensReference) {
$tokensReference.SetToken('secondaryData', 'A', 'A')
$tokensReference.SetToken('secondaryData', 'B', "$($tokensReference.GetToken('secondaryData', 'A')) and $($tokensReference.GetToken('secondaryData', 'B'))")
}
[String] ToString () {
[Collections.Generic.List[String]]$toString = [Collections.Generic.List[String]]::new()
foreach ($key in $this.secondaryData.Keys) {
$toString.Add("$key : $($this.secondaryData.$key)")
}
return [string]::Join("`n", $toString)
}
}
CLS
$secondary = [Secondary]::new()
$tokens = [Tokens]::new($secondary)
$secondary.Update($tokens)
Write-Host "$($tokens.ToString())"
This is working, exactly as expected. however, the idea of circular dependency injection has my hair standing on end. Like, it could be a real problem, or at least is a code smell. So, my question is, am I on the right track, or is this a dead end and I just haven't found that end yet? Given that PowerShell isn't "fully" object oriented yet, I imagine there could be some uniquely PS related issues, and everything I have found searching for Powershell Circular Dependency talks about removing them. So far I haven't found anything about when it is appropriate and how to do it well.
And, assuming it is a valid approach, is there anything obvious in this simplified implementation that could lead to problems or could be better done some other way?
EDIT: OK, so perhaps I am going to refine my vocabulary a bit too. I was thinking circular dependency since Secondary is a dependency, or member perhaps, of Tokens, and then I update Secondary from inside Secondary, while referencing data from Secondary, via the method in Tokens.
To clarify (I hope) the ultimate goal, these lookups are for program specific data, which I have in XML files. So, for example the data file for Autodesk Revit 2021 would include these three items
<item id="GUID" type="literal">{7346B4A0-2100-0510-0000-705C0D862004}</item>
<item id="installDataKey" type="registryKeyPath">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[Revit 2021~GUID]</item>
<item id="displayName" type="registryPropertyValue" path="installDataKey">Revit 2021</item>
In actual use I would want to get the DisplayName property found in the key defined in <item id="installDataKey">, and if the value matches the value in <item id="displayName"> then I might also look for the value of the DisplayVersion property in the same key, and make decisions based on that. But because there are new versions every year, and 20+ different software packages to address, managing these data files is a pain. So I want to validate the data I have in the files against a machine that actually has the software installed, to be sure I have my data correct. Autodesk is famous for changing things for no good reason, and often for some very customer hostile reasons. So, things like referencing the GUID as data and reusing it as a token, i.e. the [Revit 2021~GUID] above, saves effort. So during the validation process only, I would want to set the GUID, then do the standard token replacement to convert HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[Revit 2021~GUID] to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{7346B4A0-2100-0510-0000-705C0D862004} and should that key actually be found, use it to validate about 20 other registry and file paths as well as the actual value of DisplayName. If everything validates I will sign the XML, and in actual use signed XML will basically treat everything as a literal and no validation is done, or rather it was prevalidated.
But before use a reference to [product~installDataKey] when the current product is Revit 2021 would resolve first to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[Revit 2021~GUID] then to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{7346B4A0-2100-0510-0000-705C0D862004} at which point the code could use it as a registry path and see if Revit 2021 is in fact installed.
So, 99.9% of the time, I would instantiate Tokens with a constructor that just includes the full dataset and move on. But on the .1% of occasions where I am validating the data itself, I need to be able to read the xml, set the value for that GUID and immediately use the lookup to validate that Autodesk hasn't done something stupid like move some data out of the GUID key and into a secondary key. They have done that before.
I also might want to replace HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall with a token like [windows~uninstallKey] just to make life easier there too.
Hopefully that makes some sense. It's a mess to be sure, but anything to do with Autodesk is a mess.

duplicate behavior in powershell

I was just playing around with hash tables and I did this:
$C = #{color = [System.Collections.ArrayList]#(#{y = 'yellow'},#{y = 'blue'})}
and I ended up with finding I can do:
($C.color.y).Get(1) or ($C.color.y).Item(1) and it does exactly the same thing.
I also found even though the autocomplete doesn't show you can do this you can:
($C.color.y).Get(0).Contains('yellow')
($C.color.y).Get(1).Contains('blue')
The Get and Item methods appear to be interchangeable what is the difference between them?
I did check the hash codes they are the same.
($C.color.y).Get(1).GetHashCode() or ($C.color.y).Item(1).GetHashCode()

Microsoft Access DoCmd.OutputTo not recognizing acOutPutQuery

Right now, I am just working with powershell, but I plan on porting this concept to JScript and using the .NET jsc with ActiveXObject('Access.Application'). I have opened my query using $accessapp.DoCmd.OpenQuery("MyQuery") and I can see that it is loaded using $accessapp.CurrentData.AllQueries("MyQuery"). I would like to use $accessapp.DoCmd.OutputTo(acOutputQuery, "MyQuery",<acFormat>, <file>), but for some reason, I keep getting the error:
Unexpected token 'acOutputQuery' in expression or statement
Just running $accessapp.DoCmd.OutputTo shows that is what is expected:
void OutputTo (AcOutputObjectType, Variant, Variant, Variant, Variant, Variant, Variant, AcExportQuality)
Every resource I have seen, including the Microsoft OutputTo documentation uses the acOutputObjectType in this manner, so I am completely stumped.
Okay, sorry it took me a while to get back. Thanks to #HansUp for leading me down the correct path. I Used the AcOutputObjectType enumeration link he posted as well as the MS Constants Enumeration. I'll give both a powershell example and an MS JScript one. I'll use acOutputQuery and xcFormatTXT as 1 and "MS-DOS" respectively here, but there are many others in the two links above.
powershell:
$acc = New-Object -com Access.Application
$acc.OpenCurrentDatabase("<path_to_file>.accdb")
$acc.DoCmd.OpenQuery("MyQuery")
$acc.DoCmd.OutputTo(1,"MyQuery","MS-DOS", "<path_to_output_file>.txt")
MS JScript:
function writeToTextFile(dbPath,queryName,outputPath){
var acc = new ActiveXObject("Access.Application"); //create new COM instance
acc.OpenCurrentDatabase(dbPath+".accdb"); //open the database
if(!acc.CurrentData.AllQueries(queryName).IsLoaded){
acc.DoCmd.OpenQuery(queryName); //load the query if it is not loaded yet
}
acc.DoCmd.OutputTo(1,queryName,"MS-DOS",outputPath+".txt"); //write to file
}
These two are kind of quick and dirty. Since I have this working in JScript now, I will probably make a writeToFile() function that takes the format as an argument. I considered using an object to map the name string to the enumeration, but I don't know how useful that would actually be. I suppose if you wanted, you could take the output object as an argument, too, but I only plan to use query objects for now.

Swift - Getting Error while using replacingOccurrence

So I'm taking Udacity's Swift for Developers course. I attempted to look at the forums for this question but oddly, they were quiet. This is the programming prompt:
var forwardString = "stressed"
var backwardsString = forwardString.characters.reversed()
print(backwardsString)
var lottaLikes = "If likeyou wanna learn Swift likeyou should build lots of small apps cuz it's likea good way to practice."
var noLikes = lottaLikes.replacingOccurrences(of:"like", with:"")
print(noLikes)
For whatever reason, I keep getting this error message:
Be sure that you have replaced all occurences of the word "like" and removed any extra spaces.
What am I missing here? If you need clarification on this I would be happy to provide it.
Thank you
It may be that your code gets the job done, but only because your variable lottaLikes is written in a weird way. You usually would have two spaces surrounding the word "like" so just removing the word would leave 2 spaces in a row. I would suggest writing the following line:
var noLikes = lottaLikes.replacingOccurrences(of:"like ", with:"")
It may be that Udacity is not checking the actual output, but the code itself. If so, It may be looking for something like I wrote above.
If this still does not work, you may want to write another line like so:
var noExtraSpaces = noLikes.replacingOccurences(of: " ", with: " ")

Powershell returns wrong result

I came across this weird issue in Powershell (not in other languages). Could anyone please explain to me why this happened?
I tried to return a specified number (number 8), but the function keeps throwing everything at me. Is that a bug or by design?
Function GetNum() {
Return 10
}
Function Main() {
$Number10 = GetNum
$number10 #1 WHY NO OUTPUT HERE ??????? I don't want to use write host
$result = 8 # I WANT THIS NUMBER ONLY
PAUSE
return $result
}
do {
$again = Main
Write-Host "RESULT IS "$again # Weird Result, I only want Number 8
} While ($again -eq 10) # As the result is wrong, it loops forever
Is that a bug or by design?
By design. In PowerShell, cmdlets can return a stream of objects, much like using yield return in C# to return an IEnumerable collection.
The return keyword is not required for output values to be returned, it simply exits (or returns from) the current scope.
From Get-Help about_Return (emphasis added):
The Return keyword exits a function, script, or script block. It can be
used to exit a scope at a specific point, to return a value, or to indicate
that the end of the scope has been reached.
Users who are familiar with languages like C or C# might want to use the
Return keyword to make the logic of leaving a scope explicit.
In Windows PowerShell, the results of each statement are returned as
output, even without a statement that contains the Return keyword.
Languages like C or C# return only the value or values that are specified
by the Return keyword.
Mathias is spot on as usual.
I want to address this comment in your code:
$number10 #1 WHY NO OUTPUT HERE ??????? I don't want to use write host
Why don't you want to use Write-Host? Is it because you may have come across this very popular post from PowerShell's creator with the provocative title Write-Host Considered Harmful?
If so, I encourage you to read what I think is a great follow-up/companion piece by tby, titled Is Write-Host Really Harmful?
With this information, it should be clear that as Mathias said, you are returning objects to the pipeline, but you should also be armed with the information needed to choose an alternative, whether it's Write-Verbose, Write-Debug, or even Write-Host.
If I were going to be opinionated about it, I would go with Write-Verbose, altering your function definition slightly in order to support it:
function Main {
[CmdletBinding()]
param()
$Number10 = GetNum
Write-Verbose -Message $number10
$result = 8 # I WANT THIS NUMBER ONLY
PAUSE
$result
}
When you invoke it by just calling $again = Main you'll see nothing on the screen, and $again will have a value of 8. However if you call it this way:
$again = Main -Verbose
then $again will still have the value of 8, but on the screen you'll see:
VERBOSE: 10
likely in differently colored text.
What that gives is not only a way to show the value, but a way for the caller to control whether they see the value or not, without changing the return value of the function.
To drive some of the points in the articles home further, consider that it's not necessarily necessary to invoke your function with -Verbose to get that.
For example, let's say you stored that whole script in a file called FeelingNum.ps1.
If, in addition to the changes I made above, you also add the following to the very top of your file:
[CmdletBinding()]
param()
Then, you still invoked your function "normally" as $again = Main, you could still get the verbose output by invoking your script with -Verbose:
powershell.exe -File FeelingNum.ps1 -Verbose
What happens there is that using the -Verbose parameter sets a variable called $VerbosePreference, and that gets inherited on each function called down the stack (unless it's overridden). You can also set $VerbosePreference manually.
So what you get by using these built-in features is a lot of flexibility, both for you as the author and for anyone who uses your code, which is a good thing even if the only person using it is you.