libplctag / libplctag.NET

A .NET wrapper for libplctag.
https://libplctag.github.io/
Mozilla Public License 2.0
220 stars 55 forks source link

Compile with custom core C library #410

Closed wbm0002 closed 2 weeks ago

wbm0002 commented 3 months ago

I have made some modifications to the core libplctag C library on my local copy of the repository. I would like to compile the C# wrapper with these changes, but I am unsure of how to do this. Are there build instructions anywhere?

timyhac commented 3 months ago

I'm not sure I understand the question, if you've changed the C library substantially (for example you've changed the API surface of the C library), then you may need to write your own wrapper.

If the question is more about compiling the C library there are some notes here: https://github.com/libplctag/libplctag/blob/release/BUILD.md

Some standard questions from the Contributions guidance:

wbm0002 commented 3 months ago

The API surface is the same, so I expect to be able to use the same wrapper. I'm not sure how I can use my modified plctag.dll with my C# program. Admittedly, I don't really know enough about the library to know what I'm doing wrong. So far, I have tried pointing my C# project at the libplctag.dll file generated by this project. That resulted in this error:

PS C:\Users\wbm\source\repos\libplctagtest\bin\Debug> .\libplctagtest.exe

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'plctag': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
   at libplctag.NativeImport.NativeMethods.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout)
   at libplctag.NativeImport.plctag.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout) in /_/src/libplctag.NativeImport/plctag.cs:line 45
   at libplctag.Native.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout) in C:\Users\wbm\repos\libplctag.NET2\src\libplctag\Native.cs:line 19
   at libplctag.Tag.Initialize() in C:\Users\wbm\repos\libplctag.NET2\src\libplctag\Tag.cs:line 637
   at libplctag.Tag.InitializeIfRequired() in C:\Users\wbm\repos\libplctag.NET2\src\libplctag\Tag.cs:line 1042
   at libplctag.Tag.Read() in C:\Users\wbm\repos\libplctag.NET2\src\libplctag\Tag.cs:line 732
   at libplctag.Tag`2.Read() in C:\Users\wbm\repos\libplctag.NET2\src\libplctag\TagOfT.cs:line 185
   at CSharpDotNetFramework.ExampleArray.Run(String ipAddress, String objectLocation, String writeValue, Boolean writeBool) in C:\Users\wbm\source\repos\libplctagtest\Array.cs:line 197
   at Program.Main(String[] args) in C:\Users\wbm\source\repos\libplctagtest\Program.cs:line 88
PS C:\Users\wbm\source\repos\libplctagtest\bin\Debug> dir tlbimp.exe /s
Get-ChildItem: Cannot find path 'C:\Users\wbm\source\repos\libplctagtest\bin\Debug\tlbimp.exe' because it does not exist.

And attempting to add the plctag.dll file as a reference causes this error image

Using NuGet to include libplctag in the project works fine, but I'd like to be able to use the version I have made modifications to.

timyhac commented 3 months ago

Oh right - you should just be able to copy-paste your dll into the build output directory, over-writing the one that is currently in there. If you want to do it using the build system, you can set that add the dll file as a "Content" or "None" item, not a reference, which will copy it to the output directory.

Using the reference would be for managed Dlls (i.e. other .NET assemblies), not an unmanaged dll like the C library is.

Update to the latest prerelease if you can. The way the dlls were packaged has changed since the last production release

CodingJWE commented 3 months ago

I am also having a similar issue to this. Currently, I am building a project with the libplctag NuGet package (latest prerelease) and a custom-built version of the libplctag C library. When I build out the .dll from my custom project and try to access it, I get the following issue:

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'plctag': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
   at libplctag.NativeImport.NativeMethods.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout)
   at libplctag.Native.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout) in /_/src/libplctag/Native.cs:line 19
   at libplctag.Tag.Initialize() in /_/src/libplctag/Tag.cs:line 637
   at libplctag.Tag.Read() in /_/src/libplctag/Tag.cs:line 734
   at libplctag.Tag`2.Read() in /_/src/libplctag/TagOfT.cs:line 186

However, if I use the plctag.dll that is automatically built from the NuGet package, it works with no issues. Here is the version of the .dll I am talking about:

Debug Output: image

