Calling functions exported by Dlls from Silver

island

(Ram Poe) #1

I have a DLL which exports a C++ function that I want to call. I’ve attempted to use DllImportAttribute and LoadLibrary/GetProcAddress but I’m stuck with both.

This is an example DLL I want to use

class Test {
	__declspec(dllexport) static LPCWSTR DoSomeThingUseful(const LPCWSTR param) {
		return L"Hello Dll";
	}
};

And here’s my program in Silver

import rtl

@DllImportAttribute("Dll1.dll")
static __external func `?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z`(_ param: LPCWSTR) -> LPCWSTR

`?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z`("123")

The program compiles but I get an error “Entry Point Not Found - The procedure entry point for ?DoSomethingUseful could not be located in the dynamic link library …”

My second attempt using LoadLibrary and GetProcAddress

import rtl

let mod: HMODULE = LoadLibrary("Dll1.dll")!
let proc = GetProcAddress(mod, "?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z") as! (LPCWSTR) -> LPCWSTR

proc("123")

This doesn’t compile because casting fails - "Cannot cast from “FARPROC” to “Func<LPCWSTR!,LPWSTR!>!”


How to cast FARPROC! to Func<SomethingElse>?
(Carlo Kok) #2

presuming that’s the actual name (I thought declspec dllexport removed mangling) you can do something like:

@DllImportAttribute("Dll1.dll", EntryPoint = "?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z")
static __external func DoSomethingUseful(_ param: LPCWSTR) -> LPCWSTR

(Ram Poe) #3

I tried that but now it won’t build

C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\RemObjects Software\Elements\RemObjects.Elements.Island.Windows.targets(84,5): error : error: link failed
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\RemObjects Software\Elements\RemObjects.Elements.Island.Windows.targets(84,5): warning : C:\Program Files (x86)\RemObjects Software\Elements\Bin\lld.exe: warning: (import): undefined symbol: _DoSomeThingUseful

The name is correct. I can call the function using this C++ code

typedef LPCWSTR(__cdecl *myFunc)(LPCWSTR);
int main()
{
	auto m = LoadLibrary(L"Dll1.dll");
	auto p = reinterpret_cast<myFunc>(GetProcAddress(m, "?DoSomeThingUseful@Test@@CAPB_WPB_W@Z"));

	wprintf(p(L"123"));
    return 0;
}

(Carlo Kok) #4

hrmm that should have worked. I’ll log an issue to investigate.


(RemObjects) #5

Thanks, logged as bugs://79407


(RemObjects) #6

bugs://79407 got closed with status fixed.


(Carlo Kok) #7

Is using v10 an option for you?


(Carlo Kok) #8

Alternatively, a temporary workaround should be:

@SymbolName("?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z")
@DllImportAttribute("Dll1.dll", EntryPoint = "?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z")
static __external func DoSomethingUseful(_ param: LPCWSTR) -> LPCWSTR

(Ram Poe) #9

I am using v10. I was testing on 10.0.0.2241, but I am using the trial.


(Ram Poe) #10

The workaround didn’t work for me on v9.3 or v10. It compiles now but I get the “Entry Point Not Found” error on running it.


(marc hoffman) #11

Can you send us the native .dll in question? I’m betting the mangled name is just somehow not 100% correct…


(Ram Poe) #12

This is the dll I’m using for my test
Dll1.zip (4.1 KB)


(Carlo Kok) #13

It’s

?DoSomeThingUseful@Test@@SAPB_WPAPB_W@Z
instead of 
?DoSomeThingUseful@Test@@CAPB_WPAPB_W@Z
                         ^

(c vs s)


(Ram Poe) #14

Oh, that’s because I changed the exported function declaration to public instead of private. But it’s still not working.


(Carlo Kok) #15

I take it this is a dummy c++ project (no real code in it?); if so can I get the code so I can experiment with it a bit?


(Ram Poe) #16

Sure
Dll1.src.zip (5.0 KB)


(Carlo Kok) #17

Oke the upcoming beta (friday) supports this. you’ll need to use something like:

64bits:
@DllImportAttribute("Dll1.dll", EntryPoint ="\x01?DoSomeThingUseful@Test@@SAPEB_WPEAPEB_W@Z")
32bits:
@DllImportAttribute("Dll1.dll", EntryPoint ="\x01?DoSomeThingUseful@Test@@SAPB_WPAPB_W@Z")

The \x01 is used to disable escaping which is required for c++ mangling. Do note that if you use global members with extern “C” { } it will not do any mangling and there’s no need for \x01.


#18

Wouldn’t it be far more reasonable that if @SymbolName and @CallingConvention determine decorated names when using DllExport they would also determine names when using DllImport?

Currently if I have a DLL with:

@DllExport
@SymbolName("_Foo")
func some_internal_name() -> Bool {
    MessageBox(nil, "A Message", "Foo", 0)
    return true
}

I can consume it using the following code

@DllImport("My.dll", EntryPoint = "Foo")
__external func xyz() -> Bool

First of all, it sucks that I have to export the function with an underscore because when importing it the underscore gets added on its own…

Second, is that I can’t simply ad to both the caller and the callee the following line

@CallingConvention(CallingConvention.Stdcall)

Unfortunately that would change how the function is exported (it would become _Foo@0), but the importing side would continue to look for _Foo, which results in:

image

If I change the arguments I need to mess with the name decorations again.

The thing is that the compiler has all the relevant information - it knows the base name, the arguments and their types and the calling convention. This is exactly what’s needed to determine the VC++ decorated name. So why not do it?

And additional nicety might be if importing would work with @SymbolName instead of only with the EntryPoint property of the @DllImport attribute, but that’s far less important than supporting name decoration in the toolchain rather than forcing the user to deal with it.


(Carlo Kok) #19

I agree. I’ll log an issue to harmonize these.


(RemObjects) #20

Thanks, logged as bugs://79734