[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 660
  • Last Modified:

Function that has a DllImport Custom Attribute cannot return an instance of a class!?!

Hello Experts,

I am trying to call GetLargestConsoleWindowSize from a Managed C++ class. I have created the prototype in my managed code like this:

[DllImport("KERNEL32.DLL", EntryPoint="GetLargestConsoleWindowSize")]
static COORD GetLargestConsoleWindowSize(int hConsoleOutput);

COORD is a structure declared like this

[StructLayout(LayoutKind::Sequential)]
private __value struct COORD
{
   short x;
   short y;
};

which mimics the API structure used by the API's GetLargestConsoleWindowSize function.

The problem is that when I try to compile I get an error C3385 that tells me that "a function that has a DllImport Custom Attribute cannot return an instance of a class". I know for sure that the C# compiler doesn't have this limitation. I tried to find any references to this error on the net, but couldn't find any.
Did anyone else bumped into it?

Regards,
Eddie
0
Mensana
Asked:
Mensana
1 Solution
 
Svetlin_PanayotovCommented:
Yes - I had similar problems.I don't know if that's the best way around - but you can probably do something like:

static IntPtr* GetLargestConsoleWindowSize(int hConsoleOutput);

and then
Marshal::PtrToStructure(pResult,COORD);

Svetlin
0
 
MensanaAuthor Commented:
Hello Svetlin, AlexFM,

Both your postings point to the same way of doing interop: IJW. It's an untersting approach and I will split the points between you. However, before I do that I need you to help me make this work.

The "GetLargestConsoleWindowSize" API function happens to return a COORD object by value (COORD has two shorts, so it's really 4 bytes, just like its address).
Therefore I wrote my wrapper like this:

static COORD MyOtherGetLargestConsoleWindowSize( int nWindowHandle )
{
   COORD objWindowSize;

   IntPtr pCoord = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

   Marshal::PtrToStructure( pCoord, __box(objWindowSize) );

   return objWindowSize;
};

This compiles fine except I cannot include the "windows.h" header. My project is a Visual C++.NET class library (generates a managed C++ .dll) and when I include the header, ahead of all my "using namespace..." directives (as AlexFM's suggested article tells you to do) I get a bunch of errors that are somewhere else. I comment out the “#include” directive and the call to GetLargestConsoleWindowSize and everything compiles.
I can't help but feel that I am doing something wrong. IJW it is suppose to be simple, right?
The example in Nish’s article is not very good because it deals with a passed in parameter (string) and not a returned value. With returned values things are different because they are allocated in the called function and not by the caller. Especially when a structure is returned by value and not by reference.

Just for the sake of this discussion, here is my workaround:
I wrote my own unmanaged .dll and I wrapped the call like this:

#include "windows.h"

extern "C" UNMANAGEDDLL_API BOOL MyGetLargestConsoleWindowSize( HANDLE hConsoleOutput, COORD *pobjCoordinates );
{
   // return value
   BOOL bReturn = TRUE;

   // check pointer to be initialized
   if( pobjCoordinates != NULL )
   {
      // pointer is initialized => get the largest console window size
      // by calling the real API function
      *pobjCoordinates = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

      // check if structure was initialized
      if( (pobjCoordinates->X == 0) || (pobjCoordinates->Y == 0) )
         // structure not initialized => signal error
         bReturn = FALSE;
   }
   else
   {
      // the pointer is not initialized => signal error
      bReturn = FALSE;
   }

   // return
   return bReturn;
};

Now I can use P/Invoke because MyGetLargestConsoleWindowSize doesn’t return a class or structure anymore. The downside is that when calling it two .dlls must be loaded: my unmanaged dll followed by kernel32.dll that hosts the real GetLargestConsoleWindowSize API function.

0
Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

 
AlexFMCommented:
What compilation errors do you have using IJW? I created new managed C++ Clicc Library project and added windows.h to it:

// test1.h

#pragma once

#include <windows.h>

using namespace System;

namespace test1
{
    public __gc class Class1
    {
        // TODO: Add your methods for this class here.
    };
}

It compiles successfully.
IJW is not so simple because of possible name conflicts. We need to see your code and error messages.
0
 
MensanaAuthor Commented:
I figured it out. I had lots of name collisions not reported by the compiler. Since the initial path was P/Invoke I had lots of declarations such as:

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleScreenBufferSize")]
static int SetConsoleScreenBufferSize(int hConsoleOutput, COORD dwWindowSize );

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleWindowInfo")]
static int SetConsoleWindowInfo(int hConsoleOutput, int bAbsolute, const SMALL_RECT *srWindowSize );

Linking the "windows.h" header, functions with similar names were imported, hence the collision. Note to self: NEVER MIX P/INVOKE WITH IJW!

I just had to rename all my imports and everything compiled just fine.

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleScreenBufferSize")]
static int MySetConsoleScreenBufferSize(int hConsoleOutput, MyCOORD dwWindowSize );

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleWindowInfo")]
static int MySetConsoleWindowInfo(int hConsoleOutput, int bAbsolute, const MySMALL_RECT *srWindowSize );

In the end I had to modify the code and use API's GetLargestConsoleWindowSize directly:

COORD objScreenBufferSize = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

I guess that the Marshal class really comes in handy when you deal with dynamically allocated objects. With this feature I can say that Managed C++ is better than C# for those who are trying to port legacy code under .NET.

Thanks,
Eddie
0
 
AlexFMCommented:
>> NEVER MIX P/INVOKE WITH IJW!
Right, managed C++ programmer doesn't need PInvoke.
0
 
MensanaAuthor Commented:
Please close this question and return points in my pool. Points were awarded.
0
 
AlexFMCommented:
You can split points here instead of opening new questions.
If you want to refund your points ask Community Support.
0
 
MensanaAuthor Commented:
Oh, I didn't know this - probabely a new feature I wasn't aware of. Well, next time I will try it. Since Svetlin "cashed" his points, you are welcome to get yours and I hope this question will be closed by the guys from Support.
0
 
AlexFMCommented:
Ask community support to close this question. You need to post request here:
http://www.experts-exchange.com/Community_Support/

I add note to "Points for" question.
0
 
moduloCommented:
Closed, 200 points refunded.

modulo
Community Support Moderator
Experts Exchange
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now