6

I have declared a DLL import in my C# program that looks like this:

[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode", 
           CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr generateKeyCode(char[] serial, char[] option, char c_type);

It references the function generateKeyCode() inside of my DLL.

Here is the code that is causing an error (used breakpoints):

const char* generateKeyCode(char serial[], 
                 char option[], 
                 char c_type)
{
returnBufferString = "";
SHA1_CTX context;
int optionLength = 0;
#ifdef WIN32
unsigned char buffer[16384] = {0};
#else
unsigned char buffer[256] = {0}; 

#endif
//char output[80];
char keycode[OPTION_KEY_LENGTH+1]        = "";
int digest_array_size = 10; //default value for digest array size
unsigned char digest[20] = {0};
char optx[24] = {0};
char c_type_upper;
// Combine serial # and Option or Version number

char str1[30] = {0};
int i;
int size = 0;
int pos = 0;


...
...
}

Basically, I imported this DLL so I could pass the function parameters and it could do its algorithm and simply return me a result. I used this marshaler function...

public static string genKeyCode_marshal(string serial, string option, char type)
{
    return Marshal.PtrToStringAnsi(generateKeyCode(serial.ToCharArray(), 
           option.ToCharArray(), type));
}

...so I could make the call properly. Inside of my C++ header file, I have defined a string, as indicated is helpful in the answer to this question (it is the returnBufferString variable present at the top of the C/C++ function).

I make this function call several times as I use a NumericUpDown control to go from 1.0 to 9.9 in increments of 0.1 (each up or down accompanies another function call), and then back down again. However, every time I try to do this, the program hitches after a seemingly set number of function calls (stops at 1.9 on the way back down if I just go straight up and down, or earlier if I alternate up and down a bit).

Please note that it works and gives me the value I want, there are no discrepancies there.

I changed the buffer size to some smaller number (5012) and when I tried to run the program, on the first function call it threw the AccessViolationException. However, doubling the buffer size to twice (32768) the original had no effect in comparison to the original -- going straight up to 9.9 from 1.0 and down back again, it stops at 1.9 and throws the exception.

EDIT: Default is ANSI, so it is ANSI. No problems there. Is this a memory allocation issue??

Sean Glover
  • 520
  • 6
  • 22
  • Upon further testing, it seems like the function has memory issues regardless of when or where it is called in that once in a while, my debug print-outs show that there's a bunch of garbage values in the stream, and so it generates the wrong key. I feel like this is probably related. – Sean Glover Jan 04 '12 at 20:48
  • A char in C# is 2 bytes wide. In C, char is only 1 byte (I think you'd need it to be a wchar_t to be 2 bytes depending on compiler options). I'd start there. – Christopher Currens Jan 04 '12 at 20:49

2 Answers2

2

I would suggest trying the following:

[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode", 
       CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr generateKeyCode(string serial, string option, char c_type);

Note the new CharSet field of DllImport attribute.

Next idea is to use MarshalAs attribute explicitely:

[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode", 
       CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
static extern IntPtr generateKeyCode([MarshalAs(UnmanagedType.LPTStr)] string serial, [MarshalAs(UnmanagedType.LPTStr)] string option, char c_type);
Krizz
  • 11,362
  • 1
  • 30
  • 43
  • Hi Krizz. It got me .1 further (got me to 1.9 instead of 2.0 on the way back down). I had thought about it but didn't think it would help. Thanks for the reply though. – Sean Glover Jan 04 '12 at 21:01
  • could be, but the point is to also rely on CLR to marshal `string` instead of using `char[]`. Have you changed it too? – Krizz Jan 04 '12 at 21:03
  • Ok so I have changed the parameters of the imported function declaration, but it throws an exception due to my actual marshal function wherein I have the return Marshal.PtrToStringAnsi(generateKeyCode(serial.ToCharArray(), option.ToCharArray(), type)); saying "Cannot marshal 'parameter #1': Invalid managed/unmanaged type combination (Arrays can only be marshaled as LPArray, ByValArray, or SafeArray)." since I am using these toCharArray()s. – Sean Glover Jan 04 '12 at 21:04
  • 1
    So, get rid of both `.ToCharArray()`. – Krizz Jan 04 '12 at 21:05
  • It's not working. Now it's only passing the first character of the strings. – Sean Glover Jan 04 '12 at 21:07
  • It seems like it is indeed a problem in my C/C++ code... but what? Is it related to how memory is allocated to the unmanaged code? – Sean Glover Jan 04 '12 at 22:01
  • "Now it's only passing the first character of the strings." That's what happens when you use `CharSet.Unicode` on the C# end. Use `CharSet.Ansi`. Use the first DllImport in the answer, pass c_type as `byte` and don't use ToCharArray(), just pass the string directly. If it fails then, the problem is in the C++ code. – David Heffernan Jan 04 '12 at 22:21
  • Nono, I used `CharSet.Ansi` and passed as string directly, to let c# marshal it for me. And it then only uses the first character. I believe this answer probably describes my problem best: http://stackoverflow.com/questions/6708709/dllimport-returning-null-terminated-string-accessviolationexception. Thank you for the reply. – Sean Glover Jan 04 '12 at 22:38
0

I know this may be unsatisfactory, but once I removed the output redirection I was using to debug from within my C/C++ DLL, the problem stopped. Everything works now, so I guess that's essentially equivalent to answering my own question. Thanks to everyone for the replies.

Sean Glover
  • 520
  • 6
  • 22