7

I am trying to use a DLL that was writen in C++ but my application is in C#

The DLL is from another company but they have supplied an SDK for their software.

They give an example of how to load their DLL in C++ but I need to adapt it to C#.

Below is their instructions of how to do it in C++

MarkEzd.dll file is Dynamic Link Library.

MarkEzdDll.h is header file of the exports function in MarkEzd.dll

The calling way of MarkEzd.dll is explicitly link. Developer needs to load and free MarkEzd.dll by calling Windows API function.

The steps are as follows.

  1. Call Windows’ API function LoadLibrary() to load DLL dynamically;

  2. Call Windows’ API function GetProcAddrress() to get the pointer of the functions in the DLL and use the function pointer to finish the work;

  3. Call Windows’ API function FreeLibrary() to release library when you do not use DLL or the program ends.

Below is the example they have provided.

Step 2. Program software for calling markezd.dll. a) First step : Dynamic Load MarkEzd.dll

HINSTANCE hEzdDLL = LoadLibrary(_T("MarkEzd.dll"));

b) Second step: get the pointer of the function to be called

lmc1_Initial=(LMC1_INITIAL)GetProcAddress(hEzdDLL, _T("lmc1_Initial"));
lmc1_Close=(LMC1_CLOSE)GetProcAddress(hEzdDLL, _T("lmc1_Close"));
lmc1_LoadEzdFile=(LMC1_LOADEZDFILE)GetProcAddress(hEzdDLL,_T("lmc1_LoadEzdFile"));  
lmc1_Mark=(LMC1_MARK)GetProcAddress(hEzdDLL,_T("lmc1_Mark"));

c) Third step: Call the function

1) Initialization lmc1 board: lmc1_Initial().
2) Open test.ezd: lmc1_LoadEzdFile(_T(“test.ezd”)).
3) Call lmc1_Mark() for machining: lmc1_Mark().
4) Close lmc1 board: lmc1_Close().

d) Fourth step, Release markezd.dll: FreeLibrary(hEzdDLL)

Bellow is the descriptions of the commands.

lmc1_Initial
INTENTION: initialize lmc1 control board
DEFINITION: int lmc1_Initial(TCHAR* strEzCadPath, BOOL bTestMode, HWND hOwenWnd)
strEzCadPath: the full path where ezcad2.exe exists
bTestMode Whether in test mode or not
hOwenWnd: The window that has the focus. It is used to check the user’s stop messages. DESCRIPTION: you must first call lmc1¬_Initial before other function in program.
RETURN VALUE: common error code

lmc1_Close
INTENTION: Close lmc1 board
DEFINITION: int lmc1_Close();
DESCRIPTION: you must call lmc1_Close to close the lmc1 board when exit program.
RETURN VALUE: common error code

lmc1_LoadEzdFile
INTENTION: open the appointed ezd file, and clear all the object in database.
DEFINITION: int lmc1_LoadEzdFile(TCHAR* strFileName);
DESCRIPTION: this function can open an ezd file that was build by user as a template. User need not set process parameters, because they will be loaded in from the template file.
RETURN VALUE: common error code

lmc1_Mark
INTENTION: mark all the data in database
DEFINITION: int lmc1_Mark(BOOL bFlyMark);
bFlyMark= TRUE // mark on fly
DISCRIPTION: Begin to mark by calling this function after loading ezd file using lmc1_LoadEzdFile. The function will not return back until marking complete.
RETURN VALUE: common error code

They also explain how to set up VS6.0

  1. Choose “Microsoft Visual C++ 6.0” when install visual studio, and click “Change Option”.
  2. Choose “VC++ MFC and Template Libraries” and click “Change Option”.
  3. Choose “MS Foundation Class Libraries” and click “change option”.
  4. Choose the options as following picture, and click “OK”.
  5. Open the project, choose menu Project->Settings. Choose “C/C++”, add “UNICODE” and delete “MCBS” in “Preprocessor definitions”
  6. Choose “Link”, select “Output” in Category, and add “wWinMainCRTStartup” in “Entry-point symbol”
  7. Change all “char” to “TCHAR” in source code.
  8. Change all character string included by double quotation marks “…” to _T(“…”)
  9. Compile and link the project again.

most of the functions return an integer code of 0 for success.

