1

I have been able to get the following code to work and would like to understand more about STRUCT and New-Object.

If I move the STRUCT (MyRect) inside of the CLASS (MyClass), how would I reference it? Right now, it is as follow (outside of the CLASS and at the same level) and it is works.

$Rectangle = New-Object MyRECT
    

I have tried moving it inside the CLASS but it errored out. Most likely a Struct should always be at the same level as a Class right? Regardless, is there a proper way of declaring this?

$Rectangle = New-Object [MyClass]::MyRECT

If there is anything that you would like to point out, in terms of practices, please let me know, such as which of the two methods below is better to use? Thanks

clear-host

$code = 
@'
    using System;
    using System.Runtime.InteropServices;
    
    public class MyClass
    {
        [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, out MyRECT lpRect);

        public static MyRECT Test3(int intHWND)
        {
            MyRECT TT = new MyRECT();
            GetWindowRect((System.IntPtr) intHWND, out TT);
            
            return TT;
        }
    }

    public struct MyRECT
    {
        public int      Left;        // x position of upper-left corner
        public int      Top;         // y position of upper-left corner
        public int      Right;       // x position of lower-right corner
        public int      Bottom;      // y position of lower-right corner
    }
'@
Add-Type -TypeDefinition $Code

[Int]$HWND = (Get-Process -ID 9768).MainWindowHandle
$HWND

$oTest3 = [MyClass]::Test3($HWND)
$oTest3.Left
$oTest3.Top
$oTest3.Right
$oTest3.Bottom

$Rectangle = New-Object MyRECT
$null = [MyClass]::GetWindowRect([IntPtr]$HWND,[ref]$Rectangle)

$Rectangle.Left
$Rectangle.Top
$Rectangle.Right
$Rectangle.Bottom
  • 3
    Moving `MyRECT` inside the class is perfectly legal, but it would make the type name `MyClass+MyRECT`, which is then what you have to use in PowerShell (which is clumsy, but by design: you're mostly not expected to use nested types outside their declaring type). Despite how C# makes things appear, `MyRECT` does not in any way become a "member" of the class, it's just a scoping thing. – Jeroen Mostert Sep 08 '22 at 20:49
  • 1
    If it were me, I'd wrap everything up in the class, exposing its data as properties to the PowerShell code (while keeping the details of the struct private). I don't think you need to P/Invoke GetWindowRect to get the rect of an arbitrary window (though that probably means loading WinForms). You may want to look at my answer here: https://stackoverflow.com/questions/54834518/how-to-create-class-library-with-interfaces-and-or-methods-used-by-windows-and/54836589#54836589 if you want to create a full-on PowerShell module – Flydog57 Sep 08 '22 at 20:55
  • @JeroenMostert and @FlyDog57, I am learning both PowerShell and C#. My main platform is SQL Server. As you can see from this code sample that I have provided, I learned how to do it from PowerShell calling an Win32 API but I spent the entire last night on learning how to do it through C# as well. Pat on my back because was very happy to get it to work and it was a last minute "what if" moment. So some of the terminologies are still new to me. I am not sure what P/Ivoke is but will look into it and your provided link. BTW...`MyClass+MyRECT` worked. Thank You for providing that! – LiquidMetal Sep 08 '22 at 21:26
  • @Flydog57, I looked at your provided link. I like it. Your method is a PowerShell class right? And what you are recommending is to to do it in C# class like the answer (@Rufus) below yours in that link right? Also, I did see lots of P/Invoke through my research yesterday. Can you just give me a brief meaning of the method? Thank You – LiquidMetal Sep 08 '22 at 21:31
  • 1
    Yeah, the link is a way to create a full-on PowerShell module (one you load into a PowerShell session just like any other module). If all you are doing is something simple, it's probably not worth the effort. But, if you are building a bunch of functionality, it's a very clean way of working in C#, but letting your users consume your work within PowerShell. The example I show is trivial. The only time I did this was to create a PowerShell configuration scripting language. Each operation updated configuration information inside SQL server that the main application consumed. – Flydog57 Sep 08 '22 at 21:50
  • Thank You @FlyDog57. Yes, I am new to this but I am building a library of functions. That is what I did with my VBScript library functions and plan to do the same thing with PowerShell. You pointing out this practice will save me lots of headaches for the future. Again, Thank You. – LiquidMetal Sep 08 '22 at 21:55
  • 1
    Follow the Red Gate link in my linked answer (https://www.red-gate.com/simple-talk/dotnet/net-development/using-c-to-create-powershell-cmdlets-the-basics/). It's a pretty simple tutorial. If you do it right, PowerShell is a wonderful scripting language that you can coopt to your needs. – Flydog57 Sep 08 '22 at 21:58

1 Answers1

1

Thanks to Jeroen Mostert, I am now able to move the STRUCT inside the class and reference it in PowerShell as:

$Rectangle = New-Object MyClass+MyRECT

clear-host

$code = 
@'
    using System;
    using System.Runtime.InteropServices;
    
    public class MyClass
    {
        [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, out MyRECT lpRect);

        public struct MyRECT
        {
            public int      Left;        // x position of upper-left corner
            public int      Top;         // y position of upper-left corner
            public int      Right;       // x position of lower-right corner
            public int      Bottom;      // y position of lower-right corner
        }

        public static MyRECT Test3(int intHWND)
        {
            MyRECT TT = new MyRECT();
            GetWindowRect((System.IntPtr) intHWND, out TT);
            
            return TT;
        }
    }

'@
Add-Type -TypeDefinition $Code

[Int]$HWND = (Get-Process -ID 9768).MainWindowHandle
$HWND

$oTest3 = [MyClass]::Test3($HWND)
$oTest3.Left
$oTest3.Top
$oTest3.Right
$oTest3.Bottom

$Rectangle = New-Object MyClass+MyRECT
$null = [MyClass]::GetWindowRect([IntPtr]$HWND,[ref]$Rectangle)

$Rectangle.Left
$Rectangle.Top
$Rectangle.Right
$Rectangle.Bottom