In my current employment, A major task is to take an existing engineering tool, and update it, as it has stopped working on the modern OS's.
Now the tool was written in FORTRAN, and most of the source file headers state something like:
C modified by (obfuscated) at dd. mm. 19yy to do something
The employees have cycled since then, and much of the documentation was either never done, or has been lost. So it's up to us to decipher how the program operates, and then recreate that functionality in a more modern language.
For this purpose we have chosen C#.
I am somewhat able to read FORTRAN, so deciphering the math, and logic have so far been straight forward, but I am stuck when comes to dllimports.
I have no clue who made the dll's, or where the source code is, but What I have determined is that they are responsible for some of the key calculations for some of our vendors components. Hence I cannot simply replace the dll with new code as I have no clue which equations are in there..
What I have found out is that the old program sent xml formatted data to the dll entries, and got an xml-like string back. I cannot however replicate it, as I am not completely sure how it works.
Can anybody explain this, or perhaps even translate this to a C++ / C# equivalent?
! interface for CalculateStuff.dll
INTERFACE
SUBROUTINE CalculateComponent(Input, Output)
!DEC$ ATTRIBUTES REFERENCE, ALIAS : '_CC#16' :: CalculateComponent
!DEC$ ATTRIBUTES DLLIMPORT :: CalculateStuff
CHARACTER*(*) Input, Output
!DEC$ ATTRIBUTES REFERENCE :: Input
!DEC$ ATTRIBUTES REFERENCE :: Output
END SUBROUTINE
END INTERFACE
Currently I have this snippet, (C#) but it seems to fail on me:
class CalculateStuff{
[DllImport("CalculateStuff.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, EntryPoint = "_CC#16")]
public static extern void CalculateComponent(ref string input, ref string output);
}
edit 1: added charset
and now the program gave me this exception, its an improvement I think:
An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
edit 2: recompiled as 32bit application, and now I get:
External component has thrown an exception.
edit 3: changed return type to void, as this makes completely sense to me.
edit 4: added the calling convention defined as stdCall, due to many comments hinting that, it didn't help.
have also tried defining the parameter types as string, or ref string nothing changes.
Related
I'm trying to make a call from C# to a c++ function in a DLL. One argument of this function is a pointer to a short type variable.
I'm not doing any fancy data structure, or unknown-sized variables. Just a simple short value.
When the DLL c++ function tries to modify the value of this variable, it gives an "Unmanaged exception 0xC0000005: Access Violation".
I cannot modify the DLL source (it's used in other projects and I cannot touch it, though I have the source code to debug it)
Code of the c++ DLL function (please note the "//this line provokes the exception" comment line in the code)
// declaration
virtual TestFunction(short __RPC_FAR *sRet) = 0;
// definition
STDMETHODIMP CABTest::TestFunction(short *sReturnValue)
{
//some
//calculations
//here
*sReturnValue = 1; //this line provoques the exception
return 0;
}
C# code invoking the DLL
[DllImport("test.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern short TestFunction(ref short sReturn);
short sMyValue = 0;
TestFunction(ref sMyValue);
I would expect sMyValue to value 1, as the reference I'm passing is a straight short type (not a fancy data struct) but I only get an app crash (the above exception).
What can I do in the C# code to avoid this problem? All I've seen searching for similar questions required rewriting the C++ functions (changing "short *sReturnValue" for "short **sReturnValue" but I cannot touch the original DLL source code.
I think I will write a wrapper.dll that invokes the test.dll but I was wondering if there was any direct and faster solution.
I created an instance as #PaulMcKenzie suggests and it's working fine.
I erroneously thought that I could just grab the DLL and use the calculation functions needed but I guess I couldn't.
Just leaving this answer for others that might make the same mistake.
Good Day fellow developers
I have been searching the internet for 2 days now on how to do what I need and I have tried a lot of samples with no success. However, that does not mean I covered all sites - I am thinking I am blind in one eye and cannot see out the other.
At any rate, I was handed a single sheet of paper with a COM Automation Interface definition on it and was asked to develop a C# application to utilize the interface and the callbacks accordingly.
So, starting simple (or so I thought) - the C++ method signature is:
STDMETHODIMP CSimpleChatServer::CallMe(BSTR clientName, BSTR** returnMessage)
and the interface signature is:
HRESULT _stdcall CallMe([in] BSTR clientName, [in] BSTR** helloMessage);
And I need to call this from C# - I have established the Interface for ISimpleChatServer; hence, the code call I am trying is like.
string rtrnMsg = string.Empty;
ImySimpleCom.CallMe("Robert", rtrnMsg)
Since the signature is an [in], I am getting an exception on trying to access protected memory.
Now, I believe it wants an IntPtr as the second parameter; however, all my attempts to get that to be taken have failed.
Please keep in mind that I am not able to change the library - it is an "As Is" legacy interface that we need to utilize for a little while longer and the provider has nobody to update it accordingly (in fact I think they do not have anyone to work on it).
Any help would be kindly welcomed.
Kind Regards, Robert S.
In the .idl the interface should be
HRESULT _stdcall CallMe([in] BSTR clientName,
[out,retval] BSTR** helloMessage);
So just take the return value from CallMe
SimpleChatServer mySimpleCom = new SimpleChatServer();
string helloMessage = mySimpleCom( clientName );
When you return the string in C++/C method you allocate using SysAllocString and return that to caller by assigning to helloMessage.
i.e.
*helloMessage = SysAllocString(L"MyString");
All the above assumes that you have referenced the COM server in your C# project.
I read your question more carefully now, so you cannot change the "legacy" code? if it says
HRESULT _stdcall CallMe([in] BSTR clientName,
[in] BSTR** helloMessage);
Then it should be changed because that is not correct.
Thanks All and Andres
I finally got in touch with the client and yes, the information they provided was incorrect and the [in] attribute was supposed to be [out,retval] to allow for the passing back of the string - once they corrected that, all went fine.
Great appreciation to all - Thank You
Given the following c++ class in foo.dll
class a{
private:
int _answer;
public:
a(int answer) { _answer = answer; }
__declspec(dllexport) int GetAnswer() { return _answer; }
}
I would like the pInvoke GetAnswer from C#. To do that, I use the following method:
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint= "something")]
public static extern int GetAnswer(IntPtr thisA);
And I pass in an IntPtr that points to an a (that I got from somewhere else, it's not important). CallingConvention = CallingConvention.ThisCall makes sure it's handled correctly
What's cool about this question is that I know I'm right so far because it's already working great! Using Depends.exe, I can see that "GetAnswer" is exported as ?GetAnswer#a##UAEHXZ (Or something close - the point being that it's been name mangled). When I plug the mangled name into the "something" for the EntryPoint everything works great! It took me about a day before it dawned on me to use Depends.exe, so I'm going to leave this here as a help to anybody who has a similar issue.
My REAL Question is: Is there any way to disable C++ name mangling on GetAnswer so that I don't need to put the mangled name in as my entry point. Having the mangled name in there seems like it could break, because my understanding of name mangling is that it can change if the compiler changes. Also it's a pain in the butt to use Depends.exe for every instance method that I want to pInvoke.
Edit: Forgot to add what I've tried:
I don't seem to be able to put extern "C" on the function declaration, although I can stick it on the definition. This doesn't seem to help though (which is obvious when you think about it)
The only other solution I can think of is a c-style function that wraps the instance method and takes an instance of an a as a parameter. Then, disable name mangling on that wrapper and pInvoke that. I'd rather stick with the solution that I already have, though. I already told my co-workers that pInvoke is great. I'm going to look like an idiot if I have to put special functions in our c++ library just to make pInvoke work.
You cannot disable mangling for a C++ class method, but you may well be able to export the function under a name of your choice using /EXPORT or a .def file.
However, your entire approach is brittle because you rely on an implementation detail, namely that this is passed as an implicit parameter. And what's more, exporting individual methods of a class is a recipe for pain.
The most sensible strategies for exposing a C++ class to .net languages are:
Create flat C wrapper functions and p/invoke those.
Create a C++/CLI mixed mode layer that publishes a managed class that wraps the native class.
Option 2 is preferable in my opinion.
You may be able to use the comment/linker #pragma to pass the /EXPORT switch to the linker which should allow you to rename the exported symbol:
#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer#a##UAEHXZ")
Unfortunately, this does not resolve your need to look up the mangled name using depends or some other tool.
You do not have to disable the mangled name which actually contains lots of information of how the function itself is declared, it basically represents the whole signature of the function after the function name gets de-mangled. I understand you already found a word-around and the other answer has been marked as a correct answer. What I am writing below is how we can make it work as you desired.
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);
If you replace "#OrdinalNumber" with the real ordinal number of GetAnsweer, such as "#1", it will work as you desired.
You may just consider the EntryPoint property is the same as the function name we pass to GetProcAddress where you can either pass the function name or the ordinal number of the function.
Your approach to calling non-static function members of a C++ class is indeed correct and thiscall is used correctly and that is exactly thiscall calling convention comes in play in C# P/Invoke. The issue with this approach is that you will have to look into the DLL's PE information, Export Function Information and find out the ordinal number for each function you would like to call, if you have a big number of C++ functions to call, you may want to automate such a process.
From the Question Author: The solution I actually went with
I ended up going with a c-style function that wraps the instance method and takes an instance of an a as a parameter. That way, if the class ever does get inherited from, the right virtual method will get called.
I deliberately chose not to go with C++/CLI because it's just one more project to manage. If I needed to use all of the methods on a class, I would consider it, but I really only need this one method that serializes the class data.
I am new to using C++ libraries in C# and also to the C++ programming in general. I have a DLL built from a C++ code which I believe is a 'managed' code as the name of the DLL is "TestManaged.dll". I am not 100% sure if the dll/C++ code is managed/unmanaged.
I want to use classes and methods of this DLL in my C# windows forms application code. There are multiple classes in this DLL. When I chekced these classes and methods inside those classes in Object Browser, all of them have Public identifier.
So far, I have added this DLL to my references of C# application code. There are three classes I would talk about in my question: Product, ReqStatus, ProductData. I could create an object(s) for various classes of this DLL as follows.
Product testCall = new ProductClass();
There is another class called ProductData in this DLL and I could get the C++ code for this class which is as follows. In this case, ProductData is shown as class in Object Browser in C# where as it is actually a struct in C++ code. I am not sure if this is important to answer my question (at the end).
Following is a C++ code that defines ProductData struct - ProductData.h file.
#ifdef WIN32_MANAGED
public ref struct ProductData
#else
struct ProductData
#endif
{
UINT32 ProductId; //!< Product ID
UINT32 PRoductRev; //!< Build Revision
};
Following is a C++ code that defines ReqStatus enum - ReqStatus.h file. I have created the same enum in my C# code with no identifier specified.
enum ReqStatus
{
SUCCESS, //!< Method was successful
//Connection errors
NOT_CONNECTED, //!< Connection not open
CONN_TIMEOUT, //!< Connection timed out commuincating with device
};
Now, there are two methods I want to call and have problems with both:
Method 1: is a getProductData method inside Product class which accepts object of ProductData type as a parameter and returns the ReqStatus which is an enum type in C++. So following is the declaration of the gerProductData method (as seen in the Object Browser):
public ReqStatus getProductData(ProductData data)
The same method's C++ delcaration is: (The actual method is too long and hence just giving the declaration): This method is inside Prodcut.cpp file
ReqStatus Product::getProductData(ProductData PLATFORM_PTR data)
PLATFORM_PTR is defined as below in Platform.h
#ifdef WIN32_MANAGED
#define PLATFORM_PTR ^
#else
#define PLATFORM_PTR *
#endif
Method 2: is a getConnected method inside Product class which accepts a character array (I am not sure of this) and an object of ProductData type as a parameter and returns the ReqStatus which is an enum type in C++. So following is the declaration of the getConnected method (as seen in the Object Browser):
public ReqStatus getConnected(sbyte* someChar, ProductData data)
The same method's C++ delcaration is: (The actual method is too long and hence just giving the declaration): This method is inside Prodcut.cpp file
ReqStatus Product::getConnected(const char *someChar, ProductData PLATFORM_PTR data)
C++ code calls the methods as follows:
private : Product^ _testProduct;
testProduct = gcnew Product();
ProductData ^ data = gcnew ProductData();
int portNum = Decimal::ToInt16(7);
char portName[32];
_snprintf(&portName[0], sizeof(portName),"COM%d", portNum);
ReqStatus status = _testProduct->getConnected(&portName[0], data); //Calling getConnected
There is an internal call to getProductData method inside the getConnected method.
ReqStatus status = getProductData(data); //data is the same which was passed to the getConnected method
MY C# code is as follows and I got errors at both method calls: I have put errors on the same line in the below code snippet. Both methods are independent. Its just that the getProductData is called from getConnected method in C++ code. I wanted to check if I can call both individually.
ProductData pData = new ProductData(); // OK
Product _testProduct = new Product(); // OK
ReqStatus status1 = _testProduct.getConnected("COM5", pData ); //Error 1: The best overloaded method getConnected has some invalid arguments
ReqStatus status2 = (ReqStatus)_testProduct.getProductData(pData ); // Error 2: Method is inaccessible due to its protection level
For Error 1, I tried solutions from various articles on StackOverflow and other forums but, could not solve it. Just for a reference, I tried to change the "SomePortCOM" as follows but it din't work.
UPDATE: This code works fine now and I don't see Error 1(Invalid arguments). Now, I only need to get rid of the Error 2 (Protection level error). Kindly provide any suggestions. Thank you.
String str = "COM5";
byte[] bytes = Encoding.ASCII.GetBytes(str);
unsafe
{
fixed (byte* p = bytes)
{
sbyte* sp = (sbyte*)p;
//SP is now what you want
ReqStatus status1 = _testProduct.getConnected(sp, pData );
}
}
For Error2, I searched so many blogs and found that one of the possible solution could be the use of DLLImport, I tried that as well and I have following issue:
C# declaration of DLLImport:
[DllImport("TestManaged.dll",EntryPoint="getConnected")]
public static extern ReqStatus getConnected(String SerialPort, ref ProductData pData);
I am calling this function as below from my C# code:
ProductData pData = new ProductData();
String str = "COM7";
ReqStatus status1 = getConnected(str, ref pData);
However, I am getting Entry point not found error. I tried to run the dumpbin function to get the list of functions exported by this DLL. But, I do not see any functions. Rather just a random output as below.
Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\Rumit\TestManaged.dll
File Type: DLL
Summary
2000 .data
22000 .rdata
1000 .reloc
1000 .rsrc
13000 .text
UPDATE:
Also, I do not see any methods in this DLL via Dependency Walker.
Now, I have got the source code for C++. But I am fairly new to C++ coding. In case any change is required to C++ code, kindly give the directions.
Regards,
Rumit
enum ReqStatus
That's your biggest hang-up. That declares a native enum type, it is not usable in managed code and makes any code that uses it inaccessible. You must declare the managed version of it with the enum class keyword, like this:
public enum class ReqStatus {
// etc...
}
The unsafe block around your code will make it so your assembly cannot be verified for security protocols, so be wary of that. When I've called methods from C++ (native or not) from C# I've had to use PInvoke (Platform Invoke) to call them. For a protection level, I know you said everything is public in the C++, but if you're new to C++ you may have made a quick syntax mistake. In C#, all methods need to be preceded by a storage specifier (public, protected, etc...) but in C++ you place a storage specifier followed by a colon and everything between THAT storage and the next declared storage will be of that storage type. Maybe this is your issue?
Thank you Hans, for the pointing out the problem. Just that, I made the enum definition as 'public'. But, I'm not sure if you put the 'class' by mistake or was it intentional as it was giving me so many errors as it wasn't taking it as an enum and was asking for an object at every place I used enum. Let me know if I misunderstood anything here.
So, I got it worked just by making the enum public. However, I am still not able to find how to pass the correct values to that the C++ function from C#. (Error1 in my original post).
When I debuged the C++ code, it passes the "0x0034E808 "COM5" value (I assume it's a memory location and a value?) for the first parameter of the getConnected method. I tried to pass this value by implementing the unsafe method (explained in my original post for Error 1), it passes "0x0277aab8" (again seems some memory address), but could not get connected to it (Getting serial port timeout errors). Am I passing the value incorrectly compared to C++ method?
Regards,
Rumit
I am working on my phd in chemistry and for that reason I need to write a software application to help me with the imaging of samples under a microscope. This microscope is fitted with an x-y-z nanopositioning stage. The stage is controlled using an unmanaged DLL written in VC++ by the hardware vendor. I could provide you with more specifics of needed but let me start with this;
One of the methods in the dll allows me to read settings for the axis of motion:
C++ syntax:
BOOL E7XX_qSVO (int ID, const char* szAxes, BOOL* pbValueArray)
Where BOOL is int 0 or 1 according to the convention.
My C# wrapper contains:
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId, string sAxes, int []iValArray);
This seems correct to me. However when I try something like this in my main application (to query axis 1,2 and 3):
Int32 [] _iValues = new Int32[3];
E7XXController.qSVO(m_iControllerID, "123", _iValues);
I consistently get an array like this:
{6, 0, 10} while I should get {0, 0 , 0} according to the display on the device itself. The complementary function:
BOOL E7XX_SVO (int ID, const char* szAxes, const BOOL* pbValueArray) to set the same status bits on the stage also don't work...
Other commands in the dll work perfectly. I can pass strings and doubles in and out without troublem but not the BOOL type...
Do you guys have any idea what could be wrong?
BOOL in C++ is actually an "int" so make sure you use System.Int32 and not System.Boolean.
Alternatively it might be using COM data types i.e. VARIANT_BOOL, in which case you need do need System.Boolean.
Have you tried running dependency viewer to confirm the function prototype?
Have you tried specifying the MarshalAs attribute, e.g.:-
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId,
[MarshalAs(UnmanagedType.AnsiBStr)]string sAxes,
[MarshalAs(UnmanagedType.LPArray)]int[] iValArray);
Also, have you tried literally passing the sAxes string as a char array, e.g.:-
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId,
[MarshalAs(UnmanagedType.LPArray)]char[] sAxes,
[MarshalAs(UnmanagedType.LPArray)]int[] iValArray);
I've found that with interop you often need to experiment a fair bit to get it working, so try different combinations, check out the different members of the UnmanagedType enumeration and also play about with other people's suggestions too. However, please try the above two techniques and let me know whether they help sort it!
This isn't going to answer your question directly but a sweet tool for getting common Interop Win32 calls easily is using Pinvoke.net The even have a Visual Studio plugin.
http://www.pinvoke.net/index.aspx
The docs for your function say:
The VC++ compiler needs an extern "C" modifier. The declaration must also specify that these functions are to be called like standard Win-API functions. That means the VC++ compiler needs to see a WINAPI or __stdcall modifier in the declaration.
The default calling convention for DllImport is WinApi. On Windows, that's StdCall. But on CE, that's Cdecl. You want to make sure that you use the right calling convention. You might want to tray adding:
CallConvention = CallingConvention.StdCall
Also, specify our string character set:
CharSet = CharSet.Ansi
But it should work even without these. It's quite odd as your code looks right.