Is there a way to reference a COM dll in PowerShell? - powershell

I am trying to automate a task on win10 desktop in a software called AxisVM, which luckily has a good COM interface.
My problem is that my current employer does not allow any programming languages to be installed on their PCs, so I am trying to re-implement a short, but functioning python script in PowerShell.
If I was working in e.g. C# then I could reference the interop dll in my project to be able to work with the constants and data types required by AxisVM.
Is there way to have that in PowerShell too?
I need to pass by reference the following struct when I call a function:
RAccelerationValues = (
double avX // acceleration in local x direction [m/s2]
double avY // acceleration in local y direction [m/s2]
double avZ // acceleration in local z direction [m/s2]
double avXX // angular acceleration about local x direction [rad/s2]
double avYY // angular acceleration about local y direction [rad/s2]
double avZZ // angular acceleration about local z direction [rad/s2]
double avR // resultant acceleration [m/s2]
double avRR // resultant angular acceleration [rad/s2] )
I guess this is in a way similar to that if I was automating Excel and was connecting to Excel by $Excel = New-Object -ComObject Excel.Application, would there be a way to use the xl??? constants in PowerShell that are otherwise defined?
EDIT 1
This is what I could write using a lot of resources from the internet:
Add-Type -Path "C:\AxisVM_X5\Interop.AxisVM.FW4.dll"
Add-Type -Path "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll"
$Assem =#(
"C:\AxisVM_X5\Interop.AxisVM.FW4.dll",
"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll"
)
$Source = #"
using System;
using ax = AxisVM;
namespace ConsoleApp1
{
public class Program
{
public static void Main(string[] args)
{
ax.AxisVMApplication axApp = new ax.AxisVMApplication();
while (((ax.IAxisVMApplication)axApp).Loaded == ax.ELongBoolean.lbFalse)
{
System.Threading.Thread.Sleep(1000);
}
axApp.Visible = ax.ELongBoolean.lbTrue; // AxisVM starts hidden, so make it visible
axApp.CloseOnLastReleased = ax.ELongBoolean.lbFalse; // Do not close AxisVM after this code
axApp.AskCloseOnLastReleased = ax.ELongBoolean.lbTrue; // Ask whether close AxisVM after this code
axApp.AskSaveOnLastReleased = ax.ELongBoolean.lbTrue;
axApp.ApplicationClose = ax.EApplicationClose.acEnableNoWarning;
ax.AxisVMModel axModel = axApp.Models.Item[1];
axModel.LoadFromFile("C:\\Users\\x\\Documents\\_AXIS_TESZT\\tesztgerenda.axs");
ax.AxisVMAcceleration axResAcc = axModel.Results.Acceleration;
axResAcc.AnalysisType = ax.EAnalysisType.atDynamic;
ax.RAccelerationValues resout = new ax.RAccelerationValues();
string lcNAme;
int num_of_timesteps = axModel.Results.TimeStepCount[ax.EAnalysisType.atDynamic, 1];
Console.WriteLine(num_of_timesteps);
axResAcc.LoadCaseId = 2;
axResAcc.TimeStep = 1;
axResAcc.NodalAccelerationByLoadCaseId(5, ref resout, out lcNAme);
Console.WriteLine(lcNAme);
}
}
}
"#
Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp
[ConsoleApp1.Program]::Main()
...too bad I will never be able to use it because of the restrictions I have of running power shell scripts

Related

Changing Windows 10 cursor icon with Powershell without reseting

