Sorry if this is a bit long; I figured it might be helpful to provide some information on why I'm trying to do what I'm doing. If not, just skip to the "My Problem" section.
Background
I'm writing some software to automate water billing at some apartment buildings. The water meters communicate using a proprietary protocol to a receiver, which sends the data to the computer via RS232 or serial-over-Bluetooth. The data are then displayed in a .NET 4 application provided by the hardware manufacturer, where they can be exported to CSV. I have all of the hardware, and using their own software, everything works as they intended (requiring manual export to CSV). I want to write my own application that can get the meter ID, current meter reading, and a few other things, to automate this process -- basically, I can just drive by the buildings with an Internet-connected laptop, and the system will grab all the meter data and can instantly generate bills for the correct customers.
There was a 3rd-party private company doing this billing in the area, but they've since been bought by another company, and that new company refuses to add new accounts (even if the new customer is in the same building as an existing customer that they're already reading!) and has expressed interest in leaving the area entirely. There are also other issues, but they're beside the point.
Options
I figure the two most likely ways to make this happen are:
- Reverse-engineer the serial protocol, and have my own software read and interpret the data sent by the wireless reader
- Use a library that's bundled with their software, that appears to do all of the reading/interpreting already, in my application
I've already made progress on Option 1, and identified the important parts of the "normal" broadcasted packet, but I'd like to keep that as a "Plan B," since it appears there is a library that can do all of this already, and they've already accounted for all the different kinds of packets that can be sent and received, and how to separate which data bytes are for which unit, especially when 25 units are broadcasting simultaneously with possibly different packet types with different structures and lengths.
Plan A - Using Their Library
Based on the objects contained in d3g_tech_managed.dll
, it appears they have a nicely-packaged library that handles everything from opening the serial port, to receiving and decoding the broadcasted beacons, to raising events to which the main application subscribes and receives the already-decoded data.
There's even a very helpful-sounding function in the library called "ExtractValue", which is called by the main program's equally-helpful-sounding "FillMeterReadData" function.
I can add the DLL to a new C#.NET application in VS2022, and all of the objects, functions, etc. appear to be present.
My Problem
This is my first time trying to do any kind of reverse-engineering or using a 3rd-party DLL in my own program, so sorry if this is an obvious problem.
Initially, when I tried running the program, I got a:
System.BadImageFormatException: 'Could not load file or assembly 'd3g_tech_managed, Version=1.0.6422.19726, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format.'
exception. Search results suggest that error is because I ran the application as 64-bit, but the DLL is 32-bit, which is true. Switched the application to x86, but then I get a FileNotFoundException
:
System.IO.FileNotFoundException: 'Could not load file or assembly 'd3g_tech_managed.dll' or one of its dependencies. The specified module could not be found.'
How can it not find the file at all in 32-bit mode, when in 64-bit mode it had to have found the file for it to know it was the wrong format?
The DLL file is in the application folder (source code and compiled bin
folders).
I found other suggestions about installing it into the GAC (Global Assembly Cache), but the command:
"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\gacutil.exe" -i d3g_tech_managed.dll
results in an error:
Failure adding assembly to the cache: Attempt to install an assembly without a strong name
The suggestions for that error seem to involve recompiling the DLL from source code, which I don't have. And, besides, since an existing .NET 4 application is able the use that DLL, it should be possible for me to write one that uses it in the same way, right?
Some other possibly-useful information from dnSpy: clicking on the root for d3g_tech_managed (1.0.6422.19726)
shows:
// C:\Program Files (x86)\Arad Technologies\3GTechnicianNET\d3g_tech_managed.dll
// d3g_tech_managed, Version=1.0.6422.19726, Culture=neutral, PublicKeyToken=null
// Native Entry point: 0x000098AD
// Timestamp: 5980427D (2017-08-01 04:57:33)
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
[assembly: AssemblyVersion("1.0.6422.19726")]
[assembly: SecurityRules(SecurityRuleSet.Level1)]
[assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")]
[assembly: AssemblyProduct("d3g_tech_managed")]
[assembly: CLSCompliant(true)]
[assembly: AssemblyDescription("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCopyright("Copyright (c) Arad Technologies Ltd. 2007")]
[assembly: AssemblyTitle("d3g_tech_managed")]
[assembly: AssemblyCompany("Arad Technologies Ltd.")]
[assembly: AssemblyConfiguration("")]
[assembly: ComVisible(false)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode = true)]
And clicking on the item below that (d3g_tech_managed.dll
) shows:
// C:\Program Files (x86)\Arad Technologies\3GTechnicianNET\d3g_tech_managed.dll
// d3g_tech_managed.dll
// Global type: <Module>
// Native Entry point: 0x000098AD
// Architecture: x86
// This assembly contains unmanaged code.
// Runtime: .NET Framework 4
// Timestamp: 5980427D (2017-08-01 04:57:33)
using System;
I can't seem to find a solution for the above errors that would work when I don't have the source code to recompile the DLL.
Any ideas?
Thanks!
RE: the This assembly contains unmanaged code
line; did they name a DLL "...managed..." even though it isn't?
The software that I'm writing is for internal use only, and will not be used by or distributed to anyone else (so there shouldn't be any issue with unlicensed distribution of copyrighted code).
Meter Brochure: www mastermeter com/wp-content/uploads/Interpreter-II-Register_v0710.20F.pdf
Reader Brochure: www mastermeter com/wp-content/uploads/Dialog_3G_DMMR_Transceiver_v0514.20.pdf
Could not load file or assembly 'd3g_tech_managed.dll' **or one of its dependencies**
and it listed the file named3g_tech_managed.dll
, which I figured meant it couldn't find that file. Ends upd3g_tech_managed.dll
in turn depends onbase.dll
(whose name alone should have made me think), which I didn't copy. Once I did, my test program runs and correctly reports the library version on the form. So... ummm.... oops, completely my bad. But thanks @ynwarcs, your comment made me recheck the obvious!