Guidence request

ZeroEffect

Junior Contributor
Joined
Oct 24, 2004
Messages
204
Location
Detroit, MI
I have a SDK kit that I have all the documentation for, great! Now the DLL is a unsigned DLL and I can't load it into a .net project. I have used DLLImport to setup the functions I need to use. I am not sure they are right but I am trying to figure them out one by one.

All the examples are C++ which I know nothing about. Looking at the examples I can kind of see what is going on.

I have the API .lib file and the header files. Is there a way to load these into a .net project and make my own DLL to use in .net?

Currently I am stuck with a "Attempted to read or write protected memory." error. All the types are set the same but I still get the error. I have contacted the supplier of the SDK but they are slow to get back to me.

Thanks for any help you can provide.

ZeroEffect
 
You can wrap the unmanaged DLL in your own managed classes, but you would have to write all the wrapper code. If you are using the DLL in a non-trivial manner, it is probably best to wrap it one way or another.

I'd be glad to answer any specific quesions, but if you are looking for guidance I would say your best bet is to seek out tutorials/tips/etc for P/Invoke, or even topics on accessing Windows API from managed code, as this is the most common instance of P/Invoke and most of the general information will apply.

This is a plain-old unmanaged DLL, right? Or is it COM or something?
 
Thanks marble_eater,

You are correct it is, to my knowledge a plain old unmanaged DLL. I have been reading on PInvoke and using it i have been able to pull the version information using the getVersion(). I am currently trying to get the next functionb to work which is to creat an object. I am used to using,
Visual Basic:
oBlah as new objectwidget
I know this is not always used with objects.

Here is my PIvoke code.
Visual Basic:
    <DllImport("genDLL.dll")> Public Shared Function gen_allocMsg() As integer
    End Function

What this does is creat a "handle", this is the term the documentation uses. They list the interface as gen_allocMsg(GEN_HANDLE_t *handle), input handle - pointer to handle new GEN message object. Output is listed as new GEN handle identifier. it also will return a pass/fail status, 1 or 0.

GEN_HANDLE_t I foun defined in the header as an unsigned integer.

Code:
typedef unsigned int GEN_HANDLE_t;

Here is my call to the function.

Visual Basic:
dim Handle as uint
Handle = gen_allocMsg()

I have tried this and other possible formations but I keep getting this message. Attempted to read or write protected memory or PInvoke restriction: cannot return variants.

So this is where I am stuck. I will be calling the company I got the SDK from to see if they can give me some more help.

Thanks again,

ZeroEffect
 
What is the gen_allocMsg declaration in the header file? Does it specify a return type?

From the little bit you posted above it looks like the function accepts a GEN_HANDLE_t rather than returns one. This would give a delaration more like
Visual Basic:
<DllImport("genDLL.dll")> Public Shared Function gen_allocMsg(handle as IntPtr) As <return type>
    End Function
 
It looks like the (C++) signature is:
Code:
int gen_allocMsg(GEN_HANDLE_t *handle)
If you aren't familiar with handles, just think of it as a value that identifies an object. In VB, generally an IntPtr value is used for handles. When dealing with p/invoke, I prefer to declare a struct for my handles for type safety and easy-to-read code. For example:
Visual Basic:
Public Structure GEN_HANDLE_t
    Public handleValue As IntPtr
End Structure
You can then use this structure in place of IntPtr and you will always know exactly what kind of handle we are dealing with, making our code consistent with the documentation.

The DLL method is looking for a pointer to a handle. It probably wants an address where it can put the handle for you. In this case you would declare the method like so:
Visual Basic:
<DllImport("genDLL.dll")> _
Public Shared Function gen_allocMsg(ByRef handle as GEN_HANDLE_t) As Integer
    End Function
The important part is the ByRef. With P/Invoke, a ByRef parameter gives a DLL a pointer to write a value to.

Now, to use the function, call it like so:
Visual Basic:
Dim myHandle As New GEN_HANDLE_t
If gen_allocMsg(myHandle) = 1 Then
    ' We now have a handle to our new object
    ' stored in myHandle
End If
 
Thanks PlausiblyDamp and marble_eater this is great!

Ok all the function calls that only reference the handle are working. How would I go about working with more than one element?

Code:
<DllImport("genDLL.dll")> Public Shared Function gen_setTitle(ByRef handle As GEN_HANDLE_t, ByVal strTitle As String) As integer
    End Function
'c++ code
GEN_UINT8			staticStage[3];

gen_setTitle(GEN_HANDLE_t, GEN_SINT8 *title);

' what does GEN_SINT8 *title mean
'GEN_SINT8 is this a signed 8 bit integer? Looks more like an array?
'*title, what does the * mean?



