Startsidan  ▸  Texter  ▸  Teknikblogg

Anders Hesselbom

Programmerare, skeptiker, sekulärhumanist, antirasist.
Författare till bok om C64 och senbliven lantis.
Röstar pirat.

Adding references to custom types

In a great number of blog posts, I have used the Load function of System.Reflection.Assembly to load types from the .NET Framework. This (without line break) is an example that gives you access to the Forms library…

[System.Reflection.Assembly]::Load("System.Windows.Forms, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089")

…so that calls like this is possible:

[System.Windows.Forms.MessageBox]::Show("Hello!")

(To see the available assemblies, check the assembly folder in your Windows folder.)

When you use the Add-Type cmdlet to create your own types, the string that describes the type might need references as well. The required assemblies are specified in an array, and passed to the cmdlet as the -ReferencedAssemblies argument. This code adds a custom type…

$references = @("System.Windows.Forms")
Add-Type -ReferencedAssemblies $references @'
public class Test
{
   public void hej()
   {
      System.Windows.Forms.MessageBox.Show("Hello!");
   }
}
'@

…gives you a code that when invoked, does the same thing:

$test = New-Object Test
$test.hej()

The problem will show up if you have made any changes to your type and run your program again. The type called Test will already be loaded, so the Add-Type cmdlet will fail.

(If you run your program again, without changing your custom type definition, Add-Type will just be ignored – your type is there and everything is fine.)

Since types cannot be unloaded, you will need to unload your entire appdomain, and the easiest way to do this, is to close your PowerShell window. So any changes to a custom type, will be reflected in a new instance of PowerShell, thus in a new appdomain.

Environment variables

Assuming that you are running Windows 7, the environment variables can be manually accessed if you right click on Computer, and select Properties. From there, you click Advanced system settings and on the Advanced tab, you click the Environment variables button. You can check your current variables, edit them and add new variables.

I am running a Swedish installation of Windows 7.

To read them from Visual Basic, you just use the GetEnvironmentVariables function to retrieve the collection of registered variables. This example is written in VBx (Visual Basic 10):

Console.WriteLine(
   Environment.GetEnvironmentVariables().
      Item("PROCESSOR_REVISION").ToString())

Remember, when you write code to manipulate this collection, adding or changing values, you would normally want your code to run as a custom step in an MSI file, and not in a regular EXE.

Visit ACNE, view source

Visit http://www.acnestudios.com/ and view the source of their web page. There you have a small challenge for a web developer.

Thanks for the tip, Staffan!

Calling C functions

Like I mentioned earlier, functions that only are exposed through an external .NET interface are unreachable from PowerShell, but can be called using reflection. But how about C functions?

This is not a problem at all. Like always when you attempt to call a C function in a DLL, you must know how it is exposed. This is not unique for PowerShell. The name of the link library, the name of the entry point, and the parameter types. This gives you a huge number of functionality ready to call, including third party libraries and the Windows API.

I am going to use PowerShell to move the mouse cursor to location 100, 100 (left, top). The SetCursorPos function takes two integer values (X and Y) and returns a boolean. According to the Windows API documentation, the function resides in a library called User32.dll.

One way to do this, is to write Visual Basic code, use PowerShell to compile it to an assembly, and then use reflection to call it. But there is another way.

Create one array of arguments, and an array of argument types. In my case, since I’m calling the SetCursorPos function, this is what I need:

$parameterTypes = [UInt32], [UInt32]
$parameters = 100, 100

Then, the rest of the code is something written by Jouko Kynsijärvi, that Lee Holmes used to make the Invoke-Win32 function. This is my complete code. Three lines! Check this out!

#The parameter types.
$parameterTypes = [Int], [Int]

#The arguments.
$parameters = 100, 100

#The call. Library, return type, function name, types and values.
Invoke-Win32 "User32.dll" ([Bool]) "SetCursorPos" $parameterTypes $parameters

The rest of the listing is the definition of the Invoke-Win32 function that I extracted from this Lee Holmes’ post.

As you run this script, note how the mouse cursor jumps up to the upper left corner, 100 pixels from the left edge, 100 pixels from the top.

(I have a huge smile across my face when I’m writing this.)

.NET and speed: An int is just an int

So what is true about .NET and preformance? As long as you treat the an int as an int, there is no COM overhead. From a new post by Roger Alsing:

.NET have specific IL op codes in order to deal with primitives, and those opcodes will translate to pure machine code.

Also:

The value hex 7B (dec 123) is moved directly into the memory slot for the local variable.

Check out Roger’s blog.

Categories: Microsoft .NET



En kopp kaffe!

Bjud mig på en kopp kaffe (20:-) som tack för bra innehåll!

Bjud på en kopp kaffe!

Om...

Kontaktuppgifter, med mera, finns här.

Följ mig

Twitter Instagram
GitHub RSS

Public Service

Folkbildning om public service.

Hem   |   linktr.ee/hesselbom   |   winsoft.se   |   80tal.se   |   Filmtips