www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - AutoDLL

reply FoxyBrown <Foxy Brown.IPT> writes:
A DLL Loader prototype that loads DLL's, just specify the 
declaration. Probably should be worked up so it is easy to load 
DLL's.


Example:



import portaudio;
import std.conv, std.stdio;
import core.stdc.stdio;

//pragma(lib, "portaudio_x86.lib"); // Doesn't work because libs 
are invalid



alias BOOL = ubyte;
alias DWORD = uint;

struct dllimport
{
   string dllName;
}

template hasAttribute(alias sym, T)
{
	static bool helper()
	{
		foreach(a; __traits(getAttributes, sym))
		{
			if(is(a == T) || __traits(compiles, typeof(a)) && is(typeof(a) 
== T))
				return true;
		}
		return false;
	}
	enum bool hasAttribute = helper();
}

template getAttribute(alias sym, T)
{
	static T helper()
	{
		foreach(a; __traits(getAttributes, sym))
		{
			static if(is(a == T) || __traits(compiles, typeof(a)) && 
is(typeof(a) == T))
				return a;
		}
		assert(0, "attribute " ~ T.stringof ~ " not found");
	}
	enum T getAttribute = helper();
}

void doImport(alias T)()
{
	import core.sys.windows.windows, std.conv;
	bool isDLLLoaded = false;
	HINSTANCE dll;
	foreach(m; __traits(allMembers, T))
	{		
		static if(__traits(compiles, typeof(__traits(getMember, T, m))))
		{			
			static if(hasAttribute!(__traits(getMember, T, m), dllimport))
			{
				auto dllName =  getAttribute!(__traits(getMember, T, m), 
dllimport).dllName;
				if (!isDLLLoaded)
				{
					writeln("Loading DLL `"~dllName~"'...");
					isDLLLoaded = true;
					dll = LoadLibrary(to!wstring(dllName~"\0").ptr);
					if (dll == null)
					{
						// ERROR, handle
						writeln("Error, aborting!");
						return;
					}

				}
				auto q = GetProcAddress(dll, m);
				mixin(m~" = cast(typeof("~m~"))q;");

				//func(m, getAttribute!(__traits(getMember, T, m), 
dllimport).dllName, cast(void**)&__traits(getMember, T, m));
			}
		}
	}
}

mixin template DllImportFunctions()
{
	struct DllImporter
	{
		shared static this()
		{
			doImport!(__traits(parent, DllImporter))();
		}

		static loadFunction(string name, const(char)[] dllName, void** 
addr)
		{
			printf("import %.*s from %.*s into %llx\n", name.length, 
name.ptr, dllName.length, dllName.ptr, addr);
		}
	}
}

extern(Windows)  dllimport("portaudio_x86.dll") __gshared
{
	PaError function() Pa_Initialize;
	PaError function() Pa_Terminate;
	PaHostApiIndex function() Pa_GetHostApiCount;
	PaHostApiIndex function() Pa_GetDefaultHostApi;
	PaDeviceIndex function() Pa_GetDefaultOutputDevice;
	PaDeviceIndex function() Pa_GetDefaultInputDevice;
	PaDeviceIndex function() Pa_GetDeviceCount;
	const(PaHostErrorInfo)* function() Pa_GetLastHostErrorInfo;
	PaDeviceIndex function(PaHostApiIndex hostApi, int 
hostApiDeviceIndex ) Pa_HostApiDeviceIndexToDeviceIndex;
	PaHostApiIndex function(PaHostApiTypeId type) 
Pa_HostApiTypeIdToHostApiIndex;
	const(PaHostApiInfo)* function(PaHostApiIndex hostApi) 
Pa_GetHostApiInfo;
	PaError function(PaStream *stream, PaStreamFinishedCallback 
streamFinishedCallback) Pa_SetStreamFinishedCallback;
	PaError function(PaStream *stream) Pa_CloseStream;
	PaError function(PaStream** stream, int numInputChannels, int 
numOutputChannels, PaSampleFormat sampleFormat, double 
sampleRate, ulong framesPerBuffer, PaStreamCallback 
streamCallback, void *userData) Pa_OpenDefaultStream;
	PaError function(PaStream** stream, const PaStreamParameters 
*inputParameters, const PaStreamParameters *outputParameters, 
double sampleRate, ulong framesPerBuffer, PaStreamFlags 
streamFlags, PaStreamCallback streamCallback, void *userData) 
Pa_OpenStream;
	PaError function(const PaStreamParameters *inputParameters, 
const PaStreamParameters *outputParameters, double sampleRate) 
Pa_IsFormatSupported;
	const(PaDeviceInfo)* function(PaDeviceIndex device) 
Pa_GetDeviceInfo;
	PaError function(PaStream *stream) Pa_StartStream;
	PaError function(PaStream *stream) Pa_StopStream;
	PaError function(PaStream *stream) Pa_AbortStream;
	PaError function(PaStream *stream) Pa_IsStreamStopped;
	void function(long msec) Pa_Sleep;
	PaError function(PaSampleFormat format) Pa_GetSampleSize;
	long function(PaStream* stream) Pa_GetStreamWriteAvailable;
	long function(PaStream* stream) Pa_GetStreamReadAvailable;
	PaError function(PaStream* stream, const void *buffer, ulong 
frames) Pa_WriteStream;
	PaError function(PaStream* stream, void *buffer, ulong frames) 
Pa_ReadStream;
	double function(PaStream* stream) Pa_GetStreamCpuLoad;
	PaTime function(PaStream *stream) Pa_GetStreamTime;
	const(PaStreamInfo)* function(PaStream *stream) Pa_GetStreamInfo;
	PaError function(PaStream *stream) Pa_IsStreamActive;

}