I have also found in a program example they included a information block. But I don't see where it was setup. I am looking for something like in VB where you setup a class. The only thing I found were these lines of code.

Code:
GENTXi_infoBlk;

GENTXi_infoBlk def;

Wait just found the rest of it. The code before the lines above built the structure. I'll come back to that if needed. First I need to learn how to get the functions working. ;O)

Thanks again for helping me understand and learn more about how to use these functions.

ZeroEffect
 
The asterix signifies a pointer. For instance, look at the declarations below:
Code:
int   varA = 5;
int * varB = &varA;
varA is declared as an integer. varB is a pointer to an integer. (The ampersand, &, is the address-of operator, so the second line of code says "store the address of varA in varB.").

Visual Basic does not have anything equivalent to pointers. (You can store them in IntPtr objects, but can't directly use the memory they point to.)



As for GEN_SINT8 * title, I would say that it is looking for a string.

I'm guessing that GEN_SINT8 is the same as a DotNet SByte (signed byte), but that isn't necessarily important. GEN_SINT8* would be a pointer to a series of 1-byte values that represent the title, a.k.a. an ASCII/ANSI string.

Looking at our C++ signature, gen_setTitle(GEN_HANDLE_t, GEN_SINT8 *title);, it is asking for a handle (NOT a pointer to the handle, note the lack of an asterix, so use ByVal instead of ByRef) and a string (in the form of a pointer to an 8-bit encoded string). VB will marshal the string as a pointer to a series of characters, and I think ANSI is the default, but it won't hurt to be explicit.
Code:
<DllImport("dll", [B]CharSet:=CharSet.Ansi[/B])> _
Public Shared Function gen_setTitle([B]ByVal[/B] handle As GEN_HANDLE_t, ByVal strTitle As String) As integer
 
Again marble_eater thank you, for the explaining the "*" and "&" now when I look at the SDK i can follow along alot better. You were correct changing the ByRef to ByVal made things work. I am now able to set and retrieve data. The only line that isn't working is when I try to use gen_sendMsg. The the line get processed and returns the integer 1 indicating things went well but nothing is sent. Now I know you can't tell me why it didn't send but could you confirm what I am doing looks right.

Code:
    <DllImport("genDLL.dll", CharSet:=CharSet.Ansi)> Public Shared Function gen_sendMsg(ByVal handle As GEN_HANDLE_t, ByVal destAddr As String, ByVal port As Integer, ByVal msgId As integer) As Integer
    End Function

My docs state,
handle - identifies the message.
destAddr - String pointer to IP address.
port - destination port
msgId - GEN_MSG_ID_t

I understand the handle, destAddr and Port. The msgId doesn't really get explained well, but digging through the SDK I found out the following.

Thier example shows the variable for destAddr & port setup like this.
Code:
GEN_SINT8* IpAddress[64] = "name or IP address";
GEN_SINT32 Port = 2002;
Ok so the port is an integer set to 2002, IpAddress looks like it is a 64 character string set to the IP address you are sending to. Looks like I would have to use a string builder in VB.

msgId while it is used in the example, is not setup/defined in the example. So after some hunting. I found this is the header file and it looks like it would be created in the gen_createMsg function. You will see that I was tring to see if the function would create the value but it only sets the value to zero.

Code:
    <DllImport("genDLL.dll", CharSet:=CharSet.Ansi)> Public Shared Function gen_createMsg(ByVal handle As GEN_HANDLE_t, ByRef contentID As GEN_MSG_ID_t) As Integer
    End Function

typedef enum
{
	GEN_MSG_CONTENT_ID,
	GEN_MSG_FLUSH_ID
}  GEN_MSG_ID_t;
I just recieved an email stating that internally the contentID is not used. They have it set to zero. So that explaines that.

So I should be sending data. Does what I explained sound like it is correct?

Thanks

ZeroEffect
 
I was able to get more information, that wasn't in my SDK kit, It looks like I need to initialize winsockets. They say that windows needs to initialize some socket stuff (they didn't tell me what stuff) that Linux doesn't and they decided to leave it out of the libraries and leave it up to the application.

This is how they do it.
Code:
#if WIN32 || _WIN32

	WSADATA wsaData;

    int iErr = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );

    if(iErr)

    return FALSE;

#endif

I am not sure what to do here. I know how to create a socket and use it to send data, but what is this code doing?

Thanks for your help,

ZeroEffect
 
Thanks PlausiblyDamp this looks like what I need. I am having a problem though. Once this is called I get DLL error messages.

when I mouse over the form, textbox, or button I get this error.

