2

Good morning/day/evening!

I have recieved dll which has to retrieve Tags from SCADA system (Indusoft Web Studio). It came alongside with VC++ and VB samples that actually work perfectly fine. Currently i need to get those values and show them on web (using ASP.NET). I decided to use C# for values processing from SCADA to HTML (well, actually Microsoft ASP.NET guides kind of adviced to do it). And this is where i got stuck, i cannot make the function work.

I created class for the imported DLL, here what it looks like:

using System.Runtime.InteropServices;

namespace TagAccess
{
    public class ISRW
    {
        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", CharSet = CharSet.Auto )]
        public static extern string UNReadString([MarshalAs(UnmanagedType.BStr)] string szTagName);
    }
}

Unfortunatelly, when i try to call this function it gives me:

    Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\Users\Denis\Documents\Visual Studio 2013\Projects\TagAccess\TagAccess\bin\Debug\TagAccess.vshost.exe'.

    Additional information: A call to PInvoke function 'TagAccess!TagAccess.ISRW::UNReadString' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the
calling convention and parameters of the PInvoke signature match the target unmanaged signature.

What works perfectly fine in C looks like:

CString CISRWExt::UNReadString(LPCTSTR szTagName)
{
    CString result;
    static BYTE parms[] =
        VTS_BSTR;
    InvokeHelper(0x1, DISPATCH_METHOD, VT_BSTR, (void*)&result, parms,
        szTagName);
    return result;
}

Any suggestions? Thanks in advance.

A little extra for the info above. I have imported another function from DLL, now the class looks like this:

namespace TagAccess
{


    public class ISRW
    {
        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", EntryPoint = "#1", CharSet = CharSet.Unicode,  SetLastError = true, CallingConvention = CallingConvention.Winapi, ThrowOnUnmappableChar = true )]
        public static extern string UNReadString([MarshalAs(UnmanagedType.BStr)] string szTagName);

        [DllImport("C:\\Windows\\SysWOW64\\ISRWExtDLL.dll", CharSet = CharSet.Auto)]
        public static extern string UNWriteString([MarshalAs(UnmanagedType.BStr)] string szTagName, [MarshalAs(UnmanagedType.BStr)] string szValue);

      }
}

Write Function (UNWriteString) actually writes values from C# into SCADA (i can see values being changed in SCADA viewer), but right after it works fine i get another error:

An unhandled exception of type 'System.NullReferenceException' occurred in mscorlib.dll

Additional information: Object reference not set to an instance of an object.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
IIy333o
  • 33
  • 5

3 Answers3

2

What works perfectly fine in C looks like:

It is not C, it is C++. An instance method of a C++ class named CISRWExt. Pinvoking C++ class methods is not possible, you cannot properly call the constructor and destructor of the class. Only a C++ compiler can do that, you need to write a wrapper in the C++/CLI language.

The method you showed however is an auto-generated method. It was produced by a Visual Studio wizard for MFC, from the type library of a COM server. Which is a fine way to use a COM server from C++. It is not a fine way to use it from C#.

You should entirely bypass this ISRWExtDLL.dll wrapper and use the COM server directly. C# has excellent support for COM servers with a type library, you can simply use Project + Add Reference. The only thing you'll have to find out is where this type library is located. Judging from the vendor's documentation, you should be able to find it back in c:\windows\syswow64\ISRWExt.OCX. Give them a call if you need help.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

You stated that you can call from C++ like this:

CString CISRWExt::UNReadString(LPCTSTR szTagName)
{
    CString result;
    static BYTE parms[] =
        VTS_BSTR;
    InvokeHelper(0x1, DISPATCH_METHOD, VT_BSTR, (void*)&result, parms,
        szTagName); //<--- this is a COM dispatch call
    return result;
}

that mean the function is callable via COM Interop, not PInvoke. Calling with COM interop would probably solve some trouble. Try to bypass the C++ dll and directly invoke the COM object via a COM interop wrapper.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
0
CString CISRWExt::UNReadString(LPCTSTR szTagName)

First of all, this is C++ rather than C.

This C++ function cannot be called from C#. If the function is a non-static member function, then you should know that C++ classes cannot be consumed from C#. And even if the function is a static member function, it returns CString. That's an unmanaged C++ class that cannot be consumed by C# code.

You will need to create a wrapper for this DLL. The obvious way to do that is to create a mixed mode C++/CLI class library that wraps the unmanaged C++ library.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490