Trouble with DllImport syntax calling GetJob() from a printer driver library - c#

I'm trying to call the GetJob() method documented here. I think I'm having problems with the syntax of the routine right now, both calling and defining. I've finally got something to compile which is the following.
[DllImport(
"winspool.drv",
EntryPoint = "GetJob",
SetLastError = true,
CharSet = CharSet.Ansi,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern bool GetJob
([InAttribute()] IntPtr hPrinter,
[InAttribute()] Int32 JobId,
[InAttribute()] Int32 Level,
[OutAttribute()] out byte[] pJob,
[InAttribute()] Int32 cbBuf,
[OutAttribute()] out Int32 pcbNeeded);
...
...
...
...
const int BUFFER_SIZE = 250;
int pcbNeeed = 0;
unsafe
{
byte[] byteBuffer = new byte[BUFFER_SIZE];
bResult = GetJob(m_PrinterHandle, jobID, 1, out byteBuffer, BUFFER_SIZE, out pcbNeeed);
}
According to the documentation here, it seems I should be able to use a byte[] without any special marshaling code because it is "blittable". In anycase, I get a runtime exception that says:
Unable to find an entry point named 'GetJob' in DLL 'winspool.drv'.
at NQBB.Printer.PrintQueueMonitor.PrinterWatcher.GetJob(IntPtr hPrinter, Int32 JobId, Int32 Level, Byte[]& pJob, Int32 cbBuf, Int32& pcbNeeded)
I think I just have some syntax wrong here. Can anyone see the problem?

Stop using ExactSpelling and then you will link to GetJobA or GetJobW as appropriate.

Try setting EntryPoint to "GetJobA". GetJob is not actually in the winspool export list...

Related

calling c++ char* from c# project System.AccessViolationException

I'm trying to call a c++ library method from a c# project without much success. I always get the same error.
System.AccessViolationException: 'Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.'
The c++ method signature looks like this
int __stdcall getErrorMessage(int errorId, char *&errorMessage);
I have tried every combination so far but nothing seems to work.
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, ref StringBuilder errorMessage);
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, ref IntPtr errorMessage);
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, IntPtr errorMessage);
Any help will be really appreciated.
EDIT
The way Im calling it is as follows
var ptr = new IntPtr();
var ret = NativeMethods.getErrorMessage(number, ref ptr);
Also there is another call to release the memory once it's done
After you got IntPtr you should convert it to string using PtrToStringAuto Or PtrToStringAnsi
It's finally working, the problem happens as Hans Passant mentioned, when we try to query the method with any arbitrary error code.
Working code.
var ptr = new IntPtr();
var ret = NativeMethods.getErrorMessage(code, ref ptr);
if (ptr != IntPtr.Zero)
{
message = Marshal.PtrToStringAnsi(ptr);
NativeMethods.freePointer(ref ptr);
}
Thanks for your help.

C# Pinvoke invalid file handle