X64 Folder: image

For reference, when pulling from the libplctag C library github, this version of the plctag.dll is created, and when I switch out the libraries, it fails: image

NOTE: This is on a fresh pull, no edits to the C library was made.

timyhac commented 3 months ago

It works on my machine 😝

My steps to reproduce a succesful custom build of libplctag core, and include in a NET Framework project (Note that these steps would differ for .NETCore of .NET projects).

  1. Create a new NET Framework 4.7.2 Console Application (My example is called "ConsoleApp1"). Note that I am running Microsoft Windows 11 Home, Version 10.0.22631 Build 22631. I have all of these requirements satisfied.
  2. Depending on your system, with NET Framework there is a project setting called "Prefer 32 bit" which means that your application runs in 32 bit mode even when on a 64 system. I needed to untick this, because the setting was on by default.
  3. Add the libplctag.NativeImport-v2.0.0-alpha.2 nuget package to the project. My system defaults to packages.config to track packages, but I believe it should also work with a <PackageReference> style project.
  4. Replace Program.cs with the following:
    
    using System;
    using static libplctag.NativeImport.plctag;

namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { try { var result = plc_tag_decode_error(0); Console.WriteLine(result); } catch (Exception e) { Console.WriteLine(e.Message); }

        Console.Read(); // Press any key to continue
    }
}

}


5. Run this with "Debug" and "Any CPU" configuration, and confirm that you get "PLCTAG_STATUS_OK" as output. If you get Exceptions at this point, something has gone wrong and it does not make sense to proceed.
6. Download [libplctag](https://github.com/libplctag/libplctag), and change [this](https://github.com/libplctag/libplctag/blob/release/src/lib/lib.c#L673) to return "Hello world"
7. Follow the [BUILD](https://github.com/libplctag/libplctag/blob/release/BUILD.md#visual-studio-community-2019-with-windows-10-1903) instructions to create your own copy of the C library with MSVC.
8. Copy `out\build\x64-Debug\bin_dist\plctag.dll` to `ConsoleApp1\bin\Debug\X64\plctag.dll`. If you're not running x64 bit, get the appropriate one instead.

**NOTE: I have found a bug with the `libplctag.NativeImport-v2.0.0-alpha.2` package at this point** - if you build your C# project again, it will copy the original plctag.dll in the output directory, overwriting your copy. I will fix this, but this is not the problem you are having, and will not prevent you from successfully using your own copy of the core C library.

10. Open up a terminal and run `ConsoleApp1.exe` - this will get around the above problem.
   Instead of seeing "PLCTAG_STATUS_OK" you should now see "Hello world".

----

There are many things that need to go right for this to work, so when we are talking about this please include as much information as possible. System Information, Target platform, code listings, debug logs, [etc.](https://github.com/libplctag/libplctag.NET/blob/master/CONTRIBUTING.md#guidance-when-creating-an-issue)
wbm0002 commented 3 months ago

I was able to get my modified C library working by copy-pasting the .dll into the build directory! But it doesn't work anywhere else :/ The app works as expected. The build directory contains only my executable, plctag.dll (C core), libplctag.dll (wrapper), and libplctag.NativeImport.dll. When I run a command on the machine that I built the app on, it works. But when I drop those files onto a USB and try on a different machine, I get the same error as above:

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'plctag': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
   at libplctag.NativeImport.NativeMethods.plc_tag_create_ex(String lpString, callback_func_ex func, IntPtr userdata, Int32 timeout)

etc.

Per the requirements page you linked to, I've tried installing the C++ redistributables, but the problem persists. Why is it that on one machine, the executable can see the .dll, but on another, it cannot? Any further suggestions?

timyhac commented 3 months ago

Are you able to add some extra details? System Information, Target platform, code listings, debug logs, etc.. If this is different for your two systems, then add the detail for both systems.

P/Invoke is the technology used to invoke unmanaged libraries, as you can see here: https://github.com/libplctag/libplctag.NET/blob/main/src/libplctag.NativeImport/NativeMethods.cs#L23

You can always upload a zip file of c# project - I imagine that a minimal repro would just be a Console application so it should be quite small.

timyhac commented 2 months ago

@wbm0002 - have you had a chance to gather some more details?