0
votes

I writing some software to automate some 3rd party software and have found it necessary to look into calling that software's own dlls.


Note: Unfortunately, I don't have access to the source or the developer


Calling the dll's static methods has been easy using PInvoke, but from what I can tell, instantiating classes isn't possible without a C++/CLI wrapper.

The answer seems to be to create a C++/CLR wrapper around the dll, as demonstrated in this answer. While obviously an excellent answer, I've never created a C++ application before and don't know exactly what to do.

I've already used dependency walker to get the function name I'd like to use: sdk_string::sdk_string(const char *) (as decorated: ??0sdk_string@@QAE@XZ). Trying to PInvoke at that entry point just ends in sadness and self-pity.

From what I've gathered, I need to:

  1. Create a new Visual C++ CLR Class Library.
  2. In the main .h header file write something like: sdk_string* CreateSdkString(const char * chars) { return new sdk_string(chars); }
  3. Somehow get that new sdk_string(chars) call to call into the 3rd party dll?
  4. ?????? (do I need to build it any special way so I can easily PInvoke my CreateSdkString function?)
  5. Upvote and accept your answer. :-)

I think step 3 above is my question: how do I get that new sdk_string(chars) call to call into the 3rd party dll? But if I'm wrong about other things, then those other things I'm wrong about are my question.

Update:

Just for reference, here is the entire Dependency Walker function name output for this class:

const sdk_string::`vftable'
class sdk_string & sdk_string::append(class sdk_string const &)
class sdk_string & sdk_string::append(char)
class sdk_string & sdk_string::append(char *)
class sdk_string & sdk_string::append(char const *)
char const * sdk_string::c_str(void)
bool sdk_string::contains(char const *)
bool sdk_string::empty(void)
bool sdk_string::endsWith(char const *)
int sdk_string::equals(class sdk_string const &)
int sdk_string::equals(char const *)
int sdk_string::equalsIgnoreCase(class sdk_string const &)
int sdk_string::equalsIgnoreCase(char const *)
void sdk_string::erase(int,int)
bool sdk_string::hasAlphaChars(void)
int sdk_string::indexOf(class sdk_string const &)
int sdk_string::indexOf(char)
int sdk_string::indexOf(char,int)
int sdk_string::indexOf(char const *)
void sdk_string::insert(int,char const *)
bool sdk_string::isAllDigits(void)
bool sdk_string::isAllWhiteSpace(void)
int sdk_string::length(void)
class sdk_string & sdk_string::operator=(class sdk_string const &)
class sdk_string & sdk_string::operator=(char *)
void sdk_string::replace(int,int,class sdk_string const &)
void sdk_string::replace(char const *)
sdk_string::sdk_string(wchar_t * &)
sdk_string::sdk_string(class sdk_string const &)
sdk_string::sdk_string(char const *)
sdk_string::sdk_string(void)
void sdk_string::setData(char const *)
bool sdk_string::startsWith(char const *)
bool sdk_string::stretch(int)
void sdk_string::stripLeadingSpaces(void)
void sdk_string::stripSpaces(void)
void sdk_string::stripTrailingSpaces(void)
void sdk_string::substr(int,int,class sdk_string &)
void sdk_string::toUpper(class sdk_string &)
sdk_string::~sdk_string(void)

2
I'm curious that the DLL is even exporting classes. That's a pretty unusual thing to do. It constrains you to use the same compiler and runtime as the DLL targets. Are you able to do that? But in any case you can do it as described in the answer that you linked to. I can't see what is stopping you. Although, FWIW, Jared's answer involves an unmanaged C++ DLL turning the C++ class interface into a flattened interface that is called through p/invoke. Jared does not describe a C++/CLI approach, although C++/CLI is probably the way I would tackle this.David Heffernan
Hm, maybe a class isn't what I'm after here? I was just assuming the method signature foo::foo() was equivalent to class foo{ public: foo(){} }. I think I'm missing some really obvious piece of the puzzle ... how do I get the function/class from the 3rd party .dll into my new C++/CLR wrapper so I can call it? I've read about using LoadLibrary, which I have no problem doing in C#, but just don't know how to do in C++.David Murdoch
It's one thing to create a wrapper c++/CLI dll to create the unmanaged object on behalf for your .NET code; but quite another for the unmanaged code to then invoke the unmanaged object. If you intend to call the object directly the c++ code will need to be converted to c++/CLI. Of course you will need the source code. The only other way is that you create a c++/CLI proxy dll that has wrapper methods around the c++ class methods you intend to call. The latter requires much more work but it does not require a change to the original c++ code.MickyD
If you have access to the c++ source code it is considerably easier and requires significantly less maintenance to convert it to c++/CLI then to use a proxy pattern.MickyD
Unfortunately, I don't have access to the source or the developer.David Murdoch

2 Answers

1
votes

I think I'm missing some really obvious piece of the puzzle ... how do I get the function/class from the 3rd party .dll into my new C++/CLR wrapper so I can call it

You use the header file and import library supplied with the unmanaged library, just as you would import any DLL into an unmanaged project. Include the header file in any source files that refer to the library, and pass the import library to the linker.

However, a published C++ class based interface implemented in a DLL is surprising. It places severe constraints on you regarding choice of compiler. I suspect that you are reverse engineering a private library and so won't have header file or import library. Reverse engineering them is likely to be an untraceable problem. I suggest contacting the developer of the library for support.

0
votes

Apparently you just need a few methods/functions, so in that case the best you can do is create the header file (your option 2). If you need more (a lot) classes/methods you can check http://www.swig.org/ it can create the wrapper to C# but it needs the definitions, you, for instance, can create a header file, lets name it myHeader.h, like this:

class A
{
public:
int intMethod(float param);
}

Aftet that you create a file, lets name it myHeader.i with the following content:

%module myHeader_wrapper
%{
//Here could be the header file or directly the class/functions definition
    #include "myHeader.h"
%}
%include "myHeader.h"

After that you run:

swig -csharp myHeader.i

A file named myHeader_wrap.c and myHeader.cs should be created. Use the .c file to create a C++ dll and use the .cs files in your C# project to access the classes. Of course the swig thing can be automated using you IDE tools (making visual studio run post build actions for instance).