2

I have a problem I've been struggeling with for a full week now, and I'm not able to solve it by myself. I've been googeling, and searching in all kind of forums... I have found lots of "this might work", tried it, but no, no success. If anyone have any clue, please, please, help me!

I'v got, from an external source, lots of classes and functions written in VB that I need to be able to use from a C++ application. My first though was: no problem, I turn the VB code into a dll, and load it from my C++-program. This was though harder than I ever could imagine. My C++-program is not written in Visual Studio, but for simplicity I started with trying to load my VB dll (written in Visual Studio 2010) from a Visual Studio C++ application. This is my code so far:

VB-code : DllModule : Class-library project

DllModule.vb

Namespace DllModule
  Public Module DllModule

    Public Const DLL_PROCESS_DETACH = 0
    Public Const DLL_PROCESS_ATTACH = 1
    Public Const DLL_THREAD_ATTACH = 2
    Public Const DLL_THREAD_DETACH = 3

    Public Function DllMain(ByVal hInst As Long, ByVal fdwReason As Long,
      ByVal lpvReserved As Long) As Boolean
        Select Case fdwReason
            Case DLL_PROCESS_DETACH
                ' No per-process cleanup needed
            Case DLL_PROCESS_ATTACH
                DllMain = True
            Case DLL_THREAD_ATTACH
                ' No per-thread initialization needed
            Case DLL_THREAD_DETACH
                ' No per-thread cleanup needed
        End Select

        Return True
    End Function

    'Simple function
    Public Function Add(ByVal first As Integer, ByVal sec As Integer) As Integer
        Dim abc As Integer
        abc = first + sec
        Return abc
    End Function
  End Module
End Namespace

DllModule.def

NAME DllModule
LIBRARY DllModule
DESCRIPTION "My dll"
EXPORTS DllMain @1
        Add @2

C++-code : TryVbDllLoad : Console application

TryVbDllLoad.cpp

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <strsafe.h>

extern "C" {
 __declspec(dllimport) int __stdcall Add(int, int);
}

typedef int (__stdcall *ptf_test_func_1_type)(int, int);

int __cdecl _tmain(int argc, _TCHAR* argv[])
{
    HINSTANCE hdll = NULL;

    hdll = LoadLibrary("DllModule.dll");        // load the dll
    if(hdll) {
        ptf_test_func_1_type p_func1=(ptf_test_func_1_type)GetProcAddress(hdll,"Add");

        if(p_func1) {
           int ret_val = (*p_func1)(1, 2);
        } else {
        DWORD dw = GetLastError();
        }

        FreeLibrary(hdll);              // free the dll
    } else {
        DWORD dw = GetLastError();
    }

    return 0;
}

I can load the dll, but GetProcAddess returns NULL with error code 127 (the specified procedure could not be found).

I have tried to load the dll from a VB-application. This works (even without the .def-file). But I'm guessing there is no proper entry point created that the C++ application can use (when I open the dll in Dependency Walker I see no entry point or functions). I've tried compiling the VB-code both with and without "Register for COM interop".

1) What am I doing wrong?

2) If there isn't any nice way to solve this properly, what can I do instead of creating a dll? Is there any other way I can use the VB-classes and functions in my C++ application?

Kind Regards

Sara



Thanks for your answer Mare!

There must be some kind of error in my dll though, cause when I try to register is using regsvr32 I get: "The module C:/tmp/DllModule.dll was loaded, but the start address for DllRegisterServer was not found. Check that C:/tmp/DllModule.dll is a valid DLL- or OCX-file and try again."

Also, when I use

#import "C\tmp\DllModule.dll"

I get

fatal error C1083: Cannot open type library file: 'c:\tmp\dllmodule.dll'


I looked at the link with the tutorial, but there is a small problem: there are no such thing as "ActiveX DLL" to choose among all the project types. And yes, I do have Visual Studio 2010 Professional (a trial version, but still).

