Help using a C dll function in VB

R

roidy

Hi, I`ve got a C function in a dll that I need to use in VB. The C function
is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size' bytes
long and then fills it. How do I do the same in VB? I can only allocate an
array of bytes if I already know the size. Help please...

Thanks
Rob
 
L

Lloyd Sheen

roidy said:
Hi, I`ve got a C function in a dll that I need to use in VB. The C
function is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size' bytes
long and then fills it. How do I do the same in VB? I can only allocate an
array of bytes if I already know the size. Help please...

Thanks
Rob

First if you are going to be calling dll's alot I would suggest you get
P/Invoke Interop Assistant from MS.

From that product you get:


Partial Public Class NativeMethods

'''Return Type: boolean
'''data: char**
'''size: int
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="function")> _
Public Shared Function [function](ByRef data As System.IntPtr, ByVal
size As Integer) As
<System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)>
Boolean
End Function
End Class

and an error for the calling sequence so that it not correct

LS
 
R

roidy

Thanks for the reply Lloyd, I downloaded the P/Invoke Interop Assistant but
what exactly am I supposed to do with it? I tried opening the dll file with
it and just got an error saying the module was expected to contain an
assembly manifest. I have no idea how to use the tool and the help file
seems to be missing :(

Rob


Lloyd Sheen said:
roidy said:
Hi, I`ve got a C function in a dll that I need to use in VB. The C
function is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size'
bytes long and then fills it. How do I do the same in VB? I can only
allocate an array of bytes if I already know the size. Help please...

Thanks
Rob

First if you are going to be calling dll's alot I would suggest you get
P/Invoke Interop Assistant from MS.

From that product you get:


Partial Public Class NativeMethods

'''Return Type: boolean
'''data: char**
'''size: int
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="function")> _
Public Shared Function [function](ByRef data As System.IntPtr, ByVal
size As Integer) As
<System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)>
Boolean
End Function
End Class

and an error for the calling sequence so that it not correct

LS
 
R

roidy

OK wait, I`ve got P/Invoke Interop Assistant to to show me how to declare
the function in Vb:-

Public Shared Function test(ByRef data As System.IntPtr, ByVal size As
Integer) As Integer

But now how do I pass a System.IntPtr to the function and then read it back
as a byte array?

Thanks
Rob

roidy said:
Thanks for the reply Lloyd, I downloaded the P/Invoke Interop Assistant
but what exactly am I supposed to do with it? I tried opening the dll file
with it and just got an error saying the module was expected to contain an
assembly manifest. I have no idea how to use the tool and the help file
seems to be missing :(

Rob


Lloyd Sheen said:
roidy said:
Hi, I`ve got a C function in a dll that I need to use in VB. The C
function is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size'
bytes long and then fills it. How do I do the same in VB? I can only
allocate an array of bytes if I already know the size. Help please...

Thanks
Rob

First if you are going to be calling dll's alot I would suggest you get
P/Invoke Interop Assistant from MS.

From that product you get:


Partial Public Class NativeMethods

