Ok, I've searched quite a bit, but seem unable to find an answer or example for how to achieve this.
Basically, I have an app that is designed to be portable (built using VC++ in VS2010, but no MFC or managed components, raw WinAPI). I have Lua 5.2 built into it and allow the user to write scripts inside the application. I have multiple glued functions which are exposed to the Lua scripts which handle various WinAPI calls.
However, what I'd like to be able to do is allow the user to write a script in which looks something like this:
require[[Alien/alien]]
local mb = alien.User32.MessageBoxA
mb:types{ 'long', 'long', 'string', 'string', 'long' }
print(mb(0, "Hello World!", "Test", 64))
I simply cannot seem to find a way to do this. I do not want to require the user to install Lua for Windows and, ideally, there be no core.dll and struct.dll from alien; when I tried to do something with those DLLs in ./alien/, it was crashing in Lua5.1.dll because I had LuaForWindows installed, I uninstalled LFW, and then it just states that Lua5.1.dll is missing. I have Lua 5.2 built into my app, so obviously the core/struct DLLs from the Alien rock are expecting Lua5.1.dll to be in the path.
I made a worthless attempt to try to including the Alien src into the project, but doesn't seem to work that way either.
Any ideas would be greatly appreciated. I'd prefer it all be contained in my app, but I'll settle for a solution which involves including the libraries in my project to build and bundle in the distribution if that's the only alternative.
Thanks!
UPDATE:
Ok, thank you Ben Voigt! I think I'm almost there. I've pulled in core.c and struct.c and made sure all the paths are there for libffi. Everything compiles without issue, until I try to call luaopen_alien_core in core.c (the alien.core src file), claiming the identifier is undeclared. I've tried to declare the function signature in my separate source file that's trying to make the call, the compile gets further, but fails complaining of an unresolved external.
Clearly this is likely now a general C++ issue (as I'm only a novice in this area). Here's the general idea of what I have:
//core.c (from Alien project)
(...)
int luaopen_alien_core(lua_State *L) {
alien_register_library_meta(L);
alien_register_callback_meta(L);
alien_register_function_meta(L);
alien_register_buffer_meta(L);
lua_getglobal(L, "alien");
if(lua_isnil(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "alien");
}
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setfield(L, -3, "core");
alien_register_main(L);
return 1;
}
//mysource.c (the file attempting to call luaopen_alien_core(L))
void initLua()
{
L = luaL_newstate();
luaL_openlibs(L);
luaopen_alien_core(L);
(...)
}
This fails to start compiling, issuing the error:
error C3861: 'luaopen_alien_core': identifier not found
Which makes sense, so I add the following line to myheader.h:
int luaopen_alien_core(lua_State *L);
This compiles, but fails to link with:
error LNK2001: unresolved external symbol "int __cdecl luaopen_alien_core(struct lua_State *)" (?luaopen_alien_core##YAHPEAUlua_State###Z)
I've tried several things I can think of, with my limited experience, but nothing will satisfy this error. I even tried to move the contents of core.c into mysource.c, but that creates a whole different mess and seemed to be the wrong way to go as it is.
I'm hoping, and imagining, this is something really stupid, but I'm just not sure how to get it to call luaopen_alien_core, which seems to be the final piece I need.
Thanks again!
}
I imagine that the require directive both loads a dynamic library and adds its contents to the active Lua engine.
By linking alien directly into your code, you obviate the need for the dynamic library. But the content enumeration code won't have run, and you can't use require to run it, or else it'll go looking for a DLL file (along with all the DLL dependencies).
So, you should find out what functions that require directive calls after loading the DLL, and call those when creating a Lua engine. Then it will neither be necessary nor allowed for the script to start with require [[Alien/alien]], but Alien objects will be available.
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.
Not sure how to word the title so feel free to rename, but the issue I'm having is that I've got a function that works in one project, but fails in another. Below is rough pseudocode to show that one call in LibraryProject works, whereas the call in GameProject doesn't.
In ChildClass::do_stuff, the win32_window HWND is valid, whereas the second one, failed_win32_window is null and glfw throws an error saying it isn't initialized, despite it already having been initialized (since the first glfw call was successful and I've manually stepped through to verify it was):
GLFWError #65537 Happen, The GLFW library is not initialized
Here's pseudocode showing the two projects, and the files. GLFW is set initialized properly since if I do all my glfw logic within LibraryProject, the window shows up as normal.
//LibraryProject
////library_header.h
class ParentClass {
GLFW* _mainWindow; //filled in elsewhere in the real code
HWND getWin32Window() { return glfwGetWin32Window(_mainWindow); }
}
//GameProject
////game_header.h
#include "library_header.h" //from other Project
class ChildClass : public ParentClass {
void do_stuff() {
HWND win32_window = this->getWin32Window(); //this works because it goes down into LibraryProject.dll's module
HWND failed_win32_window = glfwGetWin32Window(_mainWindow); //but literally the same call here doesn't because it happens within GameProject.exe
}
}
////game_body.cpp
void function_called_elsewhere_in_game() {
//called from GameProject.exe
auto child = ChildClass();
child.do_stuff();
}
I'm not sure if this is an issue with glfw and my setup, or just my misunderstanding how projects and dependencies work.
Things I've tried:
Downloading the latest glfw3
Rebuilding the entire solution
Toggling with References and Linking Dependency Inputs
Things to note:
This is happening in the main thread, nothing else is using glfw at the same time. Its 100% reproducible too.
glfw3.lib is always being created in my GameProject output folder, based on the one inside LibraryProject
Stepping through the disassembly for each of the two glfwGetWin32Window calls has different addresses in disassembly, leading me to believe they're two different copies of the same library, but I'm not sure.
This is not an issue with cocos2d, the game engine I'm using as starting a blank project and calling glfwGetWin32Window(..) returns a valid pointer, even in GameProject, so there's something that I'm doing wrong, but I don't know what.
Images showing off the actual behaviour. magnolia_cocos_proj is GameProject and is the exe I'm running, and libcocos2d is LibraryProject I'm using as a DLL (I'm unfamiliar with the details of how linking and dlls work).
win32_window has valid value
definition of getWin32Window() to be 100% sure. Notice the module is in libcocos2d.dll now.
after going over the second line, the error throws and the second window is null
As I understood from "glfw3.lib is always being created" you use static linking. Static linking of a lib to different dll and exe lead to duplicating of all static memory of the lib. You should use a dynamic library for GLFW in the case. It's glfw3dll.lib.
There are two main cases why this error could appear:
GLFWError #65537 Happen, The GLFW library is not initialised
Case One :
The mentioned error occurs if a GLFW function was called that mustn't be called unless the library is initialised. So, you need to initialise GLFW before calling any function that requires initialisation.
Read an API introduction for reference. Use if-else statement for handling glfwInit() and errors.
Reading Moving from GLFW 2 to 3 is also useful.
Case Two :
This error quite often occurs in the case you have previous versions of GLFW installed on your machine. GLFW3 doesn't like running along with previous version installed. So, delete all the GLFW libraries and linkers and reinstall the latest GLFW 3 from scratch.
Hope this helps.
I am new to Lua so I am sorry if this is rather an easy question but it is driving me nuts. In my previous thread A simple query on calling Lua 5.2 from C++ I have used an easy C++ code to embed Lua in it. Things worked well but when I transfer that concept to a more complex project, the same code does not work. I have checked many sources but could not find a solution.
Here is my code:
#include "External/include/lua.hpp"
lua_State *luastate =NULL;
IMPLEMENT_APP(ScienceSuitApp);
bool ScienceSuitApp::OnInit()
{
luastate=luaL_newstate();
luaL_openlibs(luastate);
ScienceSuitFrame* frame = new ScienceSuitFrame(0L);
frame->Show();
return true;
}
Now when I try to compile the code, I am getting invalid arguments error for the luaL_openlibs(luastate) line. The error that the compiler gives:
Invalid arguments '
Candidates are:
void luaL_openlibs(*)
' ScienceSuitApp.cpp /ScienceLab line 33 Semantic Error
This is actually happening whenever I call a Lua function such as luaL_dostring etc.. which takes lua_State as parameter. By the way, I am using Eclipse as IDE and wxWidgets as GUI if that should give a clue. Any my configuration for this set up is:
I "think" I have solved the problem. It stems from the fact that Eclipse throws a "semantic error" and not a "syntax error". Therefore, I followed the advice from Eclipse CDT shows semantic errors, but compilation is ok. But now my question is what does it have to do with Indexing in Eclipse IDT? It feels like I am using Eclipse without knowing any of its internals.
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.
I am using XE3 and trying to construct a DLL with my third party component. Since it is a rather large project I will describe it then detail the question at hand.
I have multiple cpp files and multiple header files(classes in header files, functions in cpp files). I have everything linking and compiling fine UNTIL I put a CreateWnd() function into one of my classes
void __fastcall TICSByteEntry::CreateWnd(void)
{
TCustomControl::CreateWnd();
SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE) | WS_CLIPSIBLINGS);
}
Now it will compile with this code in it but when I put my component on a form and try to run THAT project it will give me an error
'[ilink32 error] Error: Unresolved external '__fastcall TICSByteEntry::CreateWnd() referenced from 'path'unit1.obj'
No other linking issues just that one and as soon as I comment it out everything works nicely as expected. When I was researching this online someone said it is having problems finding the entry point http://edn.embarcadero.com/article/27343. I tried what was recommended and no luck. Any one want to take a guess on what is wrong?
Instead of overriding CreateWnd() you should override CreateParams(). That way the window is created with the style you want and does not need to be changed after creation.