Code:
Unable to load DLL 'comctl32.dll': The filename or extension is too long. (Exception from HRESULT: 0x800700CE)

I am bookmarking that site thought!
 
After some more trial and error with using WSAStartup() and WSACleanup().
Code:
    Public Const SUCCESS As Integer = 0
    Public Const HIGH_VERSION As Integer = 2
    Public Const LOW_VERSION As Integer = 2
    Public Const WORD_VERSION As Short = 36

    Public Structure WSAData
        Public wVersion As Short
        Public wHighVersion As Short
        '<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=257)> _
        Public Description As String
        '<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=129)> _
        Public Status As String
        Public MaxSockets As Integer
        Public MaxUdpDg As Integer
        Public vendorInfoPointer As IntPtr
    End Structure

    <DllImport("ws2_32.dll")> Public Shared Function WSAStartup(ByVal versionRequested As Int16, ByVal infoBuffer As WSAData) As Integer
    End Function

    <DllImport("ws2_32.dll")> Public Shared Function WSACleanup() As Integer
    End Function

or like this


    Declare Function WSAStartup Lib "ws2_32.dll" (ByVal versionRequested As Int16, ByVal infoBuffer As WSAData) As Integer

    Declare Function WSACleanup Lib "ws2_32.dll" () As Integer
When I tried to use this in a new VB.net 2003 project i get this message after these are called. Let me also state that the functions run without issue it is when you access/use anything after they run that errors popup.
Code:
An unhandled exception of type 'System.NullReferenceException' occurred in mscorlib.dll

Additional information: [Resource lookup failed - infinite recursion detected.  Resource name: Arg_NullReferenceException]

Time to scour the web some more ;O)

ZeroEffect
 
ok so WSA gave my projects fits. So I thought what the hell I'll try using sockets. I setup the import and created a dummy socket.

Code:
Imports System.Net.Sockets

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim s As New Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP)

    End Sub

and now the DLL that wouldn't send data now sends data, very cool.

Now I assume this isn't the most elegant way to get the job done but it does work and I'll be able to explore the rest of the SDK. I want to get the error functions to work and I'll be able to use all the parts I need to mark the DLL work for me.

Code:
    <DllImport("genDLL.dll")> Public Shared Function gen_getErrorNum(ByVal handle As Integer) As Integer
    End Function
    <DllImport("genDLL.dll")> Public Shared Function gen_getErrorStr(ByVal handle As Integer) As String
    End Function
    <DllImport("genDLL.dll")> Public Shared Function gen_enableErrorLog(ByVal enable As Boolean, Optional ByVal FileNamePath As String = "") As Integer
    End Function

They explaine the above functions like so.

Code:
gen_getErrorNum
This function returns the last error number.
Interface: GEN_ERROR_t
gen_getErrorNum(GEN_HANDLE_t handle)
Input: Handle - Message handle identifier
Output: none
Return: error code - Last error or GEN_NO_ERROR code

gen_getErrorStr
This function returns the last error string. The error string is read-only and must not be modified.
Interface: GEN_SINT8 *gen_getErrorStr(GEN_HANDLE_t handle)
Input: handle - Message handle identifier.
Output: none
Return: error string - Pointer to error string or NULL if no error.

gen_enableErrorLog
This function enables/disables automatic error logging. The caller is responsible for opening and closing
the user-defined file. The error log file is immediately flushed when an error is generated.
Interface: void gen_enableErrorLog(GEN_BOOL isEnabled,FILE *fp)
Input: isEnabled - Set to GEN_TRUE to enable or GEN_FALSE to disable.
fp - Log file pointer (e.g., stdout, stderr, or user-defined file)
If set to NULL, “stdout” will be used.
Output: none
Return: none

I think the best one to use would be the log file. I am thinking that I would call this function like so.

Code:
gen_enableErrorLog(true,"error.log")


Thank you marble_eater and PlausiblyDamp, without your help I would still be pulling my hair out trying to get this DLL work. Your help has expanded my coding ability and understanding of C/C++ and how to make standard DLL work in .Net. I can't thank you enough for your time and help.


ZeroEffect
 
Re: Guidence request (update)

Ahh time has pasted and my application works, time to deploy. Now new problems. While everything works great on my computer I built the program on I am getting an error message when trying to call these functions from the DLL.

Visual Basic:
unable to load DLL 'genDLL.dll': the specified module could not be found.
(Exception from HRESULT: 0x8007007E)

What could I be missing? I am researching more online.

Thanks For your help.

ZeroEffect
 
Found it a DLL was missing from my system32 folder from the dotnet 1.1 framework. ARGH! I'll have to check my setup project to see why it was not included.
 
Back
Top