4

How can I create a reference for a enum declared inside a COM object to pass to a method that requires it? Specifically, there is a method SetHomeDir on a 3rd-party COM object that takes in an enum as the only parameter. The definition that parameter expects is basically:

typedef enum
{
  abFalse = 0,
  abTrue = 1,
  abInherited = -2
} SFTPAdvBool;

It appears to be defined somewhere in the COM object, but not as an object that I can create with New-Object. I have tried the following and they all give the same MX Error: 7 response:

$obj.SetHomeDir($true)
$obj.SetHomeDir(1)
$obj.SetHomeDir([Object]1)
$obj.SetHomeDir([Int16]1)

Exception calling "SetHomeDir" with "1" argument(s): "MX Error: 7 (00000007)"

Here are the results from trying some other approaches:

PS C:\> New-Object -ComObject SFTPCOMINTERFACELib.SFTPAdvBool
New-Object : Cannot load COM type SFTPCOMINTERFACELib.SFTPAdvBool.

PS C:\> [SFTPAdvBool]::abTrue
Unable to find type [SFTPAdvBool]: 
make sure that the assembly containing this type is loaded.

PS C:\> [SFTPCOMINTERFACELib.SFTPAdvBool]::abTrue
Unable to find type [SFTPCOMINTERFACELib.SFTPAdvBool]: 
make sure that the assembly containing this type is loaded.

The method signature that COM exposes to PowerShell looks like this:

PS C:\> $user.SetHomeDir

MemberType          : Method
OverloadDefinitions : {void SetHomeDir (SFTPAdvBool)}
TypeNameOfValue     : System.Management.Automation.PSMethod
Value               : void SetHomeDir (SFTPAdvBool)
Name                : SetHomeDir
IsInstance          : True

Note: This is running under PowerShell 2.0 on Windows Server 2008 R2, but I would consider upgrading the Windows Management Framework to a newer version if necessary.

Update: Here is a screenshot from the Visual Studio object explorer, in case that offers up any clues.

COM Object in Visual Studio Object Explorer

Goyuix
  • 23,614
  • 14
  • 84
  • 128

4 Answers4

3

We ended up using a compiled C# interop wrapper around the COM object. I was able to specify an int as the parameter and just used a case statement to pass the correct value from the enum. As far as I can tell there isn't a way to do this directly from Powershell and requires wrapping the COM object in managed code.

We have opened a dialog with Globalscape and hopefully this will be something they address in a future release.

Goyuix
  • 23,614
  • 14
  • 84
  • 128
1

We can try to fool the com object by creating the enum on your own and pass it to the function:

If you can upgrade to Powershell 5 try (in Powershell - enum is a new keyword in ver 5):

Enum SFTPAdvBool
{
        abFalse = 0
        abTrue = 1
        abInherited = -2
}

And call:

$obj.SetHomeDir([SFTPAdvBool]::abTrue)

for anything older than PS 5 you can try:

$code = @"

namespace SFTPCOMINTERFACELib {

    public enum SFTPAdvBool {

        abFalse = 0,
        abTrue = 1,
        abInherited = -2

    }

}
"@

Add-Type -TypeDefinition $code -Language CSharpVersion3

And call:

$obj.SetHomeDir([SFTPCOMINTERFACELib.SFTPAdvBool]::abTrue)

This will basically create a C# enum and will add it as a type into Powershell.

It-Z
  • 1,961
  • 1
  • 23
  • 33
  • I tried the Add-Type approach and it doesn't work - it complains about a type mismatch as well. Thanks for the suggestion! – Goyuix Jun 20 '16 at 20:00
0

PowerShell v2.0 is archaic at this point, but it shouldn't be stopping you here. SFTPAdvBool appears to be from the GlobalScape EFT Server COM API, so that's what I'm assuming.

The issue is that you need a value of type SFTPAdvBool, according to the C# scripting examples (See ConfigureUser.cs for one use). For a .Net object, you'd define that as [SFTPAdvBool]::abTrue or [SFTPCOMINTERFACELib.SFTPAdvBool]::abTrue, but I'm not sure if that will work. I've not worked with COM enums in PowerShell before. You might need New-Object -ComObject SFTPCOMINTERFACELib.SFTPAdvBool or some variant.

If nothing works, you could use VBScript, or C#, or contact GlobalScape in the hopes they join the 21st century and drop COM, or use WinSCP's .Net library... but I'm betting you can't since you're working someplace that paid for EFT Server.

Bacon Bits
  • 30,782
  • 5
  • 59
  • 66
  • Thanks for the suggestions - we have contacted GlobalScape and are waiting on their response. Unfortunately SFTPAdvBool and the other COM enums aren't exposed as classes in PowerShell such that I can use New-Object or the static member access operator on them - at least that I have been able to divine. – Goyuix Jun 10 '16 at 20:52
0

There are some hints in their knowledge base that the library doesn't always work correctly out of process, so that would apply remotely, or when using the 32-bit library from a 64-bit process.

For example:

The solution would be to use the 32-bit powershell in C:\Windows\SysWOW64\WindowsPowerShell\ If you need to call that remotely you may have to do it explicitly using the & operator.

Ben
  • 34,935
  • 6
  • 74
  • 113
  • The version installed on our server is 32-bit, and as such we do have to use the correct PowerShell version. I didn't see anything in your link that addressed the general problem of referencing an enum defined in a COM library. Could you maybe expand your answer with that information if it is there? – Goyuix Jun 13 '16 at 20:21
  • There is no such problem, the enum should be passed as an int32 as per my comment, that is all, it is simply a set of aliases for particular int values, it has no real type information attached to the variable.... Sometimes people use custom `Invoke` implementations which will also accept other things such as int16 or string, but Int32 should work every time. If it doesn't you have a different problem. – Ben Jun 13 '16 at 20:28
  • I agree it should really just be an int - that is how it is fundamentally stored. That is why I tried a bunch of ways to properly "box" it to match the method signature and hopefully avoid the `MX Error`. I updated the question with what PowerShell sees as the method signature to hopefully shed more light on the subject. – Goyuix Jun 13 '16 at 20:33
  • In the question you say you tried `[int16]` cast. Have you tried `[int32]`? – Ben Jun 13 '16 at 22:54
  • Yes - I tried long and Int 16,32,64 and their unsigned variants for good measure. They all kick back the dreaded `MX Error: 7` I do really appreciate your thoughts and help in walking through this with me. – Goyuix Jun 13 '16 at 23:10
  • Have you tried calling it from VBScript using 32bit cscript.exe? If you can't successfully call from there you won't be able to call from Powershell either. – Ben Jun 14 '16 at 07:21
  • Final thought: I don't think your error is the parameter, I suspect there is some other issue. Perhaps you have to call SetHomeDirString before you can call SetHomeDir? (Speculating). – Ben Jun 14 '16 at 11:42
  • That is a great suggestion for a sanity check. I tried calling it from cscript, and it does work with just the integer value: SetHomeDir(1). Very odd. – Goyuix Jun 14 '16 at 23:52