2

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:

  1. Reverse-engineer the serial protocol, and have my own software read and interpret the data sent by the wireless reader
  2. 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.

Their managed library decodes the data

Already-decoded data used in .NET 4 application

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.

Library also has a helpful-sounding "ExtractValue" 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.

Add as reference to .NET application in VS2022

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). The file is in the application folder

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

3
  • A stupid question perhaps, but did you copy the DLL to the x86 output folder after changing the compilation target? Your screenshot shows the output folder of the AnyCPU target, the x86 executable should be located in "bin\x86\Debug\".
    – ynwarcs
    May 20 at 12:16
  • 1
    @ynwarcs Thanks for the question; I have updated the screenshot to show the x86 path. Originally, the path in the first screenshot was the only one in bin; it seems like both, x86 and x64, output to the same folder. I added a specific x86 build configuration, which outputted to its own bin\x86\debug folder, but still the same problem. The reference also has "Copy Local" set to True, so it was automatically copied to the output folder.
    – Chris
    May 20 at 12:58
  • 1
    I.....think I just found the problem. I re-read the error message: Could not load file or assembly 'd3g_tech_managed.dll' **or one of its dependencies** and it listed the file name d3g_tech_managed.dll, which I figured meant it couldn't find that file. Ends up d3g_tech_managed.dll in turn depends on base.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!
    – Chris
    May 20 at 13:06

1 Answer 1

1

I guess I'll copy my comment as an answer, just to close out the question:

I re-read the error message: Could not load file or assembly 'd3g_tech_managed.dll' **or one of its dependencies** and it listed the file name d3g_tech_managed.dll, which I figured meant it couldn't find that file. Ends up d3g_tech_managed.dll in turn depends on base.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, completely my bad.

Thanks @ynwarcs, your comment made me recheck the obvious!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.