mixin DllImportFunctions;



struct Phase
{
     float left=0, right=0;
}

extern(C) int sawtooth(const(void)* inputBuffer, void* 
outputBuffer, size_t framesPerBuffer, 
const(PaStreamCallbackTimeInfo)* timeInfo, PaStreamCallbackFlags 
statusFlags, void *userData)
{
     auto phase = cast(Phase*)userData;

     auto pout = cast(float*)outputBuffer;

     enum vol = 0.2f;

     foreach(i; 0 .. framesPerBuffer)
     {
         *pout++ = vol * phase.left;
         *pout++ = vol * phase.right;

         phase.left += 0.01f;
         if (phase.left >= 1.0f) phase.left -= 2.0f;

         phase.right += 0.03f;
         if (phase.right >= 1.0f) phase.right -= 2.0f;
     }
     return 0;
}

int main()
{
     enum SAMPLE_RATE = 44100;
     enum NUM_SECONDS = 15;

     PaStream* stream;
     PaError err;
     Phase phase_data;

	auto x = Pa_Initialize;

     if ((err = Pa_Initialize()) != paNoError) goto Lerror;

     if ((err = Pa_OpenDefaultStream(&stream, 0, 2, paFloat32, 
SAMPLE_RATE, paFramesPerBufferUnspecified, &sawtooth, 
&phase_data)) != paNoError) goto Lerror;

     if ((err = Pa_StartStream(stream)) != paNoError) goto Lerror;

     Pa_Sleep(NUM_SECONDS * 1000);

     if ((err = Pa_StopStream(stream)) != paNoError) goto Lerror;
     if ((err = Pa_CloseStream(stream)) != paNoError) goto Lerror;
     if ((err = Pa_Terminate()) != paNoError) goto Lerror;

     return 0;
  Lerror:
     //stderr.writefln("error %s", 
to!string(Pa_GetErrorText(err)));
     return 1;
}
Jul 06 2017
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 6 July 2017 at 12:15:57 UTC, FoxyBrown wrote:

 //pragma(lib, "portaudio_x86.lib"); // Doesn't work because 
 libs are invalid
Probably just a OMF/COFF issue. If you try to link with a COFF library while compiling with 32-bit DMD in its default configuration on Windows, you'll get such errors. You'll need to compile with -m32mscoff MS linker instead. Either that, or run coffimplib [1] (doc at [2]) on the portaudio dll. [1] http://ftp.digitalmars.com/coffimplib.zip [2] http://www.digitalmars.com/ctg/coffimplib.html
Jul 06 2017
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Thursday, 6 July 2017 at 16:14:18 UTC, Mike Parker wrote:

 to compile with -m32mscoff MS linker instead. Either that, or
*to use* the MS linker
Jul 06 2017
prev sibling parent FoxyBrown <Foxy Brown.IPT> writes:
On Thursday, 6 July 2017 at 16:14:18 UTC, Mike Parker wrote:
 On Thursday, 6 July 2017 at 12:15:57 UTC, FoxyBrown wrote:

 //pragma(lib, "portaudio_x86.lib"); // Doesn't work because 
 libs are invalid
Probably just a OMF/COFF issue. If you try to link with a COFF library while compiling with 32-bit DMD in its default configuration on Windows, you'll get such errors. You'll need to compile with -m32mscoff MS linker instead. Either that, or run coffimplib [1] (doc at [2]) on the portaudio dll. [1] http://ftp.digitalmars.com/coffimplib.zip [2] http://www.digitalmars.com/ctg/coffimplib.html
I tried some of that stuff and it didn't work so I gave up and found the code above but it only printed the names so I added the ability to load the functions. I kept on getting that the was invalid. I figured it was more of a mess to try to get the libs right since what if I need to use a coff and omf? Seems like a royal pain in the butt to deal with.
Jul 06 2017
prev sibling parent FoxyBrown <Foxy Brown.IPT> writes:
Here is a solution that will wrap all extern C functions and 
allow one to then map them to a dll.


