Click here to Skip to main content
15,885,366 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm developing an application which communicates with a Siemens S7 PLC via OPC.
The main application needs to be compiled for x64 architecture in .NET 4.0 and is
written in C# (VS 2010).

For the communication to the OPC server i'm using the OpcRcwDa.dll which is included
with the Simatic NET package of Siemens.

I used the example code from the Simatic SDK (asyncnet) and when i build this one with for x86 architecture everything works fine, but when i build it, with x64 architecture, i get the same Exception as in my application: "AccessViolationException, attempted to read or write protected memory".

I'm really desperate and it would be nice when someone could help me. Down here i put a code snippet of the adding of some items to the OPC-connection, the function in which the exception occures.

C#
public bool StartOPCConnection()
        {
            bool retval = false;
            // Local variables
            Type svrComponenttyp;

            // initialize output parameters.
            IntPtr pResults = IntPtr.Zero;
            IntPtr pErrors = IntPtr.Zero;
            // Initialise Group properties
            int bActive = 1;
            int dwRequestedUpdateRate = 250;
            int hClientGroup = 0;
            int dwLCID = iLOCALE_ID;
            int pRevUpdateRate;
            int TimeBias = 0;
            float deadband = 0;

            // Access unmanaged COM memory
            GCHandle hTimeBias, hDeadband;

            hTimeBias = GCHandle.Alloc(TimeBias, GCHandleType.Pinned);
            hDeadband = GCHandle.Alloc(deadband, GCHandleType.Pinned);

            // 1. Connect to the local server.
            // Get the Type from the progID and create instance of the OPC Server COM
            // component
            Guid iidRequiredInterface = typeof(IOPCItemMgt).GUID;
            svrComponenttyp = System.Type.GetTypeFromProgID(this.sLOCAL_SERVER);


            try
            {
                pIOPCServer = (IOPCServer)System.Activator.CreateInstance(svrComponenttyp);
                try
                {
                    OPCITEMDEF[] ItemDefArray;
                    /* 2. Add a new group
                        Add a group object and querry for interface IOPCItemMgt
                        Parameter as following:
                        [in] not active, so no OnDataChange callback
                        [in] Request this Update Rate from Server
                        [in] Client Handle, not necessary in this sample
                        [in] No time interval to system UTC time
                        [in] No Deadband, so all data changes are reported
                        [in] Server uses english language to for text values
                        [out] Server handle to identify this group in later calls
                        [out] The answer from Server to the requested Update Rate
                        [in] requested interface type of the group object
                        [out] pointer to the requested interface
                    */
                    pIOPCServer.AddGroup(sGROUP_NAME,
                        bActive,
                        dwRequestedUpdateRate,
                        hClientGroup,
                        hTimeBias.AddrOfPinnedObject(),
                        hDeadband.AddrOfPinnedObject(),
                        dwLCID,
                        out nSvrGroupID,
                        out pRevUpdateRate,
                        ref iidRequiredInterface,
                        out pobjGroup1);

                   
                    // Initialize all IO interface pointers.
                    InitReqIOInterfaces();
                    /* 3. Add items to the group */
                    ItemDefArray = new OPCITEMDEF[uiAMOUNT_OF_ITEMS];
                    for (int iArrayIndex = 0; iArrayIndex < uiAMOUNT_OF_ITEMS; iArrayIndex++)
                    {
                        ItemDefArray[iArrayIndex].szAccessPath = "";             // Accesspath not needed for this sample
                        ItemDefArray[iArrayIndex].szItemID = saItemNames[iArrayIndex];     // ItemID, see above
                        ItemDefArray[iArrayIndex].bActive = 1;              // item is active
                        ItemDefArray[iArrayIndex].hClient = iArrayIndex;              // client handle
                        ItemDefArray[iArrayIndex].dwBlobSize = 0;              // blob size
                        ItemDefArray[iArrayIndex].pBlob = IntPtr.Zero;    // pointer to blob
                        ItemDefArray[iArrayIndex].vtRequestedDataType = 6;              // set real as datatype 
                    }


                    try
                    {
                        OPCITEMDEF[] tempItemDefArray = new OPCITEMDEF[uiAMOUNT_OF_ITEMS];

                        for (int idx = 0; idx < uiAMOUNT_OF_ITEMS; idx++)
                        {
                            tempItemDefArray[idx] = ItemDefArray[idx];
                        }


                        // Add items to group
                        lock ((IOPCItemMgt)pobjGroup1)
                        {
                            ((IOPCItemMgt)pobjGroup1).AddItems(uiAMOUNT_OF_ITEMS, tempItemDefArray, out pResults, out pErrors);
                        }
                        // Unmarshal to get the server handles out fom the m_pItemResult
                        ItemSvrHandleArray = new int[uiAMOUNT_OF_ITEMS];

                        // after checking the errors
                        int[] errors = new int[uiAMOUNT_OF_ITEMS];
                        IntPtr pos = pResults;

                        Marshal.Copy(pErrors, errors, 0, uiAMOUNT_OF_ITEMS);

                        for (int idx = 0; idx < uiAMOUNT_OF_ITEMS; idx++)
                        {
                            if (errors[idx] == 0)
                            {
                                OPCITEMRESULT[] result = new OPCITEMRESULT[uiAMOUNT_OF_ITEMS];

                                result[idx] = new OPCITEMRESULT();
                                result[idx] = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));
                                pos += Marshal.SizeOf(typeof(OPCITEMRESULT));

                                ItemSvrHandleArray[idx] = result[idx].hServer;
                                //Set returnvalue to true, because startup went OK
                                retval = true;
                            }
                            else
                            {
                                pIOPCServer.GetErrorString(errors[idx], iLOCALE_ID, out sErrors[idx]);
                                retval = false;
                            }

                        }
                        // Destroy indirect structure elements
                        Marshal.DestroyStructure(pos, typeof(OPCITEMRESULT));

                    }
                    catch (AccessViolationException error) // catch for error in adding items.
                    {
                        lc.AddLog(LoggingState.EXCEPTION, error.ToString());
                        retval = false;
                    }
                  
                }
                catch (System.Exception error) // catch for error in creation group
                {
                    lc.AddLog(LoggingState.EXCEPTION, error.ToString());
                    retval = false;
                }
                finally
                {
                    if (hDeadband.IsAllocated) hDeadband.Free();
                    if (hTimeBias.IsAllocated) hTimeBias.Free();
                }

            }
            catch (System.Exception error) // catch for error in creating server
            {
                lc.AddLog(LoggingState.EXCEPTION, error.ToString());
                retval = false;
            }
            return retval;
        }//End StartOPCConnection()