-- Sara

Sara H
  • 71
  • 1
  • 7
  • 4
    This cannot work as intended, VB.NET cannot generate native DLL entrypoints. And your C++ program cannot directly call managed code without loading the CLR. The easiest way is to use the `` attribute in your vb.net code and write COM code in your C++ program with, say, the #import directive. – Hans Passant Jun 08 '12 at 13:23
  • Thanks for your hint, Hans. I'm at the moment not sure exactly how to use or how to write COM code in a C++ program, but I will look further into this! – Sara H Jun 11 '12 at 06:30

2 Answers2

4

Thanks for all the input. I've come across another way to solve my problem, using a multifile assembly rather than my first dll approach.

I followed this HowTo-section: http://msdn.microsoft.com/en-us/library/226t7yxe.aspx#Y749

VB-code : DllModule : Class-library project

DllModule.vb

Imports System.Runtime.InteropServices

Namespace DllModuleNS
    Public Class Class1

        Public Function ClassAdd(ByRef first As Integer, ByRef sec As Integer) As Integer
            Dim abc As Integer
            abc = first + sec
            Return abc
        End Function

    End Class
End Namespace

This file I compiled using both visual studio (to produce DllModule.dll-file) and cmd-line:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Vbc.exe /t:module DllModule.vb

(to produce DllModule.netmodule-file).

C++-code : TryVbDllLoad : Console application

TryVbDllLoad.cpp

#using <mscorlib.dll>

#using ".\..\ClassLibrary1\DllModule.netmodule"
using namespace DllModule::DllModuleNS;

int _tmain(int argc, _TCHAR* argv[])
{
    Class1^ me = gcnew Class1();
    int a = 1, b = 2;
    int xx = me->ClassAdd(a, b);
    return 0;
}

In the TryVBDllLoad-project properties I changed:

  • Common Properties -> Framework and References : added DllModule-project as reference
  • Configuration Properties -> C/C++ -> General : /clr flag set
  • Configuration Properties -> Linker -> Input : Add Module To Assembly set to path to DllModule.netmodule (/ASSEMBLYMODULE:"DllModule.netmodule")

This resulted in that I could use the VB-class Class1 in VC++ code!

PROBLEM SOLVED!


I now took it one step further, and changed the TryVBDllLoad-project to a dll:

  • Configuration Properties -> General : Configurationtype Dynamic Library (.dll)
  • Configuration Properties -> Linker -> System : SubSystem Windows (/SUBSYSTEM:WINDOWS)

TryVbDllLoadClass.h

#ifndef TryVbDllLoadClass_H
#define TryVbDllLoadClass_H

class TryVbDllLoadClass
{
public:
    TryVbDllLoadClass();
    int Add(int a, int b);
};

#endif  // TryVbDllLoadClass_H

TryVbDllLoadClass.cpp

#include "TryVbDllLoadClass.h"
#using <mscorlib.dll>

#using ".\..\ClassLibrary1\DllModule.netmodule"
using namespace DllModule::DllModuleNS;


TryVbDllLoadClass::TryVbDllLoadClass() {}

int TryVbDllLoadClass::Add(int a, int b)
{
Class1^ me = gcnew Class1();
int xx = me->ClassAdd(a, b);
return xx;
}

DllExport.h

#ifndef DLLEXPORT_H
#define DLLEXPORT_H

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#ifdef __dll__
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif  // __dll__

extern "C" {
    IMPEXP int __stdcall AddFunction(int);
}

#endif  // DLLEXPORT_H

DllMain.h

#define __dll__
#include "dllExport.h"
#include " TryVbDllLoadClass.h"

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
    return 1;
}

TryVbDllLoadClass * my;

IMPEXP int __stdcall AddFunction(int first, int second)
{
    my = new TryVbDllLoadClass();
    int res = my->Add(first, second);
    delete my;
    return res;
}

