Call a WIN32 .DLL Fuction

by Hanman at 2013-03-09 05:32:00

I’m a noob to powershell

Would like to be able to write a powershell script to control a DMX Controller USB connected Hardware using its supplied DLL
The controller works fine with applications written in C++ or VB.

In +CC I can use the following to opening the interface

int interfaceOpen;
int numberOfInterface;
unsigned char dmxBlock[512];
DasUsbCommand(DHC_INIT,0, NULL);
interfaceOpen = DasUsbCommand(DHC_OPEN,0,0);


To send a signal

if (interface_open>0){
DasUsbCommand(DHC_DMXOUT, 512, dmxblock);
}


To Close the Interface

if (interface_open>0)
DasUsbCommand(DHC_CLOSE,0,0);
DasUsbCommand(DHC_EXIT,0, NULL);


Is this possible? I searched a lot on how to call a DLL function in powershell without sucess?

Thanks for the help
by MattG at 2013-03-09 06:39:44
You absolutely can call unmanaged DLL functions in PowerShell although it’s not extremely straightforward. What I’m about to describe is an advanced technique. Although, since you’re already familiar with C/C++, the concepts should translate easily. The gist of it is that you need to compile C# code which declares the function signature and applies a DllImport attribute to the DLL you’re interested in. As an example, say I want to call the user32.dll MessageBox function. This is accomplished with the following C#/PowerShell code:
$Code = @‘
using System;
using System.Runtime.InteropServices;

public class Win32
{
[DllImport(“C:\Windows\System32\user32.dll”, CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
}
’@

Add-Type -TypeDefinition $Code

[Win32]]::Zero, ‘What is your choice?’, ‘Test Message’, 3)
In your case where you are trying to import DasUsbCommand, you will need to provide the path to the DLL that exports the function as well as the function signature. Since you’re using enum values in the signature (or possibly symbolic constants), you would also need to define an enum in the C# code block or you could just opt to provide the raw values to the function. As an example, based on my the documentation I’ve read on your dll, I would probably implement the code you provided with the following script:
$Code = @‘
using System;
using System.Runtime.InteropServices;

public class YourDll
{
[DllImport(“C:\PathToDLL\DasHard2006.dll”, CharSet = CharSet.Auto)]
public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
}
’@

Add-Type -TypeDefinition $Code

$numberOfInterface = 0
$dmxBlock = New-Object Char[](512)
[YourDll]::DasUsbCommand($DHC_INIT,0, $null)
$interfaceOpen = [YourDll]::DasUsbCommand($DHC_OPEN,0,0)

if ($interfaceOpen -gt 0)
{
[YourDll]::DasUsbCommand($DHC_DMXOUT, 512, $dmxBlock)
}

if ($interfaceOpen -gt 0)
{
[YourDll]::DasUsbCommand($DHC_CLOSE,0,0)
[YourDll]::DasUsbCommand($DHC_EXIT,0, $null)
}

Your mileage may vary with the code I provided since I had no way of testing it. But if it works out for you, great!

In summary, you need to perform the following steps in order to interface with exported functions from unmanaged DLLs:

1) Define the equivalent function signature in C# for your function. Note: Sometimes, you won’t get the parameters to the function right the first time and you may need to tweak them to get the working - e.g. char[] vs. string, uint vs. int, etc.
2) Apply the appropriate parameters to the DllImport attribute.
3) Call the Add-Type cmdlet to compile the code.
4) Hopefully, you’re off and running. If you run into issues, MSDN is your friend. :smiley:

I hope this helps!
by Hanman at 2013-03-09 22:47:47
Thanks Matt for the detailed responce. I beleive I understand what you have done in terms of defining the function structure.
I tried your suggested scrpit , However It would not compile… It seems the problem starts with calling with the Add-Type… See bellow

The code I tried is the same as your suggestion , I merely changed the Dllname to MyDll
I do not underatsnd how to debug the code based on the bellow. It seems the problem lies with location of the dll or is it the parameters we defined


