0

I have a DLL written in C++ (help file says so) I have this .cs file that holds the function names and looks something like this:

    *// -------------- Part of MXIO.cs File -----
//#define _NET_WINCE
//==========================================================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace MOXA_CSharp_MXIO
{
    class MXIO_CS
    {
        public const int SUPPORT_MAX_SLOT = 16;
        public const int SUPPORT_MAX_CHANNEL = 64;
        public const int SupportMaxChOfBit  = SUPPORT_MAX_CHANNEL>>3;
        //
#if _NET_WINCE
        [StructLayout(LayoutKind.Explicit, Size = 4)]
#else
        [StructLayout(LayoutKind.Explicit, Size = 4, Pack = 1)]
#endif

        //V1.2 OPC Tag DATA Struct
#if _NET_WINCE
        [StructLayout(LayoutKind.Sequential)]
#else
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
#endif
        public struct _IOLOGIKSTRUCT
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] BytMagic;
            public UInt16 wVersion;         // struct define of version 1.0.0
            public UInt16 wLength;
            public UInt16 wHWID;            // for user to know which API to Read/write
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] dwSrcIP;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] BytSrcMAC;
            public byte BytMsgType;         // for AP side to known what kind of Message return
            public UInt16 wMsgSubType;
            //------------------------
            // tag timestamp
            public UInt16 wYear;
            public byte BytMonth;
            public byte BytDay;
            public byte BytHour;
            public byte BytMin;
            public byte BytSec;
            public UInt16 wMSec;
            //-------------------------
            public byte BytLastSlot;        //add to notice the last slot, Range 0-16, 0=>myself only
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT)]
            public byte[] BytLastCh;
            //-------------------------
            // support up to 16 slots and 64 channels //size:5408 bytes
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT * SUPPORT_MAX_CHANNEL)]
            public byte[] BytChType;    // channel I/O type
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT)]
            public UInt16[] wSlotID;    // Slot Module ID
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT * SupportMaxChOfBit)]
            public byte[] BytChNumber;  // bitwised¡A1=Enable¡A0=Disable
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT * SUPPORT_MAX_CHANNEL)] //
            public _ANALOG_VAL[] dwChValue;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = SUPPORT_MAX_SLOT * SupportMaxChOfBit)]
            public byte[] BytChLocked;  // bitwised, 0=free, 1=locked
        }
        //
#if _NET_WINCE
        [StructLayout(LayoutKind.Sequential)]
#else
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
#endif
        public struct _ACTDEV_IO
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] BytSrcMAC;
            public Int32 iHandle;
        }
        //
#if _NET_WINCE
        [StructLayout(LayoutKind.Sequential)]
#else
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
#endif
        public struct _MODULE_LIST
        {
            public UInt16 nModuleID;    //Module ID of device
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public byte[] szModuleIP;   //Save IP address of device
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] szMAC;        //Save MAC address of device
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public byte[] szModuleIP1;  //Save 2nd IP address of device
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] szMAC1;       //Save 2nd MAC address of device
            public byte bytLanUse;      //0 -> Lan0, 1 -> Lan1
        }

        public delegate void pfnCALLBACK(IntPtr bytData, UInt16 wSize);

        public delegate void pfnTagDataCALLBACK( _IOLOGIKSTRUCT[] ios, UInt16[] wMutex);

        /**********************************************/
        /*                                            */
        /*     Ethernet Module Connect Command        */
        /*                                            */
        /**********************************************/
        [DllImport("MXIO_NET.dll")]
        public static extern int MXEIO_Init();

        [DllImport("MXIO_NET.dll")]
        public static extern void MXEIO_Exit();

        [DllImport("MXIO_NET.dll")]
        public static extern int MXEIO_Connect(byte[] szIP, UInt16 wPort, UInt32 dwTimeOut, Int32[] hConnection);

        [DllImport("MXIO_NET.dll")]
        public static extern int MXEIO_Disconnect( Int32 hConnection );

        [DllImport("MXIO_NET.dll")]
        public static extern int MXEIO_CheckConnection( Int32 hConnection, UInt32 dwTimeOut, byte[] bytStatus );

        /***********************************************/
        /*                                             */
        /*              General Command                */
        /*                                             */
        /***********************************************/
        [DllImport("MXIO_NET.dll")]
        public static extern int MXIO_GetDllVersion();

        [DllImport("MXIO_NET.dll")]
        public static extern int MXIO_GetDllBuildDate();
        /*************************************************/
        /*                                               */
        /*                  Error Code                   */
        /*                                               */
        /*************************************************/
        public const int MXIO_OK = 0;
        public const int ILLEGAL_FUNCTION = 1001;
        public const int ILLEGAL_DATA_ADDRESS = 1002;

        /*************************************************/
        /*                                               */
        /*              Data Format Setting              */
        /*                                               */
        /*************************************************/
        /* Data length define   */
        public const int BIT_5 = 0x00;
        public const int BIT_6 = 0x01;
        public enum MXIO_ModuleDataIndex: int
        {
            //---------------------------------------------------------------------------
            MX_ML_MODULE_LIST_SIZE                          = 47,
            MX_ML_INDEX_WHWID                               = 0,
            MX_ML_INDEX_SZIP0                               = 2,    //SIZE:16 (STRING)
            MX_ML_INDEX_SZMAC0                              = 18,   //SIZE:6 (STRING)
            MX_ML_INDEX_SZIP1                               = 24,   //SIZE:16 (STRING)
            MX_ML_INDEX_SZMAC1                              = 40,   //SIZE:6 (STRING)
            MX_ML_INDEX_BYTLANUSE                           = 46
        };
        //---------------------------------------------------------------------------
    }
}*

I'm trying to access any of the functions inside the DLL. Here is the Delphi code:

type
  TMyCall = function: integer; stdcall;

const
  MYDLL = 'D:\DelphiProjects\DLL_Read\MXIO_NET.dll';

procedure TForm2.btnFncClick(Sender: TObject);
var
   i,j,k: integer;
   Handle: THandle;
   mCall : TMyCall;
begin
   // Load the library
   Handle := LoadLibrary(MYDLL);

   // If succesful ...
   if Handle <> 0 then
   begin
      // Assign function  from the DLL to the
      // function variable mCall
      @mCall := GetProcAddress(Handle, 'MXIO_GetDllVersion');
      // If successful
      if @mCall <> nil then
      begin
        Label3.Caption:= 'DLL version =' + IntToStr(mCall);
      end
      else
        Label3.Caption:= 'Function Not found';

      // Unload library
      FreeLibrary(Handle);
   end
   else
    Label3.Caption:= 'Version Handle = 0';
end;

Problem: GetProcAddress fails and returns nil.

I tried to cut the code into minimal and complete portion, hope I could do this time. Now I'm thinking to convert the .cs file (at least some part of it) into a Delphi unit and call the DLL functions from there. I wonder if anyone had experience on this? I'd appreciate any help

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
cengo
  • 3
  • 3
  • The `DllImport` directive is for going in the other direction. See [this question](http://stackoverflow.com/questions/4818850/is-is-possible-to-export-functions-from-a-c-sharp-dll-like-in-vs-c) for further information on what it appears you are trying to do here. – 500 - Internal Server Error Dec 31 '15 at 15:29
  • I believe that this edit has only further confused matters. In the original question, it appeared he wants to import from abc.dll which is written in C++, and that he only has a sample of DLLImport provided in .cs and reproduced above. But now in this edit, it seems he wants to actually know if he needs extern "C" declarations, which if his DLL was written by a person who gave him the source he would already have. If he wants to translate from C# dll-imports but he knows more about C, then that is his choice. It is HIS problem that he is confused, and this edit didn't help much. – Warren P Dec 31 '15 at 15:34
  • 1
    Please show the real code – David Heffernan Dec 31 '15 at 15:35
  • 1
    @Warren It seems clear to me, I think. Asker wants to call the DLL from Delphi code. The C# code is to show the spec for how to call it. – David Heffernan Dec 31 '15 at 15:38
  • @Warren No, that's fine – David Heffernan Dec 31 '15 at 15:43
  • 1
    The code presented here is an accurate translation. Although it's odd that you have both load time and run time linking. What is your question? – David Heffernan Jan 02 '16 at 21:45
  • Your C# code imports `MXIO_NET.dll`. Your Delphi imports `MOXA.dll`. which is it? – kobik Jan 03 '16 at 08:44
  • Wrong dll name was a typo while trying to make it more readable. My question was, why my function call returns nil? Dave, I couldn't understand what you refer to by "load time linking". – cengo Jan 04 '16 at 08:24
  • You didn't show the real code. I don't want to invest time when I can't trust the content of the question. Load time linking is a term that you can search for. If you don't know what something means, don't remain ignorant. Search. – David Heffernan Jan 04 '16 at 08:57
  • The original .cs file is about 3400+ lines of code, that's why I didn't post "real code" . On the other hand, you're right I should've searched for "load time linking" before asking. Anyway, never mind, thanks for your time. @wedrowycz I'm using Delphi XE3. Thanks – cengo Jan 04 '16 at 09:14
  • So what you need to do is cut the code down to a [mcve]. Do that and it will be trivially easy to tell you what is wrong. This sort of interop question is bread and butter. – David Heffernan Jan 04 '16 at 09:54

1 Answers1

2

The question is somewhat confused. There is a huge amount of C# code that has no corresponding Delphi code. One wonders if the Delphi code really does exhibit the problem that you claim it does.

Anyway, on the assumption that the GetProcAddress call that you show does fail, then the question is easy enough to answer. GetProcAddress has two failure modes:

  1. The module handle supplied is not valid.
  2. The procedure name supplied is not valid.

We can rule out item 1 since the module handle was returned by a call to LoadLibrary. If LoadLibrary succeeds then we know that we have a valid module handle.

So, the only conclusion is that the procedure name supplied is not valid. Which means that the DLL that you have loaded does not export a function named MXIO_GetDllVersion. You can confirm this yourself by calling GetLastError immediately after GetProcAddress returns nil. When you do so you will get the value 127 which is ERROR_PROC_NOT_FOUND.

You can confirm this by inspecting the function names that the DLL exports. Use a tool such as Dependency Walker to do so.

One other minor comment on your Delphi code. You've used THandle for the module handle, but that is the wrong type. It won't affect behaviour but it is semantically wrong. Use HMODULE.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David, my Delphi code is just a test code to see if I could read DLL functions, I couldn't start the real prg. I run DependencyWalker and checked out the output, I can see the function name with underscore (I've read in an article that this is normal because of the C++ dll - I tried both possibilities though) in the text output they look like: [C ] 305 (0x0131) 304 (0x0130) _MXIO_GetDllVersion@0 0x000601C0 I think I'm going to give up and may be try to learn c#. Thank you for your help, I appreciated. – cengo Jan 05 '16 at 09:49
  • `_MXIO_GetDllVersion@0` is a stdcall function with a decorated name. Likely exported by the MS C++ compiler, under `extern "C"`, using `dllexport`. Note that the `@0` indicates that the size of the parameters on the stack is `0` and that `@0` is part of the function's name. Now that you know the function's name, you can import it with `GetProcAddress(..., '_MXIO_GetDllVersion@0')`. I think that this question is done now, right? – David Heffernan Jan 05 '16 at 09:54
  • I Just did. Thanks again. – cengo Jan 05 '16 at 11:19