This dll I could then add to a non-visual-studio project just like a normal dll:

C++-code : LoadDll : Non-Visual-Studio-project (CodeBlocks in this case)

main.cpp

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "dllExport.h"

typedef int( * LPFNDLL_CREATE)(int, int);
HINSTANCE hDLL;
LPFNDLL_CREATE func;

using namespace std;

int main()
{
    cout << "Hello world!" << endl;
    int key = 35;

    hDLL = LoadLibrary("TryVbDllLoadClass.dll");

    if(hDLL)
    {
        cout << "Loaded: " << hDLL << endl;

        func = (LPFNDLL_CREATE) (GetProcAddress(hDLL, "_AddFunction@4"));
        if(func != NULL)
        {
            cout << "Connected: " << func << endl;
            cout << "Function returns: " << func(key, key) << endl;
        }
        else cout << " ::: fail: " << GetLastError() << endl;

        FreeLibrary(hDLL);
        cout << "Freed" << endl;
    }
    else cout << " ::: fail: " << GetLastError() << endl;

    printf("-> Goodbye world!\n");
    return 0;
}

This way I can use the VB-classes given to me in my existing C++-project created outside Visuabl Studio. Finally...:)

Sara H
  • 71
  • 1
  • 7
1

With VB you do not get a "normal" DLL (at least this was the case in former times). And you do not get Entry Points for functions.

But as i understood you, you have the VB source code and you can do with it whatever is necessary. Here is a possible solution:

http://www.codeproject.com/Articles/21/Beginner-s-Tutorial-Calling-Visual-Basic-ActiveX-D

but try out first this less complicated way, because i think a VB dll is always a COM dll, so you can:

register the dll using the Windows command

regsvr32  F:\proj\VBDllModule.dll

now your C++ code :

#import "F:\proj\VBDllModule.dll"

using namespace DllModule;

void CDialogTestDlg::OnButton1()
{
 HRESULT hresult;
 CLSID clsid;
 _CTest *t; // a pointer to the CTest object
 _bstr_t bstrA = L"hello";
 _bstr_t bstrB = L" world";
 _bstr_t bstrR;
 ::CoInitialize(NULL);
  hresult=CLSIDFromProgID(OLESTR("VBTestLib.CTest"), &clsid);
  hresult= CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
                               __uuidof(_CTest),(LPVOID*) &t);
  if(hresult == S_OK)
  {
     bstrR  = t->vbConcat(bstrA , bstrB);
     AfxMessageBox((char*)bstrR);
   }
}
Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113
  • Thanks to Hans Passant! ComVisible is crucial, but when you have set it, the example code above works. – Mare Infinitus Jun 08 '12 at 13:50
  • That tutorial (from 1999) is about calling **VB6** DLLs rather than **VB.Net** DLLs. I suppose it might still work if you make sure your VB.Net DLL is COM-visible. – MarkJ Jun 08 '12 at 15:19
  • the example i provided under the link works and is straightforward, but the old example in the link does a cleaner job in my opinion and provides some background. something like that is what i would do when in the need of calling a vb dll in a c++ application. (i already did that with c# dlls alot, but with clear IDL. i think that can be disturbing when new to COM). @OP please let us know whether you could call your dll that way. – Mare Infinitus Jun 08 '12 at 15:39
  • @Mare , I tried your suggestions, but unfortunately with no success. See result in bottom of original post. – Sara H Jun 11 '12 at 11:24
  • have you registered your dll with regsrv32? if yes, you could also try tlbimp.exe to generate a type library from your vb dll. that might be easier to use. – Mare Infinitus Jun 11 '12 at 11:32
  • As I wrote, I get an error message trying to register the dll. – Sara H Jun 12 '12 at 08:03
  • try using tlbexp (it should be tlbexp in the first place). this exports a typelibrary from your DllModule which can be imported as described above (#import) – Mare Infinitus Jun 12 '12 at 08:16