Posted
Comments
Sergey Alexandrovich Kryukov 29-Apr-13 15:40pm    
In what line of your code?
—SA
Eric Gerbers 29-Apr-13 15:57pm    
((IOPCItemMgt)pobjGroup1).AddItems(uiAMOUNT_OF_ITEMS, tempItemDefArray, out pResults, out pErrors);
Bernhard Hiller 30-Apr-13 3:11am    
What about Marshalling issues? That dll from Siemens is not developed in Managed C++, is it? So: is the definition of tempItemDefArray correct? And pResults is an IntPtr initialized to 0, what is it expected to hold, are you sure you do not need to initialize an array for it? Same for pErrors.

The problem is:
There are different versions of the interop dlls:
The old ones were: OpcRcw.Comm, OpcRcw.Da (and others starting with OpcRcw.* depending on the OPC functionality used).
With the new OPC Core Components Redistributable (x64) 105.1 comes a new Interop Dll:
OpcComRcw.dll which is to replace all the other OpcRcw.*.dll.
Reading the release notes for build 105.1 carefully, I finally found the following line:
The OpcComRcw.dll replaces all of the OpcRcw.*.dlls. However, the old DLLs are still installed for backward compatibility.

It would have been a good idea to make this line extra big and bold, because it is easily missed.
Exchanging the interop dlls seems to solve the problem, as preliminary testing indicates.
Unfortunately, the new interop dll has slightly different method signatures, so in order to get our complete code running we still have to make some changes, but a simple test case already worked fine.
Stefan
 
Share this answer
 
In addition the Solution 1. You can still use 32-bit library on your 64-bit platform, because 32-bit x86 instruction-set architecture is compatible with 64-bit architectures, even though you cannot combine executable modules targeted to different instruction set architectures. On Windows, this is done via WoW64. Please see:
http://en.wikipedia.org/wiki/WOW64[^],
http://en.wikipedia.org/wiki/Instruction_set[^].

Here is how: make all assemblies used in your applications compiled to the target "AnyCPU", and only one assembly, entry-point one (*.EXE, usually), compile to "x86" target, it will define the target instruction set of the whole application. However, make sure that none of the other assembles are compiled to any of the 64-bit platform target, as well as all unmanaged modules which might be used via P/Invoke, be the reason explained above and in Solution 1.