I'm making a startup script for Windows and have been wanting to change my cursor icons. However, I want to do it without a computer reset and through powershell.
Set-ItemProperty -Path "HKCU:\Control Panel\Cursors\Arrow" -Value
"F:\nutty-squirrels\callmezippy_squirrelUnavailble.cur"
I know that it's possible to change the cursor icon without reset using the GUI, but I can't seem to get it to work using scripts, as regedit does not update the cursor (or, at least, it hasn't through my testing.)
I'm thinking that reseting some process would allow the cursor changes to occur, but I have no idea what that process is. If anyone has any idea, it would be a great help!
The Set-ItemProperty call is missing the -Name argument and you need to call the WinAPI function SystemParametersInfo to notify the system about the settings change:
# Define a C# class for calling WinAPI.
Add-Type -TypeDefinition #'
public class SysParamsInfo {
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
const int SPI_SETCURSORS = 0x0057;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;
public static void CursorHasChanged() {
SystemParametersInfo(SPI_SETCURSORS, 0, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
'#
# Change the cursor
Set-ItemProperty -Path 'HKCU:\Control Panel\Cursors' -Name 'Arrow' -Value '%SystemRoot%\cursors\aero_arrow_xl.cur'
# Notify the system about settings change by calling the C# code
[SysParamsInfo]::CursorHasChanged()

Change and update the size of the cursor in Windows 10 via PowerShell

I've written the code below to affect (what I think) are the only reg keys responsible for the size of the cursor and pointer in Windows 10.
Here's the code I have so far (Some additional comments within):
$RegConnect = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"CurrentUser", "$env:COMPUTERNAME")
$RegCursorsAccess = $RegConnect.OpenSubKey("Software\Microsoft\Accessibility", $true)
$RegCursorsControlPanel = $RegConnect.OpenSubKey("Control Panel\Cursors", $true)
# In the code below I'm trying to change the size of the cursor.
$RegCursorsControlPanel.SetValue("CursorBaseSize", 48)
$RegCursorsAccess.SetValue("CursorSize", 3)
$RegCursorsAccess.Close()
$RegConnect.Close()
# This section is where I thought it would update the cursor size.
# Here is where it lists stuff relating to setting and updating any settings changed.
# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
# SPI_SETCURSORS
# 0x0057
# Reloads the system cursors. Set the uiParam parameter to zero and the pvParam parameter to NULL.
$CSharpSig = #'
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(
uint uiAction,
uint uiParam,
uint pvParam,
uint fWinIni);
'#
$CursorRefresh = Add-Type -MemberDefinition $CSharpSig -Name WinAPICall -Namespace SystemParamInfo -PassThru
$CursorRefresh::SystemParametersInfo(0x0057,0,$null,0)
It will change the correct values in the registry.
So if I run this PowerShell code the mouse size in the ease of access setting is at the correct value.
But the cursor doesn't update.
How is it possible to force the update without logging out and back in or restarting the machine.
Here are some related MS links:
WM_SETTINGCHANGE message
SystemParametersInfoA function
EDIT - Some additional info
If I run Process Monitor from Sysinternals and dig deep in there I can see this under the stack summary.
This may lead someone more knowledgeable than me to find how to update the mouse size.
The HKCU\Control Panel\Cursors\(Default) section SettingsHandlers_nt.dll
And this also for the accessibility section. Windows.UI.Accessibility.dll
Here are the settings I used in Process Monitors filter to narrow down the items.
So after a bit of hacking about SystemSettings.exe with cheat engine I found how MS sets the cursor size. It ends up still using SystemParametersInfo but with some undocumented arguments. Try the following :)
SystemParametersInfo(0x2029, 0, 16, 0x01);
to set a cursor size of 16. yup you can go below their minimum of 32, all the way down to 1 :)
But the cursor doesn't update.
After registry value changed, it requires trigger to apply these update.
It can be done using SystemParametersInfo function with SPIF_UPDATEINIFILE and SPIF_SENDCHANGE to writes the new system-wide parameter setting to the user profile and broadcasts the WM_SETTINGCHANGE message after updating the user profile.
SystemParametersInfo(SPI_SETCURSORS, 0, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
The following is a PowerShell command example:
$RegConnect = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"CurrentUser","$env:COMPUTERNAME")
$RegCursors = $RegConnect.OpenSubKey("Control Panel\Cursors",$true)
$RegCursors.SetValue("","Windows Black")
$RegCursors.SetValue("CursorBaseSize",0x40)
$RegCursors.SetValue("AppStarting","%SystemRoot%\cursors\wait_r.cur")
$RegCursors.SetValue("Arrow","%SystemRoot%\cursors\arrow_rl.cur")
$RegCursors.SetValue("Crosshair","%SystemRoot%\cursors\cross_r.cur")
$RegCursors.SetValue("Hand","")
$RegCursors.SetValue("Help","%SystemRoot%\cursors\help_r.cur")
$RegCursors.SetValue("IBeam","%SystemRoot%\cursors\beam_r.cur")
$RegCursors.SetValue("No","%SystemRoot%\cursors\no_r.cur")
$RegCursors.SetValue("NWPen","%SystemRoot%\cursors\pen_r.cur")
$RegCursors.SetValue("SizeAll","%SystemRoot%\cursors\move_r.cur")
$RegCursors.SetValue("SizeNESW","%SystemRoot%\cursors\size1_r.cur")
$RegCursors.SetValue("SizeNS","%SystemRoot%\cursors\size4_r.cur")
$RegCursors.SetValue("SizeNWSE","%SystemRoot%\cursors\size2_r.cur")
$RegCursors.SetValue("SizeWE","%SystemRoot%\cursors\size3_r.cur")
$RegCursors.SetValue("UpArrow","%SystemRoot%\cursors\up_r.cur")
$RegCursors.SetValue("Wait","%SystemRoot%\cursors\busy_r.cur")
$RegCursors.Close()
$RegConnect.Close()
function Update-UserPreferencesMask {
$Signature = #"
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
const int SPI_SETCURSORS = 0x0057;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;
public static void UpdateUserPreferencesMask() {
SystemParametersInfo(SPI_SETCURSORS, 0, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
"#
Add-Type -MemberDefinition $Signature -Name UserPreferencesMaskSPI -Namespace User32
[User32.UserPreferencesMaskSPI]::UpdateUserPreferencesMask()
}
Update-UserPreferencesMask
But unfortunately, the cursor size update doesn't work in this way.
A workaround is using arrow_rl.cur (large image) instead of arrow_r.cur.
Refer to Use PowerShell to Change the Mouse Pointer Scheme, Programmatically change custom mouse cursor in windows.

Can Powershell detect if a specific program is currently playing sound?

I am trying to find a way to detect if one specific program is playing sound. To make a long story short, the computers at my workplace all have a program called ReSoin.exe which should always be playing sound if it is functioning properly. If ReSoin is active but is not playing sound, I need to close and reopen ReSoin. I want to automate this process.
Using the method described by Persistent13 here and my very basic understanding of Powershell, I have created a loop to determine if a Windows machine is playing any sound. If Resoin is active and no sound is playing on the computer, the loop closes and reopens ReSoin.
Add-Type -TypeDefinition #'
using System;
using System.Runtime.InteropServices;
namespace Foo
{
public class Bar
{
public static bool IsWindowsPlayingSound()
{
IMMDeviceEnumerator enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
IMMDevice speakers = enumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
IAudioMeterInformation meter = (IAudioMeterInformation)speakers.Activate(typeof(IAudioMeterInformation).GUID, 0, IntPtr.Zero);
float value = meter.GetPeakValue();
return value > 1E-08;
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
private class MMDeviceEnumerator
{
}
private enum EDataFlow
{
eRender,
eCapture,
eAll,
}
private enum ERole
{
eConsole,
eMultimedia,
eCommunications,
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
private interface IMMDeviceEnumerator
{
void NotNeeded();
IMMDevice GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role);
// the rest is not defined/needed
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D666063F-1587-4E43-81F1-B948E807363F")]
private interface IMMDevice
{
[return: MarshalAs(UnmanagedType.IUnknown)]
object Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams);
// the rest is not defined/needed
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064")]
private interface IAudioMeterInformation
{
float GetPeakValue();
// the rest is not defined/needed
}
}
}
'#
While(1) {
$ReSoinIsActive = Get-Process ReSoin -ErrorAction SilentlyContinue
if($ReSoinIsActive -ne $null) {
$ReSoinIsPlayingSounds = [Foo.Bar]::IsWindowsPlayingSound()
if($ReSoinIsPlayingSounds -eq $False) {
Stop-Process -Name "ReSoin" -Force
Start-Process -FilePath "C:\Program Files (x86)\ReLANpro\Cloud And Class Student\ReSoin.exe"
}else {
# Placeholder used for testing, please ignore
}
}else{
# Placeholder used for testing, please ignore
}
start-sleep -seconds 1
}
This is functional, but it has an obvious flaw. If any other program is playing noise, the computer will assume ReSoin is working. Is there a way to adapt this so that it will only detect if ReSoin is playing noise? I'm an amateur with Powershell and batch scripting, but I'm happy to try other scripting methods if anyone has another idea. If my goal is impossible to achieve, or too difficult to ask of the StackOverflow community, please let me know. Any help would be greatly appreciated.

ActiveX component can't create object (MATLAB Compiler)

I know there are similar questions out there, but this one is a little different (I think).
I used the MATLAB Compiler to convert a .m to an Excel add-in. When I run the add-in on my machine, it works just fine. When I send it to a colleague, they get the "ActiveX component can't create object" error. They have added the add-in no problem.
Is there something going on here that's easily fixed?
MATLAB code:
function mess = createAndRouteOrderWithStyle()
c = startbbrg();
[num,text] = exportToM();
s = emsx('//blp/emapisvc_beta');
order.EMSX_ORDER_TYPE = text(1);
order.EMSX_SIDE = text(2);
order.EMSX_TICKER = text(3);
order.EMSX_AMOUNT = int32(num(1));
%order.EMSX_LIMIT_PRICE = num(2);
order.EMSX_BROKER = text(4);
order.EMSX_HAND_INSTRUCTION = text(5);
order.EMSX_TIF = text(6);
events = createOrderAndRoute(s,order);
mess = events.ERROR_MESSAGE;
close(s);
end
Excel VBA code:
Sub GO()
Cells(10,10).Formula = "=createAndRouteOrderWithStyle()"
End Sub

ExtractAssociatedIcon gives exception in network share using powershell

I am not able to load Icon when accessing from a network share drive in powershell
$IconPath = $pwd.Path + "\Icons\InstallIcon-F.ico"
$HFForm.icon = [System.Drawing.Icon]::ExtractAssociatedIcon($IconPath)
I am getting this error:
Exception calling "ExtractAssociatedIcon" with "1" argument(s): "The given path's format is not supported."
$HFForm.icon = [System.Drawing.Icon]::ExtractAssociatedIcon <<<< ($IconPath)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
My testing shows the same as PeterK's. If I use a drive letter its fine, but an unmapped network share is not.
I was able to make it work by mapping the network share to a drive letter. So:
$something = [system.drawing.icon]::extractassociatedicon("c:\windows\system32\notepad.exe")
caused no errors. Neither did:
$something = [system.drawing.icon]::extractassociatedicon(($test.fullname))
with $test.fullname just being a mapped network file path.
It is a good idea to expand out your variables too so we can see what you're actually passing in. Because if I browse to a network file share and expand $pwd.path:
Microsoft.PowerShell.Core\FileSystem::\\user-pc\users
You are almost certainly passing that in. So have a look. I haven't done much with how Powershell formats its display so I'm sure you can find the 'tty' equiv settings, but just in the meantime do this:
$IconPath = $pwd.Path.split('::')[2] + "\Icons\InstallIcon-F.ico"
Convert your icon to a base64 representation of it's binary data, then store it inside the script itself (as text).
Once it's encoded as base64, you can use this command to convert it back into an icon, bypassing the UNC path issue.
$HFForm.icon = [System.Convert]::FromBase64String('
AAABAAkAAAAAAAEAIABbfQEAlgAAAICAAAABACAAKAgBAPF9AQBgYAAAAQAgAKiUAAAZhgIASEgA
#
# There will be hundreds rows depending on your icon's size.
#
AMADAADAAwAA4AcAAPAPAADwDwAA+A8AAPgPAAD4DwAA/B8AAPwfAAA=')
BASE64 snippet.
#Be sure to edit the path to icon.
#
#Hint the result is copied to your clipboard - clip = clip.exe == google it.
$path = "A:\R2-D2-32x32.ico"
[convert]::ToBase64String((get-content $path -encoding byte)) | Clip
#After running the clip command, right click paste between the quotes
$HFForm.icon = [System.Convert]::FromBase64String('')
i have a solution for it.
At first you have to import the SHGetFileInfo Methode and create the structure SHFILEINFO.
$code = #"
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace System
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
public class SHGETFILEINFO
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes,ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
}
}
"#
Add-Type -TypeDefinition $code
Creates the structure object.
#Path to the exe.
$Path = \\test.de\tes
[System.SHFILEINFO]$FileinfoStruct = New-Object System.SHFILEINFO
Gets the size of the structure
$Size = [System.Runtime.InteropServices.Marshal]::SizeOf($FileinfoStruct)
Gets fills the structure Variable with the File infos.
[System.SHGETFILEINFO]::SHGetFileInfo($Path,0, [ref]$FileinfoStruct,$Size,0x000000100)
Creates the icon.
$ICON = [System.Drawing.Icon]::FromHandle($FileinfoStruct.hIcon)