Would this be correct?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace Start_Mark
{
    public partial class Form1 : Form
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("MarkEzd.dll")]
        [return: MarshalAs(UnmanagedType.I2)]
        public static extern int lmc1_Initial(string strEzCadPath, bool bTestMode, IntPtr hOwenWnd);

        [DllImport("MarkEzd.dll")]
        [return: MarshalAs(UnmanagedType.I2)]
        public static extern int lmc1_Close();

        [DllImport("MarkEzd.dll")]
        [return: MarshalAs(UnmanagedType.I2)]
        public static extern int lmc1_LoadEzdFile(string strFileName);

        [DllImport("MarkEzd.dll")]
        [return: MarshalAs(UnmanagedType.I2)]
        public static extern int lmc1_Mark(bool bFlyMark);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            IntPtr hEzdDLL = LoadLibrary("MarkEzd.dll");
            IntPtr iplmc1_Initial = GetProcAddress(hEzdDLL, "lmc1_Initial");
            IntPtr iplmc1_Close = GetProcAddress(hEzdDLL, "lmc1_Close");
            IntPtr iplmc1_LoadEzdFile = GetProcAddress(hEzdDLL, "lmc1_LoadEzdFile");
            IntPtr iplmc1_Mark = GetProcAddress(hEzdDLL, "lmc1_Mark");
            int intlmc1_Initial=lmc1_Initial("c:\temp", false, hEzdDLL);
            if (intlmc1_Initial > 0)
            {
                return;
            }
            int intlmc1_LoadEzdFile = lmc1_LoadEzdFile("c:\temp\test.ezd");
            if (intlmc1_LoadEzdFile > 0)
            {
                return;
            }
            int intlmc1_Mark = lmc1_Mark(true);
            if (intlmc1_Mark > 0)
            {
                return;
            }
            int intlmc1_Close = lmc1_Close();
            if (intlmc1_Close > 0)
            {
                return;
            }
            FreeLibrary(hEzdDLL);
        }
    }
}
bkjames
  • 266
  • 1
  • 3
  • 15
  • You could use explicit linking with C++/CLI to make your wrapper. Possible duplicate of http://stackoverflow.com/questions/15672351/calling-c-function-from-c-with-lots-of-complicated-input-and-output-paramete/15685123 – IdeaHat Mar 29 '13 at 17:53
  • Make sure if you have installed the correct c++ redistributable! – Andreas Feb 23 '15 at 16:12
  • @Andreas I had already posted the correct answer below. – bkjames Feb 24 '15 at 16:25

2 Answers2

6

The correct syntax is as follows.

using System;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Company.Group
{
    public class FuncList
    {
        [DllImport("MarkEzd.dll", EntryPoint = "lmc1_Initial2", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        public static extern int Initialize(string PathName, bool TestMode);
    }
}
bluish
  • 26,356
  • 27
  • 122
  • 180
bkjames
  • 266
  • 1
  • 3
  • 15
  • could you post the whole class or necessary parts of the code, thanks! – iiro Apr 14 '15 at 12:35
  • 1
    @iiro That is all the code. It's basically a delegate to the unmanaged code. The attached attribute declares the name of the unmanaged dll then the EntryPoint is the name of the method in the unmanaged dll. The signature of your delegate need to match the signature of the method in the unmanaged dll. – bkjames Apr 15 '15 at 18:34
  • ok thanks! What is the definition of Initialize-method in c++. Something like lmc1_Initial2 Initialize(char* PathName, char* TestMode); ? – iiro Apr 16 '15 at 05:21
  • @iiro No, it is lmc1_Initial2(char* anyname, bool anyboolname) – bkjames May 29 '15 at 18:27
  • What if you know the name but not the directory path of the DLL till runtime? I have been using a DLLImport from kernel32.dll to call LoadLibrary, after which a DLLImport specifying the name uses the loaded DLL, but is there a way to do that without calling the Win32 API?. I looked at the Reflection namespace, but my impression is that it wants to operate on assemblies, not DLLs. – PJTraill Nov 09 '15 at 16:56
3

Use P-Invoke to call native DLL. You might have to marshall some datatype in order to make it work.

http://msdn.microsoft.com/en-us/library/aa288468.aspx

bluish
  • 26,356
  • 27
  • 122
  • 180
MLeblanc
  • 1,816
  • 12
  • 21