I'm having a problem with kernal32 Pinvoke functions as they keeps throwing an INVALID_FILE_HANDLE. The program reads the first sector of the current hard disk. I can't see what is wrong with the following code.
class Program
{
const uint GENERIC_READ = 0x80000000;
const uint FILE_SHARE_READ = 0x00000001;
const uint OPEN_EXISTING = 0x00000003;
const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(string Disk, uint Access, uint ShareMode, IntPtr SecurityAttributes, uint CreationDisposition, uint Flags, IntPtr TemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetFilePointer([In] SafeFileHandle Handle, [In] int DistanceToMove, [Out] out int DistanceToMoveHigh, [In] int MoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
unsafe public static extern int ReadFile(SafeFileHandle Handle, [Out] byte[] Buffer, int NumberOfBytesToRead, out int NumberOfBytesRead, IntPtr Overlapped);
unsafe public static void Main(string[] args)
{
string Drive = #"\\.\C";
int SectorSize = 512;
int Sector = 0;
int BytesRead, DistanceToMoveHigh;
byte[] Buffer = new byte[SectorSize];
SafeFileHandle Handle = CreateFile(Drive, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, IntPtr.Zero);
SetFilePointer(Handle, Sector * SectorSize, out DistanceToMoveHigh, 0);
ReadFile(Handle, Buffer, SectorSize, out BytesRead, IntPtr.Zero);
Console.WriteLine(Marshal.GetLastWin32Error()); // It gives 6 which translates to an INVALID_FILE_HANDLE error
Console.ReadKey();
}
}
Your call to CreateFile fails. Of course you cannot know that because you omitted any error checking. Read the documentation. Errors for all three functions that you call are signaled by the return value. Which you ignore.
Your call to CreateFile returns INVALID_HANDLE_VALUE. You need to test for that. When you encounter that, and only then, call GetLastWin32Error. Likely ERROR_ACCESS_DENIED will be returned then.
Passing FILE_FLAG_DELETE_ON_CLOSE is a mistake. Remove that flag.
I believe that the share flags must be FILE_SHARE_READ | FILE_SHARE_WRITE.
The file name must be #"\\.\C:" with a trailing colon.
And you will need the process to be executed elevated.
You use GetLastWin32Error in a wrong way.
The method that fails here is CreateFile and it returns an INVALID_HANDLE_VALUE (indicating that it failed). To determine what went wrong you have to call GetLastWin32Error directly after CreateFile.
When you call it after trying to read, the error is of course ERROR_INVALID_HANDLE (6) as you passed an invalid handle to ReadFile.
If you call GetLastWin32Error directly after the failing CreateFile you get error 2:
The system cannot find the file specified.
That is because the the drive name misses a :
string Drive = #"\\.\C:"; // <- add colon :
I tried with that drive name, but then got the error 32:
The process cannot access the file because it is being used by another process.
I keep trying to figure out how that can be handled...

How to import void ** C++ API into C#?

In c++ API Declaration is
BOOL DCAMAPI dcam_attachbuffer ( HDCAM h, void** top, _DWORD size );
arguments :
void** top--- is the array of pointer to buffer
_DWORD size-- is size of top parameter in bytes
In c#, this is how I import the dll file:
[DllImport("dcamapi.dll", EntryPoint = "dcam_attachbuffer",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi, BestFitMapping = false,
ThrowOnUnmappableChar = true)]
[return: MarshalAsAttribute(UnmanagedType.Bool)]
public static extern bool dcam_attachbuffer(IntPtr handleCamera,
[MarshalAsAttribute(UnmanagedType.LPArray)]ref Int32[] buf,
[MarshalAsAttribute(UnmanagedType.U4)] Int32 bufsize);
My question is do I convert the type from c++ into c# correctly? and How do I declare void**in c#? please help me.
It is depending on what function dcam_attachbuffer do.
If it's taking buffer, define method
[DllImport("dcamapi.dll", EntryPoint = "dcam_attachbuffer"]
public static extern bool dcam_attachbuffer(
IntPtr handleCamera,
IntPtr ptrsBuf,
Int32 bufSize);
and pass pointer derived earlier.
If function is getting pointer of pointers, define method
[DllImport("dcamapi.dll", EntryPoint = "dcam_attachbuffer"]
public static extern bool dcam_attachbuffer(
IntPtr handleCamera,
ref IntPtr ptrsBuf,
Int32 bufSize);
and use
System.Runtime.InteropServices.Marshal.Copy(
IntPtr source,
IntPtr[] destination,
int startIndex,
int length
)
to copy pointers in IntPtr[]
You can declare pointers directly in C# in unsafe blocks.
There's also IntPtr.
Or you can write some C++/CLI to glue the two together.
The argument is IntPtr[] (no ref). With the unnecessary attributes removed:
[DllImport("dcamapi.dll")]
public static extern bool dcam_attachbuffer(IntPtr handleCamera,
IntPtr[] buf, int bufsize);
Correctly initializing the array could be a challenge as well, it is quite unclear from the question what is required.

A Problem in Pinvoke

I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...

C++ function calling from C# application. Attempted to read or write protected memory

The problem below is ralated to my previous question
Converting static link library to dynamic dll
My first step was to develop a dll, that was done. (Thanks John Knoeller prakash. Your input was very helpful)
Now when i call the function in the dll from my c# application i get the error
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Here is the C++ definition
extern "C" DEMO2_API void Decompress(char* inp_buff, unsigned short*
inp_len, char* buffer_decomp,unsigned *output_len,unsigned short* errorCode);
My C# Converstion p/Involke
private static extern void Decompress(
byte[] inp_buff,
ref ushort inp_len,
byte[] buffer_decomp,
ref int output_len,
ref ushort errorCode
);
And I am calling it as below
byte[] dst = new byte[2048];
int outlen = 2048;
ushort errorCode = 0;
Decompress(src, (ushort )src.Length, dst, ref outlen,ref errorCode);
return dst;
What is wrong?
I see a signature mismatch on the inp_len parameter. In the C++ definition you use a pointer to a short unsigned int, while in the C# method you use a ushort.
for pointers you must use IntPtr .net type
#necrostaz
It is not necessary that we use IntPtr for pointers.
Look below all of these four declarations are valid and currently i am using it.
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, String lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, StringBuilder lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, String lParam);
question is still open
In addition to the missing "ref" on the inp_len declaration that Maurits pointed out, you need to make sure that your pointer sizes match.
If you're running on a 32-bit operating system you should be OK, but if your code runs on 64-bit too, then you need to ensure that either:
You mark your .net entry assembly as x86 (not Any CPU)
or
You supply a 32-bit and 64-bit build of the C++ dll and install the correct one for the interop to call.
I have had the same problem two years ago. In my case the reason for the access violation was that the memory was allocated outside the DLL. As a solution I added two functions for memory allocation and deallocation to the DLL.
Another solution could be a change of the .net security settings. Some keywords are "Code Access Security Police Tool" (caspol.exe) and ".NET Framework Configuration Tool" (mscorcfg.msc). In VS there is also a security tab in the project property dialog. I'm not an expert in .net security so someone else should know more details.
The following code runs without any problems. It's very similar to yours:
C++:
extern "C" __declspec(dllexport) void TestFunction(char* inp_buff,
unsigned short* inp_len,
char* buffer_decomp,
unsigned *output_len,
unsigned short* errorCode)
{
//copy input buffer to output buffer
int size = min(*inp_len,*output_len);
for(int i=0; i<size; i++)
buffer_decomp[i] = inp_buff[i];
errorCode = 0;
}
C#:
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("TEST.DLL")]
public static extern void TestFunction(byte[] inp_buff,
ref ushort inp_len,
byte[] out_buff,
ref int out_len,
ref ushort errorCode);
static void Main(string[] args)
{
//prepare input buffer
byte[] inp_buff = new byte[20];
inp_buff[0] = (byte)'T';
inp_buff[1] = (byte)'E';
inp_buff[2] = (byte)'S';
inp_buff[3] = (byte)'T';
ushort inp_len = (ushort)inp_buff.Length;
//prepare output buffer
byte[] out_buff = new byte[20];
int out_len = out_buff.Length;
ushort errorCode = 0;
TestFunction(inp_buff, ref inp_len, out_buff, ref out_len, ref errorCode);
//see if copying was successful
for(int i=0; i<out_len; i++)
Console.Out.Write(out_buff[i]);
}
}
Try it out. I have taken a look at the open parts of the library you are using. Here is a direct excerpt of the function lzo_decomp:
in = lzo_malloc(IN_LEN);
out = lzo_malloc(OUT_LEN);
wrkmem = lzo_malloc(LZO1Z_999_MEM_COMPRESS);
if (in == NULL || out == NULL || wrkmem == NULL)
{
printf("out of memory\n");
}
in_len = IN_LEN;
lzo_memset(in,0,in_len );
lzo_memset ( out, 0, OUT_LEN );
memcpy ( out, &input_buffer, inp_buff_len);
lzo_free(wrkmem);
lzo_free(out);
lzo_free(in);
r = lzo1z_decompress(out,*inp_len,in,&out_len,NULL );
For serenity: "in" and "out" are not the function arguments for the input and output buffers but temporary pointers. What can you see (beside from bad formatted code)? The only two buffers lzo1z_decompress is called with are "in" and "out". And these two buffers are freed before the call. I'm not surprised that there is an access violation. I only can underline nobugz's advice: Contact the author.
The 4th parameter need to be passed using out mode instead of ref. That solved the problem.

Categories

Resources