Is it possible to consume a VB.Net .dll from C (unmanaged not c++)

I have a VB.Net dll that contains functionality I need to access from a C program. As GetProcAddress won't get me where I need to go (right ) and I cannot create an instance of anything (C not C++) is there a route possible to loading this library


Answer this question

Is it possible to consume a VB.Net .dll from C (unmanaged not c++)

  • Nick Macis

    Yeh, I was trying to work through the gyrations in my head and its still spinning! What I actually have is a VB .Net Forms application that is wrapped into a VB.Net dll. The application makes use of ActiveX components for a specific library I need. The real fun part is that the ActiveX components are really wrappers for the 'C' style .dll interfaces the vendor supplies. Weeee! Round and round we go. Anyway, the VB .Net portion was already written for a different use. The VB .Net dll was an easy and quick way to provide reuse. Rewriting the whole application in C++ and providing exported functions may very well be the 'right' way to go but time is always of the essences and unfortunately I simply do not have anywhere near enough to get there on the front end. Maybe in a later spin..... Anyway, I am very grateful for the insight provided.


  • Anarchy

    Ok,

    So I find if make the following changes I can compile

    in MgdInterface.h

    #include <windows.h>
    #using <mscorlib.dll>
    #using "TestDll1.dll"
    #include <vcclr.h>
    class MgdInterface
    {
    public:
    MgdInterface(void);
    ~MgdInterface(void);
    double ShowDisplay(double*, wchar_t*);
    double HideDisplay(double*, wchar_t*);
    private:
    gcroot<TestDll1::ProbeMeasurement*> tdll;
    };

    and in MgdInterface.cpp

    #include "mgdinterface.h"
    MgdInterface::MgdInterface()
    {
    tdll=new TestDll1::ProbeMeasurement();
    }
    MgdInterface::~MgdInterface()
    {

    }
    double MgdInterface::ShowDisplay(double * varlist, wchar_t* str)
    {
    tdll->ShowDisplay(varlist);//,System::String(str));
    return 1;
    }

    double MgdInterface::HideDisplay(double * varlist, wchar_t* str)
    {
    System::String* s=new System::String(str);
    tdll->HideDisplay(varlist,&s);//,System::String(str));
    return 1;
    }

    But when the code runs it appears that tdll is still being destroyed between the constructor call and the call of ShowDisplay. The reason I say this is in the construction of the Windows for class that is instanced by

    tdll=new TestDll1::ProbeMeasurement();

    there are a number of ActiveX components created. When the form is actually displayed the ActiveX items created in the constructor are already invalid and throw an exception. I have been able to find many examples on how to use legacy code in .Net and a few examples on how to use .Net as a COM object in legacy code but no examples of taking the approach I am following. Is what I am attempting possible Does anyone either have a pointer to an example or some direction on just what is wrong with the way I have implemented this approach


  • Kirill Tropin

    Anyone Noone
  • fddsfsdf

    It seems to me that the key problem is how you've factored the code into headers.  If the mgdinterface class heading had access to the types in the VB DLL  (i.e. the #using was before your interface class), then you could have it store any necessary state.  You could still keep this out of view of the C code since you aren't including mgdinterface.h in your C code, right

    Gordon



  • mahima

    Well, on to the next tall brick wall. I need the ability to retain a .Net class object between calls from the C .dll. If I try to create a global vaiable in a file compiled with /clr I get the compile error 3145. I don't see how I can add a vaiable to the class as the #using directive is in the .cpp file so the header knows nothing of the variable I am declaring... Besides, the header needs to be included in the file that exports the functions. So basically what I have is (simplified)

    in interface.h

    #pragma once
    #include <windows.h>
    class MgdInterface
    {
    public:
    double ShowDisplay(double*, wchar_t*);
    double HideDisplay(double*, wchar_t*);
    };

    in interface.cpp

    #include "mgdinterface.h"
    #using <mscorlib.dll>
    #using "TestDll1.dll"
    double MgdInterface::ShowDisplay(double * varlist, wchar_t* str)
    {
    TestDll1::ProbeMeasurement * tdll=new TestDll1::ProbeMeasurement();
    tdll->ShowDisplay(varlist);//,System::String(str));
    return 1;
    }

    double MgdInterface::HideDisplay(double * varlist, wchar_t* str)
    {
    TestDll1::ProbeMeasurement * tdll=new TestDll1::ProbeMeasurement();
    tdll->HideDisplay(varlist,str);//,System::String(str));
    return 1;
    }

    in wrapup.h

    #ifndef __WRAPUP_H
    #define __WRAPUP_H
    #define DLL_EXPORT __declspec(dllexport) //_stdcall

    extern "C"
    {
    double DLL_EXPORT ShowDisplay(double * varlist, char * str);
    double DLL_EXPORT HideDisplay(double * varlist, char * str);
    double DLL_EXPORT CreateInterface(double * varlist, char * str);
    double DLL_EXPORT DestroyInterface(double * varlist, char * str);
    }
    #endif // __WRAPUP_H

    in wrapup.cpp

    #pragma warning(disable: 4786)
    #include "wrapup.h"
    #include "MgdInterface.h"
    MgdInterface* myinterface;

    double DLL_EXPORT ShowDisplay(double * varlist, char * str)
    {
    myinterface->ShowDisplay(varlist,(wchar_t*)" ");
    return 1;
    }
    double DLL_EXPORT HideDisplay(double * varlist, char * str)
    {
    myinterface->ShowDisplay(varlist,(wchar_t*)" ");
    return 1;
    }

    double DLL_EXPORT CreateInterfaceDisplay(double * varlist, char * str)
    {
    myinterface=new MgdInterface();
    return 1;
    }
    double DLL_EXPORT DestroyInterface(double * varlist, char * str)
    {
    delete myinterface;
    return 1;
    }

    Which works up to a point but unfortunately the interface is destroyed before it is of any use. If there was some way to keep a pointer alive in the interface class I think I would be on my way but I simply can't see how to do it. Any pointers


  • SpunkyMan

    Gordon,

    Thanks for bearing with me on this. I am treading into unknown territory and the only thing I am finding is just how lost I am. First let me clarify that I am using the VS 2003 IDE not VS 2005 so gcnew and handles are not available (right ). Anyway, after much finagling I seem to have something that works. Well, at least the code you gave me can be compiled into something that runs. Now I have to see if I can apply this new found (oh alright given) knowledge to the real application. I may be back asking for more help in a short time!

    Thanks much.


  • Razzel

    No pointers
  • Gilles Auzemery

    Ok I am sure I am not getting how you are suggesting I factor things. If I try simply moving things into the header such as

    in MgdInterface.h

    pragma once
    #include <windows.h>
    #using <mscorlib.dll>
    #using "TestDll1.dll"
    class MgdInterface
    {
    public:
    MgdInterface(void);
    ~MgdInterface(void);
    double ShowDisplay(double*, wchar_t*);
    double HideDisplay(double*, wchar_t*);
    private:
    TestDll1::ProbeMeasurement * tdll;
    };

    I get the compile error

    c:\DLM\Source\Selley\MMITest1\wrapperDLL\MgdInterface.h(14): error C3265: cannot declare a managed 'tdll' in an unmanaged 'MgdInterface'

    If I change the class to a garbage collected class eg

    __gc class MgdInterface

    I get the error c:\Dlm\Source\Selley\MMITest1\wrapperDLL\Wrapper.cpp(4): error C3145: 'myinterface' : cannot declare a global or static managed type object or a __gc pointer

    So I have basically just moved my initial error over to a different file. What would be the right way to accomplish this


  • Sportsdude

    I looked at a few places but couldn't find a ready example of this, so I just did a simple case using static linking to an export library based on a VB code example in the MSDN docs. Obviously, it could get a lot more complicated if the wrapper has to maintain state and do a lot of data marshaling.

    array_search.vb:

    Imports System
    Imports Microsoft.VisualBasic

    Public Class SamplesArray

    Public Shared Sub Main()

    ' Creates and initializes a new Array.
    Dim myIntArray As Array = Array.CreateInstance(GetType(Int32), 5)
    Dim i As Integer
    For i = myIntArray.GetLowerBound(0) To myIntArray.GetUpperBound(0)
    myIntArray.SetValue(i * 2, i)
    Next i
    ' Displays the values of the Array.
    Console.WriteLine("The Int32 array contains the following:")
    PrintValues(myIntArray)

    ' Locates a specific object that does not exist in the Array.
    Dim myObjectOdd As Object = 3
    FindMyObject(myIntArray, myObjectOdd)

    ' Locates an object that exists in the Array.
    Dim myObjectEven As Object = 6
    FindMyObject(myIntArray, myObjectEven)
    End Sub


    Public Shared Sub FindMyObject(myArr As Array, myObject As Object)
    Dim myIndex As Integer = Array.BinarySearch(myArr, myObject)
    If myIndex < 0 Then
    Console.WriteLine("The object to search for ({0}) is not found. " _
    + "The next larger object is at index {1}.", myObject, Not(myIndex))
    Else
    Console.WriteLine("The object to search for ({0}) is at index " _
    + "{1}.", myObject, myIndex)
    End If
    End Sub


    Public Shared Sub PrintValues(myArr As Array)
    Dim myEnumerator As System.Collections.IEnumerator = _
    myArr.GetEnumerator()
    Dim i As Integer = 0
    Dim cols As Integer = myArr.GetLength((myArr.Rank - 1))
    While myEnumerator.MoveNext()
    If i < cols Then
    i += 1
    Else
    Console.WriteLine()
    i = 1
    End If
    Console.Write(ControlChars.Tab + "{0}", myEnumerator.Current)
    End While
    Console.WriteLine()
    End Sub
    End Class

    ' This code produces the following output.
    '
    ' The Int32 array contains the following:
    ' 0 2 4 6 8
    ' The object to search for (3) is not found. The next larger object is at index 2.
    ' The object to search for (6) is at index 3.

    vbc /target:library array_search.vb

    ---

    Here's the wrapper in C++ (.h and .cpp):

    // wrapper.h
    #ifdef _CPLUSPLUS
    extern "C"
    {
    #endif
    void arraymain();
    #ifdef _CPLUSPLUS
    }
    #endif

    // wrapper.cpp
    #using "array_search.dll"

    using namespace System;

    extern "C"
    {

    __declspec(dllexport)
    void arraymain()
    {
    SamplesArray^ samples_array = gcnew SamplesArray();
    samples_array->Main();
    }

    }

    cl /LD /clr wrapper.cpp

    and the client .C app:

    #include "wrapper.h"

    int main()
    {
    arraymain();
    }

    cl /MD client.c wrapper.lib

    Gordon



  • RajeshR

     David Mc Dermid wrote:
    I have a VB.Net dll that contains functionality I need to access from a C program. As GetProcAddress won't get me where I need to go (right ) and I cannot create an instance of anything (C not C++) is there a route possible to loading this library

    Yes it can be done.  A VB.Net assembly can function as a COM object without much effort. Next step is calling a COM object from C. Please see this MSDN article  :  http://msdn.microsoft.com/library/default.asp url=/archive/en-us/dnarguion/html/msdn_drguion020298.asp

     Please see the section "Using Our Object from C: The REALLY Old Way"

     Regards

       Sahir


  • MrZap

    Lol. Yeah, definitely looks like you got a "tri-lingual" situation going on here! Just bear in mind that having all all these wrappers adapting from one language to another is extra work and potential for bugs. If the ultimate code is in C++, then using it from your C code shoudn't involve any COM in the first place, just some extern "C" bridge functions.

    Brian


  • Chardiot

    Thanks,

    This is pretty much what I thought and actually started trying to slug through. For whatever reason I haven't been able to get a simple scenerio working yet. Can anyone point out a code example that does exactly this Create a VB .Net .dll, a C++ wrapper .dll that exports the VB functions and a small client that consumes them I am going to keep plugging away but alas, when I start hammering on the wrong door it usually simply stays closed and my blinders start working all the better!

     

    FWIW I can't change the 'C" code because it is a seperate programming environment (used for HMI creation for factory automation) that allows an external dll to be loaded and used under limited conditions. I could go back and recode the VB .Net to C++ but this would be a pretty tall task and I was hoping there was an easier way to simply wrap up the items in an interface. This looks like the ticket yet if I can only understand how it is done.


  • cb3431

    Doing COM in C just seems very wrong. Write your VB component client in C++ (using ATL is a good bet), and then provide a C-style wrapper in C++ around it. Use extern "C" on these wrapper functions. Then call from C.


  • Voodoo45

    I assume that it's not an option to recompile the C code as C++ That would be the easiest route (use the /Tp option). Failing that, you would need an intermediate layer of C++ code compiled with /clr that makes your calls into the VB code. This would contain a #using statement to access the VB assembly/dll and expose global functions visible to the C code. These functions would be declared as extern "C" and could be exported using __declspec(dllexport) for your GetProcAddress call as usual. Or you could link them to the C program as a static library--if you don't mind that the resulting built output contains mixed mode code.

    Gordon



  • Is it possible to consume a VB.Net .dll from C (unmanaged not c++)