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
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 can compile the solution with no errors, but when I'll try to run it, I get a crash window:
An unhandled exception of type
'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll
Additional information: Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "<myformname>.resources" was corerctly embedded or linked into assembly "<myprojectname>" at compile time, or that all the satellite assemblies required are loaded and fully signed.
And after I press Break it throws me to the line:
this->Icon = (cli::safe_cast<System::Drawing::Icon^ >(resources->GetObject(L"$this.Icon")));
If I comment this line out, everything works just fine, but my program doesn't have icon.
Anyone else had this problem? Found a solution? I couldn't find anything clear enough for me to understand, problem is really annoying me, only solution I found was to declare my form class before any other classes, but I don't even have any other classes in my solution?
I also have only one project in this solution, ms support said something about having multiple projects, which I don't have, so it was no use either.
Take a look here :
http://www.mztools.com/articles/2005/MZ2005007.aspx
The exception is thrown because your icon cannot be located. You will probably need to compiles your resources under one .dll and put this under en-US subfolder on your project output. It did the trick for me at least. There are probably other solutions to your problem too.
Do not panic like I did. The root cause of the problem is that the compiled resource file is different from the one that is asked to load at runtime. This happens because the underlying build-script cannot detect the filename or namespace changes made after the form is created.
For example, At first we started a project named x . And our $(RootNamespace) becomes x. And we created a form named y. So our XML resource file y.resx gets compiled into x.y.resource . At this point the icon change works.
Now somehow we changed the project name or the namespace to z. But our $(RootNamespace) remains the x. While at compile-time it wrongly generates old x.y.resource, but at links-time it links z.y.resource. And at this point the icon change does not work.
It can also happen if the form is under some nested namespace which is not known in the project file.
It can be fixed by changing the compilation output of the y.resx file . It can be done by right-clicking the resource and changing the Resource Logical Name to $(RootNamespace).%(Filename).resources .
I will also make sure that ProjectName,AssemblyName and RootNamespace are the same in the .vcxproj file. Somehow if the form is declared under a nested namespace like RootNamespace.gui , then the output file of the resource should be $(RootNamespace).gui.%(Filename).resources .
I would like to know the cleanest way of registering a file extension with my C++ application so that when a data file associated with my program is double clicked, the application is opened and the filename is passed as a parameter to the application.
Currently, I do this through my wix installer, but there are some instances where the application will not be installed on ths user's computer, so I also need the option of creating the registry key through the application.
Additionally, will this also mean that if the application is removed, unused entries in the registry will be left lying around?
Your basic overview of the process is found in this MSDN article. The key parts are at the bottom of the list:
Register the ProgID
A ProgID (essentially, the file type registry key) is what contains your important file type properties, such as icon, description, and context menu items including applications used when the file is double clicked. Many extensions may have the same file type. That mapping is done in the next step:
Register the file name extension for the file type
Here, you set a registry value for your extension, setting that extension's file type to the ProgID you created in the previous step.
The minimum amount of work required to get a file to open with your application is setting/creating two registry keys. In this example .reg file, I create a file type (blergcorp.blergapp.v1) and associate a file extension (.blerg) with it.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command]
#="c:\path\to\app.exe \"%1\""
[HKEY_CURRENT_USER\Software\Classes\.blerg]
#="blergcorp.blergapp.v1"
Now, you probably want to accomplish this programmatically. To be absolutely kosher, you could check for the existence of these keys, and change your program behavior accordingly, especially if you're assuming control of some common file extension. However, the goal can be accomplished by setting those two keys using the SetValue function.
I'm not positive of the exact C++ syntax, but in C# the syntax looks something like this:
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command", null, #"c:\path\to\app.exe \"%1\"");
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\.blerg", null, "blergcorp.blergapp.v1");
Of course you could manually open each sub key, manually create the ProgID and extension subkey, and then set the key value, but a nice thing about the SetValue function is that if the keys or values don't exist, they will automatically be created. Very handy.
Now, a quick word about which hive to use. Many file association examples online, including ones on MSDN, show these keys being set in HKEY_CLASSES_ROOT. I don't recommend doing this. That hive is a merged, virtual view of HKEY_LOCAL_MACHINE\Software\Classes (the system defaults) and HKEY_CURRENT_USER\Software\Classes (the per-user settings), and writes to any subkey in the hive are redirected to the same key in HKEY_LOCAL_MACHINE\Software\Classes. Now, there's no direct problem doing this, but you may run into this issue: If you write to HKCR (redirected to HKLM), and the user has specified the same keys with different values in HKCU, the HKCU values will take precedence. Therefore, your writes will succeed but you won't see any change, because HKEY_CURRENT_USER settings take precedence over HKEY_LOCAL_MACHINE settings.
Therefore, you should take this into consideration when designing your application. Now, on the flip side, you can write to only HKEY_CURRENT_USER, as my examples here show. However, that file association setting will only be loaded for the current user, and if your application has been installed for all users, your application won't launch when that other user opens the file in Windows.
That should be a decent primer for what you want to do. For further reading I suggest
Best Practices for File Association
File Types and File Association, especially
How File Associations Work
And see also my similar answer to a similar question:
Associating file extensions with a program
This is a two step process:
1. Define a program that would take care of extension: (unless you want to use existing one)
1.1 create a key in "HKCU\\Software\\Classes\\" for example
"Software\\Classes\\YourProgramName.file.ext"
1.2 create subkey "Software\\Classes\\YourProgramName.file.ext\\DefaultIcon"
1.2.1 set default value ("") to your application full path to get
icon from resources
1.3 create a subkey "Software\\Classes\\YourProgramName.file.ext\\Shell\\OperationName\\Command"
OperationName = for example Open, Print or Other
1.3.1 set default value ("") to your application full path +optional runtime params (filename)
2.Associate file extension with program.
2.1 create a key HKCU\\Software\\Classes\\.ext - here goes your extension
2.2 set default value to the program definition key
("YourProgramName.file.ext")
Below is part of the program written in c# which associate file extension. It is not c++ but i think it is simple enought to explain itself and AFAIK it is verv simmilar if not identical to the code in c++
1.
RegistryKey keyPFCTExt0 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc", true);
if (keyPFCTExt0 == null)
{
keyPFCTExt0 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc");
keyPFCTExt0.CreateSubKey("DefaultIcon");
RegistryKey keyPFCTExt0ext = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\DefaultIcon", true);
keyPFCTExt0ext.SetValue("", Application.ExecutablePath +",0");
keyPFCTExt0ext.Close();
keyPFCTExt0.CreateSubKey("Shell\\PFCT_Decrypt\\Command");
}
keyPFCTExt0.SetValue("", "PFCT.file.enc");
keyPFCTExt0.Close();
2.
RegistryKey keyPFCTExt1 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command", true);
if (keyPFCTExt1 == null)
keyPFCTExt1 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command");
keyPFCTExt1.SetValue("", Application.ExecutablePath + " !d %1"); //!d %1 are optional params, here !d string and full file path
keyPFCTExt1.Close();
I don't know why people keep saying that HKEY_CURRENT_USER\Software\Classes\<.ext>'s Default value (which will redirect you into another (software-created) class.
It does work, but it will be overridden by
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\UserChoice
And I believe Microsoft recommends the second practice- because it's what the built-in "open with" is doing. The value of Progid" key is equal to default value of HKEY_CURRENT_USER\Software\Classes\<.ext> in this case.
I found the following while trying to manipulate associations using C#:
hkcu\software\microsoft\windows\currentVersion\explorer\fileexts.reg\userchoice -> for user specific settings. The values in the openWithProgIds
key point to the keys in the hkcr.
hkcr\xfile\shell\open\muiVerb value or hkcr\xfile\shell\open\command\default value -> affects open handler. This is the value that contains the path to a program.
hkcr\ .x -> affects context menu (new x) among other things related to the menus.
I don't know the C++ code, but given these info you must be able to manipulate the registry using the registry API.