I'm attempting to follow this tutorial on the MSDN to load an image file from a resource. I've a feeling some of the code provided is guff, but I can't figure out how to make it work. The call to FindResource() keeps failing with error code 1813.
I've added a .rc resource file to my C++ project (I'm using Visual Studio 2013 as my IDE), and added a png file with the ID IDB_PNG1:
The tutorial defines the resource as IDR_SAMPLE_IMAGE IMAGE "turtle.jpg", and then calls FindResource() as
FindResource(
NULL, // This component.
L"SampleImage", // Resource name.
L"Image"); // Resource type.
I've a feeling that L"SampleImage" is supposed to be L"IDR_SAMPLE_IMAGE" and L"Image" is supposed to be L"IMAGE", since the provided values don't seem to exist anywhere, but my equivalent call doesn't work:
FindResource(
NULL, // This component
"IDB_PNG1", // Resource name
"PNG", // Resource type
);
What am I doing wrong?
I don't know if it's related, but whenever I use L"string" in my code, I get an error (Argument of type "const wchar_t *" is incompatible with parameter of type "LPCSTR"), so I've been omitting the L which seems to have worked for every other example I've followed, so I don't think that's the issue here.
The sample is a little muddled with that "SampleImage" name. The confusing part is that Win32 resources may be identified with either strings or (16-bit) integers. The sample leads you to use strings (e.g. L"SampleImage"), but the Visual Studio IDE (and, frankly, most code I've come across) prefers integers. To allow both kinds, the Win32 resource functions take parameters of type LPCWSTR and callers are supposed to use a macro, MAKEINTRESOURCE, to convert an integer ID into a "pseudo string". The same applies for resource types. There are some built-in types (ICON, CURSOR, BITMAP, et al), but you can always define your own types using strings.
If you look around in your code, you should be able to find a header file (Resource.h, probably) with the definition for IDB_PNG1. It's most likely a small integer, so you need to use the MAKEINTRESOURCE macro. PNG was probably not defined anywhere, and it's not one of the built-in resource types, so the resource compiler treated it as a string and so should you.
e.g.
FindResource(
NULL, // This component
MAKEINTRESOURCE(IDB_PNG1), // Resource name
L"PNG", // Resource type
);
Try that and let us know if it works.
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.
Does anyone have experience with using resource for styles. I'm working on a program for which we created a custom Style. We saved it as .style and as .vsf. Because we don't want the user to see/change the style of the programm we want to include it in our resource file (.res) This is done as explained in next Link: Customizing and Creating VCL Styles Afterwards the created file (Tested with .style and .vsf) is placed in the Resourcefile as RC Data.
Thats the preparation, now what didn't work. (tWinMain)
TStyleManager::SetStyle(TStyleManager::LoadFromResource((unsigned int)HInstance, "StyleName", RT_RCDATA));
This also doesn't work:
TStyleManager::LoadFromResource((unsigned int)HInstance, "StyleName", RT_RCDATA);
TStyleManager::SetStyle("StyleName");
also not working
TStyleManager_TStyleServicesHandle MyStyle;
MyStyle = TStyleManager::LoadFromResource((unsigned int)HInstance, "StyleName", RT_RCDATA);
TStyleManager::SetStyle(MyStyle);
All three methodes resulting in the error message: Invalid Style-handle
Loading the same style from a file works:
TStyleManager::LoadFromFile(stylePath + "StyleName.vsf");
TStyleManager::SetStyle("StyleName");
I had the same problem in Delphi (DX10.3) and the following worked for me
Basically the same call of "TStyleManager::LoadFromResource", but without the specification of the optional parameter "RT_RCDATA".
MyStyle = TStyleManager::LoadFromResource((unsigned int)HInstance, "StyleName");
TStyleManager::SetStyle(MyStyle);
But then the resource type "VCLSTYLE" is necessary to load the style correctly. When adding the resource in the IDE, with [Project] > [Resources and Pics...] you can only specify RCDATA in the dialog, which is bad. But you can enter the resource type directly manual with the keyboard as "VCLSTYLE". The IDE remembers this setting and now the resource is available as the correct type. As said before, it works with Delphi 10.3, with the Builder it depends on one try.
Resource-type manual input in IDE dialog
Best regards, Matthias
I want to access my resource as a char* or vector or a FILE. I tried the below and I got null. I don't understand why. The first parameter is optional the last I took from the documentation page. RT_RCDATA seems to be what I want. IDK why I am getting null
HRSRC rc = FindResourceEx(0, RT_RCDATA, MAKEINTRESOURCE(IDR_MyResource), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
I get the error 1813. I have a single executable with no dlls. The resource is in the executable
Answering (I hope) this question and your prior question here, This answer applies to FindResource, though you can adapt to FindResourceEx
The parameters to FindResource are module-to-search, id, and type.
The first is the instance handle who'd resource table to search. You can use NULL for the running process, Otherwise this is in a DLL you need to save off your instance handle from DllMain, usually in a global ghInst, and use that for your search target.
The second should be your resource id. If you're using macro ids such as from a resource.h header, the id must be wrapped with MAKEINTRESOURCE(id). Otherwise its the same string (as a string) you used for your resource id.
The third is the resource type. When you declared your custom resource you gave it a type in your resource script (something like MYRES; Ex: I use XML for xml files, for example). The type parameter is that value as a string.
Therefore, finding the custom resource of id MY_ID (taken from an included resource.h id file) of type MYDATA in the current processes resource table would be:
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(MY_ID), _T("MYDATA"));
Similarly, loading the same resource from a DLL resource table, assuming you saved the instance handle of the DLL to some global ghInst in the PROCESS_ATTACH of your DllMain, is:
HRSRC hRes = FindResource(ghInst, MAKEINTRESOURCE(MY_ID), _T("MYDATA"));
Missing ID
It is not uncommon to "forget" to properly declare the identifier used for the resource in a .h file that is included in the resource script and C/C++ code, but the resource script will happily still compile. If the following is in your resource script
MY_ID MYDATA "filename.bin"
and MY_ID is not defined via macro as a numeric id and included in your resource script, this will "name" a resource called "MY_ID" (note its a string) in the output resource table. Worse, if the id-defining-macro is properly included in the C/C++ code trying to load this thing, then this:
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(MY_ID), _T("MYDATA"));
will compile, since the C/C++ code has MY_ID properly available, but the resulting id is not the same as the one used in the resource file, which was a string name. Thus the load will fail. A sure sign this happened is if this fails:
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(MY_ID), _T("MYDATA"));
but this works:
HRSRC hRes = FindResource(NULL, _T("MY_ID"), _T("MYDATA"));
If you find this is the case, make sure your .rc file and your C/C++ code are using the same macro for your resource identifier. It can't just be the same "name". It has to be the same macro. If the macro is not available to the resource compiler it will use the name as a string, which is usually not what you want.
Best of luck.
2 things:
MAKEINTRESOURCE(RT_RCDATA) should be RT_RCDATA according to the docs.
The first parameter is the handle to the binary (DLL/exe) holding the resource, NULL implies the executable.
I've scoured the web and stackoverflow for this answer but can't find anything. I have written a com object in C++ (for the fist time) that works when used in vbscript and through cocreateinstance in an executable file. So I decided to see if it would work in Excel VBA.
So I went into "References" and located my object there. Checked the box and started coding away. The following is the VBA code.
Function doCos(x As Double) As Double
Dim t As SimpleLib.IMath
Set t = New SimpleLib.IMath ' <- "Invalid use of New keyword" error here
doCos = t.Cos(x)
End Function
Intellisense recognizes my object in the Dim statement, but it does not appear when I use a Set statement. Obviously I am using a registered type library or else intellisense wouldn't work at all. Again, the com object can be used in vbscript or an executable, but for some reason can't be used, at least with the new keyword, in VBA.
Does anyone have an idea what may be wrong, or what may have to be added to the com object? Thanks.
One approach is to define a coclass in the IDL that includes the interface needed (IMath in my case). NOTE: That the [default] interface is hidden by default. So I simply defined interface IUnknown as the default. After compiling with MIDL a type library is generated which one should register with regtlibv12.exe.
I then included an additional IF statement in DllGetClassObject like if (rclsid == CLSID_Math) where CLSID_Math is corresponds to the CLSID defined in the file automatically generated from MIDL. All I did was copy and paste the body of the IF statement from if ( rclsid == IID_IMath ), updated the DLLRegisterServer and DLLUnRegisterServer functions, recompiled the project, and regsvr32.exe.
So the following works now.
Function docos(x As Double) As Double
Dim a As SimpleLib.IMath
Set a = New SimpleLib.Math
docos = a.Cos(x)
End Function
Thanks to Hans for the tip about the coclass. Learned something new and useful.
Can anyone provide an explanation or a link that explains how exactly a STRINGTABLE in a MFC resource file is defined and can be manually expanded?
I tried to do it, but failed to do so in a multi project solution. Most projects in said solution have their own resource files and renamed resource.h-files.
When the application tries to access the string resources, the error message 'Resource string for '22392' not found' shows up. 22392 is the ID of the string I tried to create.
I don't get a similar error message if I use an already defined string ID instead.
Using the Visual Studio 2010's wizard to add a string resource didn't work either.
But it shows up correctly in the listing of resource symbols and in the string table editor.
Needless to say that I haven't participated in the creation of this solution.
Thanks for your help.
[EDIT1]
I excluded the possibility of conflict by performing a ‘find in files’ for the value used and using other values as well: 22390, 22391, 22393, 22394, 22395. Always got the same result.
[EDIT2]
I repeated the steps I did in the complex solution in a new, clean and simple MFC application with one project and it worked without problems. Therefore I assume my problem is related to the fact that the solution has multiple projects and resources.
The steps were the following:
Pick a free number in the resource.h (which is named differently in my case) and add a #define IDS_XXX free number.
Validate the chosen number by performing a ‘find in files’ with it.
Add a line to a STRINGTABLE in resource.h, preferably close to a IDS_ with a value close to the one I picked.
STRINGTABLE
BEGIN
IDS_OTHER "I have a number close to XXX"
IDS_XXX "HelloHello"
END
Access the string in the application:
CString strMyString;
strMyString.LoadString(IDS_XXX);
AfxMessageBox(strMyString, MB_YESNO | MB_ICONEXCLAMATION);
[EDIT3]
I tried to locate the call of LoadString that causes the error message.
The LoadString that fails to load my string resource is located in a class, that is in the same project as the resource file (.rc) containing said string resource. The error message 'Resource string for '22392' not found' is generated there. That explains at least why I found nothing googeling it.
[EDIT4]
I could isolate the cause further.
In cstringt.h hInst is NULL aka the string ressource can't be found:
_Check_return_ BOOL LoadString(_In_ UINT nID)
{
HINSTANCE hInst = StringTraits::FindStringResourceInstance( nID );
if( hInst == NULL )
{
return( FALSE ); // goes here, but shouldn't, hInst == NULL
}
return( LoadString( hInst, nID ) );
}
This is strange since it is possible to access another string ressource within the same resource file just fine.
The "Resource string for '22392' not found" error sounds like Windows cannot find that specific string in the string table although this conflicts with your statement "but it shows up correctly in the listing of resource symbols and in the string table editor". A few things I would do or check to narrow down the issue:
Clean/rebuild the entire project and or solution. I've seen too much strange behaviour from VS just due to bad or out of date builds that this is usually the first thing I try.
Edit the RC file in a text editor: right-click on the RC file and "View Code" in VS2010. Confirm that string 22392 is actually present and valid. Check the entries before and after it as well.
Delete the string from the string table and resource.h. Re-create the string from the resource editor.
You mention "renamed resource.h files". I'm not entirely sure what you mean but make sure these are used properly in the RC file: they should be included at the top when looking at it in a text editor.
Make sure the defined name for 22392 is not redefined somewhere else in the project.
If the string is present in the string table and you still get the "not found" error then something else is going on.
The definitions for the ID's are present in Resource.h. Probably someone might have added a string and deleted the entry in Resource.h thats why it is showing you the error message. Open the resource.h and add #define IDS_XXX 22392