Add-Type : c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : Unrecognized escape sequence
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(5) : {
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : >>> [DllImport(“C:\siudi\siudiUsb_developerkit\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(7) : public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:12 char:1
+ Add-Type -TypeDefinition $Code
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (c:\Users\Hani H…escape sequence:CompilerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand

Add-Type : c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : Unrecognized escape sequence
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(5) : {
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : >>> [DllImport(“C:\siudi\siudiUsb_developerkit\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(7) : public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:12 char:1
+ Add-Type -TypeDefinition $Code
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (c:\Users\Hani H…escape sequence:CompilerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand

Add-Type : c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : Unrecognized escape sequence
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(5) : {
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : >>> [DllImport(“C:\siudi\siudiUsb_developerkit\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(7) : public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:12 char:1
+ Add-Type -TypeDefinition $Code
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (c:\Users\Hani H…escape sequence:CompilerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand

Add-Type : c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : Unrecognized escape sequence
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(5) : {
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(6) : >>> [DllImport(“C:\siudi\siudiUsb_developerkit\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
c:\Users\Hani Hamad\AppData\Local\Temp\sdr3kj1l.0.cs(7) : public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:12 char:1
+ Add-Type -TypeDefinition $Code
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (c:\Users\Hani H…escape sequence:CompilerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand

Add-Type : Cannot add type. There were compilation errors.
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:12 char:1
+ Add-Type -TypeDefinition $Code
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:slight_smile: [Add-Type], InvalidOperationException
+ FullyQualifiedErrorId : COMPILER_ERRORS,Microsoft.PowerShell.Commands.AddTypeCommand

Unable to find type [MyDll]: make sure that the assembly containing this type is loaded.
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:16 char:1
+ [MyDll]::DasUsbCommand($DHC_INIT,0,$null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MyDll:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

Unable to find type [MyDll]: make sure that the assembly containing this type is loaded.
At C:\Users\Hani Hamad\Desktop\dmxtest.ps1:17 char:1
+ $interfaceOpen = [MyDll]::DasUsbCommand($DHC_OPEN,0,0)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MyDll:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound


Thanks again for your help
by MattG at 2013-03-10 06:24:14
You need to escape the backslashes in the path to the dll. This should work for you:
$Code = @‘
using System;
using System.Runtime.InteropServices;

public class YourDll
{
[DllImport(“C:\siudi\siudiUsb_developerkit\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
public static extern int DasUsbCommand(uint command, uint param, Char[] bloc);
}
’@

Add-Type -TypeDefinition $Code

$numberOfInterface = 0
$dmxBlock = New-Object Char[](512)
[YourDll]::DasUsbCommand($DHC_INIT,0, $null)
$interfaceOpen = [YourDll]::DasUsbCommand($DHC_OPEN,0,0)

if ($interfaceOpen -gt 0)
{
[YourDll]::DasUsbCommand($DHC_DMXOUT, 512, $dmxBlock)
}

if ($interfaceOpen -gt 0)
{
[YourDll]::DasUsbCommand($DHC_CLOSE,0,0)
[YourDll]::DasUsbCommand($DHC_EXIT,0, $null)
}
by Hanman at 2013-04-24 05:11:39
I finally got the code to work (80%)

I can see the hardware, read the firmware serial number , open it… But get an error when i try to set the output

The code now is as follows


using System;
using System.Runtime.InteropServices;
public class MyDll
{
[DllImport(“C:\siudi\siudiUsb_developerkit5\src\DasHard2006.dll”, CharSet = CharSet.Auto)]
public static extern int DasUsbCommand(uint command, uint param, byte bloc);
}
’@
Add-Type -TypeDefinition $Code

$DHC_INIT = 9
$DHC_EXIT = 10
$DHC_OPEN = 1
$DHC_CLOSE = 2
$DHC_DMXOUTOFF = 3
$DHC_DMXOUT = 4
$DHC_DMX2OUT = 13
$DHC_PORTREAD = 5
$DHC_VERSION = 7
$DHC_SERIAL = 14
$DHC_DMXENABLE = 16
$DHC_DMX2ENABLE = 12
$DHE_OK = 1
$DHE_ERROR_COMMAND = -1
$DHE_ERROR_NOTOPEN = -2

$channel_select = [int]
$hardware_ok = [int]
$channel = [int]
$result = [int]
$command = [int]
$dmxBlock = [byte]

$channel = 1
$dmxBlock = 0xff

$interfaceOpen = 0
$numberOfInterface = 0

$ban = [MyDll]::DasUsbCommand($DHC_INIT,0,0)
Write-Host “DLL is version” $ban
$interfaceOpen = [MyDll]::DasUsbCommand($DHC_OPEN,0,0)
Write-Host "Interface Open status is " $interfaceOpen
$san = [MyDll]::DasUsbCommand($DHC_VERSION,0,0)
Write-Host "Serial Number is " $san
$fan = [MyDll]::DasUsbCommand($DHC_SERIAL,0,0)
Write-Host "Firmware Version is " $fan




if ($interfaceOpen -gt 0)
{

$CAN = [MyDll]::DasUsbCommand($DHC_DMXOUTOFF,0,0)
Write-Host “All Output off Status” $CAN

}

if ($interfaceOpen -gt 0)
{

$lan = [MyDll]::DasUsbCommand($DHC_DMXOUT,$channel,$dmxBlock)
Write-Host "Sent to Channel # " $channel “Value” $dmxBlock "Status is " $lan


}

if ($interfaceOpen -gt 0)
{
[MyDll]::DasUsbCommand($DHC_CLOSE,0,0)
[MyDll]::DasUsbCommand($DHC_EXIT,0,0)
}



The output comes as

DLL is version 136
Interface Open status is 1
Serial Number is 108
Firmware Version is 262568
All Output off Status 1
Sent to Channel # 1 Value 255 Status is -1
1
1

The -1 indicates an error…

My guess is that the way we defined the unsigned char bloc is the problem.


The way the DLL is declared in a DEMO app that came with the hardware is

[code2=cpp]#include "_dashard.h"
extern int HardDllCommand(int command, int param, unsigned char *bloc);
extern int ref_open;[/code2]

I appreciate it if you can take a look and maybe spot something wrong

If i can sort that out, I will then add the loops to cycle through the channels and set the output

Thanks
by MattG at 2013-04-24 07:46:41
It looks like people treat the bloc parameter as a pointer to an unsigned byte array. Try changing your function declaration to the following:
$Code = @‘
using System;
using System.Runtime.InteropServices;

public class YourDll
{
[DllImport("C]
public static extern int DasUsbCommand(uint command, uint param, ref Byte bloc);
}
’@
Then try making the following, slightly modified call:
$lan = [MyDll]]$dmxBlock)
by Hanman at 2013-04-24 23:33:42
Thanks a lot for the prompt answer.

That did not work , Once i call the DLL with [ref] added I get an error.

I looked through the documentation again and bloc is described as a pointer to the DMX block of memory to send or read. so you are correct


block is not used in the the line bellow:

$ban = [MyDll]]

Before the last amendment, the above worked, now after the pointer [ref], it does not …

Argument: ‘3’ should be a System.Management.Automation.PSReference. Use [ref].
At D:\Folders\PS\dmxtest final 77 2.ps1:45 char:1
+ $ban = [MyDll]::DasUsbCommand($DHC_INIT,0,0)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:slight_smile: [], MethodException
+ FullyQualifiedErrorId : NonRefArgumentToRefParameterMsg


I have a pdf document describing the DLL and I have a sample application that works in C++ and another in VB. The one in VB uses another DLL

I wonder if I can send you an e-mail with these files to take a look. the support online for this Hardware is limited to this PDF file

Thanks
by Hanman at 2013-04-25 03:00:29
By the way, There is a link online for the Developer Kit with C++ samples

http://www.dmxsoft.com/global/ftp/siudi_usb_developerkit.exe

Thanks