'''Return Type: boolean
'''data: char**
'''size: int
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="function")> _
Public Shared Function [function](ByRef data As System.IntPtr, ByVal
size As Integer) As
<System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)>
Boolean
End Function
End Class

and an error for the calling sequence so that it not correct

LS
 
S

Stewart Berman

Do you have the code for the DLL? Call a C function that allocates memory can be very dangerous.
There are different types of memory and it is possible to request memory that is locked in physical
memory.

What are you doing with the returned pointer that requires the memory to be allocated via the
"function"?
 
M

Mike

roidy said:
Hi, I`ve got a C function in a dll that I need to use in VB. The C function
is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size' bytes
long and then fills it. How do I do the same in VB? I can only allocate an
array of bytes if I already know the size. Help please...

Thanks
Rob

Hi Rob, I am also currently learning the finer details of .NET
interop. Maybe we can learn together :)

First, some side points:

- In general, it is generally a "taboo" for allocating memory across
boundaries - Allocating in the DLL and deallocating in the main
process. So if you have control over the dll, you may want to
redesign in.

- The indirection char **data makes it harder with .NET interop.

Here is what I did:

Create a test C DLL:

-------- CUT HERE ---------
// File: testing\mydll.c

#include <stdio.h>
#include <windows.h>

BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

__declspec( dllexport ) int __stdcall initdata( char *data, int size)
{
int i = 0;
for (;i < size; ++i )
{
data = 'a';
}
return 1;
}

__declspec( dllexport ) int __stdcall initdata2( char **data, int size)
{
int i = 0;
for (;i < size; ++i )
{
//(*data) = 'a';
}
return 1;
}
-------- CUT HERE ---------

Compile this like so:

cl mydll.c /LD /DLL

Then create a TestMyDLL.VB

-------- CUT HERE ---------
Option Strict On
Option Explicit On

Imports System
Imports System.Runtime.InteropServices

Module Module1
Declare Auto Function initdata Lib "mydll.dll" _
(ByVal data As Byte(), _
ByVal size As Integer) As Integer

<DllImport("mydll.dll", SetLastError:=True)> _
public function initdata2 ( _
byref data As Byte(), _
ByVal size As Integer) As Integer
end function

Sub Main()
Try
Dim data() As Byte
Dim size As Integer = 10
Array.Resize(data, size)
Console.WriteLine("* B4 - data.length: {0}", data.length)
'initdata(data, size)
initdata2(data, size)
Console.WriteLine("* AD - data.length: {0}", data.length)
Dim i As Integer
For i = 0 To data.length - 1
Console.WriteLine("{0} [{1}]", i, data(i))
Next

Catch ex As Exception
Console.WriteLine("-------------------------------------")
Console.WriteLine("Exception: {0}", ex.Message)
Console.WriteLine("{0}", ex.StackTrace)
Console.WriteLine("-------------------------------------")
End Try

Console.ReadKey()

End Sub
End Module
-------- CUT HERE ---------

and try to figure it out. I haven't yet. :) except that for the
initdata() function it works. The 2nd one with the char **data is
where you run into corruption issue which the catch traps. But as the
VB is written using the byref in the InitData2() parameter #1, it
doesn't abort but you get some currupt. Run it to see what I am saying.

There are solutions but I believe it requires proper Marshalling - the
finer details I need to learn.

--
 
M

Mike

Ok rob, found this MSDN article:

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx

and that helped. Look at the table where it has the unmanaged signature:

char **arg

that be mapped in VB.NET as"

byref arg as string


So in the mydll.c code for initdata2() I have:

__declspec( dllexport ) int __stdcall initdata2(
char **data, int size)
{
int i = 0;
printf("in InitData2() size: %d\n",size);
for (;i < size; ++i )
{
(*data) = 'a';
printf("in c: data(%d) = [%c]\n",i,(*data));
}
return 1;
}

and in the vb.net code, change the dllimport

<DllImport("mydll.dll")> _
public sub initdata2( _
ByRef data as string, _
ByVal size As Integer)
end sub

and test with this:

Dim data As string = "1234"
Console.WriteLine("* B4 - data.length: {0}", data.length)
initdata2(data, data.length)
Console.WriteLine("* AD - data.length: {0}", data.length)
Dim i As Integer
For i = 0 To data.length - 1
Console.WriteLine("{0} [{1}]", i, data(i))
Next

and it work (no corruption)! "1234" is changed to "aaaa" :)

Boy, this article is going to help - now to actually read it. :)

--


roidy said:
Hi, I`ve got a C function in a dll that I need to use in VB. The C
function is declared and used as follows:-

declaration:-
bool function(char** data, int size);

usage;-
char *data;
function(&data,1000);

As far as I can tell the function allocates a block of memory 'size'
bytes long and then fills it. How do I do the same in VB? I can only
allocate an array of bytes if I already know the size. Help please...

Thanks
Rob

Hi Rob, I am also currently learning the finer details of .NET interop.
Maybe we can learn together :)

First, some side points:

- In general, it is generally a "taboo" for allocating memory across
boundaries - Allocating in the DLL and deallocating in the main
process. So if you have control over the dll, you may want to redesign in.

- The indirection char **data makes it harder with .NET interop.

Here is what I did:

Create a test C DLL:

-------- CUT HERE ---------
// File: testing\mydll.c

#include <stdio.h>
#include <windows.h>

BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

__declspec( dllexport ) int __stdcall initdata( char *data, int size)
{
int i = 0;
for (;i < size; ++i )
{
data = 'a';
}
return 1;
}

__declspec( dllexport ) int __stdcall initdata2( char **data, int size)
{
int i = 0;
for (;i < size; ++i )
{
//(*data) = 'a';
}
return 1;
}
-------- CUT HERE ---------

Compile this like so:

cl mydll.c /LD /DLL

Then create a TestMyDLL.VB

-------- CUT HERE ---------
Option Strict On
Option Explicit On

Imports System
Imports System.Runtime.InteropServices

Module Module1
Declare Auto Function initdata Lib "mydll.dll" _
(ByVal data As Byte(), _
ByVal size As Integer) As Integer

<DllImport("mydll.dll", SetLastError:=True)> _
public function initdata2 ( _
byref data As Byte(), _
ByVal size As Integer) As Integer
end function

Sub Main()
Try
Dim data() As Byte
Dim size As Integer = 10
Array.Resize(data, size)
Console.WriteLine("* B4 - data.length: {0}", data.length)
'initdata(data, size)
initdata2(data, size)
Console.WriteLine("* AD - data.length: {0}", data.length)
Dim i As Integer
For i = 0 To data.length - 1
Console.WriteLine("{0} [{1}]", i, data(i))
Next

Catch ex As Exception
Console.WriteLine("-------------------------------------")
Console.WriteLine("Exception: {0}", ex.Message)
Console.WriteLine("{0}", ex.StackTrace)
Console.WriteLine("-------------------------------------")
End Try

Console.ReadKey()

End Sub
End Module
-------- CUT HERE ---------

and try to figure it out. I haven't yet. :) except that for the
initdata() function it works. The 2nd one with the char **data is
where you run into corruption issue which the catch traps. But as the
VB is written using the byref in the InitData2() parameter #1, it
doesn't abort but you get some currupt. Run it to see what I am saying.

There are solutions but I believe it requires proper Marshalling - the
finer details I need to learn.
 
R

roidy

Thanks guys for the help,

The actual function I`m trying to use is from libmp4v2.dll, the function is
called MP4GetMetadataCoverArt and is used to extract a piece of artwork from
a mp4 file heres the c code:-

declaration:-
bool MP4GetMetadataCoverArt(MP4FileHandle hFile,
uint8_t** coverart,
uint32_t* size,
uint32_t index);

defination:-
bool MP4GetMetadataCoverArt(MP4FileHandle hFile,
uint8_t **coverArt,
uint32_t* size,
uint32_t index)
{
if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
try {
return ((MP4File*)hFile)->GetMetadataCoverArt(coverArt, size, index);
}
catch (MP4Error* e) {
PRINT_ERROR(e);
delete e;
}
}
return false;
}

usage:-
uint8_t *art;
uint32_t artsize;
MP4GetMetadataCoverArt(filehandle, &art, &artsize);

When the function returns art is a pointer to a byte array that has been
filled with the coverart data and artsize is the size in bytes of that data.
So I carn`t just create a byte array to pass to the function because I dont
know the size until the function returns. Mike while I understand your code
it assumes you already know the size of the data your working with(my fault
I messed up the intial description of the function, I was just trying to
keep it simple:).

Which brings me back to my original problem how do I pass a pointer to a
function when the memory hasn`t been allocated yet? eg.

char *data;
function(&data);

Thanks
Rob
 
M

Mike

roidy said:
Thanks guys for the help,

The actual function I`m trying to use is from libmp4v2.dll, the function is
called MP4GetMetadataCoverArt and is used to extract a piece of artwork from
a mp4 file heres the c code:-

declaration:-
bool MP4GetMetadataCoverArt(MP4FileHandle hFile,
uint8_t** coverart,
uint32_t* size,
uint32_t index);

defination:-
bool MP4GetMetadataCoverArt(MP4FileHandle hFile,
uint8_t **coverArt,
uint32_t* size,
uint32_t index)
{
if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
try {
return ((MP4File*)hFile)->GetMetadataCoverArt(coverArt, size, index);
}
catch (MP4Error* e) {
PRINT_ERROR(e);
delete e;
}
}
return false;
}

usage:-
uint8_t *art;
uint32_t artsize;
MP4GetMetadataCoverArt(filehandle, &art, &artsize);

When the function returns art is a pointer to a byte array that has been
filled with the coverart data and artsize is the size in bytes of that data.
So I carn`t just create a byte array to pass to the function because I dont
know the size until the function returns. Mike while I understand your code
it assumes you already know the size of the data your working with(my fault
I messed up the intial description of the function, I was just trying to
keep it simple:).

Which brings me back to my original problem how do I pass a pointer to a
function when the memory hasn`t been allocated yet? eg.

char *data;
function(&data);


One approach to this is create a Managed Class Library (MCL)- a .NET
DLL wrapper around the unmanaged (traditional) library.

You MCL will do all your pointer and resources management

So you have:

libmp4v1.dll <--> MCL.DLL <--> .NET Application Lanaguages

The memory managemen is now in the MLC.DLL

The beauty of .NET is that you can create the MCL in any .NET
language, VB.NET, C++ (MS's C++/CLR) and C#.

This is what I am doing myself. The hard part is dealing with the
..NET interop interfaces, which includes also making functions return
arrays.

So you might have this, in the MCL.DLL a class, which you might want
to do in C#, C++ or VB.NET, whatever is easier for you:

in VB.NET, (not 100% correct here, just an outline)

public class MP4LIB
implements iDisposable

Private stuff() as Array

public shared function MP4GetMetadataCoverArt() as Byte()
// find size and allocate local memory
// call unmanaged function
// prepare byte array to return
// deallocate local memory
// return Byte array
end function

// constructor
sub new
// allocate stuff
end sub

// destructors (VB.NET style)
sub finalize
end sub
Public Overloads Sub Dispose() Implements IDisposable.Dispose
// deallocate stuff
end sub

end class

Thats the basic idea. I have over 100 functions to deal with
different signatures, some are easy to do in VB.NET, others are harder
so currently, I am mixing my MCL with VB.NET and C++, but I am also
learning how do the more complex stuff in VB.NET too.

Start by creating a class library with the functions your want that
will map the libmp4v2.dll. They exposed .NET function do not need to
be the same prototype, it can turn the array, like above.

--
 
M

Mike

Mike said:
Start by creating a class library with the functions your want that will
map the libmp4v2.dll. They exposed .NET function do not need to be the
same prototype, it can turn the array, like above.

Sorry, I meant:

The exposed .NET functions do not need to be the same prototype, it
can return an array with no parameters or maybe just the file name,
like above.

If you need to keep file open, then the file handle is a private
resource in your MCL.DLL, which mean you have a classic file I/O wrapper:

private hfile as inptr

public shared function openmp4(byval fn as string) as boolean
hFile = open the file
end function

public shared function closemp4() as boolean
close the hfile
end function

public shared function readmp4() as byte()
read the hfile and return array
end function
 
R

roidy

Thanks Mike, although most of your post went right over my head, I`m still
pretty new to VB and .net, this is what I`m thinking. I`ll write a wrapper
around the MP4MetadataGetArtwork function in c as follows:-

char* MY_MP4GetArtwork(MP4FileHandle hFile,long *returnedsize)
{
char *art;
MP4GetMetadataArtwork(hfile,&art,&returnedsize,0);

return art;
}

now in VB can I do the following:-

Declare Function MY_MP4GetArtwork "libmp4v2.dll" (ByVal fhandle As UIntPtr,
_
, ByRef
returnedsize As UInteger) As IntPtr

dim size as UInteger
dim array() as byte = MY_MP4GetArtwork(fhandle,size)



Try not to laugh if thats total jibbarish and has no chance in hell of
working but like I say I`m pretty new to this and I`m at a loss at the
moment.

Rob
 
R

roidy

So I just tried my idea and I get an error on the following line:-

Dim array() As Byte = MY_Mp4GetArtwork(filehandle)

the error is as follows:- Value of type 'System.IntPtr' cannot be converted
to '1-dimensional array of bytes'

So I`m back to square one, I still carn`t retreve the array of bytes from
the function.........

Rob
 
M

Mike

Rob,

I just found this interesting blog article providing some insight and
history in the crazy new C++/CLR language syntax. Might help with
learning marshalling of data:

http://blogs.msdn.com/slippman/archive/2004/02/16/73932.aspx

This is helping explain how stl and cli was put together. Here is the
top paragraph, which changes how we control memory:

Of course, the definition of GetArray is a dismal failure since it
is returning a null tracking handle! One of the pitfalls of using
the dynamic programming paradigm is adjusting to the CLR reference
type semantics which, from an ISO C++ point of view, are
topsy-turvy. The correct implementation is to return the object
by gcnew, and not to fret over who owns the handle – the CLR
garbage collector owns it. [The reason the revised language
provides a deterministic finalization mechanism through a steroid
destructor mechanism is not for the automation of memory management
– Give GC a Change, as the late John Lennon once wrote, if memory
serves me – but to handle the automatic reclamation of other system
resources that are not recognized by the underlying CLR.]

Hope this helps, it is for me. :)

--
 
R

roidy

Thanks Mike, I`ll have a good read of it when I get some spare time.

Rob


Mike said:
Rob,

I just found this interesting blog article providing some insight and
history in the crazy new C++/CLR language syntax. Might help with
learning marshalling of data:

http://blogs.msdn.com/slippman/archive/2004/02/16/73932.aspx

This is helping explain how stl and cli was put together. Here is the top
paragraph, which changes how we control memory:

Of course, the definition of GetArray is a dismal failure since it
is returning a null tracking handle! One of the pitfalls of using
the dynamic programming paradigm is adjusting to the CLR reference
type semantics which, from an ISO C++ point of view, are
topsy-turvy. The correct implementation is to return the object
by gcnew, and not to fret over who owns the handle – the CLR
garbage collector owns it. [The reason the revised language
provides a deterministic finalization mechanism through a steroid
destructor mechanism is not for the automation of memory management
– Give GC a Change, as the late John Lennon once wrote, if memory
serves me – but to handle the automatic reclamation of other system
resources that are not recognized by the underlying CLR.]

Hope this helps, it is for me. :)

--

So I just tried my idea and I get an error on the following line:-

Dim array() As Byte = MY_Mp4GetArtwork(filehandle)

the error is as follows:- Value of type 'System.IntPtr' cannot be
converted to '1-dimensional array of bytes'

So I`m back to square one, I still carn`t retreve the array of bytes
from the function.........

Rob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top