Windows is a mystery to me. I had to modify a C# application to use a DLL written in C++.
I started by reading all of the documentation, which took about 3 days. Creating Reusable Code seemed to be a good place to start; however, it's terribly. I have created the demo program and documented the problems along the way which is what Microsoft should have done in the first place.
I have created a sample project that has a DLL which is called from C#. I looked for a long time for a functional example. This is a functional example by someone who does not know the Windows lingo or assumptions. The source code is available at the bottom of the page as zip files. In summary, I started on VS2005, but ended up in VS2010.
In VS2005, I created a solution (this is MS term for a tome of projects) called "DLLExampleWithCSharp". I then created a project called "simpleDLL" that is a Win32API project that is a DLL through the project wizard and exported the functions. This is the first part of where I went wrong. The wizard only partially completes the requirements for a DLL. If you will be including the DLL through headers, it is a fine method, but for "unmanaged DLL" code, it will not work for you. I will return to this topic later.
Using the wizard's format, I changed my function to be
(simpleDLL.h) SIMPLEDLL_API size_t simpleDLL_message(char *stringio, size_t ui_length); (/simpleDLL.h) (simpleDLL.c) SIMPLEDLL_API size_t simpleDLL_message(char *stringio, size_t ui_length) { char *cp_message = "suigin spars with windows.\n"; size_t i_message_length = strlen(cp_message); if(ui_length < i_message_length) { i_message_length = ui_length; } strncpy(stringio,cp_message, i_message_length); return(i_message_length); } (/simpleDLL.c)I now have a function that will take a string pointer and allocation length, and copy the cp_message into it. I now make my C# application to call the DLL. (CSharpCallsDLL.cs)
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; //required to call the DLL namespace CSharpCallsDLL { class Program { [DllImport("simpleDLL.dll")] public static extern UInt32 simpleDLL_message(StringBuilder stringio, UInt32 ui_length); static void Main(string[] args) { StringBuilder s_str = new StringBuilder("000000000"); UInt32 ui_len = (UInt32)s_str.Length; try { UInt32 ui_result = simpleDLL_message(s_str, ui_len); Console.Out.WriteLine("The result:"); Console.Out.WriteLine("ui_result=" + ui_result + ", stringio=" + s_str); } catch (Exception e) { Console.Out.WriteLine(e); } } } }(/CSharpCallsDLL.cs) I then start up my Visual Studio 2005 Command Prompt and run the program, CSharpCallsDLL.exe.
C:\Users\suigin\Documents\Visual Studio 2005\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>CSharpCallsDLL.exe System.DllNotFoundException: Unable to load DLL 'simpleDLL.dll': The specified m odule could not be found. (Exception from HRESULT: 0x8007007E) at CSharpCallsDLL.Program.simpleDLL_message(String stringio, UInt32 ui_length ) at CSharpCallsDLL.Program.Main(String[] args) in C:\Users\suigin\Documents\Visu al Studio 2005\Projects\DLLExampleWithCSharp\CSharpCallsDLL\Program.cs:line 30And, it dies with "System.DllNotFoundException". The DLL is not found, so I either have to add it to my path, but I will just copy the DLL. "copy simpleDLL.dll ..\CSharpCallsDLL\bin\Release\.", so simpleDLL.dll is not in the same directory as CSharpCallsDLL.exe. It is also worth noting that C# projects and C++ projects have a different directory layout, which I found to just be a quirk, so be sure that you are in the correct "Release" directory. The next error that I encountered was "System.BadImageFormatException".
C:\Users\suigin\Documents\Visual Studio 2005\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>CSharpCallsDLL.exe System.BadImageFormatException: An attempt was made to load a program with an in correct format. (Exception from HRESULT: 0x8007000B) at CSharpCallsDLL.CSharpCallsDLL.simpleDLL_message(String stringio, UInt32 ui _length) at CSharpCallsDLL.CSharpCallsDLL.Main(String[] args) in C:\Users\suigin\Documen ts\Visual Studio 2005\Projects\DLLExampleWithCSharp\CSharpCallsDLL\CSharpCallsDL L.cs:line 30The official Microsoft documentation for System.BadImageFormatException is rather worthless, and I could guess what the problem was. Basically, my version of .NET is too old for what I was trying to do. Visual Studio 2005 is .NET 2.0, and I have 64-bit Windows7 machine. I generally develop for the lowest possible machine; however, in this case, I'll have to upgrade. Here enters Visual Studio 2010. (In other news, I would really love to know how to fix that error, but I the best I could find was here, but I couldn't understand it.) I redid everything that I did above, and it magically got past that exception, and went on to the next one.
C:\Users\suigin\Documents\Visual Studio 2010\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>CSharpCallsDLL.exe System.EntryPointNotFoundException: Unable to find an entry point named 'simpleD LL_message' in DLL 'simpleDLL.dll'. at CSharpCallsDLL.CallDLL.simpleDLL_message(String stringio, UInt32 ui_length ) at CSharpCallsDLL.CallDLL.Main(String[] args) in C:\Users\suigin\Documents\Visu al Studio 2010\Projects\DLLExampleWithCSharp\CSharpCallsDLL\CallDLL.cs:line 33The entry to the DLL is the most important thing, and System.EntryPointNotFoundException says that I did not call the correct function name. There should be a method to recall a method manifest, but there does not seem to be. This is where Microsoft should have example code. The issue is that C# cannot see the entry point into the DLL even though I know it is there. I found that there was a tool which I could use to see the entries from the VS2010 shell, called dumpbin (I note that other pages for dumpbin are terrible).
C:\Users\suigin\Documents\Visual Studio 2010\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>dumpbin /exports simpleDLL.dll Microsoft (R) COFF/PE Dumper Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file simpleDLL.dll File Type: DLL Section contains the following exports for simpleDLL.dll 00000000 characteristics 4F368F07 time date stamp Sat Feb 11 10:53:43 2012 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001010 ?simpleDLL_message@@YAIPADI@Z = ?simpleDLL_message@@YA IPADI@Z (unsigned int __cdecl simpleDLL_message(char *,unsigned int)) Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .rsrc 1000 .textWow, what a function name. I found that I could use that awful thing to call my function, but I knew there must be a better way. I seems that it did not know how to make nice names by default. I found an obscure note in a linker how-to regarding a possibility from the days of Win 3.11, which I can no longer find the link. If you include 'extern "C"', it will solve some problems that were not specific. My new function declaration is:
extern "C" SIMPLEDLL_API size_t simpleDLL_message(char *stringio, size_t ui_length);Upon compilation, the DLL entry makes much more sense.
C:\Users\suigin\Documents\Visual Studio 2010\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>dumpbin /exports simpleDLL.dll Microsoft (R) COFF/PE Dumper Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file simpleDLL.dll File Type: DLL Section contains the following exports for simpleDLL.dll 00000000 characteristics 4F3693CF time date stamp Sat Feb 11 11:14:07 2012 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001010 simpleDLL_message = _simpleDLL_message Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .rsrc 1000 .textThe addition of 'extern "C"' has made all of the difference in the entry point definition. The program now runs and gives the following output:
C:\Users\suigin\Documents\Visual Studio 2010\Projects\DLLExampleWithCSharp\CSharpC allsDLL\bin\Release>CSharpCallsDLL.exe The result: ui_result=9, stringio=suigin spIn summary, I now know the syntax for C++ DLL calls from C#. The project files are the working Visual Studio 2010 project and the broken Visual Stuidio 2005 project.
No comments:
Post a Comment