auto BuildDLLClassFromCHeader(alias modulename, string name)()
{
	import std.traits, std.algorithm, std.meta;
	auto s = "extern (C) class " ~name~"\n{\n\timport ___import = 
"~moduleName!modulename~";\n";
	mixin("import "~moduleName!modulename~";");
	foreach(m; AliasSeq!(__traits(allMembers, modulename)))
	{		
		mixin("alias member = " ~ fullyQualifiedName!(modulename) ~ "." 
~ m ~ ";");		
		static if (is(typeof(member) == function))
			static if (functionLinkage!member != "C")
				continue;
			else
				s ~= "\tpublic static typeof(___import."~__traits(identifier, 
member)~")* "~__traits(identifier, member)~";\n";
	}
	
	return s ~ "}";
}

void DllImport(alias T)(string dllName)
{
	import core.sys.windows.windows, std.conv, std.meta;
	auto dll = LoadLibrary(to!wstring(dllName~"\0").ptr);
	if (dll == null)
		assert(0, "Cannot load DLL `"~dllName~"'");

	foreach(fname; __traits(derivedMembers, T))
	{		
		auto func = GetProcAddress(dll, fname);
		enum s = "auto p = cast(void**)&"~T.stringof~"."~fname~"; *p = 
cast(typeof(p))func;";				
		mixin(s);
	}
}

e.g.,


mixin(BuildDLLClassFromCHeader!(portaudio, "PortAudioDLL")());
DllImport!PortAudioDLL("portaudio_x86.dll");

it generates the following for portaudio.di:

extern (C) class PortAudioDLL
{
	import ___import = portaudio;
	public static typeof(___import.Pa_GetVersion)* Pa_GetVersion;
	public static typeof(___import.Pa_GetVersionText)* 
Pa_GetVersionText;
	public static typeof(___import.Pa_GetErrorText)* Pa_GetErrorText;
	public static typeof(___import.Pa_Initialize)* Pa_Initialize;
	public static typeof(___import.Pa_Terminate)* Pa_Terminate;
	public static typeof(___import.Pa_GetHostApiCount)* 
Pa_GetHostApiCount;
	public static typeof(___import.Pa_GetDefaultHostApi)* 
Pa_GetDefaultHostApi;
	public static typeof(___import.Pa_GetHostApiInfo)* 
Pa_GetHostApiInfo;
	public static typeof(___import.Pa_HostApiTypeIdToHostApiIndex)* 
Pa_HostApiTypeIdToHostApiIndex;
	public static 
typeof(___import.Pa_HostApiDeviceIndexToDeviceIndex)* 
Pa_HostApiDeviceIndexToDeviceIndex;
	public static typeof(___import.Pa_GetLastHostErrorInfo)* 
Pa_GetLastHostErrorInfo;
	public static typeof(___import.Pa_GetDeviceCount)* 
Pa_GetDeviceCount;
	public static typeof(___import.Pa_GetDefaultInputDevice)* 
Pa_GetDefaultInputDevice;
	public static typeof(___import.Pa_GetDefaultOutputDevice)* 
Pa_GetDefaultOutputDevice;
	public static typeof(___import.Pa_GetDeviceInfo)* 
Pa_GetDeviceInfo;
	public static typeof(___import.Pa_IsFormatSupported)* 
Pa_IsFormatSupported;
	public static typeof(___import.Pa_OpenStream)* Pa_OpenStream;
	public static typeof(___import.Pa_OpenDefaultStream)* 
Pa_OpenDefaultStream;
	public static typeof(___import.Pa_CloseStream)* Pa_CloseStream;
	public static typeof(___import.Pa_SetStreamFinishedCallback)* 
Pa_SetStreamFinishedCallback;
	public static typeof(___import.Pa_StartStream)* Pa_StartStream;
	public static typeof(___import.Pa_StopStream)* Pa_StopStream;
	public static typeof(___import.Pa_AbortStream)* Pa_AbortStream;
	public static typeof(___import.Pa_IsStreamStopped)* 
Pa_IsStreamStopped;
	public static typeof(___import.Pa_IsStreamActive)* 
Pa_IsStreamActive;
	public static typeof(___import.Pa_GetStreamInfo)* 
Pa_GetStreamInfo;
	public static typeof(___import.Pa_GetStreamTime)* 
Pa_GetStreamTime;
	public static typeof(___import.Pa_GetStreamCpuLoad)* 
Pa_GetStreamCpuLoad;
	public static typeof(___import.Pa_ReadStream)* Pa_ReadStream;
	public static typeof(___import.Pa_WriteStream)* Pa_WriteStream;
	public static typeof(___import.Pa_GetStreamReadAvailable)* 
Pa_GetStreamReadAvailable;
	public static typeof(___import.Pa_GetStreamWriteAvailable)* 
Pa_GetStreamWriteAvailable;
	public static typeof(___import.Pa_GetSampleSize)* 
Pa_GetSampleSize;
	public static typeof(___import.Pa_Sleep)* Pa_Sleep;
}

and then maps the function pointers to the dll.

Should work for other dlls.
Jul 06 2017