[EDIT]

This is good tool to investigate dependencies in unmanaged modules:
http://en.wikipedia.org/wiki/Dependency_Walker[^],
http://www.dependencywalker.com/[^].

For .NET assemblies, all dependencies are easy to pull by yourself using Reflection.
And it's good to have such a good tool for research, viewing, reverse engineering and even debugging as ILSpy:
http://ilspy.net/[^],
see also the reference to it here: http://en.wikipedia.org/wiki/.NET_Reflector[^].

—SA
 
Share this answer
 
v2
Comments
Eric Gerbers 29-Apr-13 16:46pm    
Sergey, thanks for the tip. I already tried this (but to be sure will trie it once more), but for the rest of the application i'm using 2 other SDKs, one from Cognex Vision Pro (which only provides 64bit compile on a 64bit machine and Quancom dll (for use with an IO card) both have to be build in x64 otherwise this part of the application won't work.
Sergey Alexandrovich Kryukov 29-Apr-13 16:54pm    
Make sure it's all x86-64 or all Itanium (IA-64). There is no such thing as x64. Check thoroughly all the dependencies.
However, I used Cognex and read a lot about Simatic (did not use myself), I feel they both have problems...
—SA
Eric Gerbers 29-Apr-13 16:59pm    
Cognex is in a stable position now, Simatic i now use for the first time (when it works, it works fine).
Dave Kreskowiak 29-Apr-13 17:04pm    
If you're using 64-bit and 32-bit libraries in the same app, and you can't get 32- or 64-bit libraries acrossed all libraries you're using, you'll have to split the application into two seperate executables and use some interprocess communication methods to get them to talk to each other and pass data back and forth.
Sergey Alexandrovich Kryukov 29-Apr-13 17:07pm    
Exactly.
—SA
What's the "bitness" of the library you're using from Siemens? I'd be willing to be it's 32-bit. This means that you can NOT compile your code 64-bit. You cannot combine 64- and 32-bit code in the same process.

An Access Violation usually means that your library code is expecting values in a certain size field and your code is passing in the wrong size value. For example, the library is expecting a 32-bit signed integer followed by a 32-bit pointer to a structure and your code passed in a 64-bit integer and/or a 64-bit pointer causing a stack misalignment.

Your best source of information on this problem is going to be Siemens. They wrote it so they're going to know what it expects from your code.
 
Share this answer
 
Comments
Eric Gerbers 29-Apr-13 16:06pm    
Dave,

Normally when you want to attach a 32bit dll to 64bits you can't use anything of the dll, or am i wrong there?
I forgot to state what function is called when the error comes:

Collapse | Copy Code
((IOPCItemMgt)pobjGroup1).AddItems(uiAMOUNT_OF_ITEMS, tempItemDefArray, out pResults, out pErrors);

Gives the exception and no code else. So when i try to add an ItemArray of type OPCITEMDEF

Going to Siemens isn't an option, i already tried that, they provided the samples as they are, without any further support. So, or they work, or they don't.
Dave Kreskowiak 29-Apr-13 17:00pm    
Normally, if you try to use a module that is the wrong "architecture" (SAK! ;)), it'll throw an Invalid Image Format exception, but it depends on how you access the library.

No support? Yikes. You sure you want to use an unsupported component in a production application? In my world, this would prevent you from using the library in a production-critical application.

Eric Gerbers 30-Apr-13 11:23am    
The problem is, you can switch to other providers as Softing for the OPC server, but our client decided to use Simatic NET and now time is to short to change plans. I'm deeply thinking about creating a separate service which is triggered by/triggers a file-change event to send/receive data between the main application and the OPC server. This will take about half a day work and solves the problem for now. But first i will try to build with the solution Sergey gave (only will try for 30 min) and if this doesnt work i change the plans
Sergey Alexandrovich Kryukov 29-Apr-13 16:32pm    
It's all correct, but there is on inaccuracy. You should talk not about "bitness", but about instruction-set architecture. In particular, x86-64 and Itanium (IA-64) have the same "bitness" but nevertheless are incompatible, could not be used in the same process. (I voted 4.)
—SA
Dave Kreskowiak 29-Apr-13 16:56pm    
How much Itanium stuff have you come acrossed here??

I know the difference. It's just rare to ever come acrossed Itanium specific 64-bit code, especially here.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900