I'm trying to get an DllExport from vb.net to unmanaged c++ working.
I'm using Robert Giesecke's Unmanaged Exports with Visual Studio 2012 and tried to follow this very helpful hints. I copy the dlls from the .Net project by an post build action in the directory where my *.cpp and *.h files reside.
I checked my dll with dumpbin /EXPORTS Nugget.Discovery.dll and it tells me that there are exports:
File Type: DLL
Section contains the following exports for \Nugget.Discovery.dll
00000000 characteristics
52554A05 time date stamp Wed Oct 09 14:20:21 2013
0.00 version
0 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
0 0 0000532E StartAnnouncing
1 1 0000533E StopAnnouncing
Summary
2000 .reloc
4000 .rsrc
2000 .sdata
4000 .text
But if I try to import it in the cpp file with
#import "Nugget.Discovery.dll"
void StartAnnouncing(int serial);
I get one IntelliSense error and one error after I try to compile:
IntelliSense: cannot open source file "Debug/Nugget.Discovery.tlh"
error C1083: Cannot open type library file: 'nugget.discovery.dll': Fehler beim Laden der Typbibliothek/DLL.
Any idea what I'm doing wrong?
Best regards!
Stefan
As part of DllExport, a .lib file is generated. You can use that to use the normal C++ linker instead of LoadLibrary/GetProcAddress.
Starting from the managed code you posted, on the native side:
extern CALLBACK void StartAnnouncingType(int serial);
extern CALLBACK int TestType(void);
int _tmain(int argc, _TCHAR* argv[])
{
int test = TestPtr();
StartAnnouncingPtr(1);
}
In the settings of you unmanaged project, add Nugget.Discovery.lib to the project properties: Configuration Properties->Linker->Input. And copy the Nugget.Discovery.dll to the output directory.
Ok, thanks to Hans Passant I came to this solution:
This is my code on the managed side:
Imports System.Runtime.InteropServices
Imports RGiesecke.DllExport
Public NotInheritable Class Beacon
Private Sub New()
End Sub
Private Shared _nuggetAnnouncement As NuggetAnnouncement
' ReSharper disable UnusedMember.Local
''' <remarks>Cannot be called from managed code!</remarks>
<DllExport("StartAnnouncing", CallingConvention.StdCall)>
Private Shared Sub StartAnnouncingNative(serial As Integer)
StartAnnouncing(serial)
End Sub
''' <remarks>Cannot be called from managed code!</remarks>
<DllExport("Test", CallingConvention.StdCall)>
Private Shared Function TestNative() As Integer
Return Test()
End Function
' ReSharper restore UnusedMember.Local
Public Shared Sub StartAnnouncing(serial As Integer)
'do something
End Sub
Public Shared Function Test() As Integer
Return 42
End Function
End Class
Interesting is, that I cannot call functions that are marked with <DllExport> from managed code (even if they are Public).
And this is the code on the native side:
typedef void (CALLBACK* StartAnnouncingType)(int);
typedef int (CALLBACK* TestType)(void);
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE dllHandle = NULL;
StartAnnouncingType StartAnnouncingPtr = NULL;
TestType TestPtr = NULL;
wchar_t dllNameWide[64];
int size = mbstowcs(dllNameWide, "Nugget.Discovery.dll", sizeof(dllNameWide));
dllHandle = LoadLibrary(dllNameWide);
if (NULL != dllHandle)
{
//Get pointer to our function using GetProcAddress:
StartAnnouncingPtr = (StartAnnouncingType)GetProcAddress(dllHandle,"StartAnnouncing");
TestPtr = (TestType)GetProcAddress(dllHandle,"Test");
int test;
if (NULL != TestPtr) test = TestPtr();
int serial = 1;
if (NULL != StartAnnouncingPtr) StartAnnouncingPtr(1);
//Free the library:
FreeLibrary(dllHandle);
}
}
Are there any other better solutions?
Ciao!
Stefan
Related
I'm writing a small application in VB.NET in Visual Studio 2013, and so far what I've written is the following code:
Public Class MainMenu
Private Declare Function Version_Get Lib "mypath/mydll.dll" () As String
' Before anyone asks, yes, the DLL is present in the mypath folder
Private Sub MainMenu_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim Temp As String
Dim Dummy As Integer
On Error GoTo Handler
Temp = Version_Get
Dummy = 1 ' This line is never reached in debug mode
Handler:
If Not IsNothing(Err.GetException()) Then
MsgBox("Error " & Str(Err.Number) & " generated by the application " & Err.Source & ControlChars.CrLf & Err.Description, vbCritical, "Error")
End
End If
End Sub
End Class
Where the DLL is supposed to return a BSTR (this is of course extern "C" etc., but for the sake of comprehension I'm writing the function pure and simple):
File .h
#ifdef EVALFUNC_EXPORTS
#define EVALFUNC_API __declspec(dllexport)
#else
#define EVALFUNC_API __declspec(dllimport)
#endif
extern "C"
{
EVALFUNC_API BSTR __stdcall Version_Get();
}
File .cpp
BSTR __stdcall Version_Get()
{
CRegKey Key;
CString sValue;
BSTR Str;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\my Software"), KEY_READ);
// Before anyone asks, yes, the application is present in the system
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("Version"), NULL, &nValueLength);
if (nValueLength > 0) LONG nC = Key.QueryStringValue(_T("Version"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
Str = sValue.AllocSysString();
return Str;
}
The problem is that even if I wrote an error handler, the code encounters no error, and Version_Get makes my code crash without any error (the MainMenu form gets loaded anyway).
I tried the DLL on another VB environment (Excel) with the same declaration.
Private Declare Function Version_Get Lib "mypath/mydll.dll" () As String
In that case the string variable is filled with the correct text.
What am I doing wrong?
I had to create a class to import the Dll by marshalling the BSTR String:
Imports System.Runtime.InteropServices
Public Class ImportDll
<DllImport("myDll.dll", SetLastError:=True, CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function Version_Get() As <MarshalAs(UnmanagedType.BStr)> String
End Function
End Class
And could call the code as follows:
Version = ImportDll.Version_Get
I wrote this code in c++:
extern "C" __declspec(dllexport) int __stdcall sumx(int a, int b)
{
int result;
result = a + b;
return result;
}
I also tried:
int __stdcall sumx(int a, int b)
{
int result;
result = a + b;
return result;
}
and build win32 dll. then copy it in PB directory.
I define it external function.
And I call it:
when I run it:
Why do error occurs?
tnx
After some tests here I think that your problem may result from a name decoration of your exported function. I.E: instead of being named sumx in the dll, it is named _sumx#8 by the compiler.
You can check that by invoking dumpbin /exports keyadll.dll. With my test dll, it shows:
C:\dev\powerbuilder\dlltest>dumpbin.exe /exports keyadll.dll
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file keyadll.dll
File Type: DLL
Section contains the following exports for keyadll.dll
00000000 characteristics
5627876B time date stamp Wed Oct 21 14:39:07 2015
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 0000100A _sumx#8 = #ILT+5(_sumx#8)
^====================== HERE is the point!
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
2000 .text
BTW, the #8 in the name stands for the 8 bytes (2 x sizeof(int)) of parameters that are given to the function.
You have 2 options to fix that:
use the exact _sumx#8 name in the declaration of the external function in PB (you can use an alias for not changing your PB code):
function int sumx (int a, int b) library "keyadll.dll" alias for '_sumx#8'
I don't consider that solution being very elegant, though.
you can force VC to name the exported as YOU want (and not the reverse!) by using a module definition file (a .def).
in VS, choose to add a new item to the project / module definition file
simply put the names of the functions to export. It will contain
LIBRARY "keyadll.dll"
EXPORTS
sumx
Rebuild your dll and it should be OK for PB.
After porting a project from visual studio to mingw. I am getting the following linker error
undefined reference to `g_Templates'
undefined reference to `g_cTemplates'
The code which it points to looks something like this
#include <tchar.h>
#endif // DEBUG
#include <strsafe.h>
#include <combase.h>
extern CFactoryTemplate g_Templates[];
extern int g_cTemplates;
HINSTANCE g_hInst;
DWORD g_amPlatform; // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)
OSVERSIONINFO g_osInfo;
//
// an instance of this is created by the DLLGetClassObject entrypoint
// it uses the CFactoryTemplate object it is given to support the
// IClassFactory interface
class CClassFactory : public IClassFactory, public CBaseObject
{
private:
const CFactoryTemplate *const m_pTemplate;
...
public:
CClassFactory(const CFactoryTemplate *);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv);
STDMETHODIMP_(ULONG)AddRef();
STDMETHODIMP_(ULONG)Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv);
STDMETHODIMP LockServer(BOOL fLock);
// allow DLLGetClassObject to know about global server lock status
static BOOL IsLocked() {
return (m_cLocked > 0);
};
};
// --- COM entrypoints -----------------------------------------
//
// Call any initialization routines
//
void DllInitClasses(BOOL bLoading)
{
// traverse the array of templates calling the init routine
// if they have one
for (i = 0; i < g_cTemplates; i++) //<---------Cannot recognize this symbol
{
const CFactoryTemplate * pT = &g_Templates[i];
if (pT->m_lpfnInit != NULL)
{
(*pT->m_lpfnInit)(bLoading, pT->m_ClsID);
}
}
}
....
....
I have been searching on this issue for a while and have not made any progress. It seems that that this symbol exists in strmbasd.lib (debug version) and is generated from DirectShow base classes. I generated strmbasd.lib using mingw64 however I am still getting this linker error. I wanted to know if there was any other approach I could try .
I have used Direct Show for Microsoft Visual C++. And found no such issue. Microsoft SDK provides the libraries and headers as well as the base classes. You may want to check the link. I haven't used MingW, so I don't know about the issue of MingW. You may try it in MSVC, MSDN provides some handful informations and references for Direct Show. Please check the previous link mentioned above.
Your including dllentry.cpp/dllsetup.cpp from DirectShow BaseClasses assumes that you develop a filter library and you are expected to define template symbols in your code (example) to satisfy linker.
If you don't see how your code is referencing factories, you can define fake array and g_cTemplates of zero to pass through, however eventually there is something that makes linker drag these symbols into output.
I have created unmanaged dll and used in VB.Net.
Both code snippiest is as follows.
VB.Net
Imports System.Text
Imports System.Runtime.InteropServices
Module Module1
Sub Main()
Dim c As cls = New cls()
c.Start()
End Sub
End Module
Public Class cls
Declare Sub Only Lib "dllproj2.dll" Alias "Only" (b As StringBuilder)
Public Sub Start()
Dim s As StringBuilder = New StringBuilder(15)
Only(s) '**Actual call to dll code **
Dim s1 As String = s.ToString.ToLower
Dim len As Integer = s.ToString.Length.ToString()
Console.Write(s.ToString())
End Sub
End Class
C++ dll
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
extern "C"{
void Only(char *a)
{
char arr[10];
printf("Reached");
sprintf(arr,"This %d",33);
printf("\n%s\n",arr);
memcpy(a,arr,10);
}
}
Now as soon as I access line Only(s) I get exception shown in image.
I am not able to understand cause of exception. Output of code is fine, but while running it using Visual Studio 2012 Express it is giving above error.
It is sample code, which we also used in production, I afraid it may cause problem in future.
Kindly suggest is there any way to get rid of exception.
Thanks.
You have to declare bas: UnmanagedType.LPStr
Declare Sub Only Lib "dllproj2.dll" Alias "Only" (<InAttribute(), MarshalAs(UnmanagedType.LPStr)> b As StringBuilder)
I have a .dll that contains some directshow filters (COM) with specific/custom interfaces to query.
Most 3rd party directshow components contain embedded .tlb files that can be used for cross-enviroment communication (C# typelib import).
I would hate to have to attempt to manually create the interfaces needed for c# because no idl/tlb files were provided.
Is it possible to generate a tlb (or at least, an idl, which I can MIDL compile) from a COM .dll?
Yes, it is possible to reverse engineer/disassemble IDL (or something very close to it). What you need to do is give yourself a new C++ Console Project which gives the default code of
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
and then you insert an #import statement underneath the #include statement. So I have been playing with C# assembly marked up to function as a COM Interop DLL and I have called it ComExample2 and it lives in the same solution as the C++ console project that I added which means I can use a nice relative pathname. So my #import statement looks like
#import "..\ComExample2\bin\Debug\ComExample2.tlb" no_namespace named_guids
Then you build your console application. If you delve into the files generated during the build you will find a file that ends with .TLH which stands for type library header. So my path is
..\ComExample2\ConsoleApplication1\Debug\comexample2.tlh
Inside my file is something which looks very much like idl. Here is an edited snippet to give you a flavour....
struct __declspec(uuid("515b1b18-1602-4d42-b743-f1b3c458a0d0"))
/* LIBID */ __ComExample2;
struct /* coclass */ ComExampleClass2;
//
// Type library items
//
struct __declspec(uuid("713007fe-e74c-4fec-b91a-5ef8df279929"))
IFoo : IDispatch
{
//
// Wrapper methods for error-handling
//
_bstr_t Greeting ( );
long Sim (
long a,
long b );
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall raw_Greeting (
/*[out,retval]*/ BSTR * pRetVal ) = 0;
virtual HRESULT __stdcall raw_Sim (
/*[in]*/ long a,
/*[in]*/ long b,
/*[out,retval]*/ long * pRetVal ) = 0;
};
struct __declspec(uuid("efe233b5-8ab3-4414-855e-1f027e0a72d5"))
ComExampleClass2;
// interface _Object
// [ default ] interface IFoo
All of this is generated code so that you can script C++ code against a COM library easily. You'll have to pick through what you need but hopefully that should be enough.
Kind regards,
Lord BattenBerg