Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C#
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.
 
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 29-Apr-13 10:30am
Comments
Sergey Alexandrovich Kryukov at 29-Apr-13 15:40pm
   
In what line of your code?
—SA
Eric Gerbers at 29-Apr-13 15:57pm
   
((IOPCItemMgt)pobjGroup1).AddItems(uiAMOUNT_OF_ITEMS, tempItemDefArray, out pResults, out pErrors);
Bernhard Hiller at 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.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

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.
  Permalink  
Comments
Eric Gerbers at 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 at 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 at 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 at 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 at 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.
Sergey Alexandrovich Kryukov at 29-Apr-13 17:11pm
   
Of course you do. This is just one small clarification...
—SA
Sergey Alexandrovich Kryukov at 29-Apr-13 16:39pm
   
I also added a solution with practical recommendations on what to do in this situation, please see.
—SA
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 3

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
  Permalink  
v2
Comments
Eric Gerbers at 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 at 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 at 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 at 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 at 29-Apr-13 17:07pm
   
Exactly.
—SA
Eric Gerbers at 30-Apr-13 4:41am
   
If Sergey's solution doesn't work i will do so Dave indeed.
Thx in andvance, cheers from the Netherlands
Sergey Alexandrovich Kryukov at 30-Apr-13 11:04am
   
If all depends on what are the actual target instruction-set architectures for each of the modules/assemblies which you have without the source code. Check up thoroughly. As to addressing to Siemens, this is a right idea. Here is what I observe: all those manufacturers of "industrial" grade products are usually adapt computer technologies too slowly (most of them are quite illiterate, unfortunately, sucked a lot of blood from my colleagues and myself), so they only in the process of adopting 64-bit architectures :-) if yesterdays versions are only 32-bit, today they provide 64-bit, but test them thoroughly... :-)
—SA
Eric Gerbers at 30-Apr-13 11:26am
   
That's right, when i visit my client, they still use windows XP, because it just works and has been used for the past 13 years already. They rely on proven protocols and software
Sergey Alexandrovich Kryukov at 30-Apr-13 11:29am
   
The said fact is: many use old stuff not because it's really proven, only because it's more tested, and because of illiteracy. Sometimes I think I see the signs of improvements...
—SA
Dave Kreskowiak at 30-Apr-13 11:32am
   
Come April 2014, they'll be using an operating system that is PROVEN to be insecure too. That's when MS stops providing security updates for XP.
Sergey Alexandrovich Kryukov at 30-Apr-13 11:43am
   
Could you explain how PROVEN? Any link? I never liked XP anyway... :-)
 
Microsoft alternates good and bad versions. Windows 7 is good and stable, so which one was bad? Many think Vista was bad, and XP was good. I cannot agree, because before XP it was 2K, which I think is good. In my opinion: 2K: good, XP:bad; (Vista: don't even count, not an OS); Windows 7: good; Windows 8: too bad... Everything alternates quite regularly... :-)
 
—SA
Dave Kreskowiak at 30-Apr-13 14:09pm
   
Easy. WinXP, Vista, 7, 8, ... they're all iterations of the same WinNT Kernel. Security fixes will keep coming for Windows Vista and above, but those fixes that correct the same problems that are shared with XP will no longer be applied to XP.
Sergey Alexandrovich Kryukov at 30-Apr-13 15:32pm
   
Right. I think, not supporting them all is more like a matter of being greedy (call it marketing :-) and unwilling to take responsibility (call it "customer satisfaction") :-)
So, do you know what's the proven insecurity, exactly?
—SA
Dave Kreskowiak at 30-Apr-13 17:15pm
   
Whatever the patches no longer cover on XP starting April 2014.
 
When is there a month that goes by where there ISN'T a patch for Windows??
Sergey Alexandrovich Kryukov at 30-Apr-13 17:17pm
   
Right, but I cannot say there is a proven insecurity, just a sign of it...
—SA
Dave Kreskowiak at 30-Apr-13 18:10pm
   
Given that MS has been patching WinXP for 13 years now, is it EVER completely secure?? It's just a matter of time before a nother exploitable hole is found.
 
Sergey Alexandrovich Kryukov at 30-Apr-13 18:27pm
   
"Completely secure" of course sounds like a joke, but believe me, it sounds as a joke even for the newest update.
I though you meant some particular exploit revealed.
 
Thank you for the discussion,
—SA
Sergey Alexandrovich Kryukov at 30-Apr-13 11:10am
   
I added some useful links to my answer, please see after [EDIT].
 
By the way, I think you can accept the answer formally (green button): even if it does not resolve your problem yet, it correctly describes the principles you should know is all such cases.
 
—SA
Eric Gerbers at 30-Apr-13 11:37am
   
you're right, i forgot to click that. Thanks in advance again.
Sergey Alexandrovich Kryukov at 30-Apr-13 11:38am
   
You are very welcome.
Good luck, call again.
—SA
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 4

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
  Permalink  

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

  Print Answers RSS
0 OriginalGriff 6,569
1 Sergey Alexandrovich Kryukov 6,168
2 DamithSL 5,228
3 Manas Bhardwaj 4,717
4 Maciej Los 4,150


Advertise | Privacy | Mobile
Web01 | 2.8.1411022.1 | Last Updated 28 Nov 2013
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100