So...I have a kernel mode component and a user mode component I'm putting together using the turnkey build environment of the NT DDK 7.1.0. The kernel component is all .c/.h/.rc files. The user mode component is .cpp/.c/.h/.rc files.
At first it seemed simplest to use build for both, as I saw you could modify the ./sources file of the user mode component to say something like:
TARGETNAME = MyUserModeComponent
TARGETTYPE = PROGRAM
UMTYPE = windows
UMENTRY = winmain
USE_MSVCRT = 1
That didn't seem to cause a problem and so I was pleased, until I tried to #include <string> (or <memory>, or whatever) Doesn't find that stuff:
error C1083: Cannot open include file: 'string': No such file or directory
Still, it's compiling the user mode piece with C++ language semantics. But how do I get the standard includes to work?
I don't technically need to use the DDK build tool for the user mode piece. I could make a visual studio solution. I'm a bit wary as I have bumped into other annoyances, like the fact that the DDK uses __stdcall instead of __cdecl by default... and there isn't any pragma or compiler switch to override this. You literally have to go into each declaration you care about and change it, assuming you have source to do so. :-/
I'm starting to wonder if this is just a fractal descent into "just because you CAN doesn't mean you SHOULD build user mode apps with the DDK. Here be dragons." So my question isn't just about this particular technical hurdle, but rather if I should abandon the idea of building a C++ user mode component with the DDK tools...just because the kernel component is pure C.
To build a user mode program with WINDDK you need to add some variables to your SOURCES file:
386_STDCALL=0 to use cdecl calling convention by default
USE_STL=1 to use STL
USE_NATIVE_EH=1 to add a support for exception handling
Everything else you already have.
I'll put my full SOURCES file for reference:
TARGETNAME = MyUserModeComponent
TARGETTYPE = PROGRAM
TARGETPATH = obj
UMTYPE = console
UMENTRY = main
USE_MSVCRT = 1
USE_NATIVE_EH=1
USE_STL=1
386_STDCALL=0
SOURCES= main.cpp
And main.cpp:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "bla bla bla!";
cout << s;
return 0;
}
Have fun!
Quick Answer
Abandon the idea of building user-mode components with DDK tools (although I find the concept fascinating :-P)
Your kernel mode component should be built separately from the user mode components as a matter of good practice.
Vague thoughts
Off the top of my head, and this really speaking from limited experience...there are a lot of subtle differences that can creep up if you try to mix the two together.
Using your own example of __cdecl vs __stdcall; You have two different calling conventions. _cdecl is all kernel stuff and all of the C++ methods are wrapped around in WINAPI (_stdcall) passing conventions and __stdcall will clean do auto stack clean up and expect frame pointers inserted all over the place. And if you by accident use compiler options to trigger a __fastcall, it would be a pain to debug.
You can definitely hack something together, but do you really want to keep track of that in your user-space code and build environment? UGH I say.
Unless you have very specific engineering reasons to mix the two environments, (and no a unified build experience is not a valid reason, because you can get that from a batch file called buildall.bat) I say use the separate toolchains.
Related
First of all happy new year to everyone, hope you're doing well!
I'm working on a C++ project in which I need to call a C# DLL I created following the first answer of this post. Once I have the DLL, I need to call it from Qt, so by using dumpcpp and the .tlb file generated by regasm, I managed to get the .cpp and .h files to use my classes. Just as a reference, the namespace of the classes is Wrapper, and the main class is Device with guid {DD4A4896-C105-4C60-839B-B18C99C8FE15}.
Once I have the generated files to use the DLL, if I try to create a Wrapper:: Device instance on Qt, I get the following error:
QAxBase::setControl: requested control {dd4a4896-c105-4c60-839b-b18c99c8fe15} could not be instantiated
QAxBase::qt_metacall: Object is not initialized, or initialization failed
It doesn't give any more information, so I tried to check if the guid was stored on the system registry (I used the regasm command explained on the previously quoted post, and It said that it was successful, but you never know). Opening Registry editor and searching for the Guid revealed that it's present at: Computer\HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{DD4A4896-C105-4C60-839B-B18C99C8FE15}, which, as far as I know, is the right route for these guids, and it points to the right DLL.
I though It may be due to some kind ActiveQt problem, and as the previously quoted post explained how to use that DLL from VS C++, I decided to give it a try, using this as an another reference. I've finished with this code, which is supposed to create an instance of my Device object
#include <iostream>
#include <atlstr.h>
#import "C:\Users\javie\Documents\Wrapper\Wrapper\bin\x86\Release\netstandard2.0\Wrapper.tlb" named_guids raw_interfaces_only
inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };
int main()
{
try
{
TESTHR(CoInitialize(0));
Wrapper::IDevicePtr devPtr = nullptr;
TESTHR(devPtr.CreateInstance("{DD4A4896-C105-4c60-839B-B18C99C8FE15}"));
}
catch (const _com_error& e)
{
CStringW out;
out.Format(L"Exception occurred. HR = %lx, error = %s", e.Error(), e.ErrorMessage());
MessageBoxW(NULL, out, L"Error", MB_OK);
}
CoUninitialize();// Uninitialize COM
std::cout << "Hello World!\n";
}
However, this doesn't work either, the createInstance method throws an exception of Class not registered and HR=80040154. Again, according to Registry editor, the class is registered, so I don't understand the error. I've also tried with devPtr.CreateInstance("Wrapper.Device"), devPtr.CreateInstance("Wrapper::Device") or `devPtr.CreateInstance("Wrapper::CLSID_Device") as the links I posted suggest, but in those cases I get another exception with HR=800401f3 and message Invalid class string.
It doesn't matter whether VS or Qt Creator are opened as administrator or not, I get the exact same error.
I have run out of ideas, and I really need to be able to use that DLL from Qt using the files generated by dumpcpp.
Does any one know what could be happening? It feels quite strange to me.
If your C++ application is 64-bit, that's the answer right there, because your C# component is 32-bit (or MSIL but registered to the 32-bit hive). In situations like these, a simple test using VBScript is always useful.
Write a simple VB Script (test.vbs)
Dim obj
Set obj = CreateObject("Wrapper.Device") ' or whatever your ProgID is
MsgBox TypeName(obj)
Now, run this macro 2 ways: with 32-bit and 64-bit versions of VBScript:
32-bit > c:\windows\SysWow64\cscript.exe test.vbs
64-bit > c:\windows\system32\cscript.exe test.vbs
This is assuming your C# component is dispatch compatible. If it's not, then it will still give you differing results that you can use to debug.
Assuming automation/IDispatch compatible, one will work and one won't if you have registered your component correctly.
Have you registered correctly? When I use regasm, I always use the the switches /tlb /codebase when registering the C# component for COM.
Ok, in case someone find the same error, I'll explain the solution I found.
The problem was that in my case, the C# class I developed depended on another 32 bits dll which was not registered on my PC. Once I registered the other dll, everything worked fine.
I don't know why VS kept telling me that the class was not registered when my class itselft was registered, it was one of its dependencies that wasn't registered.
Anyway, I discovered this thanks to Joseph's comments, so thanks a lot for your help.
Is it possible to specify a C/C++ include path to a remote preprocessor server?
The point here is to have once central location for header files. This makes upgrades, version consistency, and a host of other things much better than people running all willy-nilly including different versions of things.
Minimal, Complete, and Verifiable Example
Typical include. On Linux, would default to /usr/include/ or the like; in Windows VS, to a location specified in the $(IncludePath) variable.
#include <iostream>
int main() {
std::cout << "hello, world" << std::endl;
return 0;
}
Now imagine that we set our include path as follows:
C_INCLUDE_PATH=192.0.2.17://usr/include;/usr/include;
The above would first check the remote server at 192.0.2.17 to see if the iostream library existed. Failing this, /usr/include would be checked.
This is a bit of a stretch to illustrate the point:
#include <192.0.2.17://iostream>
int main() {
std::cout << "hello, world" << std::endl;
}
Thanks, Keith :^)
Since you want version control anyway you could just use git (like thousands of other projects). So each user has a local clone of anything needed.
To answer the original question: No. I'm not aware of any preprocessor supporting such an include scheme.
I'm not aware of any compiler that retrieves include files or libraries remotely, so this is not something you can do directly.
The best you can do is have these dependencies on an NFS share that you can mount and then add that path to your include path.
I wouldn't put references to this in the code like that, and as dbush said, you'd have to enhance the preprocessor.
But there might be cute ways to do this within the Make system. That is, if you're using Make (for instance), you could add steps to the Makefile that force a refresh of data.
However, I would suggest this is WRONG because it's not just the include files that need to be fresh. If an include has changed, the related code has probably also changed, and you would need those changes, too. Your magic #include stuff isn't going to do a thing to make sure people have the right code / libraries that the includes are for.
I'm not sure why proper use of source code repositories don't already handle this for you.
I am currently using TGUI (a GUI library based on SFML) together with ECL (so I can use Lisp for config and scripts). It works fine on Linux/gcc, but on Windows/MinGW ECL throws an error:
Condition of type: FLOATING-POINT-INVALID-OPERATION
#<a FLOATING-POINT-INVALID-OPERATION>
Available restarts:
1. (CONTINUE) Ignore signal
Top level in: #<process TOP-LEVEL>.
>
The point in code at which the error is thrown (minimal code that creates it) is:
empty_panel = tgui::Panel::Ptr(*cur_gui, "empty_panel");
empty_panel->setSize(cfg.res.x, cfg.res.y);
empty_panel->setBackgroundColor(sf::Color::Transparent);
tgui::EditBox::Ptr txt_test(*empty_panel, "txt_test");
Note that I can create Labels, Buttons, Checkboxes, etc. just fine - but once I try to make an Edit Box, ECL complains.
This does not make any sense to me whatsoever. TGUI and ECL are completely unrelated libraries, not sharing functions or depending on each other in any way that I know of. Does anyone have an idea what might cause this behavior?
EDIT: Just did a complete recompile of SFML, TGUI and ECL with the most recent version of MinGW, and it still breaks in exactly the same way. So I've decided that enough is enough and I'll rewrite my project to use SFGUI instead.
From now on, this question is of academic interest only.
I finally found out what might have happened there:
While ECL is running, it tries to handle all uncaught exceptions, segfaults, etc that happen on it's watch, even if they never interact with LISP. And the way it handles them is by immediately jumping to the REPL when they occur, to allow for manual debugging.
Which is the sane thing to do for anything that happens because of errors in a LISP program or C/C++ functions that interact with it, but is mildly confusing when the error happens in unrelated C++ code (like the presumed bug in TGUI).
Minimal way to reproduce: Load ECL and try some invalid operation, like so:
#include <ecl/ecl.h>
int main(int argc, char **argv) {
cl_boot(argc, argv);
atexit(cl_shutdown);
int x = 1/0;
return EXIT_SUCCESS; }
The Situation
I am writing a wrapper library for GPIB communications for setting up specific instruments according to a clients specifications to automate some of their processes. I have to use C++ and the old '98 compiler in VC++ 6.0 running on a Windows NT machine to maintain compatibility with some other devices they use regularly.
I am trying to make a class that combines some GPIB commands into easier to remember functions, while also keeping the capability of directly communicating with the instruments. To that end, I have compiled different parts of my project into different libs and dlls, each dll being a different device that they might want to communicate with. I also made a generic dll base class from which all the specific instrument classes inherit, hopefully making the whole setup as modular as possible.
The Problem
So, with all that said, I have an issue when I try to test the different dlls/modules. I created a project to test the generic base class, added the .lib file to the project, which links to the .dll, and also #included the header file for that dll. testGeneric.cpp looks like this:
#include "GENERIC.h"
void main(void) {
GPIBInstrument hp(2); //connects to device at primary address #2
hp.write("*IDN?");
}
Super simple. To be clear, I also have the GENERIC.lib linked in the "Resource Files" folder in VC++ 6.0, and I have GENERIC.dll accessible from the path variable.
Moving on, GENERIC.h looks like this (select parts):
#ifndef GENERIC_H
#define GENERIC_H
#include <string>
#include <windows.h>
#include "decl-32.h"
#ifdef GENERIC_EXPORT
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API __declspec(dllimport)
#endif
...(Inline exception classes)...
class GENERIC_API GPIBInstrument {
...
public:
void write(std::string command);
...
};
#endif
Just showing the relevant methods. Then GENERIC.cpp:
#define GENERIC_EXPORT
#include "GENERIC.h"
...
void GPIBInstrument::write(std::string command) {
ibwrt (handle, &command[0u], command.length());
std::cout << command << std::endl;
if (ibsta & TIMO) {
timeoutError();
}
if (ibsta & ERR) {
error("Unable to write command to instrument: " + command);
}
}
So, looks pretty good right? No issues. Compiles fine. I try running it, and BLAM! I get this: "User breakpoint called from code at 0x77f7645c". So, then I thought, well maybe it would work if I put all the code from GENERIC.h and GENERIC.cpp into one file, and #included that file all as inline code. So I tried it, and it and it compiled nicely, and ran fine.
Question (<-AHA!... But...)
What am I doing wrong!? Something with the way I'm making the .dll? Or the .lib? Or something else entirely?
EDIT (WHY!?)
So, after a bit of debugging, I found that it was something to do with passing a string literal. So I just modified it to:
std::string command = "*IDN?";
hp.write(command);
and it worked fine. My followup question, is why? What's the difference between having a string literal passed, versus assigning that to a variable and then passing it in?
Using complex types such as std::string as a parameter at a DLL boundary is tricky. You must ensure that the exe and the DLL use the exact same instance of the library code. This requires that you build them both to use the same version of the DLL version of the runtime library.
The explanation of the problem is a little long-winded, please bear with me.
I have an unmanaged C++ static library that is used for financial application. It has business day conventions, swap conventions, bond conventions, etc. Most of the conventions rely on static global variables, which are initialized on first use. The library also initializes the holiday calendars on startup by running some queries against a SQL Server database using ODBC.
I have to interact with third-party software using web services. The only way to do this realistically is through C#. That isn't an issue, and I was making good progress. However, I hit a stumbling block when it became necessary to do some date calculations in C#. Since I didn't want to port all my C++ code to C#, I figured the most efficient way to achieve this would be by writing a Managed C++ Class Library DLL that is a wrapper around my unmanaged static library. Everything seems to work fine, I get no compile-time or link-time errors, and I can add the reference to the wrapper and see all the proper object definitions. However, when I try to run my application, it just hangs. I have tried playing around with a bunch of compiler setting for the wrapper DLL, to no avail. If I remove the project dependency on my unmanaged library, everything works fine. I have a strong suspicion that my liberal use of global static variables is causing issues. Is there are way to solve this problem, are at least figure out where the issue is? Example code is below.
Thanks,
Marc.
// FSAManaged.h
#pragma once
using namespace System;
//class XLDate;
namespace FSAManaged {
public ref class Futures
{
public:
static DateTime Expiry(String ^ code, DateTime date);
};
}
The implementation does not even rely on a dependency to the unmanaged static library:
// This is the main DLL file.
#include "stdafx.h"
#include "FSAManaged.h"
namespace FSAManaged
{
DateTime Futures::Expiry(String ^ code, DateTime date) {
return DateTime::Today;
}
}
For completeness' sake, here is AssemblyInfo.cpp:
#include "stdafx.h"
using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute("FSAManaged")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("?????")];
[assembly:AssemblyProductAttribute("FSAManaged")];
[assembly:AssemblyCopyrightAttribute("??????")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly:AssemblyVersionAttribute("1.0.*")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];
[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];
Use the debugger. If you test this from C# then Project + Properties, Debug, tick "Enabled unmanaged code debugging". Setting up the symbol server in Tools + Options, Debugging, Symbols is strongly recommended. Run.
When it hangs use Debug + Break All. Debug + Windows + Threads and double-click the thread that is supposed to be doing the job. Debug + Windows + Call stack to see what is going on. Post the stack trace in your question if you can't figure it out. Anything you see in the Output window and the Visual Studio status bar is relevant too.
Static C++ variables are initialized from DllMain. There are lot's of things you should not do in DllMain; triggering the load of yet another Dll being the most important one. This is easy to break if you call into other peoples libraries in from DllMain.
I suggest you make an Init function on your Dll, which you call after the dll is up and running.