ZeroEffect Posted October 23, 2009 Posted October 23, 2009 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
Leaders snarfblam Posted October 23, 2009 Leaders Posted October 23, 2009 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? Quote [sIGPIC]e[/sIGPIC]
ZeroEffect Posted October 26, 2009 Author Posted October 26, 2009 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, oBlah as new objectwidget I know this is not always used with objects. Here is my PIvoke code. <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. typedef unsigned int GEN_HANDLE_t; [/Code] Here is my call to the function. [code=visualbasic] 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
Administrators PlausiblyDamp Posted October 26, 2009 Administrators Posted October 26, 2009 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 Public Shared Function gen_allocMsg(handle as IntPtr) As End Function Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
Leaders snarfblam Posted October 26, 2009 Leaders Posted October 26, 2009 It looks like the (C++) signature is: 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: 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: <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: 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 Quote [sIGPIC]e[/sIGPIC]
ZeroEffect Posted October 27, 2009 Author Posted October 27, 2009 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? <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. 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
Leaders snarfblam Posted October 27, 2009 Leaders Posted October 27, 2009 The asterix signifies a pointer. For instance, look at the declarations below: 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. <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 Quote [sIGPIC]e[/sIGPIC]
ZeroEffect Posted October 28, 2009 Author Posted October 28, 2009 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. <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. 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. <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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted October 29, 2009 Author Posted October 29, 2009 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. #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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
Administrators PlausiblyDamp Posted October 29, 2009 Administrators Posted October 29, 2009 http://www.pinvoke.net/default.aspx/ws2_32/WSAStartup.html looks promising and might give you a start... Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
ZeroEffect Posted October 29, 2009 Author Posted October 29, 2009 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. Unable to load DLL 'comctl32.dll': The filename or extension is too long. (Exception from HRESULT: 0x800700CE) I am bookmarking that site thought! Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted October 30, 2009 Author Posted October 30, 2009 After some more trial and error with using WSAStartup() and WSACleanup(). 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. 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted October 30, 2009 Author Posted October 30, 2009 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. 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. <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. 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. 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted January 19, 2010 Author Posted January 19, 2010 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. 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted January 19, 2010 Author Posted January 19